@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
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
export function sql() { }
|
|
2
|
+
const options = {
|
|
3
|
+
placeholder: "$%d",
|
|
4
|
+
quoteChar: '"',
|
|
5
|
+
};
|
|
6
|
+
sql.setOption = function (o, value) {
|
|
7
|
+
options[o] = value;
|
|
8
|
+
};
|
|
9
|
+
function joinParts(parts) {
|
|
10
|
+
return parts.filter((s) => !!s).join(" ");
|
|
11
|
+
}
|
|
12
|
+
export class Statement {
|
|
13
|
+
build() {
|
|
14
|
+
const values = [];
|
|
15
|
+
return {
|
|
16
|
+
text: joinParts(this.computeParts({
|
|
17
|
+
values,
|
|
18
|
+
})),
|
|
19
|
+
values,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function joinExpression(type, table, on) {
|
|
24
|
+
return () => {
|
|
25
|
+
// we cannot use sql.and() since the right part of the expression must be escaped with handleColumn() instead of handleValue()
|
|
26
|
+
const condition = Object.entries(on)
|
|
27
|
+
.map(([column, value]) => {
|
|
28
|
+
return handleColumn(column) + " = " + handleColumn(value);
|
|
29
|
+
})
|
|
30
|
+
.join(" AND ");
|
|
31
|
+
return `${type} JOIN ${handleTable(table)} ON ${condition}`;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
class SelectStatement extends Statement {
|
|
35
|
+
_table;
|
|
36
|
+
_distinct = false;
|
|
37
|
+
_columns = [];
|
|
38
|
+
_joins = [];
|
|
39
|
+
_where = [];
|
|
40
|
+
_orderBy = [];
|
|
41
|
+
_groupBy = [];
|
|
42
|
+
_having;
|
|
43
|
+
_limit;
|
|
44
|
+
_offset;
|
|
45
|
+
_forUpdate = false;
|
|
46
|
+
constructor(columns) {
|
|
47
|
+
super();
|
|
48
|
+
if (columns) {
|
|
49
|
+
this._columns.push(...columns);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
distinct() {
|
|
53
|
+
this._distinct = true;
|
|
54
|
+
return this;
|
|
55
|
+
}
|
|
56
|
+
from(table) {
|
|
57
|
+
this._table = table;
|
|
58
|
+
return this;
|
|
59
|
+
}
|
|
60
|
+
innerJoin(table, on) {
|
|
61
|
+
this._joins.push(joinExpression("INNER", table, on));
|
|
62
|
+
return this;
|
|
63
|
+
}
|
|
64
|
+
leftJoin(table, on) {
|
|
65
|
+
this._joins.push(joinExpression("LEFT", table, on));
|
|
66
|
+
return this;
|
|
67
|
+
}
|
|
68
|
+
rightJoin(table, on) {
|
|
69
|
+
this._joins.push(joinExpression("RIGHT", table, on));
|
|
70
|
+
return this;
|
|
71
|
+
}
|
|
72
|
+
fullOuterJoin(table, on) {
|
|
73
|
+
this._joins.push(joinExpression("FULL OUTER", table, on));
|
|
74
|
+
return this;
|
|
75
|
+
}
|
|
76
|
+
where(arg) {
|
|
77
|
+
if (typeof arg === "function") {
|
|
78
|
+
this._where.push(arg);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
for (const [column, value] of Object.entries(arg)) {
|
|
82
|
+
this._where.push(sql.eq(column, value));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return this;
|
|
86
|
+
}
|
|
87
|
+
groupBy(columns) {
|
|
88
|
+
this._groupBy.push(...columns);
|
|
89
|
+
return this;
|
|
90
|
+
}
|
|
91
|
+
having(expr) {
|
|
92
|
+
this._having = expr;
|
|
93
|
+
return this;
|
|
94
|
+
}
|
|
95
|
+
orderBy(columns) {
|
|
96
|
+
this._orderBy.push(...columns);
|
|
97
|
+
return this;
|
|
98
|
+
}
|
|
99
|
+
limit(limit) {
|
|
100
|
+
this._limit = limit;
|
|
101
|
+
return this;
|
|
102
|
+
}
|
|
103
|
+
offset(offset) {
|
|
104
|
+
this._offset = offset;
|
|
105
|
+
return this;
|
|
106
|
+
}
|
|
107
|
+
forUpdate() {
|
|
108
|
+
this._forUpdate = true;
|
|
109
|
+
return this;
|
|
110
|
+
}
|
|
111
|
+
computeParts(ctx) {
|
|
112
|
+
return [
|
|
113
|
+
"SELECT",
|
|
114
|
+
this.distinctPart(),
|
|
115
|
+
this.columnsPart(ctx),
|
|
116
|
+
this.fromPart(),
|
|
117
|
+
this.joinsPart(ctx),
|
|
118
|
+
this.wherePart(ctx),
|
|
119
|
+
this.groupByPart(),
|
|
120
|
+
this.havingPart(ctx),
|
|
121
|
+
this.orderByPart(),
|
|
122
|
+
this.limitPart(ctx),
|
|
123
|
+
this.offsetPart(ctx),
|
|
124
|
+
this.forUpdatePart(),
|
|
125
|
+
];
|
|
126
|
+
}
|
|
127
|
+
distinctPart() {
|
|
128
|
+
if (this._distinct) {
|
|
129
|
+
return "DISTINCT";
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
columnsPart(ctx) {
|
|
133
|
+
if (this._columns.length) {
|
|
134
|
+
return this._columns
|
|
135
|
+
.map((column) => {
|
|
136
|
+
return typeof column === "function"
|
|
137
|
+
? column(ctx)
|
|
138
|
+
: handleColumn(column);
|
|
139
|
+
})
|
|
140
|
+
.join(", ");
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
return "*";
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
fromPart() {
|
|
147
|
+
if (this._table) {
|
|
148
|
+
return `FROM ${handleTable(this._table)}`;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
joinsPart(ctx) {
|
|
152
|
+
if (this._joins.length) {
|
|
153
|
+
return this._joins.map((join) => join(ctx)).join(" ");
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
wherePart(ctx) {
|
|
157
|
+
if (this._where.length) {
|
|
158
|
+
return "WHERE " + groupExpression(this._where, " AND ", false)(ctx);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
groupByPart() {
|
|
162
|
+
if (this._groupBy.length) {
|
|
163
|
+
return "GROUP BY " + handleColumns(this._groupBy);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
havingPart(ctx) {
|
|
167
|
+
if (this._having) {
|
|
168
|
+
return "HAVING " + this._having(ctx);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
orderByPart() {
|
|
172
|
+
if (this._orderBy.length) {
|
|
173
|
+
return "ORDER BY " + handleColumns(this._orderBy);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
limitPart(ctx) {
|
|
177
|
+
if (this._limit) {
|
|
178
|
+
// PostgreSQL/SQLite syntax
|
|
179
|
+
return "LIMIT " + handleValue(this._limit, ctx);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
offsetPart(ctx) {
|
|
183
|
+
if (this._offset) {
|
|
184
|
+
// PostgreSQL/SQLite syntax
|
|
185
|
+
return "OFFSET " + handleValue(this._offset, ctx);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
forUpdatePart() {
|
|
189
|
+
if (this._forUpdate) {
|
|
190
|
+
return "FOR UPDATE";
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
sql.select = (columns) => new SelectStatement(columns);
|
|
195
|
+
class InsertStatement extends Statement {
|
|
196
|
+
_table;
|
|
197
|
+
_values = [];
|
|
198
|
+
_select;
|
|
199
|
+
_returning = [];
|
|
200
|
+
into(table) {
|
|
201
|
+
this._table = table;
|
|
202
|
+
return this;
|
|
203
|
+
}
|
|
204
|
+
values(values) {
|
|
205
|
+
this._values.push(...values);
|
|
206
|
+
return this;
|
|
207
|
+
}
|
|
208
|
+
select(statement) {
|
|
209
|
+
this._select = statement;
|
|
210
|
+
return this;
|
|
211
|
+
}
|
|
212
|
+
returning(columns = ["*"]) {
|
|
213
|
+
this._returning.push(...columns);
|
|
214
|
+
return this;
|
|
215
|
+
}
|
|
216
|
+
computeParts(ctx) {
|
|
217
|
+
return [
|
|
218
|
+
"INSERT",
|
|
219
|
+
this.intoPart(),
|
|
220
|
+
this.columnsPart(),
|
|
221
|
+
this.valuesPart(ctx),
|
|
222
|
+
this.returningPart(),
|
|
223
|
+
];
|
|
224
|
+
}
|
|
225
|
+
intoPart() {
|
|
226
|
+
if (this._table) {
|
|
227
|
+
return "INTO " + handleTable(this._table);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
columnsPart() {
|
|
231
|
+
if (this._values.length) {
|
|
232
|
+
const columns = Object.keys(this._values[0]);
|
|
233
|
+
return "(" + columns.map(handleColumn).join(", ") + ")";
|
|
234
|
+
}
|
|
235
|
+
if (this._select) {
|
|
236
|
+
// @ts-expect-error protected method
|
|
237
|
+
return "(" + this._select.columnsPart() + ")";
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
valuesPart(ctx) {
|
|
241
|
+
if (this._values.length) {
|
|
242
|
+
const columns = Object.keys(this._values[0]);
|
|
243
|
+
const values = this._values.map((values) => `(${columns.map((column) => handleValue(values[column], ctx)).join(", ")})`);
|
|
244
|
+
return "VALUES " + values.join(", ");
|
|
245
|
+
}
|
|
246
|
+
if (this._select) {
|
|
247
|
+
// @ts-expect-error protected method
|
|
248
|
+
return joinParts(this._select.computeParts(ctx));
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
returningPart() {
|
|
252
|
+
if (this._returning.length) {
|
|
253
|
+
return "RETURNING " + handleColumns(this._returning);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
sql.insert = () => new InsertStatement();
|
|
258
|
+
class UpdateStatement extends Statement {
|
|
259
|
+
table;
|
|
260
|
+
_values = {};
|
|
261
|
+
_where = [];
|
|
262
|
+
constructor(table) {
|
|
263
|
+
super();
|
|
264
|
+
this.table = table;
|
|
265
|
+
}
|
|
266
|
+
set(values) {
|
|
267
|
+
Object.assign(this._values, values);
|
|
268
|
+
return this;
|
|
269
|
+
}
|
|
270
|
+
where(arg) {
|
|
271
|
+
if (typeof arg === "function") {
|
|
272
|
+
this._where.push(arg);
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
for (const [column, value] of Object.entries(arg)) {
|
|
276
|
+
this._where.push(sql.eq(column, value));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return this;
|
|
280
|
+
}
|
|
281
|
+
computeParts(ctx) {
|
|
282
|
+
return [
|
|
283
|
+
`UPDATE ${handleTable(this.table)}`,
|
|
284
|
+
this.setPart(ctx),
|
|
285
|
+
this.wherePart(ctx),
|
|
286
|
+
];
|
|
287
|
+
}
|
|
288
|
+
setPart(ctx) {
|
|
289
|
+
const values = [];
|
|
290
|
+
for (const [column, value] of Object.entries(this._values)) {
|
|
291
|
+
values.push(`${handleColumn(column)} = ${handleValue(value, ctx)}`);
|
|
292
|
+
}
|
|
293
|
+
return "SET " + values.join(", ");
|
|
294
|
+
}
|
|
295
|
+
wherePart(ctx) {
|
|
296
|
+
if (this._where.length) {
|
|
297
|
+
return "WHERE " + groupExpression(this._where, " AND ", false)(ctx);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
sql.update = (table) => new UpdateStatement(table);
|
|
302
|
+
class DeleteStatement extends Statement {
|
|
303
|
+
_table;
|
|
304
|
+
_where = [];
|
|
305
|
+
constructor(table) {
|
|
306
|
+
super();
|
|
307
|
+
this._table = table;
|
|
308
|
+
}
|
|
309
|
+
where(arg) {
|
|
310
|
+
if (typeof arg === "function") {
|
|
311
|
+
this._where.push(arg);
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
for (const [column, value] of Object.entries(arg)) {
|
|
315
|
+
this._where.push(sql.eq(column, value));
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return this;
|
|
319
|
+
}
|
|
320
|
+
computeParts(ctx) {
|
|
321
|
+
return [`DELETE FROM ${handleTable(this._table)}`, this.wherePart(ctx)];
|
|
322
|
+
}
|
|
323
|
+
wherePart(ctx) {
|
|
324
|
+
if (this._where.length) {
|
|
325
|
+
return "WHERE " + groupExpression(this._where, " AND ", false)(ctx);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
sql.deleteFrom = (table) => new DeleteStatement(table);
|
|
330
|
+
function groupExpression(clauses, op, includeParens = true) {
|
|
331
|
+
return (ctx) => {
|
|
332
|
+
const output = clauses.map((expr) => expr(ctx)).join(op);
|
|
333
|
+
return includeParens ? `(${output})` : output;
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
sql.and = (clauses) => groupExpression(clauses, " AND ");
|
|
337
|
+
sql.or = (clauses) => groupExpression(clauses, " OR ");
|
|
338
|
+
sql.not = (expr) => (ctx) => "NOT " + expr(ctx);
|
|
339
|
+
function unaryExpression(column, op) {
|
|
340
|
+
return () => `${handleColumn(column)} ${op}`;
|
|
341
|
+
}
|
|
342
|
+
sql.isNull = (column) => unaryExpression(column, "IS NULL");
|
|
343
|
+
sql.isNotNull = (column) => unaryExpression(column, "IS NOT NULL");
|
|
344
|
+
function binaryExpression(column, op, value) {
|
|
345
|
+
return (ctx) => `${handleColumn(column)} ${op} ${handleValue(value, ctx)}`;
|
|
346
|
+
}
|
|
347
|
+
sql.eq = (column, value) => {
|
|
348
|
+
return value === null
|
|
349
|
+
? sql.isNull(column)
|
|
350
|
+
: binaryExpression(column, "=", value);
|
|
351
|
+
};
|
|
352
|
+
sql.notEq = (column, value) => {
|
|
353
|
+
return value === null
|
|
354
|
+
? sql.isNotNull(column)
|
|
355
|
+
: binaryExpression(column, "<>", value);
|
|
356
|
+
};
|
|
357
|
+
sql.lt = (column, value) => binaryExpression(column, "<", value);
|
|
358
|
+
sql.lte = (column, value) => binaryExpression(column, "<=", value);
|
|
359
|
+
sql.gt = (column, value) => binaryExpression(column, ">", value);
|
|
360
|
+
sql.gte = (column, value) => binaryExpression(column, ">=", value);
|
|
361
|
+
sql.between = (column, low, high) => {
|
|
362
|
+
return (ctx) => `${handleColumn(column)} BETWEEN ${handleValue(low, ctx)} AND ${handleValue(high, ctx)}`;
|
|
363
|
+
};
|
|
364
|
+
function likeExpression(column, op, value, escapeChar) {
|
|
365
|
+
return (ctx) => {
|
|
366
|
+
const output = `${handleColumn(column)} ${op} ${handleValue(value, ctx)}`;
|
|
367
|
+
return escapeChar ? `${output} ESCAPE '${escapeChar}'` : output;
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
sql.like = (column, value, escapeChar) => likeExpression(column, "LIKE", value, escapeChar);
|
|
371
|
+
sql.ilike = (column, value, escapeChar) => likeExpression(column, "ILIKE", value, escapeChar);
|
|
372
|
+
sql.in = (column, values) => {
|
|
373
|
+
return (ctx) => `${handleColumn(column)} IN (${values.map((value) => handleValue(value, ctx)).join(", ")})`;
|
|
374
|
+
};
|
|
375
|
+
sql.raw = (text, values = []) => {
|
|
376
|
+
return (ctx) => {
|
|
377
|
+
let i = 0;
|
|
378
|
+
return text.replace(/\?/g, () => {
|
|
379
|
+
if (i >= values.length) {
|
|
380
|
+
throw new Error("not enough values");
|
|
381
|
+
}
|
|
382
|
+
return handleValue(values[i++], ctx);
|
|
383
|
+
});
|
|
384
|
+
};
|
|
385
|
+
};
|
|
386
|
+
function handleColumns(columns) {
|
|
387
|
+
return columns.map(handleColumn).join(", ");
|
|
388
|
+
}
|
|
389
|
+
const WITH_SCHEMA_OR_ALIAS_REGEX = /^((\w+)\.)?(\w+)(( AS)? \w+)?$/i;
|
|
390
|
+
function handleTable(table) {
|
|
391
|
+
return handleColumn(table);
|
|
392
|
+
}
|
|
393
|
+
function handleColumn(name) {
|
|
394
|
+
const match = WITH_SCHEMA_OR_ALIAS_REGEX.exec(name);
|
|
395
|
+
if (match) {
|
|
396
|
+
const schema = match[2];
|
|
397
|
+
const table = match[3];
|
|
398
|
+
const alias = match[4];
|
|
399
|
+
let output = schema
|
|
400
|
+
? `${quoteKey(schema)}.${quoteKey(table)}`
|
|
401
|
+
: quoteKey(table);
|
|
402
|
+
if (alias) {
|
|
403
|
+
output += alias;
|
|
404
|
+
}
|
|
405
|
+
return output;
|
|
406
|
+
}
|
|
407
|
+
return quoteKey(name);
|
|
408
|
+
}
|
|
409
|
+
const UPPERCASE_REGEX = /[A-Z]/;
|
|
410
|
+
// SQL:2023 specification: https://en.wikipedia.org/wiki/List_of_SQL_reserved_words
|
|
411
|
+
// prettier-ignore
|
|
412
|
+
const RESERVED_KEYWORDS = new Set(["abs", "absent", "acos", "all", "allocate", "alter", "and", "any", "any_value", "are", "array", "array_agg", "array_max_cardinality", "as", "asensitive", "asin", "asymmetric", "at", "atan", "atomic", "authorization", "avg", "begin", "begin_frame", "begin_partition", "between", "bigint", "binary", "blob", "boolean", "both", "btrim", "by", "call", "called", "cardinality", "cascaded", "case", "cast", "ceil", "ceiling", "char", "char_length", "character", "character_length", "check", "classifier", "clob", "close", "coalesce", "collate", "collect", "column", "commit", "condition", "connect", "constraint", "contains", "convert", "copy", "corr", "corresponding", "cos", "cosh", "count", "covar_pop", "covar_samp", "create", "cross", "cube", "cume_dist", "current", "current_catalog", "current_date", "current_default_transform_group", "current_path", "current_role", "current_row", "current_schema", "current_time", "current_timestamp", "current_transform_group_for_type", "current_user", "cursor", "cycle", "date", "day", "deallocate", "dec", "decfloat", "decimal", "declare", "default", "define", "delete", "dense_rank", "deref", "describe", "deterministic", "disconnect", "distinct", "double", "drop", "dynamic", "each", "element", "else", "empty", "end", "end_frame", "end_partition", "end-exec", "equals", "escape", "every", "except", "exec", "execute", "exists", "exp", "external", "extract", "false", "fetch", "filter", "first_value", "float", "floor", "for", "foreign", "frame_row", "free", "from", "full", "function", "fusion", "get", "global", "grant", "greatest", "group", "grouping", "groups", "having", "hold", "hour", "identity", "in", "indicator", "initial", "inner", "inout", "insensitive", "insert", "int", "integer", "intersect", "intersection", "interval", "into", "is", "join", "json", "json_array", "json_arrayagg", "json_exists", "json_object", "json_objectagg", "json_query", "json_scalar", "json_serialize", "json_table", "json_table_primitive", "json_value", "lag", "language", "large", "last_value", "lateral", "lead", "leading", "least", "left", "like", "like_regex", "listagg", "ln", "local", "localtime", "localtimestamp", "log", "log10", "lower", "lpad", "ltrim", "match", "match_number", "match_recognize", "matches", "max", "member", "merge", "method", "min", "minute", "mod", "modifies", "module", "month", "multiset", "national", "natural", "nchar", "nclob", "new", "no", "none", "normalize", "not", "nth_value", "ntile", "null", "nullif", "numeric", "occurrences_regex", "octet_length", "of", "offset", "old", "omit", "on", "one", "only", "open", "or", "order", "out", "outer", "over", "overlaps", "overlay", "parameter", "partition", "pattern", "per", "percent", "percent_rank", "percentile_cont", "percentile_disc", "period", "portion", "position", "position_regex", "power", "precedes", "precision", "prepare", "primary", "procedure", "ptf", "range", "rank", "reads", "real", "recursive", "ref", "references", "referencing", "regr_avgx", "regr_avgy", "regr_count", "regr_intercept", "regr_r2", "regr_slope", "regr_sxx", "regr_sxy", "regr_syy", "release", "result", "return", "returns", "revoke", "right", "rollback", "rollup", "row", "row_number", "rows", "rpad", "running", "savepoint", "scope", "scroll", "search", "second", "seek", "select", "sensitive", "session_user", "set", "show", "similar", "sin", "sinh", "skip", "smallint", "some", "specific", "specifictype", "sql", "sqlexception", "sqlstate", "sqlwarning", "sqrt", "start", "static", "stddev_pop", "stddev_samp", "submultiset", "subset", "substring", "substring_regex", "succeeds", "sum", "symmetric", "system", "system_time", "system_user", "table", "tablesample", "tan", "tanh", "then", "time", "timestamp", "timezone_hour", "timezone_minute", "to", "trailing", "translate", "translate_regex", "translation", "treat", "trigger", "trim", "trim_array", "true", "truncate", "uescape", "union", "unique", "unknown", "unnest", "update", "upper", "user", "using", "value", "value_of", "values", "var_pop", "var_samp", "varbinary", "varchar", "varying", "versioning", "when", "whenever", "where", "width_bucket", "window", "with", "within", "without", "year"]);
|
|
413
|
+
function quoteKey(name) {
|
|
414
|
+
if (UPPERCASE_REGEX.test(name) || RESERVED_KEYWORDS.has(name)) {
|
|
415
|
+
return options.quoteChar + name + options.quoteChar;
|
|
416
|
+
}
|
|
417
|
+
return name;
|
|
418
|
+
}
|
|
419
|
+
function handleValue(value, ctx) {
|
|
420
|
+
ctx.values.push(value);
|
|
421
|
+
return options.placeholder.replace("%d", String(ctx.values.length));
|
|
422
|
+
}
|
|
423
|
+
class RawStatement extends Statement {
|
|
424
|
+
text;
|
|
425
|
+
constructor(text) {
|
|
426
|
+
super();
|
|
427
|
+
this.text = text;
|
|
428
|
+
}
|
|
429
|
+
computeParts() {
|
|
430
|
+
return [this.text];
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
sql.rawStatement = (text) => new RawStatement(text);
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { Readable } from "node:stream";
|
|
2
|
+
import { createBrotliCompress, createDeflate, createGzip, createZstdCompress, } from "node:zlib";
|
|
3
|
+
function toHeaders(nodeHeaders) {
|
|
4
|
+
const headers = new Headers();
|
|
5
|
+
for (const [key, value] of Object.entries(nodeHeaders)) {
|
|
6
|
+
if (Array.isArray(value)) {
|
|
7
|
+
for (const val of value) {
|
|
8
|
+
headers.append(key, val);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
else if (value !== undefined) {
|
|
12
|
+
headers.set(key, value);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return headers;
|
|
16
|
+
}
|
|
17
|
+
function readBody(req) {
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
const chunks = [];
|
|
20
|
+
req.setEncoding("utf8");
|
|
21
|
+
req.on("data", (chunk) => chunks.push(chunk));
|
|
22
|
+
req.on("end", () => resolve(chunks.join()));
|
|
23
|
+
req.on("error", (err) => reject(err));
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
function createNativeRequest(nodeReq) {
|
|
27
|
+
const req = Object.create(null);
|
|
28
|
+
req.url = nodeReq.url;
|
|
29
|
+
req.method = nodeReq.method;
|
|
30
|
+
req.headers = toHeaders(nodeReq.headers);
|
|
31
|
+
req.json = () => readBody(nodeReq).then(JSON.parse);
|
|
32
|
+
const controller = new AbortController();
|
|
33
|
+
req.signal = controller.signal;
|
|
34
|
+
nodeReq.on("close", () => controller.abort());
|
|
35
|
+
return req;
|
|
36
|
+
}
|
|
37
|
+
const AVAILABLE_COMPRESSION_ALGORITHMS = [
|
|
38
|
+
{
|
|
39
|
+
name: "zstd", // added in v22.15.0
|
|
40
|
+
createCompressor: createZstdCompress,
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: "brotli", // added in v10.16.0
|
|
44
|
+
createCompressor: createBrotliCompress,
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: "gzip",
|
|
48
|
+
createCompressor: createGzip,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: "deflate",
|
|
52
|
+
createCompressor: createDeflate,
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
function writeResponse(nodeReq, nodeRes, res) {
|
|
56
|
+
const isSSE = res.headers.get("content-type") === "text/event-stream";
|
|
57
|
+
const acceptEncoding = nodeReq.headers["accept-encoding"];
|
|
58
|
+
let compressor;
|
|
59
|
+
if (res.body && acceptEncoding && !isSSE) {
|
|
60
|
+
// TODO handle quality
|
|
61
|
+
// ref: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Accept-Encoding
|
|
62
|
+
for (const { name, createCompressor } of AVAILABLE_COMPRESSION_ALGORITHMS) {
|
|
63
|
+
if (acceptEncoding.includes(name)) {
|
|
64
|
+
res.headers.set("content-encoding", name);
|
|
65
|
+
compressor = createCompressor();
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
nodeRes.setHeaders(res.headers);
|
|
71
|
+
nodeRes.writeHead(res.status);
|
|
72
|
+
if (!res.body) {
|
|
73
|
+
return nodeRes.end();
|
|
74
|
+
}
|
|
75
|
+
if (isSSE) {
|
|
76
|
+
nodeRes.flushHeaders();
|
|
77
|
+
}
|
|
78
|
+
if (compressor) {
|
|
79
|
+
Readable.fromWeb(res.body).pipe(compressor).pipe(nodeRes);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
Readable.fromWeb(res.body).pipe(nodeRes);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
export function toNodeHandler(nativeHandler) {
|
|
86
|
+
return async function (nodeReq, nodeRes) {
|
|
87
|
+
const req = createNativeRequest(nodeReq);
|
|
88
|
+
const httpResponse = await nativeHandler(req);
|
|
89
|
+
writeResponse(nodeReq, nodeRes, httpResponse);
|
|
90
|
+
};
|
|
91
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function waitFor(target: EventTarget, type: string): Promise<unknown>;
|
package/package.json
CHANGED
|
@@ -1,7 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tymber/common",
|
|
3
|
-
"version": "0.0.1
|
|
3
|
+
"version": "0.0.1",
|
|
4
4
|
"description": "The base of the Tymber framework",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"author": "Damien ARRACHEQUESNE"
|
|
6
|
+
"author": "Damien ARRACHEQUESNE",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/tymber-framework/tymber.git"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"main": "dist/index.js",
|
|
13
|
+
"types": "dist/index.d.ts",
|
|
14
|
+
"scripts": {
|
|
15
|
+
"compile": "rm -rf dist/ && tsc",
|
|
16
|
+
"format:check": "prettier -c src/ test/ bun-tests/",
|
|
17
|
+
"format:fix": "prettier -w src/ test/ bun-tests/",
|
|
18
|
+
"test": "tsx --test 'test/**/*.test.ts'"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"src/",
|
|
22
|
+
"dist/"
|
|
23
|
+
],
|
|
24
|
+
"exports": {
|
|
25
|
+
"node": "./dist/index.js",
|
|
26
|
+
"bun": "./src/index.ts",
|
|
27
|
+
"deno": "./src/index.ts"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"ajv": "~8.17.1",
|
|
31
|
+
"ajv-formats": "~3.0.1"
|
|
32
|
+
}
|
|
7
33
|
}
|