@tymber/common 0.0.1-alpha.0 → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/dist/App.d.ts +14 -0
- package/dist/App.js +227 -0
- package/dist/Component.d.ts +16 -0
- package/dist/Component.js +116 -0
- package/dist/ConfigService.d.ts +31 -0
- package/dist/ConfigService.js +75 -0
- package/dist/Context.d.ts +32 -0
- package/dist/Context.js +10 -0
- package/dist/DB.d.ts +15 -0
- package/dist/DB.js +7 -0
- package/dist/Endpoint.d.ts +21 -0
- package/dist/Endpoint.js +87 -0
- package/dist/EventEmitter.d.ts +9 -0
- package/dist/EventEmitter.js +21 -0
- package/dist/Handler.d.ts +8 -0
- package/dist/Handler.js +8 -0
- package/dist/HttpContext.d.ts +24 -0
- package/dist/HttpContext.js +10 -0
- package/dist/I18nService.d.ts +18 -0
- package/dist/I18nService.js +72 -0
- package/dist/Middleware.d.ts +6 -0
- package/dist/Middleware.js +4 -0
- package/dist/Module.d.ts +47 -0
- package/dist/Module.js +12 -0
- package/dist/PubSubService.d.ts +20 -0
- package/dist/PubSubService.js +60 -0
- package/dist/Repository.d.ts +48 -0
- package/dist/Repository.js +133 -0
- package/dist/Router.d.ts +10 -0
- package/dist/Router.js +53 -0
- package/dist/TemplateService.d.ts +22 -0
- package/dist/TemplateService.js +66 -0
- package/dist/View.d.ts +17 -0
- package/dist/View.js +48 -0
- package/dist/ViewRenderer.d.ts +15 -0
- package/dist/ViewRenderer.js +58 -0
- package/dist/contrib/accept-language-parser.d.ts +9 -0
- package/dist/contrib/accept-language-parser.js +73 -0
- package/dist/contrib/cookie.d.ts +33 -0
- package/dist/contrib/cookie.js +207 -0
- package/dist/contrib/template.d.ts +1 -0
- package/dist/contrib/template.js +107 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +31 -0
- package/dist/utils/ajv.d.ts +2 -0
- package/dist/utils/ajv.js +10 -0
- package/dist/utils/camelToSnakeCase.d.ts +1 -0
- package/dist/utils/camelToSnakeCase.js +3 -0
- package/dist/utils/computeBaseUrl.d.ts +1 -0
- package/dist/utils/computeBaseUrl.js +37 -0
- package/dist/utils/computeContentType.d.ts +1 -0
- package/dist/utils/computeContentType.js +17 -0
- package/dist/utils/createDebug.d.ts +1 -0
- package/dist/utils/createDebug.js +4 -0
- package/dist/utils/createTestApp.d.ts +8 -0
- package/dist/utils/createTestApp.js +54 -0
- package/dist/utils/escapeValue.d.ts +1 -0
- package/dist/utils/escapeValue.js +3 -0
- package/dist/utils/fs.d.ts +6 -0
- package/dist/utils/fs.js +13 -0
- package/dist/utils/isAdmin.d.ts +2 -0
- package/dist/utils/isAdmin.js +4 -0
- package/dist/utils/isProduction.d.ts +1 -0
- package/dist/utils/isProduction.js +1 -0
- package/dist/utils/loadModules.d.ts +3 -0
- package/dist/utils/loadModules.js +83 -0
- package/dist/utils/randomId.d.ts +1 -0
- package/dist/utils/randomId.js +4 -0
- package/dist/utils/randomUUID.d.ts +1 -0
- package/dist/utils/randomUUID.js +4 -0
- package/dist/utils/runMigrations.d.ts +3 -0
- package/dist/utils/runMigrations.js +85 -0
- package/dist/utils/snakeToCamelCase.d.ts +1 -0
- package/dist/utils/snakeToCamelCase.js +3 -0
- package/dist/utils/sortBy.d.ts +1 -0
- package/dist/utils/sortBy.js +8 -0
- package/dist/utils/sql.d.ts +120 -0
- package/dist/utils/sql.js +433 -0
- package/dist/utils/toNodeHandler.d.ts +2 -0
- package/dist/utils/toNodeHandler.js +91 -0
- package/dist/utils/types.d.ts +3 -0
- package/dist/utils/types.js +1 -0
- package/dist/utils/waitFor.d.ts +1 -0
- package/dist/utils/waitFor.js +5 -0
- package/package.json +28 -2
- package/src/App.ts +302 -0
- package/src/Component.ts +166 -0
- package/src/ConfigService.ts +121 -0
- package/src/Context.ts +49 -0
- package/src/DB.ts +28 -0
- package/src/Endpoint.ts +118 -0
- package/src/EventEmitter.ts +32 -0
- package/src/Handler.ts +14 -0
- package/src/HttpContext.ts +33 -0
- package/src/I18nService.ts +96 -0
- package/src/Middleware.ts +10 -0
- package/src/Module.ts +60 -0
- package/src/PubSubService.ts +77 -0
- package/src/Repository.ts +204 -0
- package/src/Router.ts +77 -0
- package/src/TemplateService.ts +97 -0
- package/src/View.ts +60 -0
- package/src/ViewRenderer.ts +71 -0
- package/src/contrib/accept-language-parser.ts +94 -0
- package/src/contrib/cookie.ts +256 -0
- package/src/contrib/template.ts +134 -0
- package/src/index.ts +57 -0
- package/src/utils/ajv.ts +13 -0
- package/src/utils/camelToSnakeCase.ts +3 -0
- package/src/utils/computeBaseUrl.ts +46 -0
- package/src/utils/computeContentType.ts +17 -0
- package/src/utils/createDebug.ts +5 -0
- package/src/utils/createTestApp.ts +81 -0
- package/src/utils/escapeValue.ts +3 -0
- package/src/utils/fs.ts +15 -0
- package/src/utils/isAdmin.ts +5 -0
- package/src/utils/isProduction.ts +2 -0
- package/src/utils/loadModules.ts +105 -0
- package/src/utils/randomId.ts +5 -0
- package/src/utils/randomUUID.ts +5 -0
- package/src/utils/runMigrations.ts +122 -0
- package/src/utils/snakeToCamelCase.ts +3 -0
- package/src/utils/sortBy.ts +8 -0
- package/src/utils/sql.ts +553 -0
- package/src/utils/toNodeHandler.ts +121 -0
- package/src/utils/types.ts +1 -0
- package/src/utils/waitFor.ts +5 -0
package/dist/View.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type HttpContext } from "./HttpContext.js";
|
|
2
|
+
import { Handler } from "./Handler.js";
|
|
3
|
+
export declare abstract class BaseView extends Handler {
|
|
4
|
+
protected pathParamsSchema: unknown;
|
|
5
|
+
protected querySchema: unknown;
|
|
6
|
+
private validatePathParams?;
|
|
7
|
+
private validateQuery?;
|
|
8
|
+
doHandle(ctx: HttpContext): Response | Promise<Response>;
|
|
9
|
+
}
|
|
10
|
+
export declare abstract class View extends BaseView {
|
|
11
|
+
private _viewBrand;
|
|
12
|
+
doHandle(ctx: HttpContext): Response | Promise<Response>;
|
|
13
|
+
}
|
|
14
|
+
export declare abstract class AdminView extends BaseView {
|
|
15
|
+
private _adminViewBrand;
|
|
16
|
+
doHandle(ctx: HttpContext): Response | Promise<Response>;
|
|
17
|
+
}
|
package/dist/View.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import {} from "ajv";
|
|
2
|
+
import {} from "./HttpContext.js";
|
|
3
|
+
import { Handler } from "./Handler.js";
|
|
4
|
+
import { AJV_INSTANCE } from "./utils/ajv.js";
|
|
5
|
+
export class BaseView extends Handler {
|
|
6
|
+
pathParamsSchema; // JSONSchemaType<Params>;
|
|
7
|
+
querySchema; // JSONSchemaType<Query>;
|
|
8
|
+
validatePathParams;
|
|
9
|
+
validateQuery;
|
|
10
|
+
doHandle(ctx) {
|
|
11
|
+
const { pathParams, query } = ctx;
|
|
12
|
+
if (this.pathParamsSchema && !this.validatePathParams) {
|
|
13
|
+
this.validatePathParams = AJV_INSTANCE.compile(this.pathParamsSchema);
|
|
14
|
+
}
|
|
15
|
+
if (this.validatePathParams) {
|
|
16
|
+
this.validatePathParams(pathParams);
|
|
17
|
+
}
|
|
18
|
+
if (this.querySchema && !this.validateQuery) {
|
|
19
|
+
this.validateQuery = AJV_INSTANCE.compile(this.querySchema);
|
|
20
|
+
}
|
|
21
|
+
if (this.validateQuery) {
|
|
22
|
+
this.validateQuery(query);
|
|
23
|
+
}
|
|
24
|
+
return this.handle(ctx);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export class View extends BaseView {
|
|
28
|
+
_viewBrand; // nominal typing
|
|
29
|
+
doHandle(ctx) {
|
|
30
|
+
if (!this.allowAnonymous && !ctx.user) {
|
|
31
|
+
return ctx.redirect("/login");
|
|
32
|
+
}
|
|
33
|
+
if (!this.hasPermission(ctx)) {
|
|
34
|
+
return ctx.redirect("/forbidden");
|
|
35
|
+
}
|
|
36
|
+
return super.doHandle(ctx);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export class AdminView extends BaseView {
|
|
40
|
+
_adminViewBrand; // nominal typing
|
|
41
|
+
doHandle(ctx) {
|
|
42
|
+
if (!this.allowAnonymous && !ctx.admin) {
|
|
43
|
+
return ctx.redirect("/admin/login");
|
|
44
|
+
}
|
|
45
|
+
// note: no hasPermission() check for admins
|
|
46
|
+
return super.doHandle(ctx);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Component, INJECT } from "./Component.js";
|
|
2
|
+
import { BaseTemplateService, TemplateService } from "./TemplateService.js";
|
|
3
|
+
import { I18nService } from "./I18nService.js";
|
|
4
|
+
import { ModuleDefinitions } from "./Module.js";
|
|
5
|
+
import type { HttpContext } from "./HttpContext.js";
|
|
6
|
+
export declare class ViewRenderer extends Component {
|
|
7
|
+
private readonly i18nService;
|
|
8
|
+
private readonly templateService;
|
|
9
|
+
private readonly customTemplateService;
|
|
10
|
+
private readonly modules;
|
|
11
|
+
static [INJECT]: (typeof ModuleDefinitions | typeof TemplateService | typeof BaseTemplateService | typeof I18nService)[];
|
|
12
|
+
constructor(i18nService: I18nService, templateService: BaseTemplateService, customTemplateService: TemplateService, modules: ModuleDefinitions);
|
|
13
|
+
render(ctx: HttpContext, view: string | string[], data?: Record<string, any>): Promise<import("undici-types").Response>;
|
|
14
|
+
private renderTemplate;
|
|
15
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Component, INJECT } from "./Component.js";
|
|
2
|
+
import { BaseTemplateService, TemplateService } from "./TemplateService.js";
|
|
3
|
+
import { I18nService } from "./I18nService.js";
|
|
4
|
+
import { pick } from "./contrib/accept-language-parser.js";
|
|
5
|
+
import { ModuleDefinitions } from "./Module.js";
|
|
6
|
+
import { isProduction } from "./utils/isProduction.js";
|
|
7
|
+
export class ViewRenderer extends Component {
|
|
8
|
+
i18nService;
|
|
9
|
+
templateService;
|
|
10
|
+
customTemplateService;
|
|
11
|
+
modules;
|
|
12
|
+
static [INJECT] = [
|
|
13
|
+
I18nService,
|
|
14
|
+
BaseTemplateService,
|
|
15
|
+
TemplateService,
|
|
16
|
+
ModuleDefinitions,
|
|
17
|
+
];
|
|
18
|
+
constructor(i18nService, templateService, customTemplateService, modules) {
|
|
19
|
+
super();
|
|
20
|
+
this.i18nService = i18nService;
|
|
21
|
+
this.templateService = templateService;
|
|
22
|
+
this.customTemplateService = customTemplateService;
|
|
23
|
+
this.modules = modules;
|
|
24
|
+
}
|
|
25
|
+
async render(ctx, view, data = {}) {
|
|
26
|
+
const templates = Array.isArray(view) ? view.reverse() : [view];
|
|
27
|
+
data.TITLE = "Tymber";
|
|
28
|
+
data.CTX = ctx;
|
|
29
|
+
data.CTX.app = data.CTX.app || {};
|
|
30
|
+
data.CTX.app.isProduction = isProduction;
|
|
31
|
+
data.CTX.app.modules = this.modules.modules;
|
|
32
|
+
const locale = pick(this.i18nService.availableLocales(), ctx.headers.get("accept-language") || "");
|
|
33
|
+
data.CTX.locale = locale;
|
|
34
|
+
data.$t = (key, ...args) => {
|
|
35
|
+
return this.i18nService.translate(ctx, locale, key, ...args);
|
|
36
|
+
};
|
|
37
|
+
for (const template of templates) {
|
|
38
|
+
data.VIEW = await this.renderTemplate(template, data);
|
|
39
|
+
}
|
|
40
|
+
return new Response(data.VIEW, {
|
|
41
|
+
headers: {
|
|
42
|
+
"content-type": "text/html",
|
|
43
|
+
"cache-control": "no-cache",
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
renderTemplate(templateName, data) {
|
|
48
|
+
if (this.customTemplateService.canRender(templateName)) {
|
|
49
|
+
return this.customTemplateService.render(templateName, data);
|
|
50
|
+
}
|
|
51
|
+
else if (this.templateService.canRender(templateName)) {
|
|
52
|
+
return this.templateService.render(templateName, data);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
throw new Error(`template ${templateName} not found`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Brand } from "../utils/types.js";
|
|
2
|
+
export type Locale = Brand<string, "Locale">;
|
|
3
|
+
export declare function parse(al: string): ({
|
|
4
|
+
code: string;
|
|
5
|
+
script: string | null;
|
|
6
|
+
region: string;
|
|
7
|
+
quality: number;
|
|
8
|
+
} | undefined)[];
|
|
9
|
+
export declare function pick(supportedLanguages: string[], acceptLanguage: any, options?: any): Locale;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// from https://github.com/opentable/accept-language-parser
|
|
2
|
+
import {} from "../utils/types.js";
|
|
3
|
+
var regex = /((([a-zA-Z]+(-[a-zA-Z0-9]+){0,2})|\*)(;q=[0-1](\.[0-9]+)?)?)*/g;
|
|
4
|
+
var isString = function (s) {
|
|
5
|
+
return typeof s === "string";
|
|
6
|
+
};
|
|
7
|
+
export function parse(al) {
|
|
8
|
+
var strings = (al || "").match(regex);
|
|
9
|
+
return strings
|
|
10
|
+
.map(function (m) {
|
|
11
|
+
if (!m) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
var bits = m.split(";");
|
|
15
|
+
var ietf = bits[0].split("-");
|
|
16
|
+
var hasScript = ietf.length === 3;
|
|
17
|
+
return {
|
|
18
|
+
code: ietf[0],
|
|
19
|
+
script: hasScript ? ietf[1] : null,
|
|
20
|
+
region: hasScript ? ietf[2] : ietf[1],
|
|
21
|
+
quality: bits[1] ? parseFloat(bits[1].split("=")[1]) : 1.0,
|
|
22
|
+
};
|
|
23
|
+
})
|
|
24
|
+
.filter(function (r) {
|
|
25
|
+
return r;
|
|
26
|
+
})
|
|
27
|
+
.sort(function (a, b) {
|
|
28
|
+
return b.quality - a.quality;
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
export function pick(supportedLanguages, acceptLanguage, options) {
|
|
32
|
+
options = options || {};
|
|
33
|
+
// if (!supportedLanguages || !supportedLanguages.length || !acceptLanguage) {
|
|
34
|
+
// return null;
|
|
35
|
+
// }
|
|
36
|
+
if (!acceptLanguage) {
|
|
37
|
+
return supportedLanguages[0]; // pick first as default
|
|
38
|
+
}
|
|
39
|
+
if (isString(acceptLanguage)) {
|
|
40
|
+
acceptLanguage = parse(acceptLanguage);
|
|
41
|
+
}
|
|
42
|
+
var supported = supportedLanguages.map(function (support) {
|
|
43
|
+
var bits = support.split("-");
|
|
44
|
+
var hasScript = bits.length === 3;
|
|
45
|
+
return {
|
|
46
|
+
code: bits[0],
|
|
47
|
+
script: hasScript ? bits[1] : null,
|
|
48
|
+
region: hasScript ? bits[2] : bits[1],
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
for (var i = 0; i < acceptLanguage.length; i++) {
|
|
52
|
+
var lang = acceptLanguage[i];
|
|
53
|
+
var langCode = lang.code.toLowerCase();
|
|
54
|
+
var langRegion = lang.region ? lang.region.toLowerCase() : lang.region;
|
|
55
|
+
var langScript = lang.script ? lang.script.toLowerCase() : lang.script;
|
|
56
|
+
for (var j = 0; j < supported.length; j++) {
|
|
57
|
+
var supportedCode = supported[j].code.toLowerCase();
|
|
58
|
+
var supportedScript = supported[j].script
|
|
59
|
+
? supported[j].script.toLowerCase()
|
|
60
|
+
: supported[j].script;
|
|
61
|
+
var supportedRegion = supported[j].region
|
|
62
|
+
? supported[j].region.toLowerCase()
|
|
63
|
+
: supported[j].region;
|
|
64
|
+
if (langCode === supportedCode &&
|
|
65
|
+
(options.loose || !langScript || langScript === supportedScript) &&
|
|
66
|
+
(options.loose || !langRegion || langRegion === supportedRegion)) {
|
|
67
|
+
return supportedLanguages[j];
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// return null;
|
|
72
|
+
return supportedLanguages[0]; // pick first as default
|
|
73
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module variables.
|
|
3
|
+
* @private
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Parse a cookie header.
|
|
7
|
+
*
|
|
8
|
+
* Parse the given cookie header string into an object
|
|
9
|
+
* The object has the various cookies as keys(names) => values
|
|
10
|
+
*
|
|
11
|
+
* @param {string} str
|
|
12
|
+
* @param {object} [options]
|
|
13
|
+
* @return {object}
|
|
14
|
+
* @public
|
|
15
|
+
*/
|
|
16
|
+
declare function parse(str: string, options?: any): Record<string, string>;
|
|
17
|
+
/**
|
|
18
|
+
* Serialize data into a cookie header.
|
|
19
|
+
*
|
|
20
|
+
* Serialize the a name value pair into a cookie string suitable for
|
|
21
|
+
* http headers. An optional options object specified cookie parameters.
|
|
22
|
+
*
|
|
23
|
+
* serialize('foo', 'bar', { httpOnly: true })
|
|
24
|
+
* => "foo=bar; httpOnly"
|
|
25
|
+
*
|
|
26
|
+
* @param {string} name
|
|
27
|
+
* @param {string} val
|
|
28
|
+
* @param {object} [options]
|
|
29
|
+
* @return {string}
|
|
30
|
+
* @public
|
|
31
|
+
*/
|
|
32
|
+
declare function serialize(name: string, val: string, options: any): string;
|
|
33
|
+
export { parse as parseCookieHeader, serialize as createCookie };
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
// from https://www.npmjs.com/package/cookie
|
|
2
|
+
/**
|
|
3
|
+
* Module variables.
|
|
4
|
+
* @private
|
|
5
|
+
*/
|
|
6
|
+
var __toString = Object.prototype.toString;
|
|
7
|
+
/**
|
|
8
|
+
* RegExp to match field-content in RFC 7230 sec 3.2
|
|
9
|
+
*
|
|
10
|
+
* field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
|
|
11
|
+
* field-vchar = VCHAR / obs-text
|
|
12
|
+
* obs-text = %x80-FF
|
|
13
|
+
*/
|
|
14
|
+
var fieldContentRegExp = /^[\u0009\u0020-\u007e\u0080-\u00ff]+$/;
|
|
15
|
+
/**
|
|
16
|
+
* Parse a cookie header.
|
|
17
|
+
*
|
|
18
|
+
* Parse the given cookie header string into an object
|
|
19
|
+
* The object has the various cookies as keys(names) => values
|
|
20
|
+
*
|
|
21
|
+
* @param {string} str
|
|
22
|
+
* @param {object} [options]
|
|
23
|
+
* @return {object}
|
|
24
|
+
* @public
|
|
25
|
+
*/
|
|
26
|
+
function parse(str, options) {
|
|
27
|
+
if (typeof str !== "string") {
|
|
28
|
+
throw new TypeError("argument str must be a string");
|
|
29
|
+
}
|
|
30
|
+
var obj = {};
|
|
31
|
+
var opt = options || {};
|
|
32
|
+
var dec = opt.decode || decode;
|
|
33
|
+
var index = 0;
|
|
34
|
+
while (index < str.length) {
|
|
35
|
+
var eqIdx = str.indexOf("=", index);
|
|
36
|
+
// no more cookie pairs
|
|
37
|
+
if (eqIdx === -1) {
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
var endIdx = str.indexOf(";", index);
|
|
41
|
+
if (endIdx === -1) {
|
|
42
|
+
endIdx = str.length;
|
|
43
|
+
}
|
|
44
|
+
else if (endIdx < eqIdx) {
|
|
45
|
+
// backtrack on prior semicolon
|
|
46
|
+
index = str.lastIndexOf(";", eqIdx - 1) + 1;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
var key = str.slice(index, eqIdx).trim();
|
|
50
|
+
// only assign once
|
|
51
|
+
if (undefined === obj[key]) {
|
|
52
|
+
var val = str.slice(eqIdx + 1, endIdx).trim();
|
|
53
|
+
// quoted values
|
|
54
|
+
if (val.charCodeAt(0) === 0x22) {
|
|
55
|
+
val = val.slice(1, -1);
|
|
56
|
+
}
|
|
57
|
+
obj[key] = tryDecode(val, dec);
|
|
58
|
+
}
|
|
59
|
+
index = endIdx + 1;
|
|
60
|
+
}
|
|
61
|
+
return obj;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Serialize data into a cookie header.
|
|
65
|
+
*
|
|
66
|
+
* Serialize the a name value pair into a cookie string suitable for
|
|
67
|
+
* http headers. An optional options object specified cookie parameters.
|
|
68
|
+
*
|
|
69
|
+
* serialize('foo', 'bar', { httpOnly: true })
|
|
70
|
+
* => "foo=bar; httpOnly"
|
|
71
|
+
*
|
|
72
|
+
* @param {string} name
|
|
73
|
+
* @param {string} val
|
|
74
|
+
* @param {object} [options]
|
|
75
|
+
* @return {string}
|
|
76
|
+
* @public
|
|
77
|
+
*/
|
|
78
|
+
function serialize(name, val, options) {
|
|
79
|
+
var opt = options || {};
|
|
80
|
+
var enc = opt.encode || encode;
|
|
81
|
+
if (typeof enc !== "function") {
|
|
82
|
+
throw new TypeError("option encode is invalid");
|
|
83
|
+
}
|
|
84
|
+
if (!fieldContentRegExp.test(name)) {
|
|
85
|
+
throw new TypeError("argument name is invalid");
|
|
86
|
+
}
|
|
87
|
+
var value = enc(val);
|
|
88
|
+
if (value && !fieldContentRegExp.test(value)) {
|
|
89
|
+
throw new TypeError("argument val is invalid");
|
|
90
|
+
}
|
|
91
|
+
var str = name + "=" + value;
|
|
92
|
+
if (null != opt.maxAge) {
|
|
93
|
+
var maxAge = opt.maxAge - 0;
|
|
94
|
+
if (isNaN(maxAge) || !isFinite(maxAge)) {
|
|
95
|
+
throw new TypeError("option maxAge is invalid");
|
|
96
|
+
}
|
|
97
|
+
str += "; Max-Age=" + Math.floor(maxAge);
|
|
98
|
+
}
|
|
99
|
+
if (opt.domain) {
|
|
100
|
+
if (!fieldContentRegExp.test(opt.domain)) {
|
|
101
|
+
throw new TypeError("option domain is invalid");
|
|
102
|
+
}
|
|
103
|
+
str += "; Domain=" + opt.domain;
|
|
104
|
+
}
|
|
105
|
+
if (opt.path) {
|
|
106
|
+
if (!fieldContentRegExp.test(opt.path)) {
|
|
107
|
+
throw new TypeError("option path is invalid");
|
|
108
|
+
}
|
|
109
|
+
str += "; Path=" + opt.path;
|
|
110
|
+
}
|
|
111
|
+
if (opt.expires) {
|
|
112
|
+
var expires = opt.expires;
|
|
113
|
+
if (!isDate(expires) || isNaN(expires.valueOf())) {
|
|
114
|
+
throw new TypeError("option expires is invalid");
|
|
115
|
+
}
|
|
116
|
+
str += "; Expires=" + expires.toUTCString();
|
|
117
|
+
}
|
|
118
|
+
if (opt.httpOnly) {
|
|
119
|
+
str += "; HttpOnly";
|
|
120
|
+
}
|
|
121
|
+
if (opt.secure) {
|
|
122
|
+
str += "; Secure";
|
|
123
|
+
}
|
|
124
|
+
if (opt.priority) {
|
|
125
|
+
var priority = typeof opt.priority === "string"
|
|
126
|
+
? opt.priority.toLowerCase()
|
|
127
|
+
: opt.priority;
|
|
128
|
+
switch (priority) {
|
|
129
|
+
case "low":
|
|
130
|
+
str += "; Priority=Low";
|
|
131
|
+
break;
|
|
132
|
+
case "medium":
|
|
133
|
+
str += "; Priority=Medium";
|
|
134
|
+
break;
|
|
135
|
+
case "high":
|
|
136
|
+
str += "; Priority=High";
|
|
137
|
+
break;
|
|
138
|
+
default:
|
|
139
|
+
throw new TypeError("option priority is invalid");
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (opt.sameSite) {
|
|
143
|
+
var sameSite = typeof opt.sameSite === "string"
|
|
144
|
+
? opt.sameSite.toLowerCase()
|
|
145
|
+
: opt.sameSite;
|
|
146
|
+
switch (sameSite) {
|
|
147
|
+
case true:
|
|
148
|
+
str += "; SameSite=Strict";
|
|
149
|
+
break;
|
|
150
|
+
case "lax":
|
|
151
|
+
str += "; SameSite=Lax";
|
|
152
|
+
break;
|
|
153
|
+
case "strict":
|
|
154
|
+
str += "; SameSite=Strict";
|
|
155
|
+
break;
|
|
156
|
+
case "none":
|
|
157
|
+
str += "; SameSite=None";
|
|
158
|
+
break;
|
|
159
|
+
default:
|
|
160
|
+
throw new TypeError("option sameSite is invalid");
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return str;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* URL-decode string value. Optimized to skip native call when no %.
|
|
167
|
+
*
|
|
168
|
+
* @param {string} str
|
|
169
|
+
* @returns {string}
|
|
170
|
+
*/
|
|
171
|
+
function decode(str) {
|
|
172
|
+
return str.indexOf("%") !== -1 ? decodeURIComponent(str) : str;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* URL-encode value.
|
|
176
|
+
*
|
|
177
|
+
* @param {string} str
|
|
178
|
+
* @returns {string}
|
|
179
|
+
*/
|
|
180
|
+
function encode(val) {
|
|
181
|
+
return encodeURIComponent(val);
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Determine if value is a Date.
|
|
185
|
+
*
|
|
186
|
+
* @param {*} val
|
|
187
|
+
* @private
|
|
188
|
+
*/
|
|
189
|
+
function isDate(val) {
|
|
190
|
+
return __toString.call(val) === "[object Date]" || val instanceof Date;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Try decoding a string using a decoding function.
|
|
194
|
+
*
|
|
195
|
+
* @param {string} str
|
|
196
|
+
* @param {function} decode
|
|
197
|
+
* @private
|
|
198
|
+
*/
|
|
199
|
+
function tryDecode(str, decode) {
|
|
200
|
+
try {
|
|
201
|
+
return decode(str);
|
|
202
|
+
}
|
|
203
|
+
catch (e) {
|
|
204
|
+
return str;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
export { parse as parseCookieHeader, serialize as createCookie };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function compileTemplate(text: string): (data: any) => string;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// from https://github.com/jashkenas/underscore/blob/master/modules/template.js
|
|
2
|
+
const escapeMap = {
|
|
3
|
+
"&": "&",
|
|
4
|
+
"<": "<",
|
|
5
|
+
">": ">",
|
|
6
|
+
'"': """,
|
|
7
|
+
};
|
|
8
|
+
const ESCAPE_REGEX = /[&<>"]/g;
|
|
9
|
+
const _ = {
|
|
10
|
+
escape(s) {
|
|
11
|
+
return s.replace(ESCAPE_REGEX, (match) => escapeMap[match]);
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
// When customizing `_.templateSettings`, if you don't want to define an
|
|
15
|
+
// interpolation, evaluation or escaping regex, we need one that is
|
|
16
|
+
// guaranteed not to match.
|
|
17
|
+
var noMatch = /(.)^/;
|
|
18
|
+
// Certain characters need to be escaped so that they can be put into a
|
|
19
|
+
// string literal.
|
|
20
|
+
var escapes = {
|
|
21
|
+
"'": "'",
|
|
22
|
+
"\\": "\\",
|
|
23
|
+
"\r": "r",
|
|
24
|
+
"\n": "n",
|
|
25
|
+
"\u2028": "u2028",
|
|
26
|
+
"\u2029": "u2029",
|
|
27
|
+
};
|
|
28
|
+
var escapeRegExp = /\\|'|\r|\n|\u2028|\u2029/g;
|
|
29
|
+
function escapeChar(match) {
|
|
30
|
+
return "\\" + escapes[match];
|
|
31
|
+
}
|
|
32
|
+
// In order to prevent third-party code injection through
|
|
33
|
+
// `_.templateSettings.variable`, we test it against the following regular
|
|
34
|
+
// expression. It is intentionally a bit more liberal than just matching valid
|
|
35
|
+
// identifiers, but still prevents possible loopholes through defaults or
|
|
36
|
+
// destructuring assignment.
|
|
37
|
+
var bareIdentifier = /^\s*(\w|\$)+\s*$/;
|
|
38
|
+
// JavaScript micro-templating, similar to John Resig's implementation.
|
|
39
|
+
// Underscore templating handles arbitrary delimiters, preserves whitespace,
|
|
40
|
+
// and correctly escapes quotes within interpolated code.
|
|
41
|
+
// NB: `oldSettings` only exists for backwards compatibility.
|
|
42
|
+
export function compileTemplate(text) {
|
|
43
|
+
const settings = {
|
|
44
|
+
evaluate: /<%([\s\S]+?)%>/g,
|
|
45
|
+
interpolate: /<%=([\s\S]+?)%>/g,
|
|
46
|
+
escape: /<%-([\s\S]+?)%>/g,
|
|
47
|
+
};
|
|
48
|
+
// Combine delimiters into one regular expression via alternation.
|
|
49
|
+
var matcher = RegExp([
|
|
50
|
+
(settings.escape || noMatch).source,
|
|
51
|
+
(settings.interpolate || noMatch).source,
|
|
52
|
+
(settings.evaluate || noMatch).source,
|
|
53
|
+
].join("|") + "|$", "g");
|
|
54
|
+
// Compile the template source, escaping string literals appropriately.
|
|
55
|
+
var index = 0;
|
|
56
|
+
var source = "__p+='";
|
|
57
|
+
text.replace(matcher, function (match, escape, interpolate, evaluate, offset) {
|
|
58
|
+
source += text.slice(index, offset).replace(escapeRegExp, escapeChar);
|
|
59
|
+
index = offset + match.length;
|
|
60
|
+
if (escape) {
|
|
61
|
+
source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
|
|
62
|
+
}
|
|
63
|
+
else if (interpolate) {
|
|
64
|
+
source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
|
|
65
|
+
}
|
|
66
|
+
else if (evaluate) {
|
|
67
|
+
source += "';\n" + evaluate + "\n__p+='";
|
|
68
|
+
}
|
|
69
|
+
// Adobe VMs need the match returned to produce the correct offset.
|
|
70
|
+
return match;
|
|
71
|
+
});
|
|
72
|
+
source += "';\n";
|
|
73
|
+
// @ts-expect-error unknown property
|
|
74
|
+
var argument = settings.variable;
|
|
75
|
+
if (argument) {
|
|
76
|
+
// Insure against third-party code injection. (CVE-2021-23358)
|
|
77
|
+
if (!bareIdentifier.test(argument))
|
|
78
|
+
throw new Error("variable is not a bare identifier: " + argument);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
// If a variable is not specified, place data values in local scope.
|
|
82
|
+
source = "with(obj||{}){\n" + source + "}\n";
|
|
83
|
+
argument = "obj";
|
|
84
|
+
}
|
|
85
|
+
source =
|
|
86
|
+
"var __t,__p='',__j=Array.prototype.join," +
|
|
87
|
+
"print=function(){__p+=__j.call(arguments,'');};\n" +
|
|
88
|
+
source +
|
|
89
|
+
"return __p;\n";
|
|
90
|
+
var render;
|
|
91
|
+
try {
|
|
92
|
+
render = new Function(argument, "_", source);
|
|
93
|
+
}
|
|
94
|
+
catch (e) {
|
|
95
|
+
// @ts-expect-error unknown property
|
|
96
|
+
e.source = source;
|
|
97
|
+
throw e;
|
|
98
|
+
}
|
|
99
|
+
var template = function (data) {
|
|
100
|
+
// @ts-expect-error unknown this
|
|
101
|
+
return render.call(this, data, _);
|
|
102
|
+
};
|
|
103
|
+
// Provide the compiled source as a convenience for precompilation.
|
|
104
|
+
// @ts-expect-error unknown property
|
|
105
|
+
template.source = "function(" + argument + "){\n" + source + "}";
|
|
106
|
+
return template;
|
|
107
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export { Component, type Ctor, ComponentFactory, INJECT } from "./Component.js";
|
|
2
|
+
export { type UserId, type User, type OrgId, type AdminUserId, type Admin, type Context, emptyContext, } from "./Context.js";
|
|
3
|
+
export { ConfigService } from "./ConfigService.js";
|
|
4
|
+
export { DB, DuplicateKeyError } from "./DB.js";
|
|
5
|
+
export { EventEmitter } from "./EventEmitter.js";
|
|
6
|
+
export { PubSubService, NodeClusterPubSubService, initPrimary, } from "./PubSubService.js";
|
|
7
|
+
export { Endpoint, AdminEndpoint } from "./Endpoint.js";
|
|
8
|
+
export { type HttpContext } from "./HttpContext.js";
|
|
9
|
+
export { App } from "./App.js";
|
|
10
|
+
export { type Module, type AppInit, ModuleDefinitions, type Route, } from "./Module.js";
|
|
11
|
+
export { View, AdminView } from "./View.js";
|
|
12
|
+
export { Middleware } from "./Middleware.js";
|
|
13
|
+
export { Repository, type Page, type AuditedEntity, AuditedRepository, type AdminAuditedEntity, AdminAuditedRepository, } from "./Repository.js";
|
|
14
|
+
export { TemplateService } from "./TemplateService.js";
|
|
15
|
+
export { createCookie, parseCookieHeader } from "./contrib/cookie.js";
|
|
16
|
+
export { AJV_INSTANCE } from "./utils/ajv.js";
|
|
17
|
+
export { camelToSnakeCase } from "./utils/camelToSnakeCase.js";
|
|
18
|
+
export { createDebug } from "./utils/createDebug.js";
|
|
19
|
+
export { createTestApp, type BaseTestContext } from "./utils/createTestApp.js";
|
|
20
|
+
export { escapeValue } from "./utils/escapeValue.js";
|
|
21
|
+
export { FS } from "./utils/fs.js";
|
|
22
|
+
export { isAdmin } from "./utils/isAdmin.js";
|
|
23
|
+
export { isProduction } from "./utils/isProduction.js";
|
|
24
|
+
export { randomId } from "./utils/randomId.js";
|
|
25
|
+
export { randomUUID } from "./utils/randomUUID.js";
|
|
26
|
+
export { snakeToCamelCase } from "./utils/snakeToCamelCase.js";
|
|
27
|
+
export { sortBy } from "./utils/sortBy.js";
|
|
28
|
+
export { sql, Statement } from "./utils/sql.js";
|
|
29
|
+
export { toNodeHandler } from "./utils/toNodeHandler.js";
|
|
30
|
+
export { type Brand } from "./utils/types.js";
|
|
31
|
+
export { waitFor } from "./utils/waitFor.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export { Component, ComponentFactory, INJECT } from "./Component.js";
|
|
2
|
+
export { emptyContext, } from "./Context.js";
|
|
3
|
+
export { ConfigService } from "./ConfigService.js";
|
|
4
|
+
export { DB, DuplicateKeyError } from "./DB.js";
|
|
5
|
+
export { EventEmitter } from "./EventEmitter.js";
|
|
6
|
+
export { PubSubService, NodeClusterPubSubService, initPrimary, } from "./PubSubService.js";
|
|
7
|
+
export { Endpoint, AdminEndpoint } from "./Endpoint.js";
|
|
8
|
+
export {} from "./HttpContext.js";
|
|
9
|
+
export { App } from "./App.js";
|
|
10
|
+
export { ModuleDefinitions, } from "./Module.js";
|
|
11
|
+
export { View, AdminView } from "./View.js";
|
|
12
|
+
export { Middleware } from "./Middleware.js";
|
|
13
|
+
export { Repository, AuditedRepository, AdminAuditedRepository, } from "./Repository.js";
|
|
14
|
+
export { TemplateService } from "./TemplateService.js";
|
|
15
|
+
export { createCookie, parseCookieHeader } from "./contrib/cookie.js";
|
|
16
|
+
export { AJV_INSTANCE } from "./utils/ajv.js";
|
|
17
|
+
export { camelToSnakeCase } from "./utils/camelToSnakeCase.js";
|
|
18
|
+
export { createDebug } from "./utils/createDebug.js";
|
|
19
|
+
export { createTestApp } from "./utils/createTestApp.js";
|
|
20
|
+
export { escapeValue } from "./utils/escapeValue.js";
|
|
21
|
+
export { FS } from "./utils/fs.js";
|
|
22
|
+
export { isAdmin } from "./utils/isAdmin.js";
|
|
23
|
+
export { isProduction } from "./utils/isProduction.js";
|
|
24
|
+
export { randomId } from "./utils/randomId.js";
|
|
25
|
+
export { randomUUID } from "./utils/randomUUID.js";
|
|
26
|
+
export { snakeToCamelCase } from "./utils/snakeToCamelCase.js";
|
|
27
|
+
export { sortBy } from "./utils/sortBy.js";
|
|
28
|
+
export { sql, Statement } from "./utils/sql.js";
|
|
29
|
+
export { toNodeHandler } from "./utils/toNodeHandler.js";
|
|
30
|
+
export {} from "./utils/types.js";
|
|
31
|
+
export { waitFor } from "./utils/waitFor.js";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Ajv } from "ajv";
|
|
2
|
+
import addFormats from "ajv-formats";
|
|
3
|
+
// reference: https://ajv.js.org/api.html
|
|
4
|
+
export const AJV_INSTANCE = new Ajv({
|
|
5
|
+
useDefaults: true,
|
|
6
|
+
coerceTypes: true,
|
|
7
|
+
removeAdditional: true,
|
|
8
|
+
});
|
|
9
|
+
// @ts-expect-error FIXME
|
|
10
|
+
addFormats(AJV_INSTANCE);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function camelToSnakeCase(str: string): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function computeBaseUrl(headers: Headers): string;
|