js-bao 0.2.11 → 0.2.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/README.md +174 -0
  2. package/dist/BaseModel-5YQCROYE.js +17 -0
  3. package/dist/BaseModel-5YQCROYE.js.map +1 -0
  4. package/dist/BaseModel-FCNWDJBH.js +17 -0
  5. package/dist/BaseModel-FCNWDJBH.js.map +1 -0
  6. package/dist/BrowserDatabaseFactory-PXOTK2DQ.js +119 -0
  7. package/dist/BrowserDatabaseFactory-PXOTK2DQ.js.map +1 -0
  8. package/dist/BrowserDatabaseFactory-WD4VX2VZ.js +119 -0
  9. package/dist/BrowserDatabaseFactory-WD4VX2VZ.js.map +1 -0
  10. package/dist/IncludeResolver-RCKQGNPZ.js +385 -0
  11. package/dist/IncludeResolver-RCKQGNPZ.js.map +1 -0
  12. package/dist/IncludeResolver-WGSQDMS7.js +385 -0
  13. package/dist/IncludeResolver-WGSQDMS7.js.map +1 -0
  14. package/dist/NodeDatabaseFactory-J4Z36UF3.js +165 -0
  15. package/dist/NodeDatabaseFactory-J4Z36UF3.js.map +1 -0
  16. package/dist/NodeDatabaseFactory-QIEKAXBM.js +10 -0
  17. package/dist/NodeDatabaseFactory-QIEKAXBM.js.map +1 -0
  18. package/dist/NodeSqliteEngine-HJSAYE4E.js +383 -0
  19. package/dist/NodeSqliteEngine-HJSAYE4E.js.map +1 -0
  20. package/dist/NodeSqliteEngine-I5SLWLME.js +383 -0
  21. package/dist/NodeSqliteEngine-I5SLWLME.js.map +1 -0
  22. package/dist/browser.cjs +3779 -3370
  23. package/dist/browser.d.cts +18 -1
  24. package/dist/browser.d.ts +18 -1
  25. package/dist/browser.js +3750 -3341
  26. package/dist/chunk-3PZWHUZO.js +4153 -0
  27. package/dist/chunk-3PZWHUZO.js.map +1 -0
  28. package/dist/chunk-53MS4MN7.js +373 -0
  29. package/dist/chunk-53MS4MN7.js.map +1 -0
  30. package/dist/chunk-65G2P4GL.js +709 -0
  31. package/dist/chunk-65G2P4GL.js.map +1 -0
  32. package/dist/chunk-6UX3YSCW.js +4151 -0
  33. package/dist/chunk-6UX3YSCW.js.map +1 -0
  34. package/dist/chunk-DANSD6BE.js +709 -0
  35. package/dist/chunk-DANSD6BE.js.map +1 -0
  36. package/dist/chunk-DF3JEQXA.js +373 -0
  37. package/dist/chunk-DF3JEQXA.js.map +1 -0
  38. package/dist/chunk-GO3APTPX.js +61 -0
  39. package/dist/chunk-GO3APTPX.js.map +1 -0
  40. package/dist/chunk-ID4U6IQC.js +53 -0
  41. package/dist/chunk-ID4U6IQC.js.map +1 -0
  42. package/dist/chunk-RQVS3LVL.js +165 -0
  43. package/dist/chunk-RQVS3LVL.js.map +1 -0
  44. package/dist/client.cjs +837 -0
  45. package/dist/client.d.cts +1101 -0
  46. package/dist/client.d.ts +1101 -0
  47. package/dist/client.js +806 -0
  48. package/dist/cloudflare-do.cjs +3637 -0
  49. package/dist/cloudflare-do.d.cts +1366 -0
  50. package/dist/cloudflare-do.d.ts +1366 -0
  51. package/dist/cloudflare-do.js +3614 -0
  52. package/dist/cloudflare.cjs +1048 -0
  53. package/dist/cloudflare.d.cts +1381 -0
  54. package/dist/cloudflare.d.ts +1381 -0
  55. package/dist/cloudflare.js +1017 -0
  56. package/dist/codegen.cjs +260 -19
  57. package/dist/environment-TOTQICSE.js +17 -0
  58. package/dist/environment-TOTQICSE.js.map +1 -0
  59. package/dist/index.cjs +1905 -1492
  60. package/dist/index.d.cts +19 -2
  61. package/dist/index.d.ts +19 -2
  62. package/dist/index.js +1870 -1457
  63. package/dist/node.cjs +4779 -4366
  64. package/dist/node.d.cts +18 -1
  65. package/dist/node.d.ts +18 -1
  66. package/dist/node.js +4758 -4345
  67. package/package.json +42 -13
