Skip to content

Commit

Permalink
Init
Browse files Browse the repository at this point in the history
  • Loading branch information
uasan committed Jun 3, 2023
0 parents commit ca38177
Show file tree
Hide file tree
Showing 120 changed files with 5,348 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# dependencies
node_modules
package-lock.json


# misc
.DS_Store
.env*
9 changes: 9 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"tabWidth": 2,
"printWidth": 80,
"endOfLine": "auto",
"singleQuote": true,
"arrowParens": "avoid",
"trailingComma": "es5",
"bracketSameLine": true
}
Empty file added bin/dev.js
Empty file.
8 changes: 8 additions & 0 deletions bin/migrate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { presets } from '#env';
import { migrate } from '#db/migrate/run.js';

await migrate({
...presets.migrate,
command: process.argv[2] ?? 'up',
options: process.argv.slice(3),
});
12 changes: 12 additions & 0 deletions bin/prepare.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { copyFileSync } from 'fs';
import { resolve as resolvePath } from 'path';

import { presets } from '#env';
import { CWD, resolve } from '#utils/location.js';

if (!presets.app.isProduction) {
const source = resolve(import.meta, '../src/utils/pre-push');
const target = resolvePath(CWD, '.git/hooks/pre-push');

copyFileSync(source, target);
}
5 changes: 5 additions & 0 deletions bin/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { runAllTestFiles, printAllTestTree } from '#test/run.js';

process.argv[2] === 'status'
? await printAllTestTree()
: await runAllTestFiles();
Empty file added index.d.ts
Empty file.
29 changes: 29 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "@uah/server",
"private": true,
"author": "UAH",
"description": "description",
"version": "1.0.0",
"license": "ISC",
"type": "module",
"typings": "index.d.ts",
"engines": {
"node": ">=20.1.0"
},
"imports": {
"#runtime/*": "./src/runtime/*",
"#compiler/*": "./src/compiler/*"
},
"dependencies": {
"typescript": "next",
"uWebSockets.js": "uNetworking/uWebSockets.js#v20.30.0"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "latest",
"@typescript-eslint/parser": "latest",
"eslint": "latest",
"eslint-config-prettier": "latest",
"eslint-plugin-prettier": "latest",
"prettier": "latest"
}
}
67 changes: 67 additions & 0 deletions src/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import process from 'process';
import { pathToFileURL } from 'url';

export const CWD = process.cwd();
export const CWD_URL = pathToFileURL(CWD).href;

export const LIB_NAME = '@uah/server';

export const SERVER = {
host: 'localhost',
port: 80,
path: '/',
url: 'http://localhost/',
origin: 'http://localhost',

instance: null,
secure: false,

set(config) {
const url = new URL(config.url);

this.url = url.href;
this.host = url.hostname;
this.path = url.pathname;
this.origin = url.origin;

this.secure = url.protocol === 'https:';
this.port = url.port ? +url.port : this.secure ? 443 : 80;

if (config.proxy) {
this.proxy = config.proxy;
}
},
};

export function initConfig({ pathsBasePath, plugins }) {
const config = plugins?.find(({ name }) => name === LIB_NAME);

if (config?.server) {
SERVER.set(config.server);
}

if (config?.bundle) {
BUNDLE.set(config.bundle);
}

LOCATION.root.url = SERVER.path;
LOCATION.root.path = pathsBasePath + '/';

LOCATION.src.url = LOCATION.root.url + BUNDLE.source + '/';
LOCATION.src.path = LOCATION.root.path + LOCATION.src.name + '/';

LOCATION.app.url = LOCATION.src.url + LOCATION.app.name + '/';
LOCATION.app.path = LOCATION.src.path + LOCATION.app.name + '/';

LOCATION.lib.url = LOCATION.src.url + LOCATION.lib.name + '/';
LOCATION.lib.path = LOCATION.src.path + LOCATION.lib.name + '/';

LOCATION.dev.url = LOCATION.src.url + LOCATION.dev.name + '/';

LOCATION.assets.url = LOCATION.src.url + LOCATION.assets.name + '/';
LOCATION.assets.path = LOCATION.src.path + LOCATION.assets.name + '/';

LOCATION.runtime.url = LOCATION.src.url;
LOCATION.runtime.path =
LOCATION.root.path + 'node_modules/' + LIB_NAME + '/src/runtime/';
}
7 changes: 7 additions & 0 deletions src/runtime/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { startServer } from './server/app.js';

export const app = {
server: {
run: startServer,
},
};
58 changes: 58 additions & 0 deletions src/runtime/db/actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { fs } from '../utils/native.js';
import { CWD } from '../utils/location.js';

const mapVars = Object.assign(Object.create(null), {
_uid: context => context.uid,
_lang: context => context.language,
});

const getValues = (context, payload, params) => {
const values = [];

for (let i = 0; i < params.length; i++) {
const name = params[i];
values[i] = mapVars[name]?.(context) ?? payload[name];
}

return values;
};

