opacacms 0.3.18 → 0.3.19
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/dist/{chunk-1bd7fz7n.js → chunk-8mqs2q7j.js} +1 -1
- package/dist/{chunk-2abqb0h6.js → chunk-9dsw6x4x.js} +23 -16
- package/dist/{chunk-b1g8jmth.js → chunk-mvz5hmdb.js} +263 -8
- package/dist/cli/index.js +3 -2
- package/dist/db/better-sqlite.js +1632 -42
- package/dist/db/bun-sqlite.js +1627 -37
- package/dist/db/d1.js +2326 -31
- package/dist/db/index.js +29 -4
- package/dist/db/postgres.js +1623 -32
- package/dist/db/sqlite.js +1631 -41
- package/dist/index.js +7 -9
- package/dist/runtimes/bun.js +3 -7
- package/dist/runtimes/cloudflare-workers.js +3068 -13
- package/dist/runtimes/next.js +3 -7
- package/dist/runtimes/node.js +3 -7
- package/dist/server.js +18 -13
- package/dist/storage/index.js +6 -3
- package/package.json +1 -1
- package/dist/chunk-40tky6qh.js +0 -10
- package/dist/chunk-5xpf5jxd.js +0 -114
- package/dist/chunk-gzbz5jwy.js +0 -700
- package/dist/chunk-h8v093av.js +0 -185
- package/dist/chunk-jq1drsen.js +0 -82
- package/dist/chunk-q5sb5dcr.js +0 -15
- package/dist/chunk-re459gm9.js +0 -429
- package/dist/chunk-s8mqwnm1.js +0 -14
- package/dist/chunk-z9ek88xr.js +0 -15
package/dist/db/d1.js
CHANGED
|
@@ -1,33 +1,2325 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __returnValue = (v) => v;
|
|
3
|
+
function __exportSetter(name, newValue) {
|
|
4
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
5
|
+
}
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, {
|
|
9
|
+
get: all[name],
|
|
10
|
+
enumerable: true,
|
|
11
|
+
configurable: true,
|
|
12
|
+
set: __exportSetter.bind(all, name)
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
16
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
17
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
18
|
+
}) : x)(function(x) {
|
|
19
|
+
if (typeof require !== "undefined")
|
|
20
|
+
return require.apply(this, arguments);
|
|
21
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// src/db/kysely/field-mapper.ts
|
|
25
|
+
function toSnakeCase(str) {
|
|
26
|
+
const res = str.replace(/([A-Z])/g, "_$1").toLowerCase();
|
|
27
|
+
if (res.startsWith("_") && !str.startsWith("_")) {
|
|
28
|
+
return res.slice(1);
|
|
29
|
+
}
|
|
30
|
+
return res;
|
|
31
|
+
}
|
|
32
|
+
function mapFieldToPostgresType(field) {
|
|
33
|
+
switch (field.type) {
|
|
34
|
+
case "text":
|
|
35
|
+
case "richtext":
|
|
36
|
+
case "select":
|
|
37
|
+
case "radio":
|
|
38
|
+
case "relationship":
|
|
39
|
+
return "text";
|
|
40
|
+
case "number":
|
|
41
|
+
return "double precision";
|
|
42
|
+
case "boolean":
|
|
43
|
+
return "boolean";
|
|
44
|
+
case "date":
|
|
45
|
+
return "timestamptz";
|
|
46
|
+
case "json":
|
|
47
|
+
case "file":
|
|
48
|
+
return "jsonb";
|
|
49
|
+
default:
|
|
50
|
+
return "text";
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function mapFieldToSQLiteType(field) {
|
|
54
|
+
switch (field.type) {
|
|
55
|
+
case "text":
|
|
56
|
+
case "richtext":
|
|
57
|
+
case "select":
|
|
58
|
+
case "radio":
|
|
59
|
+
case "relationship":
|
|
60
|
+
case "date":
|
|
61
|
+
case "json":
|
|
62
|
+
case "file":
|
|
63
|
+
return "text";
|
|
64
|
+
case "number":
|
|
65
|
+
return "numeric";
|
|
66
|
+
case "boolean":
|
|
67
|
+
return "integer";
|
|
68
|
+
default:
|
|
69
|
+
return "text";
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function flattenFields(fields, prefix = "") {
|
|
73
|
+
const result = [];
|
|
74
|
+
for (const field of fields) {
|
|
75
|
+
if (field.type === "join" || field.type === "virtual" || field.type === "ui")
|
|
76
|
+
continue;
|
|
77
|
+
const currentName = field.name ? `${prefix}${field.name}` : undefined;
|
|
78
|
+
if (field.type === "group") {
|
|
79
|
+
if (field.fields && Array.isArray(field.fields)) {
|
|
80
|
+
const nextPrefix = currentName ? `${currentName}__` : "";
|
|
81
|
+
result.push(...flattenFields(field.fields, nextPrefix));
|
|
82
|
+
}
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (field.type === "blocks") {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (field.type === "relationship" && "hasMany" in field && field.hasMany) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (currentName) {
|
|
92
|
+
result.push({ ...field, name: currentName });
|
|
93
|
+
}
|
|
94
|
+
if (field.type === "row" || field.type === "collapsible") {
|
|
95
|
+
if (field.fields && Array.isArray(field.fields)) {
|
|
96
|
+
result.push(...flattenFields(field.fields, prefix));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (field.type === "tabs" && field.tabs && Array.isArray(field.tabs)) {
|
|
100
|
+
for (const tab of field.tabs) {
|
|
101
|
+
if (tab.fields && Array.isArray(tab.fields)) {
|
|
102
|
+
result.push(...flattenFields(tab.fields, prefix));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
function getRelationalFields(fields, prefix = "") {
|
|
110
|
+
const result = [];
|
|
111
|
+
for (const field of fields) {
|
|
112
|
+
const currentName = field.name ? `${prefix}${field.name}` : undefined;
|
|
113
|
+
if (field.type === "relationship" && "hasMany" in field && field.hasMany || field.type === "blocks") {
|
|
114
|
+
if (currentName) {
|
|
115
|
+
result.push({ ...field, name: currentName });
|
|
116
|
+
}
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (field.type === "group" || field.type === "row" || field.type === "collapsible") {
|
|
120
|
+
if (field.fields && Array.isArray(field.fields)) {
|
|
121
|
+
const nextPrefix = field.type === "group" && field.name ? `${currentName}__` : prefix;
|
|
122
|
+
result.push(...getRelationalFields(field.fields, nextPrefix));
|
|
123
|
+
}
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
if (field.type === "tabs" && field.tabs && Array.isArray(field.tabs)) {
|
|
127
|
+
for (const tab of field.tabs) {
|
|
128
|
+
if (tab.fields && Array.isArray(tab.fields)) {
|
|
129
|
+
result.push(...getRelationalFields(tab.fields, prefix));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return result;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// src/utils/context.ts
|
|
138
|
+
var {AsyncLocalStorage} = (() => ({}));
|
|
139
|
+
function getUserIdFromContext() {
|
|
140
|
+
return requestContext.getStore()?.userId || null;
|
|
141
|
+
}
|
|
142
|
+
function getPreviousDataFromContext() {
|
|
143
|
+
return requestContext.getStore()?.previousData || null;
|
|
144
|
+
}
|
|
145
|
+
var GLOBAL_CONTEXT_KEY, requestContext;
|
|
146
|
+
var init_context = __esm(() => {
|
|
147
|
+
GLOBAL_CONTEXT_KEY = Symbol.for("opacacms.requestContext");
|
|
148
|
+
if (!globalThis[GLOBAL_CONTEXT_KEY]) {
|
|
149
|
+
globalThis[GLOBAL_CONTEXT_KEY] = new AsyncLocalStorage;
|
|
150
|
+
}
|
|
151
|
+
requestContext = globalThis[GLOBAL_CONTEXT_KEY];
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// src/db/kysely/plugins/audit-logging.ts
|
|
155
|
+
class AuditLoggingPlugin {
|
|
156
|
+
auditTable;
|
|
157
|
+
getUserId;
|
|
158
|
+
db;
|
|
159
|
+
queryNodes = new WeakMap;
|
|
160
|
+
constructor(config = {}) {
|
|
161
|
+
this.auditTable = config.auditTable || "_audit_logs";
|
|
162
|
+
this.getUserId = config.getUserId;
|
|
163
|
+
}
|
|
164
|
+
setDb(db) {
|
|
165
|
+
this.db = db;
|
|
166
|
+
}
|
|
167
|
+
transformQuery(args) {
|
|
168
|
+
this.queryNodes.set(args.queryId, args.node);
|
|
169
|
+
return args.node;
|
|
170
|
+
}
|
|
171
|
+
async transformResult(args) {
|
|
172
|
+
const { result, queryId } = args;
|
|
173
|
+
const queryNode = this.queryNodes.get(queryId);
|
|
174
|
+
this.queryNodes.delete(queryId);
|
|
175
|
+
if (!queryNode || queryNode.kind === "SelectQueryNode")
|
|
176
|
+
return result;
|
|
177
|
+
let tableName = "";
|
|
178
|
+
let operation = "other";
|
|
179
|
+
if (queryNode.kind === "InsertQueryNode") {
|
|
180
|
+
tableName = this.getTableName(queryNode.into);
|
|
181
|
+
operation = "create";
|
|
182
|
+
} else if (queryNode.kind === "UpdateQueryNode") {
|
|
183
|
+
tableName = this.getTableName(queryNode.table);
|
|
184
|
+
operation = "update";
|
|
185
|
+
} else if (queryNode.kind === "DeleteQueryNode") {
|
|
186
|
+
tableName = this.getTableName(queryNode.table || queryNode.from?.froms?.[0]);
|
|
187
|
+
operation = "delete";
|
|
188
|
+
}
|
|
189
|
+
if (!tableName || tableName === this.auditTable)
|
|
190
|
+
return result;
|
|
191
|
+
const stashedPrevious = getPreviousDataFromContext();
|
|
192
|
+
this.logOperation(operation, tableName, queryNode, result, stashedPrevious).catch((err) => {
|
|
193
|
+
console.error("[OpacaCMS] Audit logging failed:", err);
|
|
194
|
+
});
|
|
195
|
+
return result;
|
|
196
|
+
}
|
|
197
|
+
getTableName(node) {
|
|
198
|
+
if (!node)
|
|
199
|
+
return "";
|
|
200
|
+
if (node.kind === "TableNode") {
|
|
201
|
+
return node.table?.identifier?.name || node.table?.name || "";
|
|
202
|
+
}
|
|
203
|
+
if (node.table?.kind === "TableNode") {
|
|
204
|
+
return node.table.table?.identifier?.name || node.table.table?.name || "";
|
|
205
|
+
}
|
|
206
|
+
return node.table?.name || node.table?.identifier?.name || node.name || "";
|
|
207
|
+
}
|
|
208
|
+
async logOperation(operation, collection, queryNode, result, stashedPrevious = null) {
|
|
209
|
+
if (!this.db) {
|
|
210
|
+
console.warn("[OpacaCMS] AuditLoggingPlugin: No database instance set, skipping log.");
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
try {
|
|
214
|
+
let entityId = "unknown";
|
|
215
|
+
let newData = null;
|
|
216
|
+
let previousData = null;
|
|
217
|
+
if (queryNode.kind === "InsertQueryNode") {
|
|
218
|
+
entityId = result.insertId?.toString() || "new";
|
|
219
|
+
const columns = queryNode.columns;
|
|
220
|
+
const valuesNode = queryNode.values;
|
|
221
|
+
if (Array.isArray(columns) && valuesNode?.kind === "ValuesNode") {
|
|
222
|
+
const row = valuesNode.values?.[0]?.values;
|
|
223
|
+
if (Array.isArray(row)) {
|
|
224
|
+
newData = {};
|
|
225
|
+
for (let i = 0;i < columns.length; i++) {
|
|
226
|
+
const col = columns[i]?.column?.name || columns[i]?.name;
|
|
227
|
+
if (col) {
|
|
228
|
+
const node = row[i];
|
|
229
|
+
newData[col] = node?.value !== undefined ? node.value : node?.name || node?.identifier?.name || null;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if ((entityId === "new" || entityId === "unknown") && stashedPrevious?.id) {
|
|
235
|
+
entityId = stashedPrevious.id.toString();
|
|
236
|
+
}
|
|
237
|
+
} else if (queryNode.kind === "UpdateQueryNode") {
|
|
238
|
+
entityId = "updated";
|
|
239
|
+
const where = queryNode.where;
|
|
240
|
+
const filter = where?.where;
|
|
241
|
+
if (filter?.kind === "BinaryOperationNode") {
|
|
242
|
+
const op = filter;
|
|
243
|
+
const leftNode = op.left;
|
|
244
|
+
const leftName = leftNode?.column?.name || leftNode?.name || leftNode?.identifier?.name;
|
|
245
|
+
if (op.operator?.operator === "=" && leftName === "id") {
|
|
246
|
+
const right = op.right;
|
|
247
|
+
entityId = (right?.value !== undefined ? right.value : right?.name || right?.identifier?.name)?.toString() || "updated";
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (entityId === "updated" && stashedPrevious?.id) {
|
|
251
|
+
entityId = stashedPrevious.id.toString();
|
|
252
|
+
}
|
|
253
|
+
const updates = queryNode.updates;
|
|
254
|
+
if (Array.isArray(updates)) {
|
|
255
|
+
newData = {};
|
|
256
|
+
for (const upd of updates) {
|
|
257
|
+
const col = upd.column?.column?.name || upd.column?.name;
|
|
258
|
+
if (col) {
|
|
259
|
+
const valNode = upd.value;
|
|
260
|
+
newData[col] = valNode?.value !== undefined ? valNode.value : valNode?.name || valNode?.identifier?.name || null;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (entityId !== "updated" && entityId !== "unknown") {
|
|
265
|
+
previousData = stashedPrevious;
|
|
266
|
+
if (previousData) {
|
|
267
|
+
console.debug(`[AuditLoggingPlugin] Used stashed previous data for ${entityId}`);
|
|
268
|
+
}
|
|
269
|
+
if (!previousData) {
|
|
270
|
+
previousData = getPreviousDataFromContext();
|
|
271
|
+
}
|
|
272
|
+
if (!previousData) {
|
|
273
|
+
try {
|
|
274
|
+
previousData = await this.db.selectFrom(collection).selectAll().where("id", "=", entityId).executeTakeFirst();
|
|
275
|
+
if (previousData) {
|
|
276
|
+
console.debug(`[AuditLoggingPlugin] Fallback SELECT fetched data for ${entityId}`);
|
|
277
|
+
}
|
|
278
|
+
} catch (e) {}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
} else if (queryNode.kind === "DeleteQueryNode") {
|
|
282
|
+
entityId = "deleted";
|
|
283
|
+
}
|
|
284
|
+
await this.db.insertInto(this.auditTable).values({
|
|
285
|
+
id: crypto.randomUUID(),
|
|
286
|
+
operation,
|
|
287
|
+
collection,
|
|
288
|
+
entity_id: entityId,
|
|
289
|
+
user_id: (this.getUserId ? this.getUserId() : null) || getUserIdFromContext(),
|
|
290
|
+
previous_data: previousData ? JSON.stringify(previousData) : null,
|
|
291
|
+
new_data: newData ? JSON.stringify(newData) : "{}",
|
|
292
|
+
timestamp: new Date().toISOString(),
|
|
293
|
+
created_at: new Date().toISOString(),
|
|
294
|
+
updated_at: new Date().toISOString()
|
|
295
|
+
}).execute();
|
|
296
|
+
} catch (err) {
|
|
297
|
+
console.error("[OpacaCMS] Failed to write audit log:", err);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
var init_audit_logging = __esm(() => {
|
|
302
|
+
init_context();
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
// src/db/kysely/plugins/auto-timestamps.ts
|
|
306
|
+
class AutoTimestampsPlugin {
|
|
307
|
+
createdAtColumn;
|
|
308
|
+
updatedAtColumn;
|
|
309
|
+
constructor(config = {}) {
|
|
310
|
+
this.createdAtColumn = config.createdAtColumn || "created_at";
|
|
311
|
+
this.updatedAtColumn = config.updatedAtColumn || "updated_at";
|
|
312
|
+
}
|
|
313
|
+
transformQuery(args) {
|
|
314
|
+
const { node } = args;
|
|
315
|
+
const now = new Date().toISOString();
|
|
316
|
+
if (node.kind === "InsertQueryNode") {} else if (node.kind === "UpdateQueryNode") {}
|
|
317
|
+
return node;
|
|
318
|
+
}
|
|
319
|
+
async transformResult(args) {
|
|
320
|
+
return args.result;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// src/db/kysely/plugins/cursor-pagination.ts
|
|
325
|
+
class CursorPaginationPlugin {
|
|
326
|
+
after;
|
|
327
|
+
cursorColumn;
|
|
328
|
+
constructor(config) {
|
|
329
|
+
this.after = config.after;
|
|
330
|
+
this.cursorColumn = config.cursorColumn || "id";
|
|
331
|
+
}
|
|
332
|
+
transformQuery(args) {
|
|
333
|
+
if (this.after === undefined)
|
|
334
|
+
return args.node;
|
|
335
|
+
const { node } = args;
|
|
336
|
+
if (node.kind === "SelectQueryNode") {}
|
|
337
|
+
return node;
|
|
338
|
+
}
|
|
339
|
+
async transformResult(args) {
|
|
340
|
+
return args.result;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// src/db/kysely/plugins/deadlock-handler.ts
|
|
345
|
+
class DeadlockRetryPlugin {
|
|
346
|
+
maxRetries;
|
|
347
|
+
initialDelay;
|
|
348
|
+
constructor(config) {
|
|
349
|
+
this.maxRetries = config.maxRetries ?? 3;
|
|
350
|
+
this.initialDelay = config.initialDelay ?? 50;
|
|
351
|
+
}
|
|
352
|
+
transformQuery(args) {
|
|
353
|
+
return args.node;
|
|
354
|
+
}
|
|
355
|
+
async transformResult(args) {
|
|
356
|
+
return args.result;
|
|
357
|
+
}
|
|
358
|
+
async retry(fn) {
|
|
359
|
+
let lastError;
|
|
360
|
+
for (let attempt = 0;attempt <= this.maxRetries; attempt++) {
|
|
361
|
+
try {
|
|
362
|
+
return await fn();
|
|
363
|
+
} catch (error) {
|
|
364
|
+
lastError = error;
|
|
365
|
+
const isLockError = error?.message?.includes("SQLITE_BUSY") || error?.message?.includes("database is locked") || error?.code === "SQLITE_BUSY";
|
|
366
|
+
if (!isLockError || attempt >= this.maxRetries) {
|
|
367
|
+
throw error;
|
|
368
|
+
}
|
|
369
|
+
const delay = this.initialDelay * 2 ** attempt;
|
|
370
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
throw lastError;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// src/db/kysely/plugins/draft-swapper.ts
|
|
378
|
+
class DraftSwapperPlugin {
|
|
379
|
+
draftMode;
|
|
380
|
+
tables;
|
|
381
|
+
constructor(config) {
|
|
382
|
+
this.draftMode = config.draftMode ?? false;
|
|
383
|
+
const allSlugs = [
|
|
384
|
+
...config.collections?.map((c) => c.slug) || [],
|
|
385
|
+
...config.globals?.map((g) => g.slug) || []
|
|
386
|
+
];
|
|
387
|
+
this.tables = new Set(allSlugs.map((s) => toSnakeCase(s)));
|
|
388
|
+
}
|
|
389
|
+
transformQuery(args) {
|
|
390
|
+
if (!this.draftMode)
|
|
391
|
+
return args.node;
|
|
392
|
+
const { node } = args;
|
|
393
|
+
if (node.kind === "SelectQueryNode") {
|
|
394
|
+
if (node.from && node.from.froms) {
|
|
395
|
+
let shouldSwap = false;
|
|
396
|
+
let originalTable = "";
|
|
397
|
+
const newFroms = node.from.froms.map((f) => {
|
|
398
|
+
if (f.kind === "TableNode" && f.table && f.table.kind === "IdentifierNode") {
|
|
399
|
+
const tableName = f.table.name;
|
|
400
|
+
if (this.tables?.has(tableName)) {
|
|
401
|
+
shouldSwap = true;
|
|
402
|
+
originalTable = tableName;
|
|
403
|
+
return {
|
|
404
|
+
...f,
|
|
405
|
+
table: {
|
|
406
|
+
...f.table,
|
|
407
|
+
name: "_doc_versions"
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return f;
|
|
413
|
+
});
|
|
414
|
+
if (shouldSwap) {
|
|
415
|
+
node.from.froms = newFroms;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
return node;
|
|
420
|
+
}
|
|
421
|
+
async transformResult(args) {
|
|
422
|
+
const { result } = args;
|
|
423
|
+
if (!this.draftMode || result.rows.length === 0)
|
|
424
|
+
return result;
|
|
425
|
+
if (result.rows[0] && "data" in result.rows[0] && "collection" in result.rows[0]) {
|
|
426
|
+
const mappedRows = result.rows.map((row) => {
|
|
427
|
+
try {
|
|
428
|
+
const parsedData = typeof row.data === "string" ? JSON.parse(row.data) : row.data;
|
|
429
|
+
return {
|
|
430
|
+
...parsedData,
|
|
431
|
+
_version_id: row.id,
|
|
432
|
+
_version_status: row.status,
|
|
433
|
+
_version_createdAt: row.createdAt
|
|
434
|
+
};
|
|
435
|
+
} catch {
|
|
436
|
+
return row;
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
return { ...result, rows: mappedRows };
|
|
440
|
+
}
|
|
441
|
+
return result;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
var init_draft_swapper = () => {};
|
|
445
|
+
|
|
446
|
+
// src/db/kysely/plugins/field-masking.ts
|
|
447
|
+
class FieldMaskingPlugin {
|
|
448
|
+
maskedFields = new Map;
|
|
449
|
+
disabled;
|
|
450
|
+
constructor(config) {
|
|
451
|
+
this.disabled = config.disabled ?? false;
|
|
452
|
+
this.parseConfig(config);
|
|
453
|
+
}
|
|
454
|
+
parseConfig(config) {
|
|
455
|
+
const allResources = [...config.collections, ...config.globals || []];
|
|
456
|
+
for (const res of allResources) {
|
|
457
|
+
if (res.slug.startsWith("_"))
|
|
458
|
+
continue;
|
|
459
|
+
const tableName = toSnakeCase(res.slug);
|
|
460
|
+
const fields = new Set;
|
|
461
|
+
const findMaskedFields = (fieldList) => {
|
|
462
|
+
for (const f of fieldList) {
|
|
463
|
+
if (f.name && (f.admin?.hidden === true || f.type === "password")) {
|
|
464
|
+
fields.add(toSnakeCase(f.name));
|
|
465
|
+
}
|
|
466
|
+
if ("fields" in f && Array.isArray(f.fields)) {
|
|
467
|
+
findMaskedFields(f.fields);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
findMaskedFields(res.fields);
|
|
472
|
+
if (fields.size > 0) {
|
|
473
|
+
this.maskedFields.set(tableName, fields);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
transformQuery(args) {
|
|
478
|
+
return args.node;
|
|
479
|
+
}
|
|
480
|
+
async transformResult(args) {
|
|
481
|
+
const { result } = args;
|
|
482
|
+
if (this.disabled)
|
|
483
|
+
return result;
|
|
484
|
+
return {
|
|
485
|
+
...result,
|
|
486
|
+
rows: result.rows.map((row) => this.maskRow(row))
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
maskRow(row) {
|
|
490
|
+
if (!row)
|
|
491
|
+
return row;
|
|
492
|
+
const newRow = { ...row };
|
|
493
|
+
for (const [_, fields] of this.maskedFields.entries()) {
|
|
494
|
+
for (const field of fields) {
|
|
495
|
+
if (field in newRow) {
|
|
496
|
+
if (field.includes("password") || field.includes("hash")) {
|
|
497
|
+
newRow[field] = "********";
|
|
498
|
+
} else {
|
|
499
|
+
delete newRow[field];
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
return newRow;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
var init_field_masking = () => {};
|
|
508
|
+
|
|
509
|
+
// src/db/kysely/plugins/fts-normalizer.ts
|
|
510
|
+
class FtsNormalizerPlugin {
|
|
511
|
+
dialect;
|
|
512
|
+
constructor(config) {
|
|
513
|
+
this.dialect = config.dialect;
|
|
514
|
+
}
|
|
515
|
+
transformQuery(args) {
|
|
516
|
+
const { node } = args;
|
|
517
|
+
return node;
|
|
518
|
+
}
|
|
519
|
+
async transformResult(args) {
|
|
520
|
+
return args.result;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// src/db/kysely/plugins/i18n-fallback.ts
|
|
525
|
+
class AutoTranslationFallbackPlugin {
|
|
526
|
+
localizedFields = new Map;
|
|
527
|
+
config;
|
|
528
|
+
constructor(config) {
|
|
529
|
+
this.config = config;
|
|
530
|
+
this.parseConfig(config);
|
|
531
|
+
}
|
|
532
|
+
parseConfig(config) {
|
|
533
|
+
const allResources = [...config.collections, ...config.globals || []];
|
|
534
|
+
for (const res of allResources) {
|
|
535
|
+
const tableName = toSnakeCase(res.slug);
|
|
536
|
+
const fields = new Set;
|
|
537
|
+
const findLocalizedFields = (fieldList) => {
|
|
538
|
+
for (const f of fieldList) {
|
|
539
|
+
if (f.name && f.localized) {
|
|
540
|
+
fields.add(toSnakeCase(f.name));
|
|
541
|
+
}
|
|
542
|
+
if ("fields" in f && Array.isArray(f.fields)) {
|
|
543
|
+
findLocalizedFields(f.fields);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
};
|
|
547
|
+
findLocalizedFields(res.fields);
|
|
548
|
+
if (fields.size > 0) {
|
|
549
|
+
this.localizedFields.set(tableName, fields);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
transformQuery(args) {
|
|
554
|
+
return args.node;
|
|
555
|
+
}
|
|
556
|
+
async transformResult(args) {
|
|
557
|
+
const { result } = args;
|
|
558
|
+
if (this.config.defaultLocale === this.config.currentLocale)
|
|
559
|
+
return result;
|
|
560
|
+
return {
|
|
561
|
+
...result,
|
|
562
|
+
rows: result.rows.map((row) => this.fallbackRow(row))
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
fallbackRow(row) {
|
|
566
|
+
if (!row)
|
|
567
|
+
return row;
|
|
568
|
+
const newRow = { ...row };
|
|
569
|
+
const currentLocSuffix = `_${this.config.currentLocale}`;
|
|
570
|
+
const defaultLocSuffix = `_${this.config.defaultLocale}`;
|
|
571
|
+
for (const [key, value] of Object.entries(newRow)) {
|
|
572
|
+
if (key.endsWith(currentLocSuffix)) {
|
|
573
|
+
const baseName = key.slice(0, -currentLocSuffix.length);
|
|
574
|
+
const defaultValueKey = `${baseName}${defaultLocSuffix}`;
|
|
575
|
+
if ((value === null || value === "" || value === undefined) && newRow[defaultValueKey] !== undefined) {
|
|
576
|
+
newRow[key] = newRow[defaultValueKey];
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
return newRow;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
var init_i18n_fallback = () => {};
|
|
584
|
+
|
|
585
|
+
// src/db/kysely/plugins/id-generation.ts
|
|
586
|
+
class IdGenerationPlugin {
|
|
587
|
+
generateId;
|
|
588
|
+
idColumn;
|
|
589
|
+
constructor(config = {}) {
|
|
590
|
+
this.generateId = config.generateId || (() => crypto.randomUUID());
|
|
591
|
+
this.idColumn = config.idColumn || "id";
|
|
592
|
+
}
|
|
593
|
+
transformQuery(args) {
|
|
594
|
+
const { node } = args;
|
|
595
|
+
if (node.kind === "InsertQueryNode") {}
|
|
596
|
+
return node;
|
|
597
|
+
}
|
|
598
|
+
async transformResult(args) {
|
|
599
|
+
return args.result;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// src/db/kysely/plugins/json-flattener.ts
|
|
604
|
+
class JsonFlattenerPlugin {
|
|
605
|
+
jsonFields = new Map;
|
|
606
|
+
constructor(config) {
|
|
607
|
+
this.parseConfig(config);
|
|
608
|
+
}
|
|
609
|
+
parseConfig(config) {
|
|
610
|
+
const allResources = [...config.collections, ...config.globals || []];
|
|
611
|
+
for (const res of allResources) {
|
|
612
|
+
const tableName = toSnakeCase(res.slug);
|
|
613
|
+
const fields = new Set;
|
|
614
|
+
const findJsonFields = (fieldList) => {
|
|
615
|
+
for (const f of fieldList) {
|
|
616
|
+
if (f.name && (["richtext", "json", "file", "blocks"].includes(f.type) || f.localized)) {
|
|
617
|
+
fields.add(toSnakeCase(f.name));
|
|
618
|
+
}
|
|
619
|
+
if ("fields" in f && Array.isArray(f.fields)) {
|
|
620
|
+
findJsonFields(f.fields);
|
|
621
|
+
}
|
|
622
|
+
if ("blocks" in f && Array.isArray(f.blocks)) {
|
|
623
|
+
for (const b of f.blocks) {
|
|
624
|
+
if (b.fields)
|
|
625
|
+
findJsonFields(b.fields);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
if ("tabs" in f && Array.isArray(f.tabs)) {
|
|
629
|
+
for (const t of f.tabs) {
|
|
630
|
+
if (t.fields)
|
|
631
|
+
findJsonFields(t.fields);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
};
|
|
636
|
+
findJsonFields(res.fields);
|
|
637
|
+
if (fields.size > 0) {
|
|
638
|
+
this.jsonFields.set(tableName, fields);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
transformQuery(args) {
|
|
643
|
+
const { node } = args;
|
|
644
|
+
if (node.kind === "InsertQueryNode" || node.kind === "UpdateQueryNode") {}
|
|
645
|
+
return node;
|
|
646
|
+
}
|
|
647
|
+
async transformResult(args) {
|
|
648
|
+
const { result } = args;
|
|
649
|
+
return {
|
|
650
|
+
...result,
|
|
651
|
+
rows: result.rows.map((row) => this.mapRow(row))
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
mapRow(row) {
|
|
655
|
+
if (!row)
|
|
656
|
+
return row;
|
|
657
|
+
const newRow = { ...row };
|
|
658
|
+
const isSystemTable = Object.keys(newRow).some((k) => k.startsWith("_"));
|
|
659
|
+
if (isSystemTable)
|
|
660
|
+
return newRow;
|
|
661
|
+
for (const [key, value] of Object.entries(newRow)) {
|
|
662
|
+
if (typeof value === "string" && (value.startsWith("{") || value.startsWith("["))) {
|
|
663
|
+
try {
|
|
664
|
+
newRow[key] = JSON.parse(value);
|
|
665
|
+
} catch (_e) {}
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
return newRow;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
var init_json_flattener = () => {};
|
|
672
|
+
|
|
673
|
+
// src/db/kysely/plugins/relationship-preloading.ts
|
|
674
|
+
class RelationshipPreloadingPlugin {
|
|
675
|
+
maxDepth;
|
|
676
|
+
constructor(config = {}) {
|
|
677
|
+
this.maxDepth = config.maxDepth ?? 2;
|
|
678
|
+
}
|
|
679
|
+
transformQuery(args) {
|
|
680
|
+
return args.node;
|
|
681
|
+
}
|
|
682
|
+
async transformResult(args) {
|
|
683
|
+
const { result } = args;
|
|
684
|
+
if (result.rows.length === 0)
|
|
685
|
+
return result;
|
|
686
|
+
return result;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// src/db/kysely/plugins/slug-generation.ts
|
|
691
|
+
class SlugGenerationPlugin {
|
|
692
|
+
sourceColumn;
|
|
693
|
+
slugColumn;
|
|
694
|
+
constructor(config = {}) {
|
|
695
|
+
this.sourceColumn = config.sourceColumn || "title";
|
|
696
|
+
this.slugColumn = config.slugColumn || "slug";
|
|
697
|
+
}
|
|
698
|
+
transformQuery(args) {
|
|
699
|
+
const { node } = args;
|
|
700
|
+
if (node.kind === "InsertQueryNode" || node.kind === "UpdateQueryNode") {}
|
|
701
|
+
return node;
|
|
702
|
+
}
|
|
703
|
+
async transformResult(args) {
|
|
704
|
+
return args.result;
|
|
705
|
+
}
|
|
706
|
+
slugify(text) {
|
|
707
|
+
return text.toString().toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// src/db/kysely/plugins/soft-delete.ts
|
|
712
|
+
class SoftDeletePlugin {
|
|
713
|
+
deletedAtColumn;
|
|
714
|
+
tables;
|
|
715
|
+
constructor(config = {}) {
|
|
716
|
+
this.deletedAtColumn = config.deletedAtColumn || "deleted_at";
|
|
717
|
+
this.tables = config.tables ? new Set(config.tables) : null;
|
|
718
|
+
}
|
|
719
|
+
transformQuery(args) {
|
|
720
|
+
const { node } = args;
|
|
721
|
+
if (node.kind === "DeleteQueryNode") {} else if (node.kind === "SelectQueryNode") {}
|
|
722
|
+
return node;
|
|
723
|
+
}
|
|
724
|
+
async transformResult(args) {
|
|
725
|
+
return args.result;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
// src/db/kysely/plugins/tree-resolver.ts
|
|
730
|
+
class TreeResolverPlugin {
|
|
731
|
+
parentColumn;
|
|
732
|
+
childrenProperty;
|
|
733
|
+
constructor(config) {
|
|
734
|
+
this.parentColumn = config.parentColumn || "parent_id";
|
|
735
|
+
this.childrenProperty = config.childrenProperty || "children";
|
|
736
|
+
}
|
|
737
|
+
transformQuery(args) {
|
|
738
|
+
return args.node;
|
|
739
|
+
}
|
|
740
|
+
async transformResult(args) {
|
|
741
|
+
const { result } = args;
|
|
742
|
+
if (result.rows.length === 0)
|
|
743
|
+
return result;
|
|
744
|
+
return {
|
|
745
|
+
...result,
|
|
746
|
+
rows: this.buildTree(result.rows)
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
buildTree(rows) {
|
|
750
|
+
const map = new Map;
|
|
751
|
+
const roots = [];
|
|
752
|
+
for (const row of rows) {
|
|
753
|
+
map.set(row.id, { ...row, [this.childrenProperty]: [] });
|
|
754
|
+
}
|
|
755
|
+
for (const row of rows) {
|
|
756
|
+
const parentId = row[this.parentColumn];
|
|
757
|
+
const item = map.get(row.id);
|
|
758
|
+
if (parentId && map.has(parentId)) {
|
|
759
|
+
map.get(parentId)[this.childrenProperty].push(item);
|
|
760
|
+
} else {
|
|
761
|
+
roots.push(item);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
return roots;
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
// src/db/kysely/plugins/virtual-field-resolver.ts
|
|
769
|
+
class VirtualFieldResolverPlugin {
|
|
770
|
+
resolvers = new Map;
|
|
771
|
+
config;
|
|
772
|
+
constructor(config) {
|
|
773
|
+
this.config = config;
|
|
774
|
+
this.parseConfig(config);
|
|
775
|
+
}
|
|
776
|
+
parseConfig(config) {
|
|
777
|
+
const allResources = [...config.collections, ...config.globals || []];
|
|
778
|
+
for (const res of allResources) {
|
|
779
|
+
const tableName = toSnakeCase(res.slug);
|
|
780
|
+
const resResolvers = new Map;
|
|
781
|
+
const findResolvers = (fieldList) => {
|
|
782
|
+
for (const f of fieldList) {
|
|
783
|
+
if (f.name && f.type === "virtual" && typeof f.resolve === "function") {
|
|
784
|
+
resResolvers.set(f.name, f.resolve);
|
|
785
|
+
} else if (f.name && f.resolve && typeof f.resolve === "function") {
|
|
786
|
+
resResolvers.set(f.name, f.resolve);
|
|
787
|
+
}
|
|
788
|
+
if ("fields" in f && Array.isArray(f.fields)) {
|
|
789
|
+
findResolvers(f.fields);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
};
|
|
793
|
+
findResolvers(res.fields);
|
|
794
|
+
if (resResolvers.size > 0) {
|
|
795
|
+
this.resolvers.set(tableName, resResolvers);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
transformQuery(args) {
|
|
800
|
+
return args.node;
|
|
801
|
+
}
|
|
802
|
+
async transformResult(args) {
|
|
803
|
+
const { result } = args;
|
|
804
|
+
if (result.rows.length === 0)
|
|
805
|
+
return result;
|
|
806
|
+
const rows = await Promise.all(result.rows.map(async (row) => {
|
|
807
|
+
const newRow = { ...row };
|
|
808
|
+
for (const [tableName, tableResolvers] of this.resolvers.entries()) {
|
|
809
|
+
for (const [fieldName, resolveFn] of tableResolvers.entries()) {
|
|
810
|
+
try {
|
|
811
|
+
newRow[fieldName] = await resolveFn({
|
|
812
|
+
data: row,
|
|
813
|
+
req: this.config.req,
|
|
814
|
+
user: this.config.user,
|
|
815
|
+
session: this.config.session,
|
|
816
|
+
apiKey: this.config.apiKey
|
|
817
|
+
});
|
|
818
|
+
} catch (e) {
|
|
819
|
+
console.error(`Error resolving virtual field ${fieldName} for table ${tableName}:`, e);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
return newRow;
|
|
824
|
+
}));
|
|
825
|
+
return {
|
|
826
|
+
...result,
|
|
827
|
+
rows
|
|
828
|
+
};
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
var init_virtual_field_resolver = () => {};
|
|
832
|
+
|
|
833
|
+
// src/db/kysely/plugins/zod-coercion.ts
|
|
834
|
+
class ZodCoercionPlugin {
|
|
835
|
+
schemas;
|
|
836
|
+
constructor(config) {
|
|
837
|
+
this.schemas = config.schemas;
|
|
838
|
+
}
|
|
839
|
+
transformQuery(args) {
|
|
840
|
+
return args.node;
|
|
841
|
+
}
|
|
842
|
+
async transformResult(args) {
|
|
843
|
+
const { result } = args;
|
|
844
|
+
if (result.rows.length === 0)
|
|
845
|
+
return result;
|
|
846
|
+
const coercedRows = result.rows.map((row) => {
|
|
847
|
+
return row;
|
|
848
|
+
});
|
|
849
|
+
return {
|
|
850
|
+
...result,
|
|
851
|
+
rows: coercedRows
|
|
852
|
+
};
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
// src/db/kysely/plugins/index.ts
|
|
857
|
+
var init_plugins = __esm(() => {
|
|
858
|
+
init_audit_logging();
|
|
859
|
+
init_draft_swapper();
|
|
860
|
+
init_field_masking();
|
|
861
|
+
init_i18n_fallback();
|
|
862
|
+
init_json_flattener();
|
|
863
|
+
init_virtual_field_resolver();
|
|
864
|
+
});
|
|
865
|
+
|
|
866
|
+
// src/db/kysely/factory.ts
|
|
867
|
+
var exports_factory = {};
|
|
868
|
+
__export(exports_factory, {
|
|
869
|
+
createOpacaKysely: () => createOpacaKysely
|
|
870
|
+
});
|
|
871
|
+
import { CamelCasePlugin, Kysely } from "kysely";
|
|
872
|
+
function createOpacaKysely(options) {
|
|
873
|
+
const { dialect, config, isAdmin = false } = options;
|
|
874
|
+
const plugins = [
|
|
875
|
+
new JsonFlattenerPlugin({
|
|
876
|
+
collections: config.collections,
|
|
877
|
+
globals: config.globals
|
|
878
|
+
}),
|
|
879
|
+
new ZodCoercionPlugin({
|
|
880
|
+
schemas: {}
|
|
881
|
+
}),
|
|
882
|
+
new AutoTimestampsPlugin,
|
|
883
|
+
new IdGenerationPlugin,
|
|
884
|
+
new SlugGenerationPlugin,
|
|
885
|
+
new AuditLoggingPlugin({
|
|
886
|
+
auditTable: "_audit_logs",
|
|
887
|
+
getUserId: options.getUserId
|
|
888
|
+
}),
|
|
889
|
+
new SoftDeletePlugin,
|
|
890
|
+
new DraftSwapperPlugin({
|
|
891
|
+
draftMode: false,
|
|
892
|
+
collections: config.collections,
|
|
893
|
+
globals: config.globals
|
|
894
|
+
}),
|
|
895
|
+
new AutoTranslationFallbackPlugin({
|
|
896
|
+
collections: config.collections,
|
|
897
|
+
globals: config.globals,
|
|
898
|
+
defaultLocale: config.i18n?.defaultLocale || "en",
|
|
899
|
+
currentLocale: config.i18n?.defaultLocale || "en"
|
|
900
|
+
}),
|
|
901
|
+
new CursorPaginationPlugin({}),
|
|
902
|
+
new FtsNormalizerPlugin({
|
|
903
|
+
dialect: config.db.name
|
|
904
|
+
}),
|
|
905
|
+
new TreeResolverPlugin({}),
|
|
906
|
+
new VirtualFieldResolverPlugin({
|
|
907
|
+
collections: config.collections,
|
|
908
|
+
globals: config.globals
|
|
909
|
+
}),
|
|
910
|
+
new RelationshipPreloadingPlugin({}),
|
|
911
|
+
new DeadlockRetryPlugin({ maxRetries: 5 })
|
|
912
|
+
];
|
|
913
|
+
const db = new Kysely({
|
|
914
|
+
dialect,
|
|
915
|
+
plugins: [new CamelCasePlugin, ...plugins]
|
|
916
|
+
});
|
|
917
|
+
for (const plugin of plugins) {
|
|
918
|
+
if (plugin && typeof plugin.setDb === "function") {
|
|
919
|
+
plugin.setDb(db);
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
return db;
|
|
923
|
+
}
|
|
924
|
+
var init_factory = __esm(() => {
|
|
925
|
+
init_plugins();
|
|
926
|
+
});
|
|
927
|
+
|
|
928
|
+
// node:path
|
|
929
|
+
var exports_path = {};
|
|
930
|
+
__export(exports_path, {
|
|
931
|
+
sep: () => sep,
|
|
932
|
+
resolve: () => resolve,
|
|
933
|
+
relative: () => relative,
|
|
934
|
+
posix: () => posix,
|
|
935
|
+
parse: () => parse,
|
|
936
|
+
normalize: () => normalize,
|
|
937
|
+
join: () => join,
|
|
938
|
+
isAbsolute: () => isAbsolute,
|
|
939
|
+
format: () => format,
|
|
940
|
+
extname: () => extname,
|
|
941
|
+
dirname: () => dirname,
|
|
942
|
+
delimiter: () => delimiter,
|
|
943
|
+
default: () => path_default,
|
|
944
|
+
basename: () => basename,
|
|
945
|
+
_makeLong: () => _makeLong
|
|
946
|
+
});
|
|
947
|
+
function assertPath(path) {
|
|
948
|
+
if (typeof path !== "string")
|
|
949
|
+
throw TypeError("Path must be a string. Received " + JSON.stringify(path));
|
|
950
|
+
}
|
|
951
|
+
function normalizeStringPosix(path, allowAboveRoot) {
|
|
952
|
+
var res = "", lastSegmentLength = 0, lastSlash = -1, dots = 0, code;
|
|
953
|
+
for (var i = 0;i <= path.length; ++i) {
|
|
954
|
+
if (i < path.length)
|
|
955
|
+
code = path.charCodeAt(i);
|
|
956
|
+
else if (code === 47)
|
|
957
|
+
break;
|
|
958
|
+
else
|
|
959
|
+
code = 47;
|
|
960
|
+
if (code === 47) {
|
|
961
|
+
if (lastSlash === i - 1 || dots === 1)
|
|
962
|
+
;
|
|
963
|
+
else if (lastSlash !== i - 1 && dots === 2) {
|
|
964
|
+
if (res.length < 2 || lastSegmentLength !== 2 || res.charCodeAt(res.length - 1) !== 46 || res.charCodeAt(res.length - 2) !== 46) {
|
|
965
|
+
if (res.length > 2) {
|
|
966
|
+
var lastSlashIndex = res.lastIndexOf("/");
|
|
967
|
+
if (lastSlashIndex !== res.length - 1) {
|
|
968
|
+
if (lastSlashIndex === -1)
|
|
969
|
+
res = "", lastSegmentLength = 0;
|
|
970
|
+
else
|
|
971
|
+
res = res.slice(0, lastSlashIndex), lastSegmentLength = res.length - 1 - res.lastIndexOf("/");
|
|
972
|
+
lastSlash = i, dots = 0;
|
|
973
|
+
continue;
|
|
974
|
+
}
|
|
975
|
+
} else if (res.length === 2 || res.length === 1) {
|
|
976
|
+
res = "", lastSegmentLength = 0, lastSlash = i, dots = 0;
|
|
977
|
+
continue;
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
if (allowAboveRoot) {
|
|
981
|
+
if (res.length > 0)
|
|
982
|
+
res += "/..";
|
|
983
|
+
else
|
|
984
|
+
res = "..";
|
|
985
|
+
lastSegmentLength = 2;
|
|
986
|
+
}
|
|
987
|
+
} else {
|
|
988
|
+
if (res.length > 0)
|
|
989
|
+
res += "/" + path.slice(lastSlash + 1, i);
|
|
990
|
+
else
|
|
991
|
+
res = path.slice(lastSlash + 1, i);
|
|
992
|
+
lastSegmentLength = i - lastSlash - 1;
|
|
993
|
+
}
|
|
994
|
+
lastSlash = i, dots = 0;
|
|
995
|
+
} else if (code === 46 && dots !== -1)
|
|
996
|
+
++dots;
|
|
997
|
+
else
|
|
998
|
+
dots = -1;
|
|
999
|
+
}
|
|
1000
|
+
return res;
|
|
1001
|
+
}
|
|
1002
|
+
function _format(sep, pathObject) {
|
|
1003
|
+
var dir = pathObject.dir || pathObject.root, base = pathObject.base || (pathObject.name || "") + (pathObject.ext || "");
|
|
1004
|
+
if (!dir)
|
|
1005
|
+
return base;
|
|
1006
|
+
if (dir === pathObject.root)
|
|
1007
|
+
return dir + base;
|
|
1008
|
+
return dir + sep + base;
|
|
1009
|
+
}
|
|
1010
|
+
function resolve() {
|
|
1011
|
+
var resolvedPath = "", resolvedAbsolute = false, cwd;
|
|
1012
|
+
for (var i = arguments.length - 1;i >= -1 && !resolvedAbsolute; i--) {
|
|
1013
|
+
var path;
|
|
1014
|
+
if (i >= 0)
|
|
1015
|
+
path = arguments[i];
|
|
1016
|
+
else {
|
|
1017
|
+
if (cwd === undefined)
|
|
1018
|
+
cwd = process.cwd();
|
|
1019
|
+
path = cwd;
|
|
1020
|
+
}
|
|
1021
|
+
if (assertPath(path), path.length === 0)
|
|
1022
|
+
continue;
|
|
1023
|
+
resolvedPath = path + "/" + resolvedPath, resolvedAbsolute = path.charCodeAt(0) === 47;
|
|
1024
|
+
}
|
|
1025
|
+
if (resolvedPath = normalizeStringPosix(resolvedPath, !resolvedAbsolute), resolvedAbsolute)
|
|
1026
|
+
if (resolvedPath.length > 0)
|
|
1027
|
+
return "/" + resolvedPath;
|
|
1028
|
+
else
|
|
1029
|
+
return "/";
|
|
1030
|
+
else if (resolvedPath.length > 0)
|
|
1031
|
+
return resolvedPath;
|
|
1032
|
+
else
|
|
1033
|
+
return ".";
|
|
1034
|
+
}
|
|
1035
|
+
function normalize(path) {
|
|
1036
|
+
if (assertPath(path), path.length === 0)
|
|
1037
|
+
return ".";
|
|
1038
|
+
var isAbsolute = path.charCodeAt(0) === 47, trailingSeparator = path.charCodeAt(path.length - 1) === 47;
|
|
1039
|
+
if (path = normalizeStringPosix(path, !isAbsolute), path.length === 0 && !isAbsolute)
|
|
1040
|
+
path = ".";
|
|
1041
|
+
if (path.length > 0 && trailingSeparator)
|
|
1042
|
+
path += "/";
|
|
1043
|
+
if (isAbsolute)
|
|
1044
|
+
return "/" + path;
|
|
1045
|
+
return path;
|
|
1046
|
+
}
|
|
1047
|
+
function isAbsolute(path) {
|
|
1048
|
+
return assertPath(path), path.length > 0 && path.charCodeAt(0) === 47;
|
|
1049
|
+
}
|
|
1050
|
+
function join() {
|
|
1051
|
+
if (arguments.length === 0)
|
|
1052
|
+
return ".";
|
|
1053
|
+
var joined;
|
|
1054
|
+
for (var i = 0;i < arguments.length; ++i) {
|
|
1055
|
+
var arg = arguments[i];
|
|
1056
|
+
if (assertPath(arg), arg.length > 0)
|
|
1057
|
+
if (joined === undefined)
|
|
1058
|
+
joined = arg;
|
|
1059
|
+
else
|
|
1060
|
+
joined += "/" + arg;
|
|
1061
|
+
}
|
|
1062
|
+
if (joined === undefined)
|
|
1063
|
+
return ".";
|
|
1064
|
+
return normalize(joined);
|
|
1065
|
+
}
|
|
1066
|
+
function relative(from, to) {
|
|
1067
|
+
if (assertPath(from), assertPath(to), from === to)
|
|
1068
|
+
return "";
|
|
1069
|
+
if (from = resolve(from), to = resolve(to), from === to)
|
|
1070
|
+
return "";
|
|
1071
|
+
var fromStart = 1;
|
|
1072
|
+
for (;fromStart < from.length; ++fromStart)
|
|
1073
|
+
if (from.charCodeAt(fromStart) !== 47)
|
|
1074
|
+
break;
|
|
1075
|
+
var fromEnd = from.length, fromLen = fromEnd - fromStart, toStart = 1;
|
|
1076
|
+
for (;toStart < to.length; ++toStart)
|
|
1077
|
+
if (to.charCodeAt(toStart) !== 47)
|
|
1078
|
+
break;
|
|
1079
|
+
var toEnd = to.length, toLen = toEnd - toStart, length = fromLen < toLen ? fromLen : toLen, lastCommonSep = -1, i = 0;
|
|
1080
|
+
for (;i <= length; ++i) {
|
|
1081
|
+
if (i === length) {
|
|
1082
|
+
if (toLen > length) {
|
|
1083
|
+
if (to.charCodeAt(toStart + i) === 47)
|
|
1084
|
+
return to.slice(toStart + i + 1);
|
|
1085
|
+
else if (i === 0)
|
|
1086
|
+
return to.slice(toStart + i);
|
|
1087
|
+
} else if (fromLen > length) {
|
|
1088
|
+
if (from.charCodeAt(fromStart + i) === 47)
|
|
1089
|
+
lastCommonSep = i;
|
|
1090
|
+
else if (i === 0)
|
|
1091
|
+
lastCommonSep = 0;
|
|
1092
|
+
}
|
|
1093
|
+
break;
|
|
1094
|
+
}
|
|
1095
|
+
var fromCode = from.charCodeAt(fromStart + i), toCode = to.charCodeAt(toStart + i);
|
|
1096
|
+
if (fromCode !== toCode)
|
|
1097
|
+
break;
|
|
1098
|
+
else if (fromCode === 47)
|
|
1099
|
+
lastCommonSep = i;
|
|
1100
|
+
}
|
|
1101
|
+
var out = "";
|
|
1102
|
+
for (i = fromStart + lastCommonSep + 1;i <= fromEnd; ++i)
|
|
1103
|
+
if (i === fromEnd || from.charCodeAt(i) === 47)
|
|
1104
|
+
if (out.length === 0)
|
|
1105
|
+
out += "..";
|
|
1106
|
+
else
|
|
1107
|
+
out += "/..";
|
|
1108
|
+
if (out.length > 0)
|
|
1109
|
+
return out + to.slice(toStart + lastCommonSep);
|
|
1110
|
+
else {
|
|
1111
|
+
if (toStart += lastCommonSep, to.charCodeAt(toStart) === 47)
|
|
1112
|
+
++toStart;
|
|
1113
|
+
return to.slice(toStart);
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
function _makeLong(path) {
|
|
1117
|
+
return path;
|
|
1118
|
+
}
|
|
1119
|
+
function dirname(path) {
|
|
1120
|
+
if (assertPath(path), path.length === 0)
|
|
1121
|
+
return ".";
|
|
1122
|
+
var code = path.charCodeAt(0), hasRoot = code === 47, end = -1, matchedSlash = true;
|
|
1123
|
+
for (var i = path.length - 1;i >= 1; --i)
|
|
1124
|
+
if (code = path.charCodeAt(i), code === 47) {
|
|
1125
|
+
if (!matchedSlash) {
|
|
1126
|
+
end = i;
|
|
1127
|
+
break;
|
|
1128
|
+
}
|
|
1129
|
+
} else
|
|
1130
|
+
matchedSlash = false;
|
|
1131
|
+
if (end === -1)
|
|
1132
|
+
return hasRoot ? "/" : ".";
|
|
1133
|
+
if (hasRoot && end === 1)
|
|
1134
|
+
return "//";
|
|
1135
|
+
return path.slice(0, end);
|
|
1136
|
+
}
|
|
1137
|
+
function basename(path, ext) {
|
|
1138
|
+
if (ext !== undefined && typeof ext !== "string")
|
|
1139
|
+
throw TypeError('"ext" argument must be a string');
|
|
1140
|
+
assertPath(path);
|
|
1141
|
+
var start = 0, end = -1, matchedSlash = true, i;
|
|
1142
|
+
if (ext !== undefined && ext.length > 0 && ext.length <= path.length) {
|
|
1143
|
+
if (ext.length === path.length && ext === path)
|
|
1144
|
+
return "";
|
|
1145
|
+
var extIdx = ext.length - 1, firstNonSlashEnd = -1;
|
|
1146
|
+
for (i = path.length - 1;i >= 0; --i) {
|
|
1147
|
+
var code = path.charCodeAt(i);
|
|
1148
|
+
if (code === 47) {
|
|
1149
|
+
if (!matchedSlash) {
|
|
1150
|
+
start = i + 1;
|
|
1151
|
+
break;
|
|
1152
|
+
}
|
|
1153
|
+
} else {
|
|
1154
|
+
if (firstNonSlashEnd === -1)
|
|
1155
|
+
matchedSlash = false, firstNonSlashEnd = i + 1;
|
|
1156
|
+
if (extIdx >= 0)
|
|
1157
|
+
if (code === ext.charCodeAt(extIdx)) {
|
|
1158
|
+
if (--extIdx === -1)
|
|
1159
|
+
end = i;
|
|
1160
|
+
} else
|
|
1161
|
+
extIdx = -1, end = firstNonSlashEnd;
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
if (start === end)
|
|
1165
|
+
end = firstNonSlashEnd;
|
|
1166
|
+
else if (end === -1)
|
|
1167
|
+
end = path.length;
|
|
1168
|
+
return path.slice(start, end);
|
|
1169
|
+
} else {
|
|
1170
|
+
for (i = path.length - 1;i >= 0; --i)
|
|
1171
|
+
if (path.charCodeAt(i) === 47) {
|
|
1172
|
+
if (!matchedSlash) {
|
|
1173
|
+
start = i + 1;
|
|
1174
|
+
break;
|
|
1175
|
+
}
|
|
1176
|
+
} else if (end === -1)
|
|
1177
|
+
matchedSlash = false, end = i + 1;
|
|
1178
|
+
if (end === -1)
|
|
1179
|
+
return "";
|
|
1180
|
+
return path.slice(start, end);
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
function extname(path) {
|
|
1184
|
+
assertPath(path);
|
|
1185
|
+
var startDot = -1, startPart = 0, end = -1, matchedSlash = true, preDotState = 0;
|
|
1186
|
+
for (var i = path.length - 1;i >= 0; --i) {
|
|
1187
|
+
var code = path.charCodeAt(i);
|
|
1188
|
+
if (code === 47) {
|
|
1189
|
+
if (!matchedSlash) {
|
|
1190
|
+
startPart = i + 1;
|
|
1191
|
+
break;
|
|
1192
|
+
}
|
|
1193
|
+
continue;
|
|
1194
|
+
}
|
|
1195
|
+
if (end === -1)
|
|
1196
|
+
matchedSlash = false, end = i + 1;
|
|
1197
|
+
if (code === 46) {
|
|
1198
|
+
if (startDot === -1)
|
|
1199
|
+
startDot = i;
|
|
1200
|
+
else if (preDotState !== 1)
|
|
1201
|
+
preDotState = 1;
|
|
1202
|
+
} else if (startDot !== -1)
|
|
1203
|
+
preDotState = -1;
|
|
1204
|
+
}
|
|
1205
|
+
if (startDot === -1 || end === -1 || preDotState === 0 || preDotState === 1 && startDot === end - 1 && startDot === startPart + 1)
|
|
1206
|
+
return "";
|
|
1207
|
+
return path.slice(startDot, end);
|
|
1208
|
+
}
|
|
1209
|
+
function format(pathObject) {
|
|
1210
|
+
if (pathObject === null || typeof pathObject !== "object")
|
|
1211
|
+
throw TypeError('The "pathObject" argument must be of type Object. Received type ' + typeof pathObject);
|
|
1212
|
+
return _format("/", pathObject);
|
|
1213
|
+
}
|
|
1214
|
+
function parse(path) {
|
|
1215
|
+
assertPath(path);
|
|
1216
|
+
var ret = { root: "", dir: "", base: "", ext: "", name: "" };
|
|
1217
|
+
if (path.length === 0)
|
|
1218
|
+
return ret;
|
|
1219
|
+
var code = path.charCodeAt(0), isAbsolute2 = code === 47, start;
|
|
1220
|
+
if (isAbsolute2)
|
|
1221
|
+
ret.root = "/", start = 1;
|
|
1222
|
+
else
|
|
1223
|
+
start = 0;
|
|
1224
|
+
var startDot = -1, startPart = 0, end = -1, matchedSlash = true, i = path.length - 1, preDotState = 0;
|
|
1225
|
+
for (;i >= start; --i) {
|
|
1226
|
+
if (code = path.charCodeAt(i), code === 47) {
|
|
1227
|
+
if (!matchedSlash) {
|
|
1228
|
+
startPart = i + 1;
|
|
1229
|
+
break;
|
|
1230
|
+
}
|
|
1231
|
+
continue;
|
|
1232
|
+
}
|
|
1233
|
+
if (end === -1)
|
|
1234
|
+
matchedSlash = false, end = i + 1;
|
|
1235
|
+
if (code === 46) {
|
|
1236
|
+
if (startDot === -1)
|
|
1237
|
+
startDot = i;
|
|
1238
|
+
else if (preDotState !== 1)
|
|
1239
|
+
preDotState = 1;
|
|
1240
|
+
} else if (startDot !== -1)
|
|
1241
|
+
preDotState = -1;
|
|
1242
|
+
}
|
|
1243
|
+
if (startDot === -1 || end === -1 || preDotState === 0 || preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) {
|
|
1244
|
+
if (end !== -1)
|
|
1245
|
+
if (startPart === 0 && isAbsolute2)
|
|
1246
|
+
ret.base = ret.name = path.slice(1, end);
|
|
1247
|
+
else
|
|
1248
|
+
ret.base = ret.name = path.slice(startPart, end);
|
|
1249
|
+
} else {
|
|
1250
|
+
if (startPart === 0 && isAbsolute2)
|
|
1251
|
+
ret.name = path.slice(1, startDot), ret.base = path.slice(1, end);
|
|
1252
|
+
else
|
|
1253
|
+
ret.name = path.slice(startPart, startDot), ret.base = path.slice(startPart, end);
|
|
1254
|
+
ret.ext = path.slice(startDot, end);
|
|
1255
|
+
}
|
|
1256
|
+
if (startPart > 0)
|
|
1257
|
+
ret.dir = path.slice(0, startPart - 1);
|
|
1258
|
+
else if (isAbsolute2)
|
|
1259
|
+
ret.dir = "/";
|
|
1260
|
+
return ret;
|
|
1261
|
+
}
|
|
1262
|
+
var sep = "/", delimiter = ":", posix, path_default;
|
|
1263
|
+
var init_path = __esm(() => {
|
|
1264
|
+
posix = ((p) => (p.posix = p, p))({ resolve, normalize, isAbsolute, join, relative, _makeLong, dirname, basename, extname, format, parse, sep, delimiter, win32: null, posix: null });
|
|
1265
|
+
path_default = posix;
|
|
1266
|
+
});
|
|
1267
|
+
|
|
1268
|
+
// node:url
|
|
1269
|
+
var exports_url = {};
|
|
1270
|
+
__export(exports_url, {
|
|
1271
|
+
resolveObject: () => urlResolveObject,
|
|
1272
|
+
resolve: () => urlResolve,
|
|
1273
|
+
parse: () => urlParse,
|
|
1274
|
+
format: () => urlFormat,
|
|
1275
|
+
default: () => url_default,
|
|
1276
|
+
Url: () => Url,
|
|
1277
|
+
URLSearchParams: () => URLSearchParams,
|
|
1278
|
+
URL: () => URL
|
|
1279
|
+
});
|
|
1280
|
+
function util_isString(arg) {
|
|
1281
|
+
return typeof arg === "string";
|
|
1282
|
+
}
|
|
1283
|
+
function util_isObject(arg) {
|
|
1284
|
+
return typeof arg === "object" && arg !== null;
|
|
1285
|
+
}
|
|
1286
|
+
function util_isNull(arg) {
|
|
1287
|
+
return arg === null;
|
|
1288
|
+
}
|
|
1289
|
+
function util_isNullOrUndefined(arg) {
|
|
1290
|
+
return arg == null;
|
|
1291
|
+
}
|
|
1292
|
+
function Url() {
|
|
1293
|
+
this.protocol = null, this.slashes = null, this.auth = null, this.host = null, this.port = null, this.hostname = null, this.hash = null, this.search = null, this.query = null, this.pathname = null, this.path = null, this.href = null;
|
|
1294
|
+
}
|
|
1295
|
+
function urlParse(url, parseQueryString, slashesDenoteHost) {
|
|
1296
|
+
if (url && util_isObject(url) && url instanceof Url)
|
|
1297
|
+
return url;
|
|
1298
|
+
var u = new Url;
|
|
1299
|
+
return u.parse(url, parseQueryString, slashesDenoteHost), u;
|
|
1300
|
+
}
|
|
1301
|
+
function urlFormat(obj) {
|
|
1302
|
+
if (util_isString(obj))
|
|
1303
|
+
obj = urlParse(obj);
|
|
1304
|
+
if (!(obj instanceof Url))
|
|
1305
|
+
return Url.prototype.format.call(obj);
|
|
1306
|
+
return obj.format();
|
|
1307
|
+
}
|
|
1308
|
+
function urlResolve(source, relative2) {
|
|
1309
|
+
return urlParse(source, false, true).resolve(relative2);
|
|
1310
|
+
}
|
|
1311
|
+
function urlResolveObject(source, relative2) {
|
|
1312
|
+
if (!source)
|
|
1313
|
+
return relative2;
|
|
1314
|
+
return urlParse(source, false, true).resolveObject(relative2);
|
|
1315
|
+
}
|
|
1316
|
+
var URL, URLSearchParams, protocolPattern, portPattern, simplePathPattern, delims, unwise, autoEscape, nonHostChars, hostEndingChars, hostnameMaxLen = 255, hostnamePartPattern, hostnamePartStart, unsafeProtocol, hostlessProtocol, slashedProtocol, querystring, url_default;
|
|
1317
|
+
var init_url = __esm(() => {
|
|
1318
|
+
({ URL, URLSearchParams } = globalThis);
|
|
1319
|
+
protocolPattern = /^([a-z0-9.+-]+:)/i;
|
|
1320
|
+
portPattern = /:[0-9]*$/;
|
|
1321
|
+
simplePathPattern = /^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/;
|
|
1322
|
+
delims = ["<", ">", '"', "`", " ", "\r", `
|
|
1323
|
+
`, "\t"];
|
|
1324
|
+
unwise = ["{", "}", "|", "\\", "^", "`"].concat(delims);
|
|
1325
|
+
autoEscape = ["'"].concat(unwise);
|
|
1326
|
+
nonHostChars = ["%", "/", "?", ";", "#"].concat(autoEscape);
|
|
1327
|
+
hostEndingChars = ["/", "?", "#"];
|
|
1328
|
+
hostnamePartPattern = /^[+a-z0-9A-Z_-]{0,63}$/;
|
|
1329
|
+
hostnamePartStart = /^([+a-z0-9A-Z_-]{0,63})(.*)$/;
|
|
1330
|
+
unsafeProtocol = { javascript: true, "javascript:": true };
|
|
1331
|
+
hostlessProtocol = { javascript: true, "javascript:": true };
|
|
1332
|
+
slashedProtocol = { http: true, https: true, ftp: true, gopher: true, file: true, "http:": true, "https:": true, "ftp:": true, "gopher:": true, "file:": true };
|
|
1333
|
+
querystring = { parse(str) {
|
|
1334
|
+
var decode = decodeURIComponent;
|
|
1335
|
+
return (str + "").replace(/\+/g, " ").split("&").filter(Boolean).reduce(function(obj, item, index) {
|
|
1336
|
+
var ref = item.split("="), key = decode(ref[0] || ""), val = decode(ref[1] || ""), prev = obj[key];
|
|
1337
|
+
return obj[key] = prev === undefined ? val : [].concat(prev, val), obj;
|
|
1338
|
+
}, {});
|
|
1339
|
+
}, stringify(obj) {
|
|
1340
|
+
var encode = encodeURIComponent;
|
|
1341
|
+
return Object.keys(obj || {}).reduce(function(arr, key) {
|
|
1342
|
+
return [].concat(obj[key]).forEach(function(v) {
|
|
1343
|
+
arr.push(encode(key) + "=" + encode(v));
|
|
1344
|
+
}), arr;
|
|
1345
|
+
}, []).join("&").replace(/\s/g, "+");
|
|
1346
|
+
} };
|
|
1347
|
+
Url.prototype.parse = function(url, parseQueryString, slashesDenoteHost) {
|
|
1348
|
+
if (!util_isString(url))
|
|
1349
|
+
throw TypeError("Parameter 'url' must be a string, not " + typeof url);
|
|
1350
|
+
var queryIndex = url.indexOf("?"), splitter = queryIndex !== -1 && queryIndex < url.indexOf("#") ? "?" : "#", uSplit = url.split(splitter), slashRegex = /\\/g;
|
|
1351
|
+
uSplit[0] = uSplit[0].replace(slashRegex, "/"), url = uSplit.join(splitter);
|
|
1352
|
+
var rest = url;
|
|
1353
|
+
if (rest = rest.trim(), !slashesDenoteHost && url.split("#").length === 1) {
|
|
1354
|
+
var simplePath = simplePathPattern.exec(rest);
|
|
1355
|
+
if (simplePath) {
|
|
1356
|
+
if (this.path = rest, this.href = rest, this.pathname = simplePath[1], simplePath[2])
|
|
1357
|
+
if (this.search = simplePath[2], parseQueryString)
|
|
1358
|
+
this.query = querystring.parse(this.search.substr(1));
|
|
1359
|
+
else
|
|
1360
|
+
this.query = this.search.substr(1);
|
|
1361
|
+
else if (parseQueryString)
|
|
1362
|
+
this.search = "", this.query = {};
|
|
1363
|
+
return this;
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
var proto = protocolPattern.exec(rest);
|
|
1367
|
+
if (proto) {
|
|
1368
|
+
proto = proto[0];
|
|
1369
|
+
var lowerProto = proto.toLowerCase();
|
|
1370
|
+
this.protocol = lowerProto, rest = rest.substr(proto.length);
|
|
1371
|
+
}
|
|
1372
|
+
if (slashesDenoteHost || proto || rest.match(/^\/\/[^@\/]+@[^@\/]+/)) {
|
|
1373
|
+
var slashes = rest.substr(0, 2) === "//";
|
|
1374
|
+
if (slashes && !(proto && hostlessProtocol[proto]))
|
|
1375
|
+
rest = rest.substr(2), this.slashes = true;
|
|
1376
|
+
}
|
|
1377
|
+
if (!hostlessProtocol[proto] && (slashes || proto && !slashedProtocol[proto])) {
|
|
1378
|
+
var hostEnd = -1;
|
|
1379
|
+
for (var i = 0;i < hostEndingChars.length; i++) {
|
|
1380
|
+
var hec = rest.indexOf(hostEndingChars[i]);
|
|
1381
|
+
if (hec !== -1 && (hostEnd === -1 || hec < hostEnd))
|
|
1382
|
+
hostEnd = hec;
|
|
1383
|
+
}
|
|
1384
|
+
var auth, atSign;
|
|
1385
|
+
if (hostEnd === -1)
|
|
1386
|
+
atSign = rest.lastIndexOf("@");
|
|
1387
|
+
else
|
|
1388
|
+
atSign = rest.lastIndexOf("@", hostEnd);
|
|
1389
|
+
if (atSign !== -1)
|
|
1390
|
+
auth = rest.slice(0, atSign), rest = rest.slice(atSign + 1), this.auth = decodeURIComponent(auth);
|
|
1391
|
+
hostEnd = -1;
|
|
1392
|
+
for (var i = 0;i < nonHostChars.length; i++) {
|
|
1393
|
+
var hec = rest.indexOf(nonHostChars[i]);
|
|
1394
|
+
if (hec !== -1 && (hostEnd === -1 || hec < hostEnd))
|
|
1395
|
+
hostEnd = hec;
|
|
1396
|
+
}
|
|
1397
|
+
if (hostEnd === -1)
|
|
1398
|
+
hostEnd = rest.length;
|
|
1399
|
+
this.host = rest.slice(0, hostEnd), rest = rest.slice(hostEnd), this.parseHost(), this.hostname = this.hostname || "";
|
|
1400
|
+
var ipv6Hostname = this.hostname[0] === "[" && this.hostname[this.hostname.length - 1] === "]";
|
|
1401
|
+
if (!ipv6Hostname) {
|
|
1402
|
+
var hostparts = this.hostname.split(/\./);
|
|
1403
|
+
for (var i = 0, l = hostparts.length;i < l; i++) {
|
|
1404
|
+
var part = hostparts[i];
|
|
1405
|
+
if (!part)
|
|
1406
|
+
continue;
|
|
1407
|
+
if (!part.match(hostnamePartPattern)) {
|
|
1408
|
+
var newpart = "";
|
|
1409
|
+
for (var j = 0, k = part.length;j < k; j++)
|
|
1410
|
+
if (part.charCodeAt(j) > 127)
|
|
1411
|
+
newpart += "x";
|
|
1412
|
+
else
|
|
1413
|
+
newpart += part[j];
|
|
1414
|
+
if (!newpart.match(hostnamePartPattern)) {
|
|
1415
|
+
var validParts = hostparts.slice(0, i), notHost = hostparts.slice(i + 1), bit = part.match(hostnamePartStart);
|
|
1416
|
+
if (bit)
|
|
1417
|
+
validParts.push(bit[1]), notHost.unshift(bit[2]);
|
|
1418
|
+
if (notHost.length)
|
|
1419
|
+
rest = "/" + notHost.join(".") + rest;
|
|
1420
|
+
this.hostname = validParts.join(".");
|
|
1421
|
+
break;
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
if (this.hostname.length > hostnameMaxLen)
|
|
1427
|
+
this.hostname = "";
|
|
1428
|
+
else
|
|
1429
|
+
this.hostname = this.hostname.toLowerCase();
|
|
1430
|
+
if (!ipv6Hostname)
|
|
1431
|
+
this.hostname = new URL(`https://${this.hostname}`).hostname;
|
|
1432
|
+
var p = this.port ? ":" + this.port : "", h = this.hostname || "";
|
|
1433
|
+
if (this.host = h + p, this.href += this.host, ipv6Hostname) {
|
|
1434
|
+
if (this.hostname = this.hostname.substr(1, this.hostname.length - 2), rest[0] !== "/")
|
|
1435
|
+
rest = "/" + rest;
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
if (!unsafeProtocol[lowerProto])
|
|
1439
|
+
for (var i = 0, l = autoEscape.length;i < l; i++) {
|
|
1440
|
+
var ae = autoEscape[i];
|
|
1441
|
+
if (rest.indexOf(ae) === -1)
|
|
1442
|
+
continue;
|
|
1443
|
+
var esc = encodeURIComponent(ae);
|
|
1444
|
+
if (esc === ae)
|
|
1445
|
+
esc = escape(ae);
|
|
1446
|
+
rest = rest.split(ae).join(esc);
|
|
1447
|
+
}
|
|
1448
|
+
var hash = rest.indexOf("#");
|
|
1449
|
+
if (hash !== -1)
|
|
1450
|
+
this.hash = rest.substr(hash), rest = rest.slice(0, hash);
|
|
1451
|
+
var qm = rest.indexOf("?");
|
|
1452
|
+
if (qm !== -1) {
|
|
1453
|
+
if (this.search = rest.substr(qm), this.query = rest.substr(qm + 1), parseQueryString)
|
|
1454
|
+
this.query = querystring.parse(this.query);
|
|
1455
|
+
rest = rest.slice(0, qm);
|
|
1456
|
+
} else if (parseQueryString)
|
|
1457
|
+
this.search = "", this.query = {};
|
|
1458
|
+
if (rest)
|
|
1459
|
+
this.pathname = rest;
|
|
1460
|
+
if (slashedProtocol[lowerProto] && this.hostname && !this.pathname)
|
|
1461
|
+
this.pathname = "/";
|
|
1462
|
+
if (this.pathname || this.search) {
|
|
1463
|
+
var p = this.pathname || "", s = this.search || "";
|
|
1464
|
+
this.path = p + s;
|
|
1465
|
+
}
|
|
1466
|
+
return this.href = this.format(), this;
|
|
1467
|
+
};
|
|
1468
|
+
Url.prototype.format = function() {
|
|
1469
|
+
var auth = this.auth || "";
|
|
1470
|
+
if (auth)
|
|
1471
|
+
auth = encodeURIComponent(auth), auth = auth.replace(/%3A/i, ":"), auth += "@";
|
|
1472
|
+
var protocol = this.protocol || "", pathname = this.pathname || "", hash = this.hash || "", host = false, query = "";
|
|
1473
|
+
if (this.host)
|
|
1474
|
+
host = auth + this.host;
|
|
1475
|
+
else if (this.hostname) {
|
|
1476
|
+
if (host = auth + (this.hostname.indexOf(":") === -1 ? this.hostname : "[" + this.hostname + "]"), this.port)
|
|
1477
|
+
host += ":" + this.port;
|
|
1478
|
+
}
|
|
1479
|
+
if (this.query && util_isObject(this.query) && Object.keys(this.query).length)
|
|
1480
|
+
query = querystring.stringify(this.query);
|
|
1481
|
+
var search = this.search || query && "?" + query || "";
|
|
1482
|
+
if (protocol && protocol.substr(-1) !== ":")
|
|
1483
|
+
protocol += ":";
|
|
1484
|
+
if (this.slashes || (!protocol || slashedProtocol[protocol]) && host !== false) {
|
|
1485
|
+
if (host = "//" + (host || ""), pathname && pathname.charAt(0) !== "/")
|
|
1486
|
+
pathname = "/" + pathname;
|
|
1487
|
+
} else if (!host)
|
|
1488
|
+
host = "";
|
|
1489
|
+
if (hash && hash.charAt(0) !== "#")
|
|
1490
|
+
hash = "#" + hash;
|
|
1491
|
+
if (search && search.charAt(0) !== "?")
|
|
1492
|
+
search = "?" + search;
|
|
1493
|
+
return pathname = pathname.replace(/[?#]/g, function(match) {
|
|
1494
|
+
return encodeURIComponent(match);
|
|
1495
|
+
}), search = search.replace("#", "%23"), protocol + host + pathname + search + hash;
|
|
1496
|
+
};
|
|
1497
|
+
Url.prototype.resolve = function(relative2) {
|
|
1498
|
+
return this.resolveObject(urlParse(relative2, false, true)).format();
|
|
1499
|
+
};
|
|
1500
|
+
Url.prototype.resolveObject = function(relative2) {
|
|
1501
|
+
if (util_isString(relative2)) {
|
|
1502
|
+
var rel = new Url;
|
|
1503
|
+
rel.parse(relative2, false, true), relative2 = rel;
|
|
1504
|
+
}
|
|
1505
|
+
var result = new Url, tkeys = Object.keys(this);
|
|
1506
|
+
for (var tk = 0;tk < tkeys.length; tk++) {
|
|
1507
|
+
var tkey = tkeys[tk];
|
|
1508
|
+
result[tkey] = this[tkey];
|
|
1509
|
+
}
|
|
1510
|
+
if (result.hash = relative2.hash, relative2.href === "")
|
|
1511
|
+
return result.href = result.format(), result;
|
|
1512
|
+
if (relative2.slashes && !relative2.protocol) {
|
|
1513
|
+
var rkeys = Object.keys(relative2);
|
|
1514
|
+
for (var rk = 0;rk < rkeys.length; rk++) {
|
|
1515
|
+
var rkey = rkeys[rk];
|
|
1516
|
+
if (rkey !== "protocol")
|
|
1517
|
+
result[rkey] = relative2[rkey];
|
|
1518
|
+
}
|
|
1519
|
+
if (slashedProtocol[result.protocol] && result.hostname && !result.pathname)
|
|
1520
|
+
result.path = result.pathname = "/";
|
|
1521
|
+
return result.href = result.format(), result;
|
|
1522
|
+
}
|
|
1523
|
+
if (relative2.protocol && relative2.protocol !== result.protocol) {
|
|
1524
|
+
if (!slashedProtocol[relative2.protocol]) {
|
|
1525
|
+
var keys = Object.keys(relative2);
|
|
1526
|
+
for (var v = 0;v < keys.length; v++) {
|
|
1527
|
+
var k = keys[v];
|
|
1528
|
+
result[k] = relative2[k];
|
|
1529
|
+
}
|
|
1530
|
+
return result.href = result.format(), result;
|
|
1531
|
+
}
|
|
1532
|
+
if (result.protocol = relative2.protocol, !relative2.host && !hostlessProtocol[relative2.protocol]) {
|
|
1533
|
+
var relPath = (relative2.pathname || "").split("/");
|
|
1534
|
+
while (relPath.length && !(relative2.host = relPath.shift()))
|
|
1535
|
+
;
|
|
1536
|
+
if (!relative2.host)
|
|
1537
|
+
relative2.host = "";
|
|
1538
|
+
if (!relative2.hostname)
|
|
1539
|
+
relative2.hostname = "";
|
|
1540
|
+
if (relPath[0] !== "")
|
|
1541
|
+
relPath.unshift("");
|
|
1542
|
+
if (relPath.length < 2)
|
|
1543
|
+
relPath.unshift("");
|
|
1544
|
+
result.pathname = relPath.join("/");
|
|
1545
|
+
} else
|
|
1546
|
+
result.pathname = relative2.pathname;
|
|
1547
|
+
if (result.search = relative2.search, result.query = relative2.query, result.host = relative2.host || "", result.auth = relative2.auth, result.hostname = relative2.hostname || relative2.host, result.port = relative2.port, result.pathname || result.search) {
|
|
1548
|
+
var p = result.pathname || "", s = result.search || "";
|
|
1549
|
+
result.path = p + s;
|
|
1550
|
+
}
|
|
1551
|
+
return result.slashes = result.slashes || relative2.slashes, result.href = result.format(), result;
|
|
1552
|
+
}
|
|
1553
|
+
var isSourceAbs = result.pathname && result.pathname.charAt(0) === "/", isRelAbs = relative2.host || relative2.pathname && relative2.pathname.charAt(0) === "/", mustEndAbs = isRelAbs || isSourceAbs || result.host && relative2.pathname, removeAllDots = mustEndAbs, srcPath = result.pathname && result.pathname.split("/") || [], relPath = relative2.pathname && relative2.pathname.split("/") || [], psychotic = result.protocol && !slashedProtocol[result.protocol];
|
|
1554
|
+
if (psychotic) {
|
|
1555
|
+
if (result.hostname = "", result.port = null, result.host)
|
|
1556
|
+
if (srcPath[0] === "")
|
|
1557
|
+
srcPath[0] = result.host;
|
|
1558
|
+
else
|
|
1559
|
+
srcPath.unshift(result.host);
|
|
1560
|
+
if (result.host = "", relative2.protocol) {
|
|
1561
|
+
if (relative2.hostname = null, relative2.port = null, relative2.host)
|
|
1562
|
+
if (relPath[0] === "")
|
|
1563
|
+
relPath[0] = relative2.host;
|
|
1564
|
+
else
|
|
1565
|
+
relPath.unshift(relative2.host);
|
|
1566
|
+
relative2.host = null;
|
|
1567
|
+
}
|
|
1568
|
+
mustEndAbs = mustEndAbs && (relPath[0] === "" || srcPath[0] === "");
|
|
1569
|
+
}
|
|
1570
|
+
if (isRelAbs)
|
|
1571
|
+
result.host = relative2.host || relative2.host === "" ? relative2.host : result.host, result.hostname = relative2.hostname || relative2.hostname === "" ? relative2.hostname : result.hostname, result.search = relative2.search, result.query = relative2.query, srcPath = relPath;
|
|
1572
|
+
else if (relPath.length) {
|
|
1573
|
+
if (!srcPath)
|
|
1574
|
+
srcPath = [];
|
|
1575
|
+
srcPath.pop(), srcPath = srcPath.concat(relPath), result.search = relative2.search, result.query = relative2.query;
|
|
1576
|
+
} else if (!util_isNullOrUndefined(relative2.search)) {
|
|
1577
|
+
if (psychotic) {
|
|
1578
|
+
result.hostname = result.host = srcPath.shift();
|
|
1579
|
+
var authInHost = result.host && result.host.indexOf("@") > 0 ? result.host.split("@") : false;
|
|
1580
|
+
if (authInHost)
|
|
1581
|
+
result.auth = authInHost.shift(), result.host = result.hostname = authInHost.shift();
|
|
1582
|
+
}
|
|
1583
|
+
if (result.search = relative2.search, result.query = relative2.query, !util_isNull(result.pathname) || !util_isNull(result.search))
|
|
1584
|
+
result.path = (result.pathname ? result.pathname : "") + (result.search ? result.search : "");
|
|
1585
|
+
return result.href = result.format(), result;
|
|
1586
|
+
}
|
|
1587
|
+
if (!srcPath.length) {
|
|
1588
|
+
if (result.pathname = null, result.search)
|
|
1589
|
+
result.path = "/" + result.search;
|
|
1590
|
+
else
|
|
1591
|
+
result.path = null;
|
|
1592
|
+
return result.href = result.format(), result;
|
|
1593
|
+
}
|
|
1594
|
+
var last = srcPath.slice(-1)[0], hasTrailingSlash = (result.host || relative2.host || srcPath.length > 1) && (last === "." || last === "..") || last === "", up = 0;
|
|
1595
|
+
for (var i = srcPath.length;i >= 0; i--)
|
|
1596
|
+
if (last = srcPath[i], last === ".")
|
|
1597
|
+
srcPath.splice(i, 1);
|
|
1598
|
+
else if (last === "..")
|
|
1599
|
+
srcPath.splice(i, 1), up++;
|
|
1600
|
+
else if (up)
|
|
1601
|
+
srcPath.splice(i, 1), up--;
|
|
1602
|
+
if (!mustEndAbs && !removeAllDots)
|
|
1603
|
+
for (;up--; up)
|
|
1604
|
+
srcPath.unshift("..");
|
|
1605
|
+
if (mustEndAbs && srcPath[0] !== "" && (!srcPath[0] || srcPath[0].charAt(0) !== "/"))
|
|
1606
|
+
srcPath.unshift("");
|
|
1607
|
+
if (hasTrailingSlash && srcPath.join("/").substr(-1) !== "/")
|
|
1608
|
+
srcPath.push("");
|
|
1609
|
+
var isAbsolute2 = srcPath[0] === "" || srcPath[0] && srcPath[0].charAt(0) === "/";
|
|
1610
|
+
if (psychotic) {
|
|
1611
|
+
result.hostname = result.host = isAbsolute2 ? "" : srcPath.length ? srcPath.shift() : "";
|
|
1612
|
+
var authInHost = result.host && result.host.indexOf("@") > 0 ? result.host.split("@") : false;
|
|
1613
|
+
if (authInHost)
|
|
1614
|
+
result.auth = authInHost.shift(), result.host = result.hostname = authInHost.shift();
|
|
1615
|
+
}
|
|
1616
|
+
if (mustEndAbs = mustEndAbs || result.host && srcPath.length, mustEndAbs && !isAbsolute2)
|
|
1617
|
+
srcPath.unshift("");
|
|
1618
|
+
if (!srcPath.length)
|
|
1619
|
+
result.pathname = null, result.path = null;
|
|
1620
|
+
else
|
|
1621
|
+
result.pathname = srcPath.join("/");
|
|
1622
|
+
if (!util_isNull(result.pathname) || !util_isNull(result.search))
|
|
1623
|
+
result.path = (result.pathname ? result.pathname : "") + (result.search ? result.search : "");
|
|
1624
|
+
return result.auth = relative2.auth || result.auth, result.slashes = result.slashes || relative2.slashes, result.href = result.format(), result;
|
|
1625
|
+
};
|
|
1626
|
+
Url.prototype.parseHost = function() {
|
|
1627
|
+
var host = this.host, port = portPattern.exec(host);
|
|
1628
|
+
if (port) {
|
|
1629
|
+
if (port = port[0], port !== ":")
|
|
1630
|
+
this.port = port.substr(1);
|
|
1631
|
+
host = host.substr(0, host.length - port.length);
|
|
1632
|
+
}
|
|
1633
|
+
if (host)
|
|
1634
|
+
this.hostname = host;
|
|
1635
|
+
};
|
|
1636
|
+
url_default = { parse: urlParse, resolve: urlResolve, resolveObject: urlResolveObject, format: urlFormat, Url, URL, URLSearchParams };
|
|
1637
|
+
});
|
|
25
1638
|
|
|
26
1639
|
// src/db/d1.ts
|
|
27
|
-
import fs from "node:fs/promises";
|
|
28
|
-
import path from "node:path";
|
|
29
|
-
import { pathToFileURL } from "node:url";
|
|
30
1640
|
import { CompiledQuery, FileMigrationProvider, Migrator } from "kysely";
|
|
1641
|
+
|
|
1642
|
+
// src/db/adapter.ts
|
|
1643
|
+
class BaseDatabaseAdapter {
|
|
1644
|
+
get raw() {
|
|
1645
|
+
return;
|
|
1646
|
+
}
|
|
1647
|
+
get db() {
|
|
1648
|
+
return;
|
|
1649
|
+
}
|
|
1650
|
+
push;
|
|
1651
|
+
pushDestructive;
|
|
1652
|
+
migrationDir;
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
// src/db/kysely/data-mapper.ts
|
|
1656
|
+
function toCamelCase(str) {
|
|
1657
|
+
if (str.startsWith("_"))
|
|
1658
|
+
return str;
|
|
1659
|
+
return str.replace(/_+([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
1660
|
+
}
|
|
1661
|
+
function flattenPayload(payload, prefix = "", excludeKeys = []) {
|
|
1662
|
+
if (typeof payload !== "object" || payload === null || Array.isArray(payload))
|
|
1663
|
+
return payload;
|
|
1664
|
+
const result = {};
|
|
1665
|
+
for (const key in payload) {
|
|
1666
|
+
if (Object.hasOwn(payload, key)) {
|
|
1667
|
+
const value = payload[key];
|
|
1668
|
+
if (value === undefined)
|
|
1669
|
+
continue;
|
|
1670
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date) && !excludeKeys.includes(key)) {
|
|
1671
|
+
if (key.startsWith("_") && prefix === "") {
|
|
1672
|
+
result[key] = value;
|
|
1673
|
+
} else {
|
|
1674
|
+
const flatNested = flattenPayload(value, `${prefix}${key}__`);
|
|
1675
|
+
Object.assign(result, flatNested);
|
|
1676
|
+
}
|
|
1677
|
+
} else {
|
|
1678
|
+
if (value === undefined)
|
|
1679
|
+
continue;
|
|
1680
|
+
const colName = `${prefix}${toSnakeCase(key)}`;
|
|
1681
|
+
if (typeof value === "object" && value !== null && !(value instanceof Date)) {
|
|
1682
|
+
result[colName] = JSON.stringify(value);
|
|
1683
|
+
} else {
|
|
1684
|
+
result[colName] = value;
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
return result;
|
|
1690
|
+
}
|
|
1691
|
+
function unflattenRow(row) {
|
|
1692
|
+
if (typeof row !== "object" || row === null || Array.isArray(row))
|
|
1693
|
+
return row;
|
|
1694
|
+
const result = {};
|
|
1695
|
+
for (const key in row) {
|
|
1696
|
+
if (Object.hasOwn(row, key)) {
|
|
1697
|
+
let value = row[key];
|
|
1698
|
+
if (typeof value === "string") {
|
|
1699
|
+
const trimmed = value.trim();
|
|
1700
|
+
if (trimmed.startsWith("[") && trimmed.endsWith("]") || trimmed.startsWith("{") && trimmed.endsWith("}")) {
|
|
1701
|
+
try {
|
|
1702
|
+
value = JSON.parse(value);
|
|
1703
|
+
} catch (e) {}
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
if (key === "created_at" || key === "updated_at" || key === "createdAt" || key === "updatedAt") {
|
|
1707
|
+
if (typeof value === "string" && !value.includes("T") && value.includes(" ")) {
|
|
1708
|
+
value = `${value.replace(" ", "T")}Z`;
|
|
1709
|
+
}
|
|
1710
|
+
result[toCamelCase(key)] = value;
|
|
1711
|
+
continue;
|
|
1712
|
+
}
|
|
1713
|
+
if (key.startsWith("_")) {
|
|
1714
|
+
result[key] = value;
|
|
1715
|
+
continue;
|
|
1716
|
+
}
|
|
1717
|
+
const parts = key.split("__");
|
|
1718
|
+
if (parts.length === 1) {
|
|
1719
|
+
result[toCamelCase(key)] = value;
|
|
1720
|
+
continue;
|
|
1721
|
+
}
|
|
1722
|
+
let current = result;
|
|
1723
|
+
let collision = false;
|
|
1724
|
+
const path = [];
|
|
1725
|
+
for (let i = 0;i < parts.length - 1; i++) {
|
|
1726
|
+
const part = toCamelCase(parts[i]);
|
|
1727
|
+
if (current[part] !== undefined && (typeof current[part] !== "object" || current[part] === null)) {
|
|
1728
|
+
collision = true;
|
|
1729
|
+
break;
|
|
1730
|
+
}
|
|
1731
|
+
path.push({ obj: current, key: part });
|
|
1732
|
+
if (!current[part]) {
|
|
1733
|
+
current[part] = {};
|
|
1734
|
+
}
|
|
1735
|
+
current = current[part];
|
|
1736
|
+
}
|
|
1737
|
+
if (!collision) {
|
|
1738
|
+
const lastPart = toCamelCase(parts[parts.length - 1]);
|
|
1739
|
+
if (current[lastPart] !== undefined && typeof current[lastPart] === "object") {
|
|
1740
|
+
collision = true;
|
|
1741
|
+
} else {
|
|
1742
|
+
current[lastPart] = value;
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
if (collision) {
|
|
1746
|
+
result[toCamelCase(key)] = value;
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
return result;
|
|
1751
|
+
}
|
|
1752
|
+
// src/db/kysely/query-builder.ts
|
|
1753
|
+
function buildKyselyWhere(eb, query) {
|
|
1754
|
+
if (!query || Object.keys(query).length === 0)
|
|
1755
|
+
return;
|
|
1756
|
+
const conditions = [];
|
|
1757
|
+
for (const [key, value] of Object.entries(query)) {
|
|
1758
|
+
if (value === undefined)
|
|
1759
|
+
continue;
|
|
1760
|
+
if (key === "or" && Array.isArray(value)) {
|
|
1761
|
+
const orConditions = value.map((v) => buildKyselyWhere(eb, v)).filter((c) => c !== undefined);
|
|
1762
|
+
if (orConditions.length > 0) {
|
|
1763
|
+
conditions.push(eb.or(orConditions));
|
|
1764
|
+
}
|
|
1765
|
+
continue;
|
|
1766
|
+
}
|
|
1767
|
+
if (key === "and" && Array.isArray(value)) {
|
|
1768
|
+
const andConditions = value.map((v) => buildKyselyWhere(eb, v)).filter((c) => c !== undefined);
|
|
1769
|
+
if (andConditions.length > 0) {
|
|
1770
|
+
conditions.push(eb.and(andConditions));
|
|
1771
|
+
}
|
|
1772
|
+
continue;
|
|
1773
|
+
}
|
|
1774
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date)) {
|
|
1775
|
+
for (const [op, val] of Object.entries(value)) {
|
|
1776
|
+
if (val === undefined)
|
|
1777
|
+
continue;
|
|
1778
|
+
switch (op) {
|
|
1779
|
+
case "gt":
|
|
1780
|
+
conditions.push(eb(key, ">", val));
|
|
1781
|
+
break;
|
|
1782
|
+
case "gte":
|
|
1783
|
+
conditions.push(eb(key, ">=", val));
|
|
1784
|
+
break;
|
|
1785
|
+
case "lt":
|
|
1786
|
+
conditions.push(eb(key, "<", val));
|
|
1787
|
+
break;
|
|
1788
|
+
case "lte":
|
|
1789
|
+
conditions.push(eb(key, "<=", val));
|
|
1790
|
+
break;
|
|
1791
|
+
case "like":
|
|
1792
|
+
conditions.push(eb(key, "like", String(val)));
|
|
1793
|
+
break;
|
|
1794
|
+
case "ne":
|
|
1795
|
+
if (val === null)
|
|
1796
|
+
conditions.push(eb(key, "is not", null));
|
|
1797
|
+
else
|
|
1798
|
+
conditions.push(eb(key, "!=", val));
|
|
1799
|
+
break;
|
|
1800
|
+
case "in":
|
|
1801
|
+
if (Array.isArray(val))
|
|
1802
|
+
conditions.push(eb(key, "in", val));
|
|
1803
|
+
break;
|
|
1804
|
+
case "is":
|
|
1805
|
+
if (val === null)
|
|
1806
|
+
conditions.push(eb(key, "is", null));
|
|
1807
|
+
break;
|
|
1808
|
+
default:
|
|
1809
|
+
if (val === null)
|
|
1810
|
+
conditions.push(eb(key, "is", null));
|
|
1811
|
+
else
|
|
1812
|
+
conditions.push(eb(key, "=", val));
|
|
1813
|
+
break;
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
} else {
|
|
1817
|
+
if (value === null) {
|
|
1818
|
+
conditions.push(eb(key, "is", null));
|
|
1819
|
+
} else {
|
|
1820
|
+
conditions.push(eb(key, "=", value));
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
if (conditions.length === 0)
|
|
1825
|
+
return;
|
|
1826
|
+
if (conditions.length === 1)
|
|
1827
|
+
return conditions[0];
|
|
1828
|
+
return eb.and(conditions);
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1831
|
+
// src/db/kysely/schema-builder.ts
|
|
1832
|
+
import { sql } from "kysely";
|
|
1833
|
+
|
|
1834
|
+
// src/db/kysely/sql-utils.ts
|
|
1835
|
+
function assertSafeIdentifier(identifier) {
|
|
1836
|
+
if (typeof identifier !== "string" || !identifier) {
|
|
1837
|
+
throw new Error(`Invalid identifier: must be a non-empty string. Got: ${identifier}`);
|
|
1838
|
+
}
|
|
1839
|
+
const isValid = /^[a-zA-Z0-9_-]+$/.test(identifier);
|
|
1840
|
+
if (!isValid) {
|
|
1841
|
+
throw new Error(`API Error: Unsafe SQL identifier detected: "${identifier}". Identifiers can only contain alphanumeric characters, underscores, and hyphens.`);
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
// src/db/system-schema.ts
|
|
1846
|
+
var getSystemCollections = () => [
|
|
1847
|
+
{
|
|
1848
|
+
slug: "_assets",
|
|
1849
|
+
label: "Assets",
|
|
1850
|
+
apiPath: "assets",
|
|
1851
|
+
fields: [
|
|
1852
|
+
{ name: "id", type: "text", required: true },
|
|
1853
|
+
{ name: "key", type: "text", required: true },
|
|
1854
|
+
{ name: "filename", type: "text", required: true },
|
|
1855
|
+
{ name: "originalFilename", type: "text", required: true },
|
|
1856
|
+
{ name: "mimeType", type: "text", required: true },
|
|
1857
|
+
{ name: "filesize", type: "number", required: true },
|
|
1858
|
+
{ name: "bucket", type: "text", required: true },
|
|
1859
|
+
{ name: "folder", type: "text" },
|
|
1860
|
+
{ name: "altText", type: "text" },
|
|
1861
|
+
{ name: "caption", type: "text" },
|
|
1862
|
+
{ name: "uploadedBy", type: "text" }
|
|
1863
|
+
],
|
|
1864
|
+
timestamps: true
|
|
1865
|
+
},
|
|
1866
|
+
{
|
|
1867
|
+
slug: "_users",
|
|
1868
|
+
apiPath: "users",
|
|
1869
|
+
fields: [
|
|
1870
|
+
{ name: "id", type: "text", required: true },
|
|
1871
|
+
{ name: "name", type: "text", required: true },
|
|
1872
|
+
{ name: "email", type: "text", required: true, unique: true },
|
|
1873
|
+
{ name: "emailVerified", type: "boolean", required: true, defaultValue: false },
|
|
1874
|
+
{ name: "image", type: "text" },
|
|
1875
|
+
{ name: "role", type: "text" },
|
|
1876
|
+
{ name: "banned", type: "boolean" },
|
|
1877
|
+
{ name: "banReason", type: "text" },
|
|
1878
|
+
{ name: "banExpires", type: "date" }
|
|
1879
|
+
],
|
|
1880
|
+
timestamps: true
|
|
1881
|
+
},
|
|
1882
|
+
{
|
|
1883
|
+
slug: "_sessions",
|
|
1884
|
+
fields: [
|
|
1885
|
+
{ name: "id", type: "text", required: true },
|
|
1886
|
+
{ name: "expiresAt", type: "date", required: true },
|
|
1887
|
+
{ name: "token", type: "text", required: true, unique: true },
|
|
1888
|
+
{ name: "ipAddress", type: "text" },
|
|
1889
|
+
{ name: "userAgent", type: "text" },
|
|
1890
|
+
{
|
|
1891
|
+
name: "userId",
|
|
1892
|
+
type: "text",
|
|
1893
|
+
required: true,
|
|
1894
|
+
references: { table: "_users", column: "id", onDelete: "cascade" }
|
|
1895
|
+
},
|
|
1896
|
+
{ name: "impersonatedBy", type: "text" }
|
|
1897
|
+
],
|
|
1898
|
+
timestamps: true,
|
|
1899
|
+
hidden: true
|
|
1900
|
+
},
|
|
1901
|
+
{
|
|
1902
|
+
slug: "_accounts",
|
|
1903
|
+
fields: [
|
|
1904
|
+
{ name: "id", type: "text", required: true },
|
|
1905
|
+
{ name: "accountId", type: "text", required: true },
|
|
1906
|
+
{ name: "providerId", type: "text", required: true },
|
|
1907
|
+
{
|
|
1908
|
+
name: "userId",
|
|
1909
|
+
type: "text",
|
|
1910
|
+
required: true,
|
|
1911
|
+
references: { table: "_users", column: "id", onDelete: "cascade" }
|
|
1912
|
+
},
|
|
1913
|
+
{ name: "accessToken", type: "text" },
|
|
1914
|
+
{ name: "refreshToken", type: "text" },
|
|
1915
|
+
{ name: "idToken", type: "text" },
|
|
1916
|
+
{ name: "accessTokenExpiresAt", type: "date" },
|
|
1917
|
+
{ name: "refreshTokenExpiresAt", type: "date" },
|
|
1918
|
+
{ name: "scope", type: "text" },
|
|
1919
|
+
{ name: "password", type: "text" }
|
|
1920
|
+
],
|
|
1921
|
+
timestamps: true,
|
|
1922
|
+
hidden: true
|
|
1923
|
+
},
|
|
1924
|
+
{
|
|
1925
|
+
slug: "_verifications",
|
|
1926
|
+
fields: [
|
|
1927
|
+
{ name: "id", type: "text", required: true },
|
|
1928
|
+
{ name: "identifier", type: "text", required: true },
|
|
1929
|
+
{ name: "value", type: "text", required: true },
|
|
1930
|
+
{ name: "expiresAt", type: "date", required: true }
|
|
1931
|
+
],
|
|
1932
|
+
timestamps: true,
|
|
1933
|
+
hidden: true
|
|
1934
|
+
},
|
|
1935
|
+
{
|
|
1936
|
+
slug: "_api_keys",
|
|
1937
|
+
fields: [
|
|
1938
|
+
{ name: "id", type: "text", required: true },
|
|
1939
|
+
{ name: "configId", type: "text", required: true },
|
|
1940
|
+
{ name: "name", type: "text" },
|
|
1941
|
+
{ name: "start", type: "text" },
|
|
1942
|
+
{ name: "prefix", type: "text" },
|
|
1943
|
+
{ name: "key", type: "text", required: true },
|
|
1944
|
+
{ name: "referenceId", type: "text", required: true },
|
|
1945
|
+
{ name: "refillInterval", type: "number" },
|
|
1946
|
+
{ name: "refillAmount", type: "number" },
|
|
1947
|
+
{ name: "lastRefillAt", type: "date" },
|
|
1948
|
+
{ name: "enabled", type: "boolean", required: true },
|
|
1949
|
+
{ name: "rateLimitEnabled", type: "boolean", required: true },
|
|
1950
|
+
{ name: "rateLimitTimeWindow", type: "number" },
|
|
1951
|
+
{ name: "rateLimitMax", type: "number" },
|
|
1952
|
+
{ name: "requestCount", type: "number", required: true },
|
|
1953
|
+
{ name: "remaining", type: "number" },
|
|
1954
|
+
{ name: "lastRequest", type: "date" },
|
|
1955
|
+
{ name: "expiresAt", type: "date" },
|
|
1956
|
+
{ name: "permissions", type: "text" },
|
|
1957
|
+
{ name: "metadata", type: "text" }
|
|
1958
|
+
],
|
|
1959
|
+
timestamps: { createdAt: "createdAt", updatedAt: "updatedAt" },
|
|
1960
|
+
hidden: true
|
|
1961
|
+
},
|
|
1962
|
+
{
|
|
1963
|
+
slug: "_plugin_settings",
|
|
1964
|
+
label: "Plugin Settings",
|
|
1965
|
+
apiPath: "plugin-settings",
|
|
1966
|
+
fields: [
|
|
1967
|
+
{ name: "pluginName", type: "text", required: true, unique: true },
|
|
1968
|
+
{ name: "config", type: "json", required: true }
|
|
1969
|
+
],
|
|
1970
|
+
timestamps: true,
|
|
1971
|
+
hidden: false,
|
|
1972
|
+
admin: {
|
|
1973
|
+
hidden: true,
|
|
1974
|
+
disableAdmin: true
|
|
1975
|
+
}
|
|
1976
|
+
},
|
|
1977
|
+
{
|
|
1978
|
+
slug: "_audit_logs",
|
|
1979
|
+
label: "Audit Logs",
|
|
1980
|
+
apiPath: "audit-logs",
|
|
1981
|
+
fields: [
|
|
1982
|
+
{ name: "id", type: "text", required: true },
|
|
1983
|
+
{ name: "operation", type: "text", required: true },
|
|
1984
|
+
{ name: "collection", type: "text", required: true },
|
|
1985
|
+
{ name: "entity_id", type: "text", required: true },
|
|
1986
|
+
{ name: "user_id", type: "text" },
|
|
1987
|
+
{ name: "previous_data", type: "json" },
|
|
1988
|
+
{ name: "new_data", type: "json" },
|
|
1989
|
+
{ name: "timestamp", type: "date" }
|
|
1990
|
+
],
|
|
1991
|
+
timestamps: true,
|
|
1992
|
+
admin: {
|
|
1993
|
+
hidden: true,
|
|
1994
|
+
disableAdmin: true
|
|
1995
|
+
}
|
|
1996
|
+
},
|
|
1997
|
+
{
|
|
1998
|
+
slug: "_doc_versions",
|
|
1999
|
+
label: "Document Versions",
|
|
2000
|
+
apiPath: "doc-versions",
|
|
2001
|
+
fields: [
|
|
2002
|
+
{ name: "id", type: "text", required: true },
|
|
2003
|
+
{ name: "collection", type: "text", required: true },
|
|
2004
|
+
{ name: "entity_id", type: "text", required: true },
|
|
2005
|
+
{ name: "data", type: "json", required: true },
|
|
2006
|
+
{ name: "status", type: "text" },
|
|
2007
|
+
{ name: "autosave", type: "boolean", defaultValue: false },
|
|
2008
|
+
{ name: "version_name", type: "text" },
|
|
2009
|
+
{ name: "created_by", type: "text" }
|
|
2010
|
+
],
|
|
2011
|
+
timestamps: true,
|
|
2012
|
+
admin: {
|
|
2013
|
+
hidden: true,
|
|
2014
|
+
disableAdmin: true
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
];
|
|
2018
|
+
|
|
2019
|
+
// src/utils/logger.ts
|
|
2020
|
+
var RESET = "\x1B[0m";
|
|
2021
|
+
var BOLD = "\x1B[1m";
|
|
2022
|
+
var BLUE = "\x1B[34m";
|
|
2023
|
+
var GREEN = "\x1B[32m";
|
|
2024
|
+
var YELLOW = "\x1B[33m";
|
|
2025
|
+
var RED = "\x1B[31m";
|
|
2026
|
+
var GRAY = "\x1B[90m";
|
|
2027
|
+
var PREFIX = `${BLUE}${BOLD}[OpacaCMS]${RESET}`;
|
|
2028
|
+
var LOG_LEVELS = {
|
|
2029
|
+
debug: 0,
|
|
2030
|
+
info: 1,
|
|
2031
|
+
warn: 2,
|
|
2032
|
+
error: 3
|
|
2033
|
+
};
|
|
2034
|
+
|
|
2035
|
+
class OpacaLogger {
|
|
2036
|
+
config;
|
|
2037
|
+
constructor(config = {}) {
|
|
2038
|
+
this.config = config;
|
|
2039
|
+
}
|
|
2040
|
+
shouldLog(level) {
|
|
2041
|
+
if (this.config.disabled)
|
|
2042
|
+
return false;
|
|
2043
|
+
const configLevel = this.config.level || "info";
|
|
2044
|
+
return LOG_LEVELS[level] >= LOG_LEVELS[configLevel];
|
|
2045
|
+
}
|
|
2046
|
+
info(message, ...args) {
|
|
2047
|
+
if (!this.shouldLog("info"))
|
|
2048
|
+
return;
|
|
2049
|
+
console.log(`${PREFIX} ${message}`, ...args);
|
|
2050
|
+
}
|
|
2051
|
+
success(message, ...args) {
|
|
2052
|
+
if (!this.shouldLog("info"))
|
|
2053
|
+
return;
|
|
2054
|
+
console.log(`${PREFIX} ${GREEN}${message}${RESET}`, ...args);
|
|
2055
|
+
}
|
|
2056
|
+
debug(message, ...args) {
|
|
2057
|
+
if (!this.shouldLog("debug"))
|
|
2058
|
+
return;
|
|
2059
|
+
console.log(`${PREFIX} ${GRAY}${message}${RESET}`, ...args);
|
|
2060
|
+
}
|
|
2061
|
+
warn(message, ...args) {
|
|
2062
|
+
if (!this.shouldLog("warn"))
|
|
2063
|
+
return;
|
|
2064
|
+
console.warn(`${PREFIX} ${YELLOW}Warning: ${message}${RESET}`, ...args);
|
|
2065
|
+
}
|
|
2066
|
+
error(message, ...args) {
|
|
2067
|
+
if (!this.shouldLog("error"))
|
|
2068
|
+
return;
|
|
2069
|
+
console.error(`${PREFIX} ${RED}Error: ${message}${RESET}`, ...args);
|
|
2070
|
+
}
|
|
2071
|
+
log(message, ...args) {
|
|
2072
|
+
if (this.config.disabled)
|
|
2073
|
+
return;
|
|
2074
|
+
console.log(message, ...args);
|
|
2075
|
+
}
|
|
2076
|
+
bold(msg) {
|
|
2077
|
+
if (this.config.disableColors)
|
|
2078
|
+
return msg;
|
|
2079
|
+
return `${BOLD}${msg}${RESET}`;
|
|
2080
|
+
}
|
|
2081
|
+
format(color, msg) {
|
|
2082
|
+
if (this.config.disableColors)
|
|
2083
|
+
return msg;
|
|
2084
|
+
switch (color) {
|
|
2085
|
+
case "green":
|
|
2086
|
+
return `${GREEN}${msg}${RESET}`;
|
|
2087
|
+
case "red":
|
|
2088
|
+
return `${RED}${msg}${RESET}`;
|
|
2089
|
+
case "yellow":
|
|
2090
|
+
return `${YELLOW}${msg}${RESET}`;
|
|
2091
|
+
case "gray":
|
|
2092
|
+
return `${GRAY}${msg}${RESET}`;
|
|
2093
|
+
default:
|
|
2094
|
+
return msg;
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
var logger = new OpacaLogger;
|
|
2099
|
+
|
|
2100
|
+
// src/db/kysely/schema-builder.ts
|
|
2101
|
+
async function pushSchema(db, dialect, collections, globals = [], options = {}) {
|
|
2102
|
+
const rawSchemas = [...getSystemCollections(), ...collections, ...globals];
|
|
2103
|
+
const allSchemas = [];
|
|
2104
|
+
const seenSlugs = new Set;
|
|
2105
|
+
for (const schema of rawSchemas) {
|
|
2106
|
+
if (!seenSlugs.has(schema.slug)) {
|
|
2107
|
+
seenSlugs.add(schema.slug);
|
|
2108
|
+
allSchemas.push(schema);
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
2111
|
+
const { pushDestructive } = options;
|
|
2112
|
+
logger.debug(`Starting schema push via Kysely (${dialect})...`);
|
|
2113
|
+
const mapType = dialect === "postgres" ? mapFieldToPostgresType : mapFieldToSQLiteType;
|
|
2114
|
+
let tablesMetadata = [];
|
|
2115
|
+
try {
|
|
2116
|
+
tablesMetadata = await db.introspection.getTables();
|
|
2117
|
+
} catch (e) {
|
|
2118
|
+
logger.warn("Failed to fetch database introspection. Falling back to empty state.", e);
|
|
2119
|
+
}
|
|
2120
|
+
const existingTableMap = new Map(tablesMetadata.map((t) => [t.name, t]));
|
|
2121
|
+
const existingTables = Array.from(existingTableMap.keys());
|
|
2122
|
+
logger.debug(`Existing tables found: ${existingTables.join(", ")}`);
|
|
2123
|
+
const expectedTableNames = new Set;
|
|
2124
|
+
for (const collection of allSchemas) {
|
|
2125
|
+
const slug = collection.slug;
|
|
2126
|
+
const tableName = toSnakeCase(slug);
|
|
2127
|
+
assertSafeIdentifier(tableName);
|
|
2128
|
+
expectedTableNames.add(tableName);
|
|
2129
|
+
const flattenedFields = flattenFields(collection.fields);
|
|
2130
|
+
const hasTimestamps = true;
|
|
2131
|
+
const versions = collection.versions;
|
|
2132
|
+
const existingTable = existingTableMap.get(tableName);
|
|
2133
|
+
if (!existingTable) {
|
|
2134
|
+
logger.info(` ${logger.format("green", "✔")} Creating table: ${logger.bold(`"${tableName}"`)}`);
|
|
2135
|
+
let builder = db.schema.createTable(tableName).ifNotExists();
|
|
2136
|
+
builder = builder.addColumn("id", "text", (col) => col.primaryKey());
|
|
2137
|
+
for (const field of flattenedFields) {
|
|
2138
|
+
if (field.type === "relationship" && "hasMany" in field && field.hasMany)
|
|
2139
|
+
continue;
|
|
2140
|
+
const colName = toSnakeCase(field.name);
|
|
2141
|
+
if (colName === "id")
|
|
2142
|
+
continue;
|
|
2143
|
+
assertSafeIdentifier(colName);
|
|
2144
|
+
const colType = mapType(field);
|
|
2145
|
+
builder = builder.addColumn(colName, colType, (col) => {
|
|
2146
|
+
let c = col;
|
|
2147
|
+
if (field.unique)
|
|
2148
|
+
c = c.unique();
|
|
2149
|
+
if (field.required)
|
|
2150
|
+
c = c.notNull();
|
|
2151
|
+
if (field.defaultValue !== undefined)
|
|
2152
|
+
c = c.defaultTo(field.defaultValue);
|
|
2153
|
+
return c;
|
|
2154
|
+
});
|
|
2155
|
+
}
|
|
2156
|
+
if (versions?.drafts) {
|
|
2157
|
+
builder = builder.addColumn("_status", "text", (col) => col.defaultTo("draft"));
|
|
2158
|
+
}
|
|
2159
|
+
if (hasTimestamps) {
|
|
2160
|
+
const ts = collection.timestamps ?? {};
|
|
2161
|
+
const config = typeof ts === "object" ? ts : {};
|
|
2162
|
+
const createdField = toSnakeCase(config.createdAt ?? "createdAt");
|
|
2163
|
+
const updatedField = toSnakeCase(config.updatedAt ?? "updatedAt");
|
|
2164
|
+
const tsType = dialect === "postgres" ? "timestamptz" : "text";
|
|
2165
|
+
builder = builder.addColumn(createdField, tsType, (col) => col.defaultTo(sql`CURRENT_TIMESTAMP`));
|
|
2166
|
+
builder = builder.addColumn(updatedField, tsType, (col) => col.defaultTo(sql`CURRENT_TIMESTAMP`));
|
|
2167
|
+
}
|
|
2168
|
+
await builder.execute();
|
|
2169
|
+
} else {
|
|
2170
|
+
const existingColSet = new Set(existingTable.columns.map((c) => c.name));
|
|
2171
|
+
for (const field of flattenedFields) {
|
|
2172
|
+
if (field.type === "relationship" && "hasMany" in field && field.hasMany)
|
|
2173
|
+
continue;
|
|
2174
|
+
const colName = toSnakeCase(field.name);
|
|
2175
|
+
if (colName === "id")
|
|
2176
|
+
continue;
|
|
2177
|
+
assertSafeIdentifier(colName);
|
|
2178
|
+
if (!existingColSet.has(colName)) {
|
|
2179
|
+
logger.info(` ${logger.format("green", "✔")} Adding column: ${logger.bold(`${tableName}.${colName}`)}`);
|
|
2180
|
+
const colType = mapType(field);
|
|
2181
|
+
await db.schema.alterTable(tableName).addColumn(colName, colType).execute();
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
if (versions?.drafts && !existingColSet.has("_status")) {
|
|
2185
|
+
logger.info(` ${logger.format("green", "✔")} Adding status column to: ${tableName}`);
|
|
2186
|
+
await db.schema.alterTable(tableName).addColumn("_status", "text", (col) => col.defaultTo("draft")).execute();
|
|
2187
|
+
}
|
|
2188
|
+
if (hasTimestamps) {
|
|
2189
|
+
const ts = collection.timestamps ?? {};
|
|
2190
|
+
const config = typeof ts === "object" ? ts : {};
|
|
2191
|
+
const createdField = toSnakeCase(config.createdAt ?? "createdAt");
|
|
2192
|
+
const updatedField = toSnakeCase(config.updatedAt ?? "updatedAt");
|
|
2193
|
+
const tsType = dialect === "postgres" ? "timestamptz" : "text";
|
|
2194
|
+
if (!existingColSet.has(createdField)) {
|
|
2195
|
+
logger.info(` -> Adding missing timestamp column: ${tableName}.${createdField}`);
|
|
2196
|
+
await db.schema.alterTable(tableName).addColumn(createdField, tsType, (col) => col.defaultTo(sql`CURRENT_TIMESTAMP`)).execute();
|
|
2197
|
+
}
|
|
2198
|
+
if (!existingColSet.has(updatedField)) {
|
|
2199
|
+
logger.info(` -> Adding missing timestamp column: ${tableName}.${updatedField}`);
|
|
2200
|
+
await db.schema.alterTable(tableName).addColumn(updatedField, tsType, (col) => col.defaultTo(sql`CURRENT_TIMESTAMP`)).execute();
|
|
2201
|
+
}
|
|
2202
|
+
}
|
|
2203
|
+
if (pushDestructive) {
|
|
2204
|
+
const expectedCols = new Set(flattenedFields.filter((f) => !(f.type === "relationship" && f.hasMany)).map((f) => toSnakeCase(f.name)));
|
|
2205
|
+
if (hasTimestamps) {
|
|
2206
|
+
const ts = collection.timestamps ?? {};
|
|
2207
|
+
const config = typeof ts === "object" ? ts : {};
|
|
2208
|
+
expectedCols.add(toSnakeCase(config.createdAt ?? "createdAt"));
|
|
2209
|
+
expectedCols.add(toSnakeCase(config.updatedAt ?? "updatedAt"));
|
|
2210
|
+
}
|
|
2211
|
+
if (versions?.drafts)
|
|
2212
|
+
expectedCols.add("_status");
|
|
2213
|
+
expectedCols.add("id");
|
|
2214
|
+
for (const existingCol of existingColSet) {
|
|
2215
|
+
const colName = existingCol;
|
|
2216
|
+
if (!expectedCols.has(colName)) {
|
|
2217
|
+
logger.info(` -> Dropping stale column: ${logger.format("red", `${tableName}.${colName}`)}`);
|
|
2218
|
+
try {
|
|
2219
|
+
await db.schema.alterTable(tableName).dropColumn(colName).execute();
|
|
2220
|
+
} catch (_) {
|
|
2221
|
+
logger.warn(`Failed to drop column ${colName}.`);
|
|
2222
|
+
}
|
|
2223
|
+
}
|
|
2224
|
+
}
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
2227
|
+
const relationalFields = getRelationalFields(collection.fields);
|
|
2228
|
+
for (const field of relationalFields) {
|
|
2229
|
+
if (field.type === "relationship" && "hasMany" in field && field.hasMany) {
|
|
2230
|
+
const colName = toSnakeCase(field.name);
|
|
2231
|
+
const joinTableName = `${tableName}_${colName}_relations`.toLowerCase();
|
|
2232
|
+
assertSafeIdentifier(joinTableName);
|
|
2233
|
+
expectedTableNames.add(joinTableName);
|
|
2234
|
+
if (!existingTableMap.has(joinTableName)) {
|
|
2235
|
+
logger.info(` -> Creating relation table: ${logger.format("green", `"${joinTableName}"`)}`);
|
|
2236
|
+
await db.schema.createTable(joinTableName).ifNotExists().addColumn("id", "text", (col) => col.primaryKey()).addColumn("source_id", "text", (col) => col.notNull()).addColumn("target_id", "text", (col) => col.notNull()).addColumn("order", "integer", (col) => col.defaultTo(0)).execute();
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
for (const field of relationalFields) {
|
|
2241
|
+
if (field.type === "blocks" && field.blocks && Array.isArray(field.blocks)) {
|
|
2242
|
+
for (const block of field.blocks) {
|
|
2243
|
+
const blockName = toSnakeCase(field.name);
|
|
2244
|
+
const blockSlug = toSnakeCase(block.slug);
|
|
2245
|
+
assertSafeIdentifier(blockName);
|
|
2246
|
+
assertSafeIdentifier(blockSlug);
|
|
2247
|
+
const blockTableName = `${tableName}_${blockName}_${blockSlug}`.toLowerCase();
|
|
2248
|
+
assertSafeIdentifier(blockTableName);
|
|
2249
|
+
expectedTableNames.add(blockTableName);
|
|
2250
|
+
const existingBlockTable = existingTableMap.get(blockTableName);
|
|
2251
|
+
if (!existingBlockTable) {
|
|
2252
|
+
logger.info(` -> Creating block table: ${logger.format("green", `"${blockTableName}"`)}`);
|
|
2253
|
+
let bBuilder = db.schema.createTable(blockTableName).ifNotExists().addColumn("id", "text", (col) => col.primaryKey()).addColumn("_parent_id", "text", (col) => col.notNull()).addColumn("_order", "integer", (col) => col.defaultTo(0)).addColumn("block_type", "text", (col) => col.notNull().defaultTo(block.slug));
|
|
2254
|
+
const blockFlattened = flattenFields(block.fields);
|
|
2255
|
+
for (const bField of blockFlattened) {
|
|
2256
|
+
const bColName = toSnakeCase(bField.name);
|
|
2257
|
+
if (bColName === "id")
|
|
2258
|
+
continue;
|
|
2259
|
+
assertSafeIdentifier(bColName);
|
|
2260
|
+
const bColType = mapType(bField);
|
|
2261
|
+
bBuilder = bBuilder.addColumn(bColName, bColType, (col) => {
|
|
2262
|
+
let c = col;
|
|
2263
|
+
if (bField.unique)
|
|
2264
|
+
c = c.unique();
|
|
2265
|
+
if (bField.required)
|
|
2266
|
+
c = c.notNull();
|
|
2267
|
+
if (bField.defaultValue !== undefined)
|
|
2268
|
+
c = c.defaultTo(bField.defaultValue);
|
|
2269
|
+
return c;
|
|
2270
|
+
});
|
|
2271
|
+
}
|
|
2272
|
+
const tsType = dialect === "postgres" ? "timestamptz" : "text";
|
|
2273
|
+
bBuilder = bBuilder.addColumn("created_at", tsType, (col) => col.defaultTo(sql`CURRENT_TIMESTAMP`));
|
|
2274
|
+
bBuilder = bBuilder.addColumn("updated_at", tsType, (col) => col.defaultTo(sql`CURRENT_TIMESTAMP`));
|
|
2275
|
+
await bBuilder.execute();
|
|
2276
|
+
} else {
|
|
2277
|
+
const existingBColSet = new Set(existingBlockTable.columns.map((c) => c.name));
|
|
2278
|
+
const blockFlattened = flattenFields(block.fields);
|
|
2279
|
+
for (const bField of blockFlattened) {
|
|
2280
|
+
const bColName = toSnakeCase(bField.name);
|
|
2281
|
+
if (bColName === "id")
|
|
2282
|
+
continue;
|
|
2283
|
+
if (!existingBColSet.has(bColName)) {
|
|
2284
|
+
const bColType = mapType(bField);
|
|
2285
|
+
await db.schema.alterTable(blockTableName).addColumn(bColName, bColType).execute();
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
2288
|
+
if (!existingBColSet.has("_parent_id")) {
|
|
2289
|
+
await db.schema.alterTable(blockTableName).addColumn("_parent_id", "text", (col) => col.notNull().defaultTo("")).execute();
|
|
2290
|
+
}
|
|
2291
|
+
if (!existingBColSet.has("_order")) {
|
|
2292
|
+
await db.schema.alterTable(blockTableName).addColumn("_order", "integer", (col) => col.defaultTo(0)).execute();
|
|
2293
|
+
}
|
|
2294
|
+
if (!existingBColSet.has("block_type")) {
|
|
2295
|
+
await db.schema.alterTable(blockTableName).addColumn("block_type", "text", (col) => col.notNull().defaultTo(block.slug)).execute();
|
|
2296
|
+
}
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
}
|
|
2302
|
+
if (pushDestructive) {
|
|
2303
|
+
for (const tableName of existingTables) {
|
|
2304
|
+
if (!expectedTableNames.has(tableName) && !tableName.startsWith("better_auth") && !tableName.startsWith("__kysely")) {
|
|
2305
|
+
logger.info(` -> Dropping stale table: ${logger.format("red", `"${tableName}"`)}`);
|
|
2306
|
+
try {
|
|
2307
|
+
await db.schema.dropTable(tableName).ifExists().cascade().execute();
|
|
2308
|
+
} catch (_) {
|
|
2309
|
+
try {
|
|
2310
|
+
await db.schema.dropTable(tableName).ifExists().execute();
|
|
2311
|
+
} catch (_2) {
|
|
2312
|
+
logger.warn(`Failed to drop table ${tableName}`);
|
|
2313
|
+
}
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
logger.info(`${logger.format("green", "✔")} Schema synchronization complete.`);
|
|
2319
|
+
}
|
|
2320
|
+
|
|
2321
|
+
// src/db/d1.ts
|
|
2322
|
+
init_context();
|
|
31
2323
|
class D1Adapter extends BaseDatabaseAdapter {
|
|
32
2324
|
name = "d1";
|
|
33
2325
|
_rawDb;
|
|
@@ -57,8 +2349,8 @@ class D1Adapter extends BaseDatabaseAdapter {
|
|
|
57
2349
|
if (this._db)
|
|
58
2350
|
return;
|
|
59
2351
|
const { D1Dialect } = await import("kysely-d1");
|
|
60
|
-
const { createOpacaKysely } = await
|
|
61
|
-
this._db =
|
|
2352
|
+
const { createOpacaKysely: createOpacaKysely2 } = await Promise.resolve().then(() => (init_factory(), exports_factory));
|
|
2353
|
+
this._db = createOpacaKysely2({
|
|
62
2354
|
dialect: new D1Dialect({ database: this._rawDb }),
|
|
63
2355
|
config: {
|
|
64
2356
|
collections: this._collections,
|
|
@@ -572,11 +2864,14 @@ class D1Adapter extends BaseDatabaseAdapter {
|
|
|
572
2864
|
return this.findGlobal(slug);
|
|
573
2865
|
}
|
|
574
2866
|
async runMigrations() {
|
|
2867
|
+
const fs = await import("node:fs/promises");
|
|
2868
|
+
const path = await Promise.resolve().then(() => (init_path(), exports_path));
|
|
2869
|
+
const { pathToFileURL } = await Promise.resolve().then(() => (init_url(), exports_url));
|
|
575
2870
|
const migrator = new Migrator({
|
|
576
2871
|
db: this.db,
|
|
577
2872
|
provider: new FileMigrationProvider({
|
|
578
|
-
fs,
|
|
579
|
-
path,
|
|
2873
|
+
fs: fs.default || fs,
|
|
2874
|
+
path: path.default || path,
|
|
580
2875
|
migrationFolder: pathToFileURL(path.resolve(process.cwd(), this.migrationDir)).href
|
|
581
2876
|
})
|
|
582
2877
|
});
|