package/dist/client.js ADDED
@@ -0,0 +1,806 @@
1
+ var __getOwnPropNames = Object.getOwnPropertyNames;
2
+ var __esm = (fn, res) => function __init() {
3
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
4
+ };
5
+
6
+ // src/models/StringSet.ts
7
+ var init_StringSet = __esm({
8
+ "src/models/StringSet.ts"() {
9
+ "use strict";
10
+ }
11
+ });
12
+
13
+ // src/types/documentTypes.ts
14
+ var init_documentTypes = __esm({
15
+ "src/types/documentTypes.ts"() {
16
+ "use strict";
17
+ }
18
+ });
19
+
20
+ // src/types/queryTypes.ts
21
+ var init_queryTypes = __esm({
22
+ "src/types/queryTypes.ts"() {
23
+ "use strict";
24
+ }
25
+ });
26
+
27
+ // src/query/CursorManager.ts
28
+ var init_CursorManager = __esm({
29
+ "src/query/CursorManager.ts"() {
30
+ "use strict";
31
+ init_queryTypes();
32
+ }
33
+ });
34
+
35
+ // src/utils/sql.ts
36
+ var init_sql = __esm({
37
+ "src/utils/sql.ts"() {
38
+ "use strict";
39
+ }
40
+ });
41
+
42
+ // src/utils/patterns.ts
43
+ var init_patterns = __esm({
44
+ "src/utils/patterns.ts"() {
45
+ "use strict";
46
+ }
47
+ });
48
+
49
+ // src/query/DocumentQueryTranslator.ts
50
+ var init_DocumentQueryTranslator = __esm({
51
+ "src/query/DocumentQueryTranslator.ts"() {
52
+ "use strict";
53
+ init_queryTypes();
54
+ init_CursorManager();
55
+ init_sql();
56
+ init_patterns();
57
+ }
58
+ });
59
+
60
+ // src/models/BaseModel.ts
61
+ import * as Y from "yjs";
62
+ import { ulid } from "ulid";
63
+ var Logger;
64
+ var init_BaseModel = __esm({
65
+ "src/models/BaseModel.ts"() {
66
+ "use strict";
67
+ init_StringSet();
68
+ init_documentTypes();
69
+ init_DocumentQueryTranslator();
70
+ init_CursorManager();
71
+ init_sql();
72
+ Logger = class {
73
+ static _logLevel = 1 /* ERROR */;
74
+ static _logCallback = null;
75
+ static setLogLevel(level) {
76
+ this._logLevel = level;
77
+ }
78
+ static getLogLevel() {
79
+ return this._logLevel;
80
+ }
81
+ static setLogCallback(callback) {
82
+ this._logCallback = callback;
83
+ }
84
+ static error(message, ...args) {
85
+ if (this._logLevel >= 1 /* ERROR */) {
86
+ const fullMessage = args.length > 0 ? `${message} ${args.map(
87
+ (arg) => typeof arg === "object" ? JSON.stringify(arg) : String(arg)
88
+ ).join(" ")}` : message;
89
+ console.error(`[ERROR] ${message}`, ...args);
90
+ if (this._logCallback) {
91
+ this._logCallback(fullMessage, 1 /* ERROR */);
92
+ }
93
+ }
94
+ }
95
+ static warn(message, ...args) {
96
+ if (this._logLevel >= 2 /* WARN */) {
97
+ const fullMessage = args.length > 0 ? `${message} ${args.map(
98
+ (arg) => typeof arg === "object" ? JSON.stringify(arg) : String(arg)
99
+ ).join(" ")}` : message;
100
+ console.warn(`[WARN] ${message}`, ...args);
101
+ if (this._logCallback) {
102
+ this._logCallback(fullMessage, 2 /* WARN */);
103
+ }
104
+ }
105
+ }
106
+ static info(message, ...args) {
107
+ if (this._logLevel >= 3 /* INFO */) {
108
+ const fullMessage = args.length > 0 ? `${message} ${args.map(
109
+ (arg) => typeof arg === "object" ? JSON.stringify(arg) : String(arg)
110
+ ).join(" ")}` : message;
111
+ console.log(`[INFO] ${message}`, ...args);
112
+ if (this._logCallback) {
113
+ this._logCallback(fullMessage, 3 /* INFO */);
114
+ }
115
+ }
116
+ }
117
+ static debug(message, ...args) {
118
+ if (this._logLevel >= 4 /* DEBUG */) {
119
+ const fullMessage = args.length > 0 ? `${message} ${args.map(
120
+ (arg) => typeof arg === "object" ? JSON.stringify(arg) : String(arg)
121
+ ).join(" ")}` : message;
122
+ console.log(`[DEBUG] ${message}`, ...args);
123
+ if (this._logCallback) {
124
+ this._logCallback(fullMessage, 4 /* DEBUG */);
125
+ }
126
+ }
127
+ }
128
+ static verbose(message, ...args) {
129
+ if (this._logLevel >= 5 /* VERBOSE */) {
130
+ const fullMessage = args.length > 0 ? `${message} ${args.map(
131
+ (arg) => typeof arg === "object" ? JSON.stringify(arg) : String(arg)
132
+ ).join(" ")}` : message;
133
+ console.log(`[VERBOSE] ${message}`, ...args);
134
+ if (this._logCallback) {
135
+ this._logCallback(fullMessage, 5 /* VERBOSE */);
136
+ }
137
+ }
138
+ }
139
+ };
140
+ }
141
+ });
142
+
143
+ // src/initialize-do.ts
144
+ init_BaseModel();
145
+
146
+ // src/engines/DatabaseEngine.ts
147
+ var DatabaseEngine = class {
148
+ createTable(_modelName, _schema, _options) {
149
+ throw new Error("Method not implemented.");
150
+ }
151
+ createStringSetJunctionTable(_modelName, _fieldName) {
152
+ throw new Error("Method not implemented.");
153
+ }
154
+ insertStringSetValues(_modelName, _fieldName, _recordId, _values) {
155
+ throw new Error("Method not implemented.");
156
+ }
157
+ removeStringSetValues(_modelName, _fieldName, _recordId, _values) {
158
+ throw new Error("Method not implemented.");
159
+ }
160
+ insert(_modelName, _data) {
161
+ throw new Error("Method not implemented.");
162
+ }
163
+ delete(_modelName, _id) {
164
+ throw new Error("Method not implemented.");
165
+ }
166
+ /**
167
+ * Deletes all records for a specific document from the given model table.
168
+ * This is used when disconnecting a document to remove all its data.
169
+ * @param modelName The name of the model/table
170
+ * @param docId The document ID to filter by
171
+ */
172
+ deleteByDocumentId(_modelName, _docId) {
173
+ throw new Error("Method not implemented.");
174
+ }
175
+ getTableName(_modelName) {
176
+ throw new Error("Method not implemented.");
177
+ }
178
+ // Transaction support
179
+ async withTransaction(_callback) {
180
+ throw new Error("Method not implemented.");
181
+ }
182
+ };
183
+
184
+ // src/engines/cloudflare/DOClientEngine.ts
185
+ var DOClientEngine = class extends DatabaseEngine {
186
+ endpoint;
187
+ customFetch;
188
+ authorization;
189
+ timeout;
190
+ currentDocId = null;
191
+ constructor(config) {
192
+ super();
193
+ this.endpoint = config.endpoint.replace(/\/$/, "");
194
+ this.customFetch = config.fetch || fetch;
195
+ this.authorization = config.authorization;
196
+ this.timeout = config.timeout || 3e4;
197
+ }
198
+ /**
199
+ * Set the current document ID for subsequent operations.
200
+ */
201
+ setCurrentDocument(docId) {
202
+ this.currentDocId = docId;
203
+ }
204
+ /**
205
+ * Get the current document ID.
206
+ */
207
+ getCurrentDocument() {
208
+ return this.currentDocId;
209
+ }
210
+ /**
211
+ * Build the URL for a DO endpoint.
212
+ */
213
+ buildUrl(path, extraParams) {
214
+ if (!this.currentDocId) {
215
+ throw new Error("No document ID set. Call setCurrentDocument() first.");
216
+ }
217
+ const url = new URL(`${this.endpoint}${path}`);
218
+ url.searchParams.set("docId", this.currentDocId);
219
+ if (extraParams) {
220
+ for (const [key, value] of Object.entries(extraParams)) {
221
+ url.searchParams.set(key, value);
222
+ }
223
+ }
224
+ return url.toString();
225
+ }
226
+ /**
227
+ * Make a fetch request to the DO.
228
+ */
229
+ async doFetch(path, body) {
230
+ const url = this.buildUrl(path);
231
+ const headers = {
232
+ "Content-Type": "application/json"
233
+ };
234
+ if (this.authorization) {
235
+ headers["Authorization"] = this.authorization;
236
+ }
237
+ const controller = new AbortController();
238
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
239
+ try {
240
+ const response = await this.customFetch(url, {
241
+ method: "POST",
242
+ headers,
243
+ body: JSON.stringify(body),
244
+ signal: controller.signal
245
+ });
246
+ clearTimeout(timeoutId);
247
+ if (!response.ok) {
248
+ const errorBody = await response.json();
249
+ throw new Error(errorBody.error || `HTTP ${response.status}`);
250
+ }
251
+ return response.json();
252
+ } catch (error) {
253
+ clearTimeout(timeoutId);
254
+ if (error instanceof Error && error.name === "AbortError") {
255
+ throw new Error(`Request timeout after ${this.timeout}ms`);
256
+ }
257
+ throw error;
258
+ }
259
+ }
260
+ /**
261
+ * Query records from the DO.
262
+ */
263
+ async queryModel(modelName, filter = {}, options = {}) {
264
+ const request = {
265
+ modelName,
266
+ filter,
267
+ options
268
+ };
269
+ const response = await this.doFetch("/query", request);
270
+ return {
271
+ data: response.data,
272
+ hasMore: response.hasMore ?? false,
273
+ nextCursor: response.nextCursor,
274
+ prevCursor: response.prevCursor
275
+ };
276
+ }
277
+ /**
278
+ * Save a record to the DO.
279
+ */
280
+ async saveModel(modelName, id, data, stringSets, options) {
281
+ const request = {
282
+ modelName,
283
+ id,
284
+ data,
285
+ stringSets,
286
+ ifNotExists: options?.ifNotExists,
287
+ condition: options?.condition
288
+ };
289
+ const response = await this.doFetch("/save", request);
290
+ return response.id;
291
+ }
292
+ /**
293
+ * Patch (partial update) a record in the DO.
294
+ * Only the provided fields are updated; existing fields are preserved.
295
+ */
296
+ async patchModel(modelName, id, data, stringSets, options) {
297
+ const request = {
298
+ modelName,
299
+ id,
300
+ data,
301
+ stringSets,
302
+ condition: options?.condition
303
+ };
304
+ const response = await this.doFetch("/patch", request);
305
+ return response.id;
306
+ }
307
+ /**
308
+ * Delete a record from the DO.
309
+ */
310
+ async deleteModel(modelName, id, options) {
311
+ const request = {
312
+ modelName,
313
+ id,
314
+ condition: options?.condition
315
+ };
316
+ const response = await this.doFetch("/delete", request);
317
+ return response.success;
318
+ }
319
+ /**
320
+ * Count records matching a filter.
321
+ */
322
+ async countModel(modelName, filter = {}) {
323
+ const request = {
324
+ modelName,
325
+ filter
326
+ };
327
+ const response = await this.doFetch("/count", request);
328
+ return response.count;
329
+ }
330
+ /**
331
+ * Aggregate records with groupBy and operations (count, sum, avg, min, max).
332
+ */
333
+ async aggregateModel(modelName, options) {
334
+ const request = {
335
+ modelName,
336
+ options
337
+ };
338
+ const response = await this.doFetch("/aggregate", request);
339
+ return response.result;
340
+ }
341
+ /**
342
+ * Atomically add values to StringSet fields on a record.
343
+ */
344
+ async addToStringSet(modelName, id, sets, options) {
345
+ const request = { modelName, id, sets, condition: options?.condition };
346
+ await this.doFetch("/stringset/add", request);
347
+ }
348
+ /**
349
+ * Atomically remove values from StringSet fields on a record.
350
+ */
351
+ async removeFromStringSet(modelName, id, sets, options) {
352
+ const request = { modelName, id, sets, condition: options?.condition };
353
+ await this.doFetch("/stringset/remove", request);
354
+ }
355
+ /**
356
+ * Atomically increment/decrement numeric fields on a record.
357
+ * Returns the new values after the increment.
358
+ */
359
+ async incrementFields(modelName, id, fields, options) {
360
+ const request = { modelName, id, fields, condition: options?.condition };
361
+ const response = await this.doFetch("/increment", request);
362
+ return response.values;
363
+ }
364
+ /**
365
+ * Execute multiple save/patch/delete operations in a single request.
366
+ * All operations run in a single transaction on the server.
367
+ */
368
+ async batchWrite(operations) {
369
+ const request = { operations };
370
+ const response = await this.doFetch("/batch", request);
371
+ return response.results;
372
+ }
373
+ /**
374
+ * Check if the DO is healthy.
375
+ */
376
+ async healthCheck() {
377
+ const url = this.buildUrl("/health");
378
+ const response = await this.customFetch(url, {
379
+ method: "GET",
380
+ headers: this.authorization ? { Authorization: this.authorization } : void 0
381
+ });
382
+ if (!response.ok) {
383
+ throw new Error(`Health check failed: HTTP ${response.status}`);
384
+ }
385
+ return response.json();
386
+ }
387
+ /**
388
+ * Batch sync indexes: send all desired indexes in one request.
389
+ * The DO compares against its _indexes table and registers only what's missing.
390
+ * Returns the number of indexes/constraints newly registered.
391
+ */
392
+ async syncIndexesBatch(request) {
393
+ const response = await this.doFetch("/indexes/sync", request);
394
+ return response.registered;
395
+ }
396
+ /**
397
+ * Register an index on a model field.
398
+ * Creates a SQLite index on json_extract(data_json, '$.fieldName').
399
+ * Set unique=true to enforce uniqueness on this field.
400
+ */
401
+ async registerIndex(modelName, fieldName, fieldType = "string", unique = false) {
402
+ const request = {
403
+ modelName,
404
+ fieldName,
405
+ fieldType,
406
+ unique
407
+ };
408
+ await this.doFetch("/index/register", request);
409
+ }
410
+ /**
411
+ * Drop an index from a model field.
412
+ */
413
+ async dropIndex(modelName, fieldName) {
414
+ const request = {
415
+ modelName,
416
+ fieldName
417
+ };
418
+ await this.doFetch("/index/drop", request);
419
+ }
420
+ /**
421
+ * List indexes, optionally filtered by model name.
422
+ */
423
+ async listIndexes(modelName) {
424
+ const url = this.buildUrl(
425
+ "/indexes",
426
+ modelName ? { modelName } : void 0
427
+ );
428
+ const response = await this.customFetch(url, {
429
+ method: "GET",
430
+ headers: this.authorization ? { Authorization: this.authorization } : void 0
431
+ });
432
+ if (!response.ok) {
433
+ throw new Error(`List indexes failed: HTTP ${response.status}`);
434
+ }
435
+ const body = await response.json();
436
+ return body.indexes;
437
+ }
438
+ /**
439
+ * Describe tracked fields for a model.
440
+ */
441
+ async describe(modelName) {
442
+ const url = this.buildUrl("/describe", { modelName });
443
+ const response = await this.customFetch(url, {
444
+ method: "GET",
445
+ headers: this.authorization ? { Authorization: this.authorization } : void 0
446
+ });
447
+ if (!response.ok) {
448
+ throw new Error(`Describe failed: HTTP ${response.status}`);
449
+ }
450
+ const body = await response.json();
451
+ return body.fields;
452
+ }
453
+ /**
454
+ * Register a composite unique constraint across multiple fields.
455
+ */
456
+ async registerUniqueConstraint(modelName, constraintName, fields) {
457
+ const request = {
458
+ modelName,
459
+ constraintName,
460
+ fields
461
+ };
462
+ await this.doFetch(
463
+ "/unique-constraint/register",
464
+ request
465
+ );
466
+ }
467
+ /**
468
+ * Drop a composite unique constraint.
469
+ */
470
+ async dropUniqueConstraint(modelName, constraintName) {
471
+ const request = {
472
+ modelName,
473
+ constraintName
474
+ };
475
+ await this.doFetch(
476
+ "/unique-constraint/drop",
477
+ request
478
+ );
479
+ }
480
+ /**
481
+ * List composite unique constraints, optionally filtered by model name.
482
+ */
483
+ async listUniqueConstraints(modelName) {
484
+ const url = this.buildUrl(
485
+ "/unique-constraints",
486
+ modelName ? { modelName } : void 0
487
+ );
488
+ const response = await this.customFetch(url, {
489
+ method: "GET",
490
+ headers: this.authorization ? { Authorization: this.authorization } : void 0
491
+ });
492
+ if (!response.ok) {
493
+ throw new Error(
494
+ `List unique constraints failed: HTTP ${response.status}`
495
+ );
496
+ }
497
+ const body = await response.json();
498
+ return body.constraints;
499
+ }
500
+ // ========================================
501
+ // DatabaseEngine interface implementation
502
+ // ========================================
503
+ /**
504
+ * Ensure the engine is ready.
505
+ * For DOClient, this verifies connectivity.
506
+ */
507
+ async ensureReady() {
508
+ if (this.currentDocId) {
509
+ await this.healthCheck();
510
+ }
511
+ }
512
+ /**
513
+ * Execute raw SQL.
514
+ * Not supported in DO client mode - use queryModel instead.
515
+ */
516
+ async query(_sql, _params) {
517
+ throw new Error(
518
+ "Raw SQL queries not supported in DO client mode. Use queryModel() instead."
519
+ );
520
+ }
521
+ /**
522
+ * Get last error message.
523
+ */
524
+ getLastErrorMessage() {
525
+ return void 0;
526
+ }
527
+ /**
528
+ * Get table schema.
529
+ * Not directly supported - schema is managed by the DO.
530
+ */
531
+ async getTableSchema(_tableName) {
532
+ throw new Error("getTableSchema not supported in DO client mode");
533
+ }
534
+ /**
535
+ * Destroy the engine.
536
+ * Nothing to clean up for the client.
537
+ */
538
+ async destroy() {
539
+ this.currentDocId = null;
540
+ }
541
+ /**
542
+ * Create table.
543
+ * No-op in DO client mode - schema is managed by the DO.
544
+ */
545
+ async createTable(_modelName, _schema, _options) {
546
+ }
547
+ /**
548
+ * Create StringSet junction table.
549
+ * No-op in DO client mode.
550
+ */
551
+ async createStringSetJunctionTable(_modelName, _fieldName) {
552
+ }
553
+ /**
554
+ * Insert a record.
555
+ * Delegates to saveModel.
556
+ */
557
+ async insert(modelName, data) {
558
+ const { id, ...rest } = data;
559
+ await this.saveModel(modelName, id, rest);
560
+ }
561
+ /**
562
+ * Delete a record.
563
+ * Delegates to deleteModel.
564
+ */
565
+ async delete(modelName, id) {
566
+ await this.deleteModel(modelName, id);
567
+ }
568
+ /**
569
+ * Get table name.
570
+ * All models use 'records' in JSON schema.
571
+ */
572
+ getTableName(_modelName) {
573
+ return "records";
574
+ }
575
+ /**
576
+ * Transaction support.
577
+ * DO client doesn't support client-side transactions.
578
+ * Operations are atomic on the DO side.
579
+ */
580
+ async withTransaction(callback) {
581
+ const ops = {
582
+ insert: async (modelName, data) => {
583
+ await this.insert(modelName, data);
584
+ },
585
+ delete: async (modelName, id) => {
586
+ await this.delete(modelName, id);
587
+ },
588
+ query: async (_sql, _params) => {
589
+ throw new Error("Raw SQL not supported in DO client transactions");
590
+ }
591
+ };
592
+ return callback(ops);
593
+ }
594
+ };
595
+
596
+ // src/initialize-do.ts
597
+ function resolveModelName(model) {
598
+ if (typeof model === "string") {
599
+ if (!model) throw new Error("Model name must be a non-empty string");
600
+ return model;
601
+ }
602
+ const name = model.modelName;
603
+ if (!name) {
604
+ throw new Error(
605
+ `Model ${model.name} has no modelName. Ensure it was defined with defineModelSchema + attachSchemaToClass.`
606
+ );
607
+ }
608
+ return name;
609
+ }
610
+ function createModelAccessor(engine, modelName) {
611
+ return {
612
+ query: (filter = {}, options = {}) => {
613
+ return engine.queryModel(modelName, filter, options);
614
+ },
615
+ find: async (id) => {
616
+ const result = await engine.queryModel(modelName, { id }, {});
617
+ return result.data.length > 0 ? result.data[0] : null;
618
+ },
619
+ save: (data, options) => {
620
+ if (!data.id) {
621
+ throw new Error("Record must have an 'id' field");
622
+ }
623
+ let stringSets;
624
+ let ifNotExists;
625
+ let condition;
626
+ if (options && typeof options === "object") {
627
+ if (options.ifNotExists !== void 0 || options.stringSets !== void 0 || options.condition !== void 0) {
628
+ stringSets = options.stringSets;
629
+ ifNotExists = options.ifNotExists;
630
+ condition = options.condition;
631
+ } else {
632
+ stringSets = options;
633
+ }
634
+ }
635
+ return engine.saveModel(modelName, data.id, data, stringSets, { ifNotExists, condition });
636
+ },
637
+ patch: (id, data, options) => {
638
+ let stringSets;
639
+ let condition;
640
+ if (options && typeof options === "object") {
641
+ if (options.condition !== void 0 || options.stringSets !== void 0) {
642
+ stringSets = options.stringSets;
643
+ condition = options.condition;
644
+ } else {
645
+ stringSets = options;
646
+ }
647
+ }
648
+ return engine.patchModel(modelName, id, data, stringSets, { condition });
649
+ },
650
+ delete: (id, options) => {
651
+ return engine.deleteModel(modelName, id, { condition: options?.condition });
652
+ },
653
+ count: (filter = {}) => {
654
+ return engine.countModel(modelName, filter);
655
+ },
656
+ aggregate: (options) => {
657
+ return engine.aggregateModel(modelName, options);
658
+ },
659
+ increment: (id, fields, options) => {
660
+ return engine.incrementFields(modelName, id, fields, { condition: options?.condition });
661
+ },
662
+ addToSet: (id, sets, options) => {
663
+ return engine.addToStringSet(modelName, id, sets, { condition: options?.condition });
664
+ },
665
+ removeFromSet: (id, sets, options) => {
666
+ return engine.removeFromStringSet(modelName, id, sets, { condition: options?.condition });
667
+ }
668
+ };
669
+ }
670
+ function extractModelSyncState(modelClass) {
671
+ const schema = modelClass.getSchema?.();
672
+ if (!schema || !schema.fields || !schema.options?.name) {
673
+ throw new Error(
674
+ `Cannot sync indexes: model ${modelClass.name} has no schema. Ensure it was defined with defineModelSchema + attachAndRegisterModel.`
675
+ );
676
+ }
677
+ const modelName = schema.options.name;
678
+ const fields = schema.fields;
679
+ const indexes = [];
680
+ for (const [fieldName, opts] of fields.entries()) {
681
+ if (fieldName === "id") continue;
682
+ if (!opts.indexed && !opts.unique) continue;
683
+ indexes.push({
684
+ fieldName,
685
+ fieldType: opts.type || "string",
686
+ unique: opts.unique ?? false
687
+ });
688
+ }
689
+ const uniqueConstraints = (schema.options.uniqueConstraints ?? []).map(
690
+ (c) => ({ name: c.name, fields: c.fields })
691
+ );
692
+ return { modelName, indexes, uniqueConstraints };
693
+ }
694
+ function buildSyncIndexes(engine) {
695
+ return async (modelClass) => {
696
+ const modelState = extractModelSyncState(modelClass);
697
+ return engine.syncIndexesBatch({ models: [modelState] });
698
+ };
699
+ }
700
+ function connectDoDb(options) {
701
+ const { endpoint, id, models, authorization, fetch: customFetch, timeout } = options;
702
+ const engine = new DOClientEngine({
703
+ endpoint,
704
+ authorization,
705
+ fetch: customFetch,
706
+ timeout
707
+ });
708
+ engine.setCurrentDocument(id);
709
+ const syncIndexes = buildSyncIndexes(engine);
710
+ const db = {
711
+ engine,
712
+ docId: id,
713
+ // Ad-hoc methods
714
+ query: (model, filter = {}, opts = {}) => {
715
+ return engine.queryModel(resolveModelName(model), filter, opts);
716
+ },
717
+ find: async (model, findId) => {
718
+ const result = await engine.queryModel(resolveModelName(model), { id: findId }, {});
719
+ return result.data.length > 0 ? result.data[0] : null;
720
+ },
721
+ save: (model, data, options2) => {
722
+ if (!data.id) {
723
+ throw new Error("Record must have an 'id' field");
724
+ }
725
+ let stringSets;
726
+ let ifNotExists;
727
+ let condition;
728
+ if (options2 && typeof options2 === "object") {
729
+ if (options2.ifNotExists !== void 0 || options2.stringSets !== void 0 || options2.condition !== void 0) {
730
+ stringSets = options2.stringSets;
731
+ ifNotExists = options2.ifNotExists;
732
+ condition = options2.condition;
733
+ } else {
734
+ stringSets = options2;
735
+ }
736
+ }
737
+ return engine.saveModel(resolveModelName(model), data.id, data, stringSets, { ifNotExists, condition });
738
+ },
739
+ patch: (model, id2, data, options2) => {
740
+ let stringSets;
741
+ let condition;
742
+ if (options2 && typeof options2 === "object") {
743
+ if (options2.condition !== void 0 || options2.stringSets !== void 0) {
744
+ stringSets = options2.stringSets;
745
+ condition = options2.condition;
746
+ } else {
747
+ stringSets = options2;
748
+ }
749
+ }
750
+ return engine.patchModel(resolveModelName(model), id2, data, stringSets, { condition });
751
+ },
752
+ delete: (model, deleteId, options2) => {
753
+ return engine.deleteModel(resolveModelName(model), deleteId, { condition: options2?.condition });
754
+ },
755
+ count: (model, filter = {}) => {
756
+ return engine.countModel(resolveModelName(model), filter);
757
+ },
758
+ aggregate: (model, options2) => {
759
+ return engine.aggregateModel(resolveModelName(model), options2);
760
+ },
761
+ increment: (model, id2, fields, options2) => {
762
+ return engine.incrementFields(resolveModelName(model), id2, fields, { condition: options2?.condition });
763
+ },
764
+ addToSet: (model, id2, sets, options2) => {
765
+ return engine.addToStringSet(resolveModelName(model), id2, sets, { condition: options2?.condition });
766
+ },
767
+ removeFromSet: (model, id2, sets, options2) => {
768
+ return engine.removeFromStringSet(resolveModelName(model), id2, sets, { condition: options2?.condition });
769
+ },
770
+ batch: (operations) => {
771
+ return engine.batchWrite(operations);
772
+ },
773
+ // Schema introspection
774
+ describe: (modelName) => {
775
+ return engine.describe(modelName);
776
+ },
777
+ // Index management
778
+ registerIndex: (modelName, fieldName, fieldType = "string", unique = false) => engine.registerIndex(modelName, fieldName, fieldType, unique),
779
+ dropIndex: (modelName, fieldName) => engine.dropIndex(modelName, fieldName),
780
+ listIndexes: (modelName) => engine.listIndexes(modelName),
781
+ registerUniqueConstraint: (modelName, constraintName, fields) => engine.registerUniqueConstraint(modelName, constraintName, fields),
782
+ dropUniqueConstraint: (modelName, constraintName) => engine.dropUniqueConstraint(modelName, constraintName),
783
+ listUniqueConstraints: (modelName) => engine.listUniqueConstraints(modelName),
784
+ syncIndexes,
785
+ syncAllIndexes: async () => {
786
+ if (!models || models.length === 0) return 0;
787
+ const request = {
788
+ models: models.map(extractModelSyncState)
789
+ };
790
+ return engine.syncIndexesBatch(request);
791
+ }
792
+ };
793
+ if (models) {
794
+ for (const model of models) {
795
+ const modelName = resolveModelName(model);
796
+ const className = model.name;
797
+ db[className] = createModelAccessor(engine, modelName);
798
+ }
799
+ }
800
+ Logger.info(`[connectDoDb] Connected to document: ${id} at ${endpoint}`);
801
+ return db;
802
+ }
803
+ export {
804
+ DOClientEngine,
805
+ connectDoDb
806
+ };