@tymber/common 0.0.1-alpha.0 → 0.1.0

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