export const createAction =
({ text, params, returnMethod }) =>
async (context, payload) =>
returnMethod(
await context.db.unsafe(text, getValues(context, payload, params))
);

export const createActionFromFileSQL = ({ path: names }) => {
let action = async (context, payload) => {
const name = `${names[0]}/${names[1]}/${names[2]}`;
const path = `${CWD}/src/app/${names[0]}/${names[1]}/sql/${names[2]}.sql`;

const params = [];
const source = (await fs.readFile(path, 'utf8')).split('${');

for (let i = 1; i < source.length; i++) {
const sql = source[i];
const index = sql.indexOf('}');

const key = sql.slice(0, index).trim();
const num = params.includes(key)
? params.indexOf(key) + 1
: params.push(key);

source[i] = '$' + num + sql.slice(index + 1);
}

action = createAction({
name,
params,
text: source.join(''),
returnMethod: res => res.rows,
});

return await action(context, payload);
};

return (context, payload) => action(context, payload);
};
38 changes: 38 additions & 0 deletions src/runtime/db/client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import postgres from 'postgres';
import { presets } from '#env';
import { noop } from '#utils/native.js';
import { onAborted } from '#utils/process.js';

export const getConnectOptions = (
options = presets.db,
ns = presets.app.id.toUpperCase()
) => {
options = {
onnotice: noop,
connect_timeout: 60,
...options,
connection: {
application_name: presets.app.id,
...options?.connection,
},
};

const { env } = process;

if (env[ns + '_DB_USER']) options.username = env[ns + '_DB_USER'];
if (env[ns + '_DB_PASS']) options.password = env[ns + '_DB_PASS'];

if (env.DB_PORT) options.port = env.DB_PORT;
if (env.DB_HOST) options.host = env.DB_HOST;
if (env.DB_NAME) options.database = env.DB_NAME;

return options;
};

export const createClient = (options = getConnectOptions()) => {
const client = postgres(options);

onAborted(() => client.end({ timeout: 0 }));

return client;
};
15 changes: 15 additions & 0 deletions src/runtime/db/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export const CODES = {
INVALID_TEXT_REPRESENTATION: '22P02',
INTEGRITY_CONSTRAINT_VIOLATION: '23000',
RESTRICT_VIOLATION: '23001',
NOT_NULL_VIOLATION: '23502',
FOREIGN_KEY_VIOLATION: '23503',
UNIQUE_VIOLATION: '23505',
CHECK_VIOLATION: '23514',
EXCLUSION_VIOLATION: '23P01',
INSUFFICIENT_PRIVILEGE: '42501',
INVALID_DATETIME_FORMAT: '22007',
DATETIME_FIELD_OVERFLOW: '22008',
INVALID_BINARY_REPRESENTATION: '22P03',
NUMERIC_VALUE_OUT_OF_RANGE: '22003',
};
83 changes: 83 additions & 0 deletions src/runtime/db/context.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { presets } from '#env';
import { quoteLiteral } from './utils/text.js';
import { createClient } from './client.js';
import { factory } from './sql/query.js';

const DB_POOL = Symbol('DB POOL');

function setCustomParams({ db, uid, route, service }, params) {
let sql = 'SET LOCAL "custom.app"=' + quoteLiteral(presets.app.id);

if (uid) {
sql += ';SET LOCAL "custom.uid"=' + quoteLiteral(uid);
}

if (route?.name) {
sql += ';SET LOCAL "custom.action"=' + quoteLiteral(route.name);
} else if (service?.name) {
sql += ';SET LOCAL "custom.action"=' + quoteLiteral(service.name);
}

if (params) {
for (const key of Object.keys(params)) {
const value = params[key];
if (value != null) {
sql += `;SET LOCAL "custom.${key}"=`;
sql +=
value === true
? "'t'"
: value === false
? "'f'"
: quoteLiteral(value);
}
}
}

return db.unsafe(sql);
}

async function transaction(action, payload, params) {
if (this[DB_POOL]) {
return await this.db.savepoint(async () => {
await setCustomParams(this, params?.custom);
return await action(this, payload);
});
}

this[DB_POOL] = this.db;

try {
return await this.db.begin(async db => {
this.db = db;
await setCustomParams(this, params?.custom);
return await action(this, payload);
});
} finally {
this.db = this[DB_POOL];
this[DB_POOL] = null;

if (this.transactionTasks) {
for (const action of this.transactionTasks)
await action(this).catch(console.error);
this.transactionTasks.clear();
}
}
}

function isTransaction() {
return !!this[DB_POOL];
}

async function runAfterTransaction(action) {
(this.transactionTasks ??= new Set()).add(action);
}

export const setDataBaseContext = (context, options) => {
context[DB_POOL] = null;
context.transaction = transaction;
context.transactionTasks = null;
context.isTransaction = isTransaction;
context.runAfterTransaction = runAfterTransaction;
context.db = createClient(options);
context.sql = factory(context);
};
Loading

0 comments on commit ca38177

Please sign in to comment.