js-bao 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
- handleRequest: () => handleRequest
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();