js-bao 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.cjs +367 -1
- package/dist/browser.d.cts +126 -1
- package/dist/browser.d.ts +126 -1
- package/dist/browser.js +367 -1
- package/dist/client.cjs +16 -11
- package/dist/client.d.cts +3 -1
- package/dist/client.d.ts +3 -1
- package/dist/client.js +16 -11
- package/dist/cloudflare-do.cjs +937 -286
- package/dist/cloudflare-do.d.cts +517 -15
- package/dist/cloudflare-do.d.ts +517 -15
- package/dist/cloudflare-do.js +928 -286
- package/dist/cloudflare.cjs +573 -18
- package/dist/cloudflare.d.cts +147 -2
- package/dist/cloudflare.d.ts +147 -2
- package/dist/cloudflare.js +573 -18
- package/dist/codegen.cjs +6 -6
- package/dist/index.cjs +19 -10
- package/dist/index.js +19 -10
- package/dist/node.cjs +388 -6
- package/dist/node.d.cts +131 -1
- package/dist/node.d.ts +131 -1
- package/dist/node.js +377 -5
- package/package.json +6 -6
package/dist/cloudflare-do.cjs
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __esm = (fn, res) => function __init() {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
};
|
|
6
11
|
var __export = (target, all) => {
|
|
7
12
|
for (var name in all)
|
|
8
13
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -15,17 +20,542 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
20
|
}
|
|
16
21
|
return to;
|
|
17
22
|
};
|
|
23
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
24
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
25
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
26
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
27
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
28
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
29
|
+
mod
|
|
30
|
+
));
|
|
18
31
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
32
|
|
|
33
|
+
// src/utils/sql.ts
|
|
34
|
+
function isValidIdentifier(name) {
|
|
35
|
+
return IDENTIFIER_PATTERN.test(name);
|
|
36
|
+
}
|
|
37
|
+
function assertValidIdentifier(name, context) {
|
|
38
|
+
if (!isValidIdentifier(name)) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
`${context}: Identifier "${name}" must match ${IDENTIFIER_PATTERN.source}`
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function quoteIdentifier(name) {
|
|
45
|
+
return `"${name.replace(/"/g, '""')}"`;
|
|
46
|
+
}
|
|
47
|
+
var IDENTIFIER_PATTERN;
|
|
48
|
+
var init_sql = __esm({
|
|
49
|
+
"src/utils/sql.ts"() {
|
|
50
|
+
"use strict";
|
|
51
|
+
IDENTIFIER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// src/types/queryTypes.ts
|
|
56
|
+
var DocumentQueryError, InvalidOperatorError, InvalidFieldError, InvalidCursorError;
|
|
57
|
+
var init_queryTypes = __esm({
|
|
58
|
+
"src/types/queryTypes.ts"() {
|
|
59
|
+
"use strict";
|
|
60
|
+
DocumentQueryError = class _DocumentQueryError extends Error {
|
|
61
|
+
constructor(message) {
|
|
62
|
+
super(message);
|
|
63
|
+
this.name = "DocumentQueryError";
|
|
64
|
+
Object.setPrototypeOf(this, _DocumentQueryError.prototype);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
InvalidOperatorError = class _InvalidOperatorError extends DocumentQueryError {
|
|
68
|
+
field;
|
|
69
|
+
operator;
|
|
70
|
+
fieldType;
|
|
71
|
+
constructor(message, field, operator, fieldType) {
|
|
72
|
+
super(message);
|
|
73
|
+
this.name = "InvalidOperatorError";
|
|
74
|
+
this.field = field;
|
|
75
|
+
this.operator = operator;
|
|
76
|
+
this.fieldType = fieldType;
|
|
77
|
+
Object.setPrototypeOf(this, _InvalidOperatorError.prototype);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
InvalidFieldError = class _InvalidFieldError extends DocumentQueryError {
|
|
81
|
+
field;
|
|
82
|
+
modelName;
|
|
83
|
+
constructor(message, field, modelName) {
|
|
84
|
+
super(message);
|
|
85
|
+
this.name = "InvalidFieldError";
|
|
86
|
+
this.field = field;
|
|
87
|
+
this.modelName = modelName;
|
|
88
|
+
Object.setPrototypeOf(this, _InvalidFieldError.prototype);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
InvalidCursorError = class _InvalidCursorError extends DocumentQueryError {
|
|
92
|
+
cursor;
|
|
93
|
+
constructor(message, cursor) {
|
|
94
|
+
super(message);
|
|
95
|
+
this.name = "InvalidCursorError";
|
|
96
|
+
this.cursor = cursor;
|
|
97
|
+
Object.setPrototypeOf(this, _InvalidCursorError.prototype);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// src/query/CursorManager.ts
|
|
104
|
+
var base64Encode, base64Decode, CursorManager;
|
|
105
|
+
var init_CursorManager = __esm({
|
|
106
|
+
"src/query/CursorManager.ts"() {
|
|
107
|
+
"use strict";
|
|
108
|
+
init_queryTypes();
|
|
109
|
+
base64Encode = (str) => {
|
|
110
|
+
if (typeof btoa !== "undefined") {
|
|
111
|
+
return btoa(str);
|
|
112
|
+
} else if (typeof Buffer !== "undefined") {
|
|
113
|
+
return Buffer.from(str, "utf-8").toString("base64");
|
|
114
|
+
} else {
|
|
115
|
+
throw new Error("No base64 encoding available");
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
base64Decode = (str) => {
|
|
119
|
+
if (typeof atob !== "undefined") {
|
|
120
|
+
return atob(str);
|
|
121
|
+
} else if (typeof Buffer !== "undefined") {
|
|
122
|
+
return Buffer.from(str, "base64").toString("utf-8");
|
|
123
|
+
} else {
|
|
124
|
+
throw new Error("No base64 decoding available");
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
CursorManager = class {
|
|
128
|
+
/**
|
|
129
|
+
* Encode cursor data to base64 string
|
|
130
|
+
*/
|
|
131
|
+
static encodeCursor(cursorData) {
|
|
132
|
+
try {
|
|
133
|
+
const jsonString = JSON.stringify(cursorData);
|
|
134
|
+
return base64Encode(jsonString);
|
|
135
|
+
} catch (error) {
|
|
136
|
+
throw new InvalidCursorError(
|
|
137
|
+
`Failed to encode cursor: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
138
|
+
JSON.stringify(cursorData)
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Decode base64 cursor string to cursor data
|
|
144
|
+
*/
|
|
145
|
+
static decodeCursor(cursor) {
|
|
146
|
+
try {
|
|
147
|
+
const jsonString = base64Decode(cursor);
|
|
148
|
+
const parsed = JSON.parse(jsonString);
|
|
149
|
+
if (!parsed || typeof parsed !== "object") {
|
|
150
|
+
throw new Error("Cursor must be an object");
|
|
151
|
+
}
|
|
152
|
+
if (!parsed.values || typeof parsed.values !== "object") {
|
|
153
|
+
throw new Error("Cursor must have values object");
|
|
154
|
+
}
|
|
155
|
+
if (!Array.isArray(parsed.sortFields)) {
|
|
156
|
+
throw new Error("Cursor must have sortFields array");
|
|
157
|
+
}
|
|
158
|
+
if (parsed.direction !== 1 && parsed.direction !== -1) {
|
|
159
|
+
throw new Error("Cursor direction must be 1 or -1");
|
|
160
|
+
}
|
|
161
|
+
return parsed;
|
|
162
|
+
} catch (error) {
|
|
163
|
+
throw new InvalidCursorError(
|
|
164
|
+
`Failed to decode cursor: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
165
|
+
cursor
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Generate cursor from a record and sort specification
|
|
171
|
+
*/
|
|
172
|
+
static generateCursor(record, sortFields, direction) {
|
|
173
|
+
const values = {};
|
|
174
|
+
for (const field of sortFields) {
|
|
175
|
+
if (record.hasOwnProperty(field)) {
|
|
176
|
+
values[field] = record[field];
|
|
177
|
+
} else {
|
|
178
|
+
throw new Error(
|
|
179
|
+
`Cannot generate cursor: record missing sort field '${field}'`
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
const cursorData = {
|
|
184
|
+
values,
|
|
185
|
+
sortFields,
|
|
186
|
+
direction
|
|
187
|
+
};
|
|
188
|
+
return this.encodeCursor(cursorData);
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Build SQL pagination conditions based on cursor
|
|
192
|
+
* Uses lexicographic ordering for stable pagination with multiple sort fields
|
|
193
|
+
*/
|
|
194
|
+
static buildPaginationConditions(cursor, currentSortFields, sortDirections, requestedDirection, fieldFormatter = (field) => field) {
|
|
195
|
+
if (!this.arraysEqual(cursor.sortFields, currentSortFields)) {
|
|
196
|
+
throw new InvalidCursorError(
|
|
197
|
+
`Cursor sort fields [${cursor.sortFields.join(
|
|
198
|
+
", "
|
|
199
|
+
)}] don't match query sort fields [${currentSortFields.join(", ")}]`,
|
|
200
|
+
JSON.stringify(cursor)
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
const conditions = [];
|
|
204
|
+
const params = [];
|
|
205
|
+
const sortFields = cursor.sortFields;
|
|
206
|
+
const direction = requestedDirection;
|
|
207
|
+
for (let i = 0; i < sortFields.length; i++) {
|
|
208
|
+
const fieldConditions = [];
|
|
209
|
+
const fieldParams = [];
|
|
210
|
+
for (let j = 0; j < i; j++) {
|
|
211
|
+
const field = sortFields[j];
|
|
212
|
+
const value = cursor.values[field];
|
|
213
|
+
fieldConditions.push(`${fieldFormatter(field)} = ?`);
|
|
214
|
+
fieldParams.push(value);
|
|
215
|
+
}
|
|
216
|
+
const currentField = sortFields[i];
|
|
217
|
+
const currentValue = cursor.values[currentField];
|
|
218
|
+
const fieldSortDir = sortDirections[i] ?? 1;
|
|
219
|
+
const forwardOp = fieldSortDir === 1 ? ">" : "<";
|
|
220
|
+
const operator = direction === 1 ? forwardOp : forwardOp === ">" ? "<" : ">";
|
|
221
|
+
fieldConditions.push(`${fieldFormatter(currentField)} ${operator} ?`);
|
|
222
|
+
fieldParams.push(currentValue);
|
|
223
|
+
const levelCondition = fieldConditions.join(" AND ");
|
|
224
|
+
conditions.push(`(${levelCondition})`);
|
|
225
|
+
params.push(...fieldParams);
|
|
226
|
+
}
|
|
227
|
+
const sql = `(${conditions.join(" OR ")})`;
|
|
228
|
+
return { sql, params };
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Extract sort fields from sort specification, ensuring 'id' is always included for stability
|
|
232
|
+
*/
|
|
233
|
+
static extractSortFields(sort) {
|
|
234
|
+
const fields = [];
|
|
235
|
+
if (sort && Object.keys(sort).length > 0) {
|
|
236
|
+
fields.push(...Object.keys(sort));
|
|
237
|
+
}
|
|
238
|
+
if (!fields.includes("id")) {
|
|
239
|
+
fields.push("id");
|
|
240
|
+
}
|
|
241
|
+
return fields;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Build ORDER BY clause from sort specification
|
|
245
|
+
*/
|
|
246
|
+
static buildOrderClause(sort, fieldFormatter = (field) => field) {
|
|
247
|
+
const fields = this.extractSortFields(sort);
|
|
248
|
+
const clauses = [];
|
|
249
|
+
const directions = [];
|
|
250
|
+
if (sort && Object.keys(sort).length > 0) {
|
|
251
|
+
for (const [field, dir] of Object.entries(sort)) {
|
|
252
|
+
const sqlDirection = dir === 1 ? "ASC" : "DESC";
|
|
253
|
+
clauses.push(`${fieldFormatter(field)} ${sqlDirection}`);
|
|
254
|
+
directions.push(dir);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (!sort || !sort.hasOwnProperty("id")) {
|
|
258
|
+
clauses.push(`${fieldFormatter("id")} ASC`);
|
|
259
|
+
directions.push(1);
|
|
260
|
+
} else if (sort && sort.hasOwnProperty("id")) {
|
|
261
|
+
directions.push(sort["id"]);
|
|
262
|
+
}
|
|
263
|
+
return {
|
|
264
|
+
sql: clauses.join(", "),
|
|
265
|
+
fields,
|
|
266
|
+
directions
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Determine if there are more results available for pagination
|
|
271
|
+
*/
|
|
272
|
+
static hasMoreResults(requestedLimit, actualResultCount) {
|
|
273
|
+
if (!requestedLimit || requestedLimit <= 0) {
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
return actualResultCount >= requestedLimit;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Generate next and previous cursors from result set
|
|
280
|
+
*/
|
|
281
|
+
static generateResultCursors(results, sortFields, _requestedDirection, hasMore, isFirstPage = false) {
|
|
282
|
+
if (results.length === 0) {
|
|
283
|
+
return {};
|
|
284
|
+
}
|
|
285
|
+
const cursors = {};
|
|
286
|
+
if (hasMore && results.length > 0) {
|
|
287
|
+
const lastResult = results[results.length - 1];
|
|
288
|
+
cursors.nextCursor = this.generateCursor(lastResult, sortFields, 1);
|
|
289
|
+
}
|
|
290
|
+
if (results.length > 0 && !isFirstPage) {
|
|
291
|
+
const firstResult = results[0];
|
|
292
|
+
cursors.prevCursor = this.generateCursor(firstResult, sortFields, -1);
|
|
293
|
+
}
|
|
294
|
+
return cursors;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Utility to compare arrays for equality
|
|
298
|
+
*/
|
|
299
|
+
static arraysEqual(a, b) {
|
|
300
|
+
if (a.length !== b.length) return false;
|
|
301
|
+
return a.every((val, index) => val === b[index]);
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
// src/utils/patterns.ts
|
|
308
|
+
function escapeLikeLiteral(input) {
|
|
309
|
+
return input.replace(/\\/g, "\\\\").replace(/%/g, "\\%").replace(/_/g, "\\_");
|
|
310
|
+
}
|
|
311
|
+
function buildLikePattern(value, mode) {
|
|
312
|
+
const trimmed = (value ?? "").trim();
|
|
313
|
+
if (trimmed.length === 0) return null;
|
|
314
|
+
if (trimmed.length > 1024) {
|
|
315
|
+
throw new Error("substring value exceeds 1024 characters");
|
|
316
|
+
}
|
|
317
|
+
const escaped = escapeLikeLiteral(trimmed);
|
|
318
|
+
switch (mode) {
|
|
319
|
+
case "startsWith":
|
|
320
|
+
return `${escaped}%`;
|
|
321
|
+
case "endsWith":
|
|
322
|
+
return `%${escaped}`;
|
|
323
|
+
case "containsText":
|
|
324
|
+
return `%${escaped}%`;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
function shouldLogLikeEscapes() {
|
|
328
|
+
try {
|
|
329
|
+
if (typeof process !== "undefined" && process.env) {
|
|
330
|
+
return !!process.env.JS_BAO_DEBUG_LIKE_ESCAPES;
|
|
331
|
+
}
|
|
332
|
+
} catch {
|
|
333
|
+
}
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
336
|
+
var init_patterns = __esm({
|
|
337
|
+
"src/utils/patterns.ts"() {
|
|
338
|
+
"use strict";
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
// src/models/StringSet.ts
|
|
343
|
+
var init_StringSet = __esm({
|
|
344
|
+
"src/models/StringSet.ts"() {
|
|
345
|
+
"use strict";
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
// src/types/documentTypes.ts
|
|
350
|
+
var init_documentTypes = __esm({
|
|
351
|
+
"src/types/documentTypes.ts"() {
|
|
352
|
+
"use strict";
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
// src/query/DocumentQueryTranslator.ts
|
|
357
|
+
var init_DocumentQueryTranslator = __esm({
|
|
358
|
+
"src/query/DocumentQueryTranslator.ts"() {
|
|
359
|
+
"use strict";
|
|
360
|
+
init_queryTypes();
|
|
361
|
+
init_CursorManager();
|
|
362
|
+
init_sql();
|
|
363
|
+
init_patterns();
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
// src/models/metaSync.ts
|
|
368
|
+
function inferFieldType(value) {
|
|
369
|
+
if (value instanceof Y2.Map) return "stringset";
|
|
370
|
+
switch (typeof value) {
|
|
371
|
+
case "string":
|
|
372
|
+
return "string";
|
|
373
|
+
case "number":
|
|
374
|
+
return "number";
|
|
375
|
+
case "boolean":
|
|
376
|
+
return "boolean";
|
|
377
|
+
default:
|
|
378
|
+
return null;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
function registerFunctionDefault(fn, name) {
|
|
382
|
+
KNOWN_FUNCTION_DEFAULTS.set(fn, name);
|
|
383
|
+
}
|
|
384
|
+
function encodeDefault(value) {
|
|
385
|
+
if (value === void 0 || value === null) return void 0;
|
|
386
|
+
if (typeof value === "function") {
|
|
387
|
+
const name = KNOWN_FUNCTION_DEFAULTS.get(value);
|
|
388
|
+
return name ? `$${name}` : "$unknown_function";
|
|
389
|
+
}
|
|
390
|
+
return value;
|
|
391
|
+
}
|
|
392
|
+
function clearMetaSyncCache(yDoc) {
|
|
393
|
+
if (yDoc) {
|
|
394
|
+
_syncedCache.delete(yDoc);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
function syncModelMeta(yDoc, modelName, schema) {
|
|
398
|
+
let synced = _syncedCache.get(yDoc);
|
|
399
|
+
if (synced?.has(modelName)) return;
|
|
400
|
+
if (!synced) {
|
|
401
|
+
synced = /* @__PURE__ */ new Set();
|
|
402
|
+
_syncedCache.set(yDoc, synced);
|
|
403
|
+
}
|
|
404
|
+
const meta = yDoc.getMap(`_meta_${modelName}`);
|
|
405
|
+
for (const [fieldName, fieldOpts] of schema.fields.entries()) {
|
|
406
|
+
syncFieldMeta(meta, fieldName, fieldOpts);
|
|
407
|
+
}
|
|
408
|
+
const compoundConstraints = schema.resolvedUniqueConstraints.filter(
|
|
409
|
+
(c) => c.fields.length > 1
|
|
410
|
+
);
|
|
411
|
+
if (compoundConstraints.length > 0) {
|
|
412
|
+
let constraints = meta.get("_constraints");
|
|
413
|
+
if (!constraints) {
|
|
414
|
+
constraints = new Y2.Map();
|
|
415
|
+
meta.set("_constraints", constraints);
|
|
416
|
+
}
|
|
417
|
+
for (const constraint of compoundConstraints) {
|
|
418
|
+
syncConstraintMeta(constraints, constraint);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
const relationships = schema.options.relationships;
|
|
422
|
+
if (relationships && Object.keys(relationships).length > 0) {
|
|
423
|
+
let rels = meta.get("_relationships");
|
|
424
|
+
if (!rels) {
|
|
425
|
+
rels = new Y2.Map();
|
|
426
|
+
meta.set("_relationships", rels);
|
|
427
|
+
}
|
|
428
|
+
for (const [relName, relConfig] of Object.entries(relationships)) {
|
|
429
|
+
syncRelationshipMeta(rels, relName, relConfig);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
synced.add(modelName);
|
|
433
|
+
}
|
|
434
|
+
function syncFieldMeta(metaMap, fieldName, fieldOpts) {
|
|
435
|
+
let fieldMeta = metaMap.get(fieldName);
|
|
436
|
+
if (!fieldMeta) {
|
|
437
|
+
fieldMeta = new Y2.Map();
|
|
438
|
+
metaMap.set(fieldName, fieldMeta);
|
|
439
|
+
}
|
|
440
|
+
setIfChanged(fieldMeta, "type", fieldOpts.type);
|
|
441
|
+
if (fieldOpts.indexed) setIfChanged(fieldMeta, "indexed", true);
|
|
442
|
+
if (fieldOpts.unique) setIfChanged(fieldMeta, "unique", true);
|
|
443
|
+
if (fieldOpts.required) setIfChanged(fieldMeta, "required", true);
|
|
444
|
+
if (fieldOpts.autoAssign) setIfChanged(fieldMeta, "autoAssign", true);
|
|
445
|
+
if (fieldOpts.maxLength !== void 0) setIfChanged(fieldMeta, "maxLength", fieldOpts.maxLength);
|
|
446
|
+
if (fieldOpts.maxCount !== void 0) setIfChanged(fieldMeta, "maxCount", fieldOpts.maxCount);
|
|
447
|
+
const encoded = encodeDefault(fieldOpts.default);
|
|
448
|
+
if (encoded !== void 0) setIfChanged(fieldMeta, "default", encoded);
|
|
449
|
+
}
|
|
450
|
+
function syncConstraintMeta(constraintsMap, constraint) {
|
|
451
|
+
let cMeta = constraintsMap.get(constraint.name);
|
|
452
|
+
if (!cMeta) {
|
|
453
|
+
cMeta = new Y2.Map();
|
|
454
|
+
constraintsMap.set(constraint.name, cMeta);
|
|
455
|
+
}
|
|
456
|
+
setIfChanged(cMeta, "type", "unique");
|
|
457
|
+
const fieldsJson = JSON.stringify(constraint.fields);
|
|
458
|
+
setIfChanged(cMeta, "fields", fieldsJson);
|
|
459
|
+
}
|
|
460
|
+
function syncRelationshipMeta(relsMap, relName, relConfig) {
|
|
461
|
+
let relMeta = relsMap.get(relName);
|
|
462
|
+
if (!relMeta) {
|
|
463
|
+
relMeta = new Y2.Map();
|
|
464
|
+
relsMap.set(relName, relMeta);
|
|
465
|
+
}
|
|
466
|
+
for (const [key, value] of Object.entries(relConfig)) {
|
|
467
|
+
if (value !== void 0) {
|
|
468
|
+
setIfChanged(relMeta, key, value);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
function syncInferredMeta(yDoc, modelName, recordData) {
|
|
473
|
+
const meta = yDoc.getMap(`_meta_${modelName}`);
|
|
474
|
+
for (const [fieldName, value] of Object.entries(recordData)) {
|
|
475
|
+
if (fieldName.startsWith("_")) continue;
|
|
476
|
+
let fieldMeta = meta.get(fieldName);
|
|
477
|
+
if (!fieldMeta) {
|
|
478
|
+
fieldMeta = new Y2.Map();
|
|
479
|
+
meta.set(fieldName, fieldMeta);
|
|
480
|
+
}
|
|
481
|
+
if (!fieldMeta.has("type")) {
|
|
482
|
+
const inferredType = inferFieldType(value);
|
|
483
|
+
if (inferredType) {
|
|
484
|
+
fieldMeta.set("type", inferredType);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
function setIfChanged(map, key, value) {
|
|
490
|
+
if (map.get(key) !== value) {
|
|
491
|
+
map.set(key, value);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
var Y2, KNOWN_FUNCTION_DEFAULTS, _syncedCache;
|
|
495
|
+
var init_metaSync = __esm({
|
|
496
|
+
"src/models/metaSync.ts"() {
|
|
497
|
+
"use strict";
|
|
498
|
+
Y2 = __toESM(require("yjs"), 1);
|
|
499
|
+
KNOWN_FUNCTION_DEFAULTS = /* @__PURE__ */ new WeakMap();
|
|
500
|
+
_syncedCache = /* @__PURE__ */ new WeakMap();
|
|
501
|
+
}
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
// src/models/BaseModel.ts
|
|
505
|
+
function generateULID() {
|
|
506
|
+
return (0, import_ulid2.ulid)();
|
|
507
|
+
}
|
|
508
|
+
var Y3, import_ulid2;
|
|
509
|
+
var init_BaseModel = __esm({
|
|
510
|
+
"src/models/BaseModel.ts"() {
|
|
511
|
+
"use strict";
|
|
512
|
+
Y3 = __toESM(require("yjs"), 1);
|
|
513
|
+
import_ulid2 = require("ulid");
|
|
514
|
+
init_StringSet();
|
|
515
|
+
init_documentTypes();
|
|
516
|
+
init_DocumentQueryTranslator();
|
|
517
|
+
init_CursorManager();
|
|
518
|
+
init_sql();
|
|
519
|
+
init_metaSync();
|
|
520
|
+
registerFunctionDefault(generateULID, "generate_ulid");
|
|
521
|
+
}
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
// src/models/relationshipManager.ts
|
|
525
|
+
var init_relationshipManager = __esm({
|
|
526
|
+
"src/models/relationshipManager.ts"() {
|
|
527
|
+
"use strict";
|
|
528
|
+
init_BaseModel();
|
|
529
|
+
}
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
// src/models/ModelRegistry.ts
|
|
533
|
+
var init_ModelRegistry = __esm({
|
|
534
|
+
"src/models/ModelRegistry.ts"() {
|
|
535
|
+
"use strict";
|
|
536
|
+
init_BaseModel();
|
|
537
|
+
init_relationshipManager();
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
|
|
20
541
|
// src/cloudflare-do.ts
|
|
21
542
|
var cloudflare_do_exports = {};
|
|
22
543
|
__export(cloudflare_do_exports, {
|
|
23
544
|
DurableObjectEngine: () => DurableObjectEngine,
|
|
24
545
|
JsonQueryTranslator: () => JsonQueryTranslator,
|
|
25
546
|
JsonSchemaDDL: () => JsonSchemaDDL,
|
|
547
|
+
clearMetaSyncCache: () => clearMetaSyncCache,
|
|
26
548
|
createDatabaseDO: () => createDatabaseDO,
|
|
27
549
|
createDocumentDO: () => createDocumentDO,
|
|
28
|
-
|
|
550
|
+
discoverModelNames: () => discoverModelNames,
|
|
551
|
+
discoverSchema: () => discoverSchema,
|
|
552
|
+
handleRequest: () => handleRequest,
|
|
553
|
+
inferFieldType: () => inferFieldType,
|
|
554
|
+
loadSchemaFromTomlString: () => loadSchemaFromTomlString,
|
|
555
|
+
registerFunctionDefault: () => registerFunctionDefault,
|
|
556
|
+
schemaToToml: () => schemaToToml,
|
|
557
|
+
syncInferredMeta: () => syncInferredMeta,
|
|
558
|
+
syncModelMeta: () => syncModelMeta
|
|
29
559
|
});
|
|
30
560
|
module.exports = __toCommonJS(cloudflare_do_exports);
|
|
31
561
|
|
|
@@ -67,23 +597,8 @@ var DatabaseEngine = class {
|
|
|
67
597
|
}
|
|
68
598
|
};
|
|
69
599
|
|
|
70
|
-
// src/utils/sql.ts
|
|
71
|
-
var IDENTIFIER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
72
|
-
function isValidIdentifier(name) {
|
|
73
|
-
return IDENTIFIER_PATTERN.test(name);
|
|
74
|
-
}
|
|
75
|
-
function assertValidIdentifier(name, context) {
|
|
76
|
-
if (!isValidIdentifier(name)) {
|
|
77
|
-
throw new Error(
|
|
78
|
-
`${context}: Identifier "${name}" must match ${IDENTIFIER_PATTERN.source}`
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
function quoteIdentifier(name) {
|
|
83
|
-
return `"${name.replace(/"/g, '""')}"`;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
600
|
// src/schema/JsonSchemaDDL.ts
|
|
601
|
+
init_sql();
|
|
87
602
|
var JsonSchemaDDL = class {
|
|
88
603
|
/**
|
|
89
604
|
* Generate CREATE TABLE statement for the records table
|
|
@@ -477,6 +992,7 @@ var JsonSchemaEngine = class extends DatabaseEngine {
|
|
|
477
992
|
};
|
|
478
993
|
|
|
479
994
|
// src/engines/cloudflare/DurableObjectEngine.ts
|
|
995
|
+
init_sql();
|
|
480
996
|
var DurableObjectEngine = class extends JsonSchemaEngine {
|
|
481
997
|
sql;
|
|
482
998
|
storage;
|
|
@@ -1047,276 +1563,11 @@ var DurableObjectEngine = class extends JsonSchemaEngine {
|
|
|
1047
1563
|
}
|
|
1048
1564
|
};
|
|
1049
1565
|
|
|
1050
|
-
// src/types/queryTypes.ts
|
|
1051
|
-
var DocumentQueryError = class _DocumentQueryError extends Error {
|
|
1052
|
-
constructor(message) {
|
|
1053
|
-
super(message);
|
|
1054
|
-
this.name = "DocumentQueryError";
|
|
1055
|
-
Object.setPrototypeOf(this, _DocumentQueryError.prototype);
|
|
1056
|
-
}
|
|
1057
|
-
};
|
|
1058
|
-
var InvalidOperatorError = class _InvalidOperatorError extends DocumentQueryError {
|
|
1059
|
-
field;
|
|
1060
|
-
operator;
|
|
1061
|
-
fieldType;
|
|
1062
|
-
constructor(message, field, operator, fieldType) {
|
|
1063
|
-
super(message);
|
|
1064
|
-
this.name = "InvalidOperatorError";
|
|
1065
|
-
this.field = field;
|
|
1066
|
-
this.operator = operator;
|
|
1067
|
-
this.fieldType = fieldType;
|
|
1068
|
-
Object.setPrototypeOf(this, _InvalidOperatorError.prototype);
|
|
1069
|
-
}
|
|
1070
|
-
};
|
|
1071
|
-
var InvalidFieldError = class _InvalidFieldError extends DocumentQueryError {
|
|
1072
|
-
field;
|
|
1073
|
-
modelName;
|
|
1074
|
-
constructor(message, field, modelName) {
|
|
1075
|
-
super(message);
|
|
1076
|
-
this.name = "InvalidFieldError";
|
|
1077
|
-
this.field = field;
|
|
1078
|
-
this.modelName = modelName;
|
|
1079
|
-
Object.setPrototypeOf(this, _InvalidFieldError.prototype);
|
|
1080
|
-
}
|
|
1081
|
-
};
|
|
1082
|
-
var InvalidCursorError = class _InvalidCursorError extends DocumentQueryError {
|
|
1083
|
-
cursor;
|
|
1084
|
-
constructor(message, cursor) {
|
|
1085
|
-
super(message);
|
|
1086
|
-
this.name = "InvalidCursorError";
|
|
1087
|
-
this.cursor = cursor;
|
|
1088
|
-
Object.setPrototypeOf(this, _InvalidCursorError.prototype);
|
|
1089
|
-
}
|
|
1090
|
-
};
|
|
1091
|
-
|
|
1092
|
-
// src/query/CursorManager.ts
|
|
1093
|
-
var base64Encode = (str) => {
|
|
1094
|
-
if (typeof btoa !== "undefined") {
|
|
1095
|
-
return btoa(str);
|
|
1096
|
-
} else if (typeof Buffer !== "undefined") {
|
|
1097
|
-
return Buffer.from(str, "utf-8").toString("base64");
|
|
1098
|
-
} else {
|
|
1099
|
-
throw new Error("No base64 encoding available");
|
|
1100
|
-
}
|
|
1101
|
-
};
|
|
1102
|
-
var base64Decode = (str) => {
|
|
1103
|
-
if (typeof atob !== "undefined") {
|
|
1104
|
-
return atob(str);
|
|
1105
|
-
} else if (typeof Buffer !== "undefined") {
|
|
1106
|
-
return Buffer.from(str, "base64").toString("utf-8");
|
|
1107
|
-
} else {
|
|
1108
|
-
throw new Error("No base64 decoding available");
|
|
1109
|
-
}
|
|
1110
|
-
};
|
|
1111
|
-
var CursorManager = class {
|
|
1112
|
-
/**
|
|
1113
|
-
* Encode cursor data to base64 string
|
|
1114
|
-
*/
|
|
1115
|
-
static encodeCursor(cursorData) {
|
|
1116
|
-
try {
|
|
1117
|
-
const jsonString = JSON.stringify(cursorData);
|
|
1118
|
-
return base64Encode(jsonString);
|
|
1119
|
-
} catch (error) {
|
|
1120
|
-
throw new InvalidCursorError(
|
|
1121
|
-
`Failed to encode cursor: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
1122
|
-
JSON.stringify(cursorData)
|
|
1123
|
-
);
|
|
1124
|
-
}
|
|
1125
|
-
}
|
|
1126
|
-
/**
|
|
1127
|
-
* Decode base64 cursor string to cursor data
|
|
1128
|
-
*/
|
|
1129
|
-
static decodeCursor(cursor) {
|
|
1130
|
-
try {
|
|
1131
|
-
const jsonString = base64Decode(cursor);
|
|
1132
|
-
const parsed = JSON.parse(jsonString);
|
|
1133
|
-
if (!parsed || typeof parsed !== "object") {
|
|
1134
|
-
throw new Error("Cursor must be an object");
|
|
1135
|
-
}
|
|
1136
|
-
if (!parsed.values || typeof parsed.values !== "object") {
|
|
1137
|
-
throw new Error("Cursor must have values object");
|
|
1138
|
-
}
|
|
1139
|
-
if (!Array.isArray(parsed.sortFields)) {
|
|
1140
|
-
throw new Error("Cursor must have sortFields array");
|
|
1141
|
-
}
|
|
1142
|
-
if (parsed.direction !== 1 && parsed.direction !== -1) {
|
|
1143
|
-
throw new Error("Cursor direction must be 1 or -1");
|
|
1144
|
-
}
|
|
1145
|
-
return parsed;
|
|
1146
|
-
} catch (error) {
|
|
1147
|
-
throw new InvalidCursorError(
|
|
1148
|
-
`Failed to decode cursor: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
1149
|
-
cursor
|
|
1150
|
-
);
|
|
1151
|
-
}
|
|
1152
|
-
}
|
|
1153
|
-
/**
|
|
1154
|
-
* Generate cursor from a record and sort specification
|
|
1155
|
-
*/
|
|
1156
|
-
static generateCursor(record, sortFields, direction) {
|
|
1157
|
-
const values = {};
|
|
1158
|
-
for (const field of sortFields) {
|
|
1159
|
-
if (record.hasOwnProperty(field)) {
|
|
1160
|
-
values[field] = record[field];
|
|
1161
|
-
} else {
|
|
1162
|
-
throw new Error(
|
|
1163
|
-
`Cannot generate cursor: record missing sort field '${field}'`
|
|
1164
|
-
);
|
|
1165
|
-
}
|
|
1166
|
-
}
|
|
1167
|
-
const cursorData = {
|
|
1168
|
-
values,
|
|
1169
|
-
sortFields,
|
|
1170
|
-
direction
|
|
1171
|
-
};
|
|
1172
|
-
return this.encodeCursor(cursorData);
|
|
1173
|
-
}
|
|
1174
|
-
/**
|
|
1175
|
-
* Build SQL pagination conditions based on cursor
|
|
1176
|
-
* Uses lexicographic ordering for stable pagination with multiple sort fields
|
|
1177
|
-
*/
|
|
1178
|
-
static buildPaginationConditions(cursor, currentSortFields, sortDirections, requestedDirection, fieldFormatter = (field) => field) {
|
|
1179
|
-
if (!this.arraysEqual(cursor.sortFields, currentSortFields)) {
|
|
1180
|
-
throw new InvalidCursorError(
|
|
1181
|
-
`Cursor sort fields [${cursor.sortFields.join(
|
|
1182
|
-
", "
|
|
1183
|
-
)}] don't match query sort fields [${currentSortFields.join(", ")}]`,
|
|
1184
|
-
JSON.stringify(cursor)
|
|
1185
|
-
);
|
|
1186
|
-
}
|
|
1187
|
-
const conditions = [];
|
|
1188
|
-
const params = [];
|
|
1189
|
-
const sortFields = cursor.sortFields;
|
|
1190
|
-
const direction = requestedDirection;
|
|
1191
|
-
for (let i = 0; i < sortFields.length; i++) {
|
|
1192
|
-
const fieldConditions = [];
|
|
1193
|
-
const fieldParams = [];
|
|
1194
|
-
for (let j = 0; j < i; j++) {
|
|
1195
|
-
const field = sortFields[j];
|
|
1196
|
-
const value = cursor.values[field];
|
|
1197
|
-
fieldConditions.push(`${fieldFormatter(field)} = ?`);
|
|
1198
|
-
fieldParams.push(value);
|
|
1199
|
-
}
|
|
1200
|
-
const currentField = sortFields[i];
|
|
1201
|
-
const currentValue = cursor.values[currentField];
|
|
1202
|
-
const fieldSortDir = sortDirections[i] ?? 1;
|
|
1203
|
-
const forwardOp = fieldSortDir === 1 ? ">" : "<";
|
|
1204
|
-
const operator = direction === 1 ? forwardOp : forwardOp === ">" ? "<" : ">";
|
|
1205
|
-
fieldConditions.push(`${fieldFormatter(currentField)} ${operator} ?`);
|
|
1206
|
-
fieldParams.push(currentValue);
|
|
1207
|
-
const levelCondition = fieldConditions.join(" AND ");
|
|
1208
|
-
conditions.push(`(${levelCondition})`);
|
|
1209
|
-
params.push(...fieldParams);
|
|
1210
|
-
}
|
|
1211
|
-
const sql = `(${conditions.join(" OR ")})`;
|
|
1212
|
-
return { sql, params };
|
|
1213
|
-
}
|
|
1214
|
-
/**
|
|
1215
|
-
* Extract sort fields from sort specification, ensuring 'id' is always included for stability
|
|
1216
|
-
*/
|
|
1217
|
-
static extractSortFields(sort) {
|
|
1218
|
-
const fields = [];
|
|
1219
|
-
if (sort && Object.keys(sort).length > 0) {
|
|
1220
|
-
fields.push(...Object.keys(sort));
|
|
1221
|
-
}
|
|
1222
|
-
if (!fields.includes("id")) {
|
|
1223
|
-
fields.push("id");
|
|
1224
|
-
}
|
|
1225
|
-
return fields;
|
|
1226
|
-
}
|
|
1227
|
-
/**
|
|
1228
|
-
* Build ORDER BY clause from sort specification
|
|
1229
|
-
*/
|
|
1230
|
-
static buildOrderClause(sort, fieldFormatter = (field) => field) {
|
|
1231
|
-
const fields = this.extractSortFields(sort);
|
|
1232
|
-
const clauses = [];
|
|
1233
|
-
const directions = [];
|
|
1234
|
-
if (sort && Object.keys(sort).length > 0) {
|
|
1235
|
-
for (const [field, dir] of Object.entries(sort)) {
|
|
1236
|
-
const sqlDirection = dir === 1 ? "ASC" : "DESC";
|
|
1237
|
-
clauses.push(`${fieldFormatter(field)} ${sqlDirection}`);
|
|
1238
|
-
directions.push(dir);
|
|
1239
|
-
}
|
|
1240
|
-
}
|
|
1241
|
-
if (!sort || !sort.hasOwnProperty("id")) {
|
|
1242
|
-
clauses.push(`${fieldFormatter("id")} ASC`);
|
|
1243
|
-
directions.push(1);
|
|
1244
|
-
} else if (sort && sort.hasOwnProperty("id")) {
|
|
1245
|
-
directions.push(sort["id"]);
|
|
1246
|
-
}
|
|
1247
|
-
return {
|
|
1248
|
-
sql: clauses.join(", "),
|
|
1249
|
-
fields,
|
|
1250
|
-
directions
|
|
1251
|
-
};
|
|
1252
|
-
}
|
|
1253
|
-
/**
|
|
1254
|
-
* Determine if there are more results available for pagination
|
|
1255
|
-
*/
|
|
1256
|
-
static hasMoreResults(requestedLimit, actualResultCount) {
|
|
1257
|
-
if (!requestedLimit || requestedLimit <= 0) {
|
|
1258
|
-
return false;
|
|
1259
|
-
}
|
|
1260
|
-
return actualResultCount >= requestedLimit;
|
|
1261
|
-
}
|
|
1262
|
-
/**
|
|
1263
|
-
* Generate next and previous cursors from result set
|
|
1264
|
-
*/
|
|
1265
|
-
static generateResultCursors(results, sortFields, _requestedDirection, hasMore, isFirstPage = false) {
|
|
1266
|
-
if (results.length === 0) {
|
|
1267
|
-
return {};
|
|
1268
|
-
}
|
|
1269
|
-
const cursors = {};
|
|
1270
|
-
if (hasMore && results.length > 0) {
|
|
1271
|
-
const lastResult = results[results.length - 1];
|
|
1272
|
-
cursors.nextCursor = this.generateCursor(lastResult, sortFields, 1);
|
|
1273
|
-
}
|
|
1274
|
-
if (results.length > 0 && !isFirstPage) {
|
|
1275
|
-
const firstResult = results[0];
|
|
1276
|
-
cursors.prevCursor = this.generateCursor(firstResult, sortFields, -1);
|
|
1277
|
-
}
|
|
1278
|
-
return cursors;
|
|
1279
|
-
}
|
|
1280
|
-
/**
|
|
1281
|
-
* Utility to compare arrays for equality
|
|
1282
|
-
*/
|
|
1283
|
-
static arraysEqual(a, b) {
|
|
1284
|
-
if (a.length !== b.length) return false;
|
|
1285
|
-
return a.every((val, index) => val === b[index]);
|
|
1286
|
-
}
|
|
1287
|
-
};
|
|
1288
|
-
|
|
1289
|
-
// src/utils/patterns.ts
|
|
1290
|
-
function escapeLikeLiteral(input) {
|
|
1291
|
-
return input.replace(/\\/g, "\\\\").replace(/%/g, "\\%").replace(/_/g, "\\_");
|
|
1292
|
-
}
|
|
1293
|
-
function buildLikePattern(value, mode) {
|
|
1294
|
-
const trimmed = (value ?? "").trim();
|
|
1295
|
-
if (trimmed.length === 0) return null;
|
|
1296
|
-
if (trimmed.length > 1024) {
|
|
1297
|
-
throw new Error("substring value exceeds 1024 characters");
|
|
1298
|
-
}
|
|
1299
|
-
const escaped = escapeLikeLiteral(trimmed);
|
|
1300
|
-
switch (mode) {
|
|
1301
|
-
case "startsWith":
|
|
1302
|
-
return `${escaped}%`;
|
|
1303
|
-
case "endsWith":
|
|
1304
|
-
return `%${escaped}`;
|
|
1305
|
-
case "containsText":
|
|
1306
|
-
return `%${escaped}%`;
|
|
1307
|
-
}
|
|
1308
|
-
}
|
|
1309
|
-
function shouldLogLikeEscapes() {
|
|
1310
|
-
try {
|
|
1311
|
-
if (typeof process !== "undefined" && process.env) {
|
|
1312
|
-
return !!process.env.JS_BAO_DEBUG_LIKE_ESCAPES;
|
|
1313
|
-
}
|
|
1314
|
-
} catch {
|
|
1315
|
-
}
|
|
1316
|
-
return false;
|
|
1317
|
-
}
|
|
1318
|
-
|
|
1319
1566
|
// src/query/JsonQueryTranslator.ts
|
|
1567
|
+
init_queryTypes();
|
|
1568
|
+
init_CursorManager();
|
|
1569
|
+
init_sql();
|
|
1570
|
+
init_patterns();
|
|
1320
1571
|
var SYSTEM_FIELDS = /* @__PURE__ */ new Set(["id", "type"]);
|
|
1321
1572
|
var JsonQueryTranslator = class {
|
|
1322
1573
|
modelName;
|
|
@@ -1995,6 +2246,8 @@ var JsonQueryTranslator = class {
|
|
|
1995
2246
|
};
|
|
1996
2247
|
|
|
1997
2248
|
// src/engines/cloudflare/createDocumentDO.ts
|
|
2249
|
+
init_CursorManager();
|
|
2250
|
+
init_sql();
|
|
1998
2251
|
var import_ulid = require("ulid");
|
|
1999
2252
|
function createDatabaseDO(config = {}) {
|
|
2000
2253
|
const hooks = config.hooks;
|
|
@@ -3804,3 +4057,401 @@ async function handleRequest(request, env) {
|
|
|
3804
4057
|
const response = await stub.fetch(doRequest);
|
|
3805
4058
|
return addCorsHeaders(response);
|
|
3806
4059
|
}
|
|
4060
|
+
|
|
4061
|
+
// src/utils/yDocSchema.ts
|
|
4062
|
+
var Y = __toESM(require("yjs"), 1);
|
|
4063
|
+
function discoverSchema(yDoc) {
|
|
4064
|
+
const models = {};
|
|
4065
|
+
const metaNames = /* @__PURE__ */ new Set();
|
|
4066
|
+
for (const key of yDoc.share.keys()) {
|
|
4067
|
+
if (!key.startsWith("_meta_")) continue;
|
|
4068
|
+
const map = materializeMap(yDoc, key);
|
|
4069
|
+
if (!map) continue;
|
|
4070
|
+
const modelName = key.slice("_meta_".length);
|
|
4071
|
+
metaNames.add(modelName);
|
|
4072
|
+
models[modelName] = readModelMeta(map);
|
|
4073
|
+
}
|
|
4074
|
+
for (const key of yDoc.share.keys()) {
|
|
4075
|
+
if (key.startsWith("_")) continue;
|
|
4076
|
+
if (metaNames.has(key)) continue;
|
|
4077
|
+
const map = materializeMap(yDoc, key);
|
|
4078
|
+
if (!map || map.size === 0) continue;
|
|
4079
|
+
const inferred = inferModelFromData(map);
|
|
4080
|
+
if (inferred) models[key] = inferred;
|
|
4081
|
+
}
|
|
4082
|
+
return { models };
|
|
4083
|
+
}
|
|
4084
|
+
function discoverModelNames(yDoc) {
|
|
4085
|
+
const names = [];
|
|
4086
|
+
for (const key of yDoc.share.keys()) {
|
|
4087
|
+
if (key.startsWith("_")) continue;
|
|
4088
|
+
const map = materializeMap(yDoc, key);
|
|
4089
|
+
if (map) names.push(key);
|
|
4090
|
+
}
|
|
4091
|
+
return names.sort();
|
|
4092
|
+
}
|
|
4093
|
+
function materializeMap(yDoc, key) {
|
|
4094
|
+
try {
|
|
4095
|
+
const map = yDoc.getMap(key);
|
|
4096
|
+
return map instanceof Y.Map ? map : null;
|
|
4097
|
+
} catch {
|
|
4098
|
+
return null;
|
|
4099
|
+
}
|
|
4100
|
+
}
|
|
4101
|
+
function readModelMeta(metaMap) {
|
|
4102
|
+
const fields = {};
|
|
4103
|
+
let constraints;
|
|
4104
|
+
let relationships;
|
|
4105
|
+
for (const [key, value] of metaMap.entries()) {
|
|
4106
|
+
if (key === "_constraints" && value instanceof Y.Map) {
|
|
4107
|
+
constraints = readConstraints(value);
|
|
4108
|
+
} else if (key === "_relationships" && value instanceof Y.Map) {
|
|
4109
|
+
relationships = readRelationships(value);
|
|
4110
|
+
} else if (value instanceof Y.Map) {
|
|
4111
|
+
fields[key] = readFieldMeta(value);
|
|
4112
|
+
}
|
|
4113
|
+
}
|
|
4114
|
+
const model = { fields };
|
|
4115
|
+
if (constraints && Object.keys(constraints).length > 0) {
|
|
4116
|
+
model.constraints = constraints;
|
|
4117
|
+
}
|
|
4118
|
+
if (relationships && Object.keys(relationships).length > 0) {
|
|
4119
|
+
model.relationships = relationships;
|
|
4120
|
+
}
|
|
4121
|
+
return model;
|
|
4122
|
+
}
|
|
4123
|
+
function readFieldMeta(fieldMap) {
|
|
4124
|
+
const field = { type: fieldMap.get("type") ?? "unknown" };
|
|
4125
|
+
if (fieldMap.get("indexed") === true) field.indexed = true;
|
|
4126
|
+
if (fieldMap.get("unique") === true) field.unique = true;
|
|
4127
|
+
if (fieldMap.get("required") === true) field.required = true;
|
|
4128
|
+
if (fieldMap.get("autoAssign") === true) field.autoAssign = true;
|
|
4129
|
+
const def = fieldMap.get("default");
|
|
4130
|
+
if (def !== void 0) field.default = def;
|
|
4131
|
+
const maxLength = fieldMap.get("maxLength");
|
|
4132
|
+
if (maxLength !== void 0) field.maxLength = maxLength;
|
|
4133
|
+
const maxCount = fieldMap.get("maxCount");
|
|
4134
|
+
if (maxCount !== void 0) field.maxCount = maxCount;
|
|
4135
|
+
return field;
|
|
4136
|
+
}
|
|
4137
|
+
function inferModelFromData(dataMap) {
|
|
4138
|
+
const fields = {};
|
|
4139
|
+
let sampled = 0;
|
|
4140
|
+
for (const [_recordId, recordValue] of dataMap.entries()) {
|
|
4141
|
+
if (!(recordValue instanceof Y.Map)) continue;
|
|
4142
|
+
if (++sampled > 5) break;
|
|
4143
|
+
for (const [fieldName, value] of recordValue.entries()) {
|
|
4144
|
+
if (fieldName.startsWith("_")) continue;
|
|
4145
|
+
if (fields[fieldName]) continue;
|
|
4146
|
+
const type = inferTypeFromValue(value);
|
|
4147
|
+
if (type) fields[fieldName] = { type };
|
|
4148
|
+
}
|
|
4149
|
+
}
|
|
4150
|
+
if (Object.keys(fields).length === 0) return null;
|
|
4151
|
+
return { fields };
|
|
4152
|
+
}
|
|
4153
|
+
function inferTypeFromValue(value) {
|
|
4154
|
+
if (value instanceof Y.Map) return "stringset";
|
|
4155
|
+
switch (typeof value) {
|
|
4156
|
+
case "string":
|
|
4157
|
+
return "string";
|
|
4158
|
+
case "number":
|
|
4159
|
+
return "number";
|
|
4160
|
+
case "boolean":
|
|
4161
|
+
return "boolean";
|
|
4162
|
+
default:
|
|
4163
|
+
return null;
|
|
4164
|
+
}
|
|
4165
|
+
}
|
|
4166
|
+
function readConstraints(constraintsMap) {
|
|
4167
|
+
const out = {};
|
|
4168
|
+
for (const [name, value] of constraintsMap.entries()) {
|
|
4169
|
+
if (!(value instanceof Y.Map)) continue;
|
|
4170
|
+
let fields = [];
|
|
4171
|
+
const rawFields = value.get("fields");
|
|
4172
|
+
if (typeof rawFields === "string") {
|
|
4173
|
+
try {
|
|
4174
|
+
fields = JSON.parse(rawFields);
|
|
4175
|
+
} catch {
|
|
4176
|
+
}
|
|
4177
|
+
}
|
|
4178
|
+
out[name] = {
|
|
4179
|
+
type: value.get("type") ?? "unknown",
|
|
4180
|
+
fields
|
|
4181
|
+
};
|
|
4182
|
+
}
|
|
4183
|
+
return out;
|
|
4184
|
+
}
|
|
4185
|
+
function readRelationships(relsMap) {
|
|
4186
|
+
const out = {};
|
|
4187
|
+
for (const [name, value] of relsMap.entries()) {
|
|
4188
|
+
if (!(value instanceof Y.Map)) continue;
|
|
4189
|
+
const rel = {};
|
|
4190
|
+
for (const [k, v] of value.entries()) {
|
|
4191
|
+
rel[k] = v;
|
|
4192
|
+
}
|
|
4193
|
+
out[name] = rel;
|
|
4194
|
+
}
|
|
4195
|
+
return out;
|
|
4196
|
+
}
|
|
4197
|
+
var CAMEL_TO_SNAKE = {
|
|
4198
|
+
autoAssign: "auto_assign",
|
|
4199
|
+
maxLength: "max_length",
|
|
4200
|
+
maxCount: "max_count",
|
|
4201
|
+
relatedIdField: "related_id_field",
|
|
4202
|
+
joinModel: "join_model",
|
|
4203
|
+
joinModelLocalField: "join_model_local_field",
|
|
4204
|
+
joinModelRelatedField: "join_model_related_field",
|
|
4205
|
+
joinModelOrderByField: "join_model_order_by_field",
|
|
4206
|
+
joinModelOrderDirection: "join_model_order_direction",
|
|
4207
|
+
orderByField: "order_by_field",
|
|
4208
|
+
orderDirection: "order_direction"
|
|
4209
|
+
};
|
|
4210
|
+
function toSnake(key) {
|
|
4211
|
+
return CAMEL_TO_SNAKE[key] ?? key;
|
|
4212
|
+
}
|
|
4213
|
+
function tomlValue(v) {
|
|
4214
|
+
if (typeof v === "string") {
|
|
4215
|
+
return `"${v.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t")}"`;
|
|
4216
|
+
}
|
|
4217
|
+
return String(v);
|
|
4218
|
+
}
|
|
4219
|
+
function schemaToToml(schema) {
|
|
4220
|
+
const lines = [];
|
|
4221
|
+
for (const [modelName, model] of Object.entries(schema.models)) {
|
|
4222
|
+
if (lines.length > 0) lines.push("", "");
|
|
4223
|
+
lines.push(`[models.${modelName}]`);
|
|
4224
|
+
for (const [fieldName, field] of Object.entries(model.fields)) {
|
|
4225
|
+
lines.push("");
|
|
4226
|
+
lines.push(`[models.${modelName}.fields.${fieldName}]`);
|
|
4227
|
+
lines.push(`type = ${tomlValue(field.type)}`);
|
|
4228
|
+
if (field.autoAssign) lines.push("auto_assign = true");
|
|
4229
|
+
if (field.indexed) lines.push("indexed = true");
|
|
4230
|
+
if (field.unique) lines.push("unique = true");
|
|
4231
|
+
if (field.required) lines.push("required = true");
|
|
4232
|
+
if (field.maxLength !== void 0) lines.push(`max_length = ${field.maxLength}`);
|
|
4233
|
+
if (field.maxCount !== void 0) lines.push(`max_count = ${field.maxCount}`);
|
|
4234
|
+
if (field.default !== void 0) lines.push(`default = ${tomlValue(field.default)}`);
|
|
4235
|
+
}
|
|
4236
|
+
if (model.relationships) {
|
|
4237
|
+
for (const [relName, rel] of Object.entries(model.relationships)) {
|
|
4238
|
+
lines.push("");
|
|
4239
|
+
lines.push(`[models.${modelName}.relationships.${relName}]`);
|
|
4240
|
+
for (const [k, v] of Object.entries(rel)) {
|
|
4241
|
+
if (v === void 0) continue;
|
|
4242
|
+
lines.push(`${toSnake(k)} = ${tomlValue(v)}`);
|
|
4243
|
+
}
|
|
4244
|
+
}
|
|
4245
|
+
}
|
|
4246
|
+
if (model.constraints) {
|
|
4247
|
+
for (const [cName, c] of Object.entries(model.constraints)) {
|
|
4248
|
+
lines.push("");
|
|
4249
|
+
lines.push(`[[models.${modelName}.unique_constraints]]`);
|
|
4250
|
+
lines.push(`name = ${tomlValue(cName)}`);
|
|
4251
|
+
lines.push(`fields = [${c.fields.map((f) => tomlValue(f)).join(", ")}]`);
|
|
4252
|
+
}
|
|
4253
|
+
}
|
|
4254
|
+
}
|
|
4255
|
+
lines.push("");
|
|
4256
|
+
return lines.join("\n");
|
|
4257
|
+
}
|
|
4258
|
+
|
|
4259
|
+
// src/models/tomlLoader.ts
|
|
4260
|
+
var import_smol_toml = require("smol-toml");
|
|
4261
|
+
|
|
4262
|
+
// src/models/schema.ts
|
|
4263
|
+
init_BaseModel();
|
|
4264
|
+
init_ModelRegistry();
|
|
4265
|
+
function defineModelSchema(input) {
|
|
4266
|
+
const { name, fields } = input;
|
|
4267
|
+
const options = {
|
|
4268
|
+
name,
|
|
4269
|
+
uniqueConstraints: input.options?.uniqueConstraints ? [...input.options.uniqueConstraints] : void 0,
|
|
4270
|
+
relationships: input.options?.relationships
|
|
4271
|
+
};
|
|
4272
|
+
const schema = {
|
|
4273
|
+
name,
|
|
4274
|
+
fields,
|
|
4275
|
+
options,
|
|
4276
|
+
runtimeShape: void 0,
|
|
4277
|
+
buildRuntimeShape(modelClass) {
|
|
4278
|
+
if (schema.runtimeShape && schema.runtimeShape.class === modelClass) {
|
|
4279
|
+
return schema.runtimeShape;
|
|
4280
|
+
}
|
|
4281
|
+
const fieldsMap = /* @__PURE__ */ new Map();
|
|
4282
|
+
for (const [fieldName, fieldOptions] of Object.entries(fields)) {
|
|
4283
|
+
fieldsMap.set(fieldName, { ...fieldOptions });
|
|
4284
|
+
}
|
|
4285
|
+
const resolvedUniqueConstraints = resolveUniqueConstraints(
|
|
4286
|
+
name,
|
|
4287
|
+
fieldsMap,
|
|
4288
|
+
options.uniqueConstraints
|
|
4289
|
+
);
|
|
4290
|
+
schema.runtimeShape = {
|
|
4291
|
+
class: modelClass,
|
|
4292
|
+
options,
|
|
4293
|
+
fields: fieldsMap,
|
|
4294
|
+
resolvedUniqueConstraints
|
|
4295
|
+
};
|
|
4296
|
+
return schema.runtimeShape;
|
|
4297
|
+
}
|
|
4298
|
+
};
|
|
4299
|
+
return schema;
|
|
4300
|
+
}
|
|
4301
|
+
function resolveUniqueConstraints(modelName, fields, customConstraints) {
|
|
4302
|
+
const resolved = [];
|
|
4303
|
+
for (const [fieldName, options] of fields.entries()) {
|
|
4304
|
+
if (options.unique) {
|
|
4305
|
+
resolved.push({
|
|
4306
|
+
name: `${modelName}_${fieldName}_unique`,
|
|
4307
|
+
fields: [fieldName]
|
|
4308
|
+
});
|
|
4309
|
+
}
|
|
4310
|
+
}
|
|
4311
|
+
if (customConstraints) {
|
|
4312
|
+
for (const constraint of customConstraints) {
|
|
4313
|
+
const missingField = constraint.fields.find(
|
|
4314
|
+
(field) => !fields.has(field)
|
|
4315
|
+
);
|
|
4316
|
+
if (missingField) {
|
|
4317
|
+
console.warn(
|
|
4318
|
+
`[defineModelSchema] Unique constraint "${constraint.name}" for model "${modelName}" references unknown field "${missingField}". Skipping.`
|
|
4319
|
+
);
|
|
4320
|
+
continue;
|
|
4321
|
+
}
|
|
4322
|
+
if (resolved.some((item) => item.name === constraint.name)) {
|
|
4323
|
+
console.warn(
|
|
4324
|
+
`[defineModelSchema] Duplicate unique constraint name "${constraint.name}" in model "${modelName}".`
|
|
4325
|
+
);
|
|
4326
|
+
}
|
|
4327
|
+
resolved.push({
|
|
4328
|
+
name: constraint.name,
|
|
4329
|
+
fields: [...constraint.fields]
|
|
4330
|
+
});
|
|
4331
|
+
}
|
|
4332
|
+
}
|
|
4333
|
+
return resolved;
|
|
4334
|
+
}
|
|
4335
|
+
|
|
4336
|
+
// src/models/tomlLoader.ts
|
|
4337
|
+
var VALID_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
4338
|
+
"string",
|
|
4339
|
+
"number",
|
|
4340
|
+
"boolean",
|
|
4341
|
+
"date",
|
|
4342
|
+
"id",
|
|
4343
|
+
"stringset"
|
|
4344
|
+
]);
|
|
4345
|
+
function parseFieldOptions(raw) {
|
|
4346
|
+
if (!raw.type || !VALID_FIELD_TYPES.has(raw.type)) {
|
|
4347
|
+
throw new Error(
|
|
4348
|
+
`Invalid field type "${raw.type}". Must be one of: ${[...VALID_FIELD_TYPES].join(", ")}`
|
|
4349
|
+
);
|
|
4350
|
+
}
|
|
4351
|
+
const opts = { type: raw.type };
|
|
4352
|
+
if (raw.indexed === true) opts.indexed = true;
|
|
4353
|
+
if (raw.unique === true) opts.unique = true;
|
|
4354
|
+
if (raw.required === true) opts.required = true;
|
|
4355
|
+
if (raw.auto_assign === true) opts.autoAssign = true;
|
|
4356
|
+
if (raw.max_length !== void 0) opts.maxLength = raw.max_length;
|
|
4357
|
+
if (raw.max_count !== void 0) opts.maxCount = raw.max_count;
|
|
4358
|
+
if (raw.default !== void 0) opts.default = raw.default;
|
|
4359
|
+
return opts;
|
|
4360
|
+
}
|
|
4361
|
+
function requireField(raw, field, context) {
|
|
4362
|
+
if (!raw[field]) {
|
|
4363
|
+
throw new Error(`Relationship ${context}: missing required field "${field}"`);
|
|
4364
|
+
}
|
|
4365
|
+
}
|
|
4366
|
+
function parseRelationship(raw) {
|
|
4367
|
+
const type = raw.type;
|
|
4368
|
+
if (type === "refersTo") {
|
|
4369
|
+
requireField(raw, "model", "refersTo");
|
|
4370
|
+
requireField(raw, "related_id_field", "refersTo");
|
|
4371
|
+
return {
|
|
4372
|
+
type: "refersTo",
|
|
4373
|
+
model: raw.model,
|
|
4374
|
+
relatedIdField: raw.related_id_field
|
|
4375
|
+
};
|
|
4376
|
+
}
|
|
4377
|
+
if (type === "hasMany") {
|
|
4378
|
+
requireField(raw, "model", "hasMany");
|
|
4379
|
+
requireField(raw, "related_id_field", "hasMany");
|
|
4380
|
+
const rel = {
|
|
4381
|
+
type: "hasMany",
|
|
4382
|
+
model: raw.model,
|
|
4383
|
+
relatedIdField: raw.related_id_field
|
|
4384
|
+
};
|
|
4385
|
+
if (raw.order_by_field) rel.orderByField = raw.order_by_field;
|
|
4386
|
+
if (raw.order_direction) rel.orderDirection = raw.order_direction;
|
|
4387
|
+
return rel;
|
|
4388
|
+
}
|
|
4389
|
+
if (type === "hasManyThrough") {
|
|
4390
|
+
requireField(raw, "model", "hasManyThrough");
|
|
4391
|
+
requireField(raw, "join_model", "hasManyThrough");
|
|
4392
|
+
requireField(raw, "join_model_local_field", "hasManyThrough");
|
|
4393
|
+
requireField(raw, "join_model_related_field", "hasManyThrough");
|
|
4394
|
+
const rel = {
|
|
4395
|
+
type: "hasManyThrough",
|
|
4396
|
+
model: raw.model,
|
|
4397
|
+
joinModel: raw.join_model,
|
|
4398
|
+
joinModelLocalField: raw.join_model_local_field,
|
|
4399
|
+
joinModelRelatedField: raw.join_model_related_field
|
|
4400
|
+
};
|
|
4401
|
+
if (raw.join_model_order_by_field)
|
|
4402
|
+
rel.joinModelOrderByField = raw.join_model_order_by_field;
|
|
4403
|
+
if (raw.join_model_order_direction)
|
|
4404
|
+
rel.joinModelOrderDirection = raw.join_model_order_direction;
|
|
4405
|
+
return rel;
|
|
4406
|
+
}
|
|
4407
|
+
throw new Error(`Unknown relationship type: ${type}`);
|
|
4408
|
+
}
|
|
4409
|
+
function loadSchemaFromTomlString(tomlString) {
|
|
4410
|
+
const parsed = (0, import_smol_toml.parse)(tomlString);
|
|
4411
|
+
const models = parsed.models;
|
|
4412
|
+
if (!models || typeof models !== "object") {
|
|
4413
|
+
throw new Error("TOML schema must have a [models] section");
|
|
4414
|
+
}
|
|
4415
|
+
const schemas = [];
|
|
4416
|
+
for (const [modelName, modelDef] of Object.entries(models)) {
|
|
4417
|
+
const fields = {};
|
|
4418
|
+
if (modelDef.fields) {
|
|
4419
|
+
for (const [fieldName, fieldDef] of Object.entries(modelDef.fields)) {
|
|
4420
|
+
fields[fieldName] = parseFieldOptions(fieldDef);
|
|
4421
|
+
}
|
|
4422
|
+
}
|
|
4423
|
+
let relationships;
|
|
4424
|
+
if (modelDef.relationships) {
|
|
4425
|
+
relationships = {};
|
|
4426
|
+
for (const [relName, relDef] of Object.entries(
|
|
4427
|
+
modelDef.relationships
|
|
4428
|
+
)) {
|
|
4429
|
+
relationships[relName] = parseRelationship(relDef);
|
|
4430
|
+
}
|
|
4431
|
+
}
|
|
4432
|
+
let uniqueConstraints;
|
|
4433
|
+
if (modelDef.unique_constraints) {
|
|
4434
|
+
uniqueConstraints = [];
|
|
4435
|
+
for (const raw of modelDef.unique_constraints) {
|
|
4436
|
+
uniqueConstraints.push({
|
|
4437
|
+
name: raw.name,
|
|
4438
|
+
fields: [...raw.fields]
|
|
4439
|
+
});
|
|
4440
|
+
}
|
|
4441
|
+
}
|
|
4442
|
+
schemas.push(
|
|
4443
|
+
defineModelSchema({
|
|
4444
|
+
name: modelName,
|
|
4445
|
+
fields,
|
|
4446
|
+
options: {
|
|
4447
|
+
uniqueConstraints,
|
|
4448
|
+
relationships
|
|
4449
|
+
}
|
|
4450
|
+
})
|
|
4451
|
+
);
|
|
4452
|
+
}
|
|
4453
|
+
return schemas;
|
|
4454
|
+
}
|
|
4455
|
+
|
|
4456
|
+
// src/cloudflare-do.ts
|
|
4457
|
+
init_metaSync();
|