js-bao 0.5.1 → 0.6.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.
@@ -100,6 +100,201 @@ var init_yjsValues = __esm({
100
100
  }
101
101
  });
102
102
 
103
+ // src/models/writeLifecycle.ts
104
+ function assertNever(x) {
105
+ throw new Error(`Unhandled field type: ${String(x)}`);
106
+ }
107
+ function fieldWriteKind(type) {
108
+ switch (type) {
109
+ case "string":
110
+ case "number":
111
+ case "boolean":
112
+ case "date":
113
+ case "id":
114
+ return "scalar";
115
+ case "stringset":
116
+ return "stringset";
117
+ default:
118
+ return assertNever(type);
119
+ }
120
+ }
121
+ function isKnownFieldType(type) {
122
+ return KNOWN_FIELD_TYPES.has(type);
123
+ }
124
+ function ensureStringSetYMap(rm, fieldName) {
125
+ const nested = rm.get(fieldName);
126
+ if (nested instanceof Y2.Map) return nested;
127
+ const fresh = new Y2.Map();
128
+ if (nested && typeof nested === "object") {
129
+ for (const [member, marker] of Object.entries(
130
+ nested
131
+ )) {
132
+ if (Boolean(marker)) fresh.set(member, true);
133
+ }
134
+ }
135
+ rm.set(fieldName, fresh);
136
+ return fresh;
137
+ }
138
+ function applyStringSetDelta(target, delta) {
139
+ if (delta.clear) {
140
+ for (const member of Array.from(target.keys())) target.delete(member);
141
+ }
142
+ if (delta.remove) {
143
+ for (const member of delta.remove) target.delete(String(member));
144
+ }
145
+ if (delta.add) {
146
+ for (const member of delta.add) {
147
+ const key = String(member);
148
+ if (target.get(key) !== true) target.set(key, true);
149
+ }
150
+ }
151
+ }
152
+ function replaceStringSet(rm, fieldName, members) {
153
+ const target = ensureStringSetYMap(rm, fieldName);
154
+ const desired = /* @__PURE__ */ new Set();
155
+ for (const member of members) desired.add(String(member));
156
+ for (const existing of Array.from(target.keys())) {
157
+ if (!desired.has(existing)) target.delete(existing);
158
+ }
159
+ for (const member of desired) {
160
+ if (target.get(member) !== true) target.set(member, true);
161
+ }
162
+ }
163
+ function applyStringSetChange(recordYMap, fieldName, change) {
164
+ const target = ensureStringSetYMap(recordYMap, fieldName);
165
+ applyStringSetDelta(target, {
166
+ add: change.additions,
167
+ remove: change.removals
168
+ });
169
+ }
170
+ function parseStringSetOp(value) {
171
+ if (value === null || typeof value !== "object" || Array.isArray(value) || value instanceof Y2.AbstractType) {
172
+ return null;
173
+ }
174
+ const keys = Object.keys(value);
175
+ const opKeys = keys.filter((k) => k.startsWith("$"));
176
+ if (opKeys.length === 0) return null;
177
+ const known = /* @__PURE__ */ new Set(["$add", "$remove", "$clear"]);
178
+ const unknown = keys.filter((k) => !known.has(k));
179
+ if (unknown.length > 0) {
180
+ throw new Error(
181
+ `Invalid stringset op: unexpected key(s) [${unknown.join(", ")}]. Only $add, $remove and $clear are supported.`
182
+ );
183
+ }
184
+ const obj = value;
185
+ const toMembers = (raw, opName) => {
186
+ if (raw === void 0) return [];
187
+ if (!Array.isArray(raw)) {
188
+ throw new Error(`Invalid stringset op: ${opName} must be an array.`);
189
+ }
190
+ return raw.map((m) => String(m));
191
+ };
192
+ return {
193
+ add: toMembers(obj.$add, "$add"),
194
+ remove: toMembers(obj.$remove, "$remove"),
195
+ clear: obj.$clear === true
196
+ };
197
+ }
198
+ function writeField(rm, declaredType, key, value, modelName) {
199
+ const kind = declaredType && isKnownFieldType(declaredType) ? fieldWriteKind(declaredType) : "scalar";
200
+ if (Array.isArray(value)) {
201
+ if (kind !== "stringset") {
202
+ throw new Error(
203
+ `Cannot write array value to field "${key}" on model "${modelName}": its declared type could not be resolved to a collection (stringset). Collection fields require schema metadata in the document (_meta_${modelName}); the model must be saved by a client at least once before a workflow writes one of its collection fields.`
204
+ );
205
+ }
206
+ replaceStringSet(rm, key, value);
207
+ return;
208
+ }
209
+ const op = parseStringSetOp(value);
210
+ if (op) {
211
+ if (kind !== "stringset") {
212
+ throw new Error(
213
+ `Cannot apply a stringset op ({ $add/$remove/$clear }) to field "${key}" on model "${modelName}": its declared type is ${declaredType ? `"${declaredType}"` : "unresolved"}, not a collection (stringset). Op-objects are only valid for stringset / refersToMany fields with schema metadata (_meta_${modelName}).`
214
+ );
215
+ }
216
+ applyStringSetDelta(ensureStringSetYMap(rm, key), op);
217
+ return;
218
+ }
219
+ if (kind === "stringset") {
220
+ throw new Error(
221
+ `Field "${key}" on model "${modelName}" is a stringset and requires an array (replace) or { $add/$remove/$clear } (delta) value; received ${value === null ? "null" : typeof value}.`
222
+ );
223
+ }
224
+ rm.set(key, value);
225
+ }
226
+ function registerFunctionDefaultGenerator(name, fn) {
227
+ FUNCTION_DEFAULT_GENERATORS.set(name, fn);
228
+ }
229
+ function decodeFunctionDefault(name) {
230
+ return FUNCTION_DEFAULT_GENERATORS.get(name);
231
+ }
232
+ function resolveDefaultValue(rawDefault, opts = {}) {
233
+ if (typeof rawDefault === "function") return rawDefault();
234
+ if (typeof rawDefault === "string" && rawDefault.startsWith("$")) {
235
+ if (rawDefault.startsWith("$$")) return rawDefault.slice(1);
236
+ const name = rawDefault.slice(1);
237
+ const gen = opts.decode?.(name) ?? decodeFunctionDefault(name);
238
+ if (!gen) {
239
+ throw new Error(
240
+ `Unknown function default "${rawDefault}" for field "${opts.fieldName ?? "?"}"${opts.modelName ? ` on model "${opts.modelName}"` : ""}: no generator is registered for "${name}". Register it via registerFunctionDefaultGenerator("${name}", fn) so the server can populate the field the same way the client does.`
241
+ );
242
+ }
243
+ return gen();
244
+ }
245
+ return rawDefault;
246
+ }
247
+ function applyDefaultsFromSchema(fields, data, opts = {}) {
248
+ for (const [fieldName, field] of Object.entries(fields)) {
249
+ if (fieldName === "id") continue;
250
+ if (!field || field.default === void 0) continue;
251
+ if (field.default === UNKNOWN_FUNCTION_DEFAULT) {
252
+ continue;
253
+ }
254
+ if (Object.prototype.hasOwnProperty.call(data, fieldName) && data[fieldName] !== void 0) {
255
+ continue;
256
+ }
257
+ data[fieldName] = resolveDefaultValue(field.default, {
258
+ fieldName,
259
+ modelName: opts.modelName,
260
+ decode: opts.decodeFunctionDefault
261
+ });
262
+ }
263
+ return data;
264
+ }
265
+ function applyAutoStampsFromSchema(fields, data, opts) {
266
+ const { isNew, now } = opts;
267
+ for (const [fieldName, field] of Object.entries(fields)) {
268
+ const stamp = field?.autoStamp;
269
+ if (!stamp) continue;
270
+ const explicit = Object.prototype.hasOwnProperty.call(data, fieldName) && data[fieldName] !== null && data[fieldName] !== void 0;
271
+ if (explicit) continue;
272
+ if (stamp === "create" && !isNew) continue;
273
+ data[fieldName] = now;
274
+ }
275
+ return data;
276
+ }
277
+ var Y2, import_ulid, KNOWN_FIELD_TYPE_MAP, KNOWN_FIELD_TYPES, FUNCTION_DEFAULT_GENERATORS, UNKNOWN_FUNCTION_DEFAULT;
278
+ var init_writeLifecycle = __esm({
279
+ "src/models/writeLifecycle.ts"() {
280
+ "use strict";
281
+ Y2 = __toESM(require("yjs"), 1);
282
+ import_ulid = require("ulid");
283
+ KNOWN_FIELD_TYPE_MAP = {
284
+ string: true,
285
+ number: true,
286
+ boolean: true,
287
+ date: true,
288
+ id: true,
289
+ stringset: true
290
+ };
291
+ KNOWN_FIELD_TYPES = new Set(Object.keys(KNOWN_FIELD_TYPE_MAP));
292
+ FUNCTION_DEFAULT_GENERATORS = /* @__PURE__ */ new Map();
293
+ registerFunctionDefaultGenerator("generate_ulid", () => (0, import_ulid.ulid)());
294
+ UNKNOWN_FUNCTION_DEFAULT = "$unknown_function";
295
+ }
296
+ });
297
+
103
298
  // src/models/metaSync.ts
104
299
  function inferFieldType(value) {
105
300
  if (isStringSetYMap(value)) return "stringset";
@@ -121,7 +316,10 @@ function encodeDefault(value) {
121
316
  if (value === void 0 || value === null) return void 0;
122
317
  if (typeof value === "function") {
123
318
  const name = KNOWN_FUNCTION_DEFAULTS.get(value);
124
- return name ? `$${name}` : "$unknown_function";
319
+ return name ? `$${name}` : UNKNOWN_FUNCTION_DEFAULT;
320
+ }
321
+ if (typeof value === "string" && value.startsWith("$")) {
322
+ return `$${value}`;
125
323
  }
126
324
  return value;
127
325
  }
@@ -147,7 +345,7 @@ function syncModelMeta(yDoc, modelName, schema) {
147
345
  if (compoundConstraints.length > 0) {
148
346
  let constraints = meta.get("_constraints");
149
347
  if (!constraints) {
150
- constraints = new Y2.Map();
348
+ constraints = new Y3.Map();
151
349
  meta.set("_constraints", constraints);
152
350
  }
153
351
  for (const constraint of compoundConstraints) {
@@ -158,7 +356,7 @@ function syncModelMeta(yDoc, modelName, schema) {
158
356
  if (relationships && Object.keys(relationships).length > 0) {
159
357
  let rels = meta.get("_relationships");
160
358
  if (!rels) {
161
- rels = new Y2.Map();
359
+ rels = new Y3.Map();
162
360
  meta.set("_relationships", rels);
163
361
  }
164
362
  for (const [relName, relConfig] of Object.entries(relationships)) {
@@ -170,7 +368,7 @@ function syncModelMeta(yDoc, modelName, schema) {
170
368
  function syncFieldMeta(metaMap, fieldName, fieldOpts) {
171
369
  let fieldMeta = metaMap.get(fieldName);
172
370
  if (!fieldMeta) {
173
- fieldMeta = new Y2.Map();
371
+ fieldMeta = new Y3.Map();
174
372
  metaMap.set(fieldName, fieldMeta);
175
373
  }
176
374
  setIfChanged(fieldMeta, "type", fieldOpts.type);
@@ -187,7 +385,7 @@ function syncFieldMeta(metaMap, fieldName, fieldOpts) {
187
385
  function syncConstraintMeta(constraintsMap, constraint) {
188
386
  let cMeta = constraintsMap.get(constraint.name);
189
387
  if (!cMeta) {
190
- cMeta = new Y2.Map();
388
+ cMeta = new Y3.Map();
191
389
  constraintsMap.set(constraint.name, cMeta);
192
390
  }
193
391
  setIfChanged(cMeta, "type", "unique");
@@ -197,7 +395,7 @@ function syncConstraintMeta(constraintsMap, constraint) {
197
395
  function syncRelationshipMeta(relsMap, relName, relConfig) {
198
396
  let relMeta = relsMap.get(relName);
199
397
  if (!relMeta) {
200
- relMeta = new Y2.Map();
398
+ relMeta = new Y3.Map();
201
399
  relsMap.set(relName, relMeta);
202
400
  }
203
401
  for (const [key, value] of Object.entries(relConfig)) {
@@ -212,7 +410,7 @@ function syncInferredMeta(yDoc, modelName, recordData) {
212
410
  if (fieldName.startsWith("_")) continue;
213
411
  let fieldMeta = meta.get(fieldName);
214
412
  if (!fieldMeta) {
215
- fieldMeta = new Y2.Map();
413
+ fieldMeta = new Y3.Map();
216
414
  meta.set(fieldName, fieldMeta);
217
415
  }
218
416
  if (!fieldMeta.has("type")) {
@@ -228,34 +426,378 @@ function setIfChanged(map, key, value) {
228
426
  map.set(key, value);
229
427
  }
230
428
  }
231
- var Y2, KNOWN_FUNCTION_DEFAULTS, _syncedCache;
429
+ var Y3, KNOWN_FUNCTION_DEFAULTS, _syncedCache;
232
430
  var init_metaSync = __esm({
233
431
  "src/models/metaSync.ts"() {
234
432
  "use strict";
235
- Y2 = __toESM(require("yjs"), 1);
433
+ Y3 = __toESM(require("yjs"), 1);
236
434
  init_yjsValues();
435
+ init_writeLifecycle();
237
436
  KNOWN_FUNCTION_DEFAULTS = /* @__PURE__ */ new WeakMap();
238
437
  _syncedCache = /* @__PURE__ */ new WeakMap();
239
438
  }
240
439
  });
241
440
 
441
+ // src/utils/uniqueIndex.ts
442
+ function buildUniqueKey(recordData, fields) {
443
+ const keyParts = [];
444
+ for (const field of fields) {
445
+ const value = recordData instanceof Y4.Map ? recordData.get(field) : recordData[field];
446
+ if (value === null || value === void 0) {
447
+ return null;
448
+ }
449
+ keyParts.push(String(value));
450
+ }
451
+ return keyParts.join("|");
452
+ }
453
+ function uniqueIndexMapName(modelName, constraintName) {
454
+ return `_uniqueIdx_${modelName}_${constraintName}`;
455
+ }
456
+ function fieldUniqueConstraintName(modelName, fieldName) {
457
+ return `${modelName}_${fieldName}_unique`;
458
+ }
459
+ var Y4;
460
+ var init_uniqueIndex = __esm({
461
+ "src/utils/uniqueIndex.ts"() {
462
+ "use strict";
463
+ Y4 = __toESM(require("yjs"), 1);
464
+ }
465
+ });
466
+
467
+ // src/utils/largeDocuments.ts
468
+ var Y5;
469
+ var init_largeDocuments = __esm({
470
+ "src/utils/largeDocuments.ts"() {
471
+ "use strict";
472
+ Y5 = __toESM(require("yjs"), 1);
473
+ init_yjsValues();
474
+ }
475
+ });
476
+
477
+ // src/models/OrmContext.ts
478
+ var OrmContext, defaultOrmContext, liveOrmContexts;
479
+ var init_OrmContext = __esm({
480
+ "src/models/OrmContext.ts"() {
481
+ "use strict";
482
+ OrmContext = class _OrmContext {
483
+ /** Declared before the field that reads it: static initialization is ordered. */
484
+ static instanceCounter = 0;
485
+ /**
486
+ * Which instance this is, for diagnostics. A process holding several clients
487
+ * needs a way to say WHICH one a warning is about, so every context takes the
488
+ * next number as it is created and keeps it for its lifetime.
489
+ */
490
+ instanceId = ++_OrmContext.instanceCounter;
491
+ /** The engine this instance's models read and write through. */
492
+ dbEngine = null;
493
+ connectedDocuments = /* @__PURE__ */ new Map();
494
+ /** `docId_modelName` → the Y.Map that document holds for that model. */
495
+ documentYMaps = /* @__PURE__ */ new Map();
496
+ /** `subscribe()` callbacks, by model name. */
497
+ listenersMap = /* @__PURE__ */ new Map();
498
+ // Default-document resolution
499
+ modelNameToDefaultDocId = /* @__PURE__ */ new Map();
500
+ globalDefaultDocId = void 0;
501
+ defaultDocChangedListeners = /* @__PURE__ */ new Set();
502
+ modelDocMappingChangedListeners = /* @__PURE__ */ new Set();
503
+ /** Format-2 ("large document", #2816) record stores by document id. */
504
+ format2Documents = /* @__PURE__ */ new Map();
505
+ /** Format-2 epoch moves in flight (#2816). */
506
+ format2Transitions = /* @__PURE__ */ new Map();
507
+ /**
508
+ * Nested-Y.Map stringset fields this context has already observed. Per
509
+ * context, not global: a Y.Doc reconnected to a fresh instance has to be
510
+ * observed again by that instance, and a global guard would silently
511
+ * suppress it.
512
+ */
513
+ observedStringSetMaps = /* @__PURE__ */ new WeakSet();
514
+ /**
515
+ * The registry this instance's schema lookups use. `null` on the standing
516
+ * default context, where callers fall back to the global singleton.
517
+ */
518
+ modelRegistry = null;
519
+ /** Model classes routed to this context. */
520
+ boundModels = /* @__PURE__ */ new Set();
521
+ /** True once {@link destroy} has run; bound classes keep pointing here. */
522
+ destroyed = false;
523
+ /** Yjs `unobserve` thunks by owning document id. */
524
+ observerDisposers = /* @__PURE__ */ new Map();
525
+ /** Teardown the owner (the document manager) registers for `destroy()`. */
526
+ teardownHooks = /* @__PURE__ */ new Set();
527
+ static NO_DOCUMENT_KEY = "__no_document__";
528
+ /**
529
+ * Record how to remove an observer this context installed. Every `observe()`
530
+ * in the ORM pairs with one of these: without it a torn-down instance's
531
+ * callbacks keep firing on a Y.Doc that outlives it.
532
+ */
533
+ registerObserverDisposer(docId, dispose) {
534
+ const key = docId ?? _OrmContext.NO_DOCUMENT_KEY;
535
+ const list = this.observerDisposers.get(key);
536
+ if (list) list.push(dispose);
537
+ else this.observerDisposers.set(key, [dispose]);
538
+ }
539
+ /** Remove every observer this context installed for `docId`. */
540
+ disposeObserversForDocument(docId) {
541
+ const list = this.observerDisposers.get(docId);
542
+ if (!list) return;
543
+ this.observerDisposers.delete(docId);
544
+ for (const dispose of list) {
545
+ try {
546
+ dispose();
547
+ } catch {
548
+ }
549
+ }
550
+ }
551
+ disposeAllObservers() {
552
+ const buckets = Array.from(this.observerDisposers.values());
553
+ this.observerDisposers.clear();
554
+ for (const list of buckets) {
555
+ for (const dispose of list) {
556
+ try {
557
+ dispose();
558
+ } catch {
559
+ }
560
+ }
561
+ }
562
+ }
563
+ /** Run `hook` as part of `destroy()` (the format-2 binding registers here). */
564
+ onDestroy(hook) {
565
+ this.teardownHooks.add(hook);
566
+ }
567
+ /** The record store for `docId`, or `null` when the document is format 1. */
568
+ getFormat2Store(docId) {
569
+ if (!docId) return null;
570
+ return this.format2Documents.get(docId) ?? null;
571
+ }
572
+ /**
573
+ * The epoch move a write to `docId` has to wait out (#2816, behavior 10), or
574
+ * `null` when none is in flight.
575
+ */
576
+ format2WritesHeld(docId) {
577
+ if (!docId) return null;
578
+ return this.format2Transitions.get(docId) ?? null;
579
+ }
580
+ /** Run an epoch move with `docId`'s local writes held back (#2816). */
581
+ async holdFormat2Writes(docId, run) {
582
+ const inFlight = this.format2Transitions.get(docId);
583
+ if (inFlight) await inFlight.catch(() => {
584
+ });
585
+ let release;
586
+ const held = new Promise((resolve) => {
587
+ release = resolve;
588
+ });
589
+ this.format2Transitions.set(docId, held);
590
+ try {
591
+ return await run();
592
+ } finally {
593
+ if (this.format2Transitions.get(docId) === held) {
594
+ this.format2Transitions.delete(docId);
595
+ }
596
+ release();
597
+ }
598
+ }
599
+ /** Notify `subscribe()` callbacks registered on this context for `modelName`. */
600
+ notifyListeners(modelName, log, onError) {
601
+ const listeners = this.listenersMap.get(modelName);
602
+ if (!listeners) return;
603
+ log?.(`Notifying ${listeners.size} listeners for ${modelName}`);
604
+ listeners.forEach((callback) => {
605
+ try {
606
+ callback();
607
+ } catch (e) {
608
+ onError?.(e);
609
+ }
610
+ });
611
+ }
612
+ // ---- Default-document resolution (the `BaseModel` statics delegate here) ----
613
+ setModelDefaultDocumentId(modelName, docId) {
614
+ const previous = this.modelNameToDefaultDocId.get(modelName);
615
+ this.modelNameToDefaultDocId.set(modelName, docId);
616
+ this.announceModelDocMapping(modelName, previous, docId);
617
+ }
618
+ removeModelDefaultDocumentId(modelName) {
619
+ const previous = this.modelNameToDefaultDocId.get(modelName);
620
+ this.modelNameToDefaultDocId.delete(modelName);
621
+ this.announceModelDocMapping(modelName, previous, void 0);
622
+ }
623
+ clearModelDefaultDocumentIds() {
624
+ const entries = Array.from(this.modelNameToDefaultDocId.entries());
625
+ this.modelNameToDefaultDocId.clear();
626
+ for (const [modelName, previous] of entries) {
627
+ this.announceModelDocMapping(modelName, previous, void 0);
628
+ }
629
+ }
630
+ setGlobalDefaultDocumentId(docId) {
631
+ const previous = this.globalDefaultDocId;
632
+ this.globalDefaultDocId = docId;
633
+ this.announceDefaultDoc(previous, docId);
634
+ }
635
+ clearGlobalDefaultDocumentId() {
636
+ const previous = this.globalDefaultDocId;
637
+ this.globalDefaultDocId = void 0;
638
+ this.announceDefaultDoc(previous, void 0);
639
+ }
640
+ getModelDefaultDocumentMapping() {
641
+ return Object.fromEntries(this.modelNameToDefaultDocId.entries());
642
+ }
643
+ getDocumentIdForModel(modelName) {
644
+ return this.modelNameToDefaultDocId.get(modelName);
645
+ }
646
+ getGlobalDefaultDocumentId() {
647
+ return this.globalDefaultDocId;
648
+ }
649
+ onDefaultDocChanged(listener) {
650
+ this.defaultDocChangedListeners.add(listener);
651
+ return () => this.defaultDocChangedListeners.delete(listener);
652
+ }
653
+ onModelDocMappingChanged(listener) {
654
+ this.modelDocMappingChangedListeners.add(listener);
655
+ return () => this.modelDocMappingChangedListeners.delete(listener);
656
+ }
657
+ /** Drop the model→document defaults that point at a disconnected document. */
658
+ clearMappingsForDocId(docId) {
659
+ const toRemove = [];
660
+ for (const [modelName, mappedDocId] of this.modelNameToDefaultDocId) {
661
+ if (mappedDocId === docId) toRemove.push(modelName);
662
+ }
663
+ for (const modelName of toRemove) {
664
+ this.removeModelDefaultDocumentId(modelName);
665
+ }
666
+ if (this.globalDefaultDocId === docId) {
667
+ this.clearGlobalDefaultDocumentId();
668
+ }
669
+ }
670
+ announceDefaultDoc(previous, current) {
671
+ for (const listener of this.defaultDocChangedListeners) {
672
+ try {
673
+ listener({ previous, current });
674
+ } catch {
675
+ }
676
+ }
677
+ }
678
+ announceModelDocMapping(modelName, previous, current) {
679
+ for (const listener of this.modelDocMappingChangedListeners) {
680
+ try {
681
+ listener({ modelName, previous, current });
682
+ } catch {
683
+ }
684
+ }
685
+ }
686
+ /**
687
+ * Tear this instance down: observers off, engine destroyed, state cleared.
688
+ *
689
+ * Deliberately does NOT unbind its model classes. They keep resolving this
690
+ * context, whose engine is now null, so an operation through them throws the
691
+ * ordinary not-initialized error instead of silently landing on whatever
692
+ * other instance happens to be live (#3039, behavior 7). Only a later
693
+ * `initJsBao` may rebind a class whose context is destroyed.
694
+ */
695
+ async destroy() {
696
+ if (this.destroyed) return;
697
+ this.destroyed = true;
698
+ this.disposeAllObservers();
699
+ const hooks = Array.from(this.teardownHooks);
700
+ this.teardownHooks.clear();
701
+ for (const hook of hooks) {
702
+ try {
703
+ await hook();
704
+ } catch {
705
+ }
706
+ }
707
+ const engine = this.dbEngine;
708
+ this.dbEngine = null;
709
+ if (engine) {
710
+ try {
711
+ await engine.destroy();
712
+ } catch {
713
+ }
714
+ }
715
+ this.connectedDocuments.clear();
716
+ this.documentYMaps.clear();
717
+ this.listenersMap.clear();
718
+ this.modelNameToDefaultDocId.clear();
719
+ this.globalDefaultDocId = void 0;
720
+ this.defaultDocChangedListeners.clear();
721
+ this.modelDocMappingChangedListeners.clear();
722
+ this.format2Documents.clear();
723
+ this.format2Transitions.clear();
724
+ this.observedStringSetMaps = /* @__PURE__ */ new WeakSet();
725
+ liveOrmContexts.delete(this);
726
+ if (defaultOrmContext === this) {
727
+ defaultOrmContext = new _OrmContext();
728
+ }
729
+ }
730
+ };
731
+ defaultOrmContext = new OrmContext();
732
+ liveOrmContexts = /* @__PURE__ */ new Set();
733
+ }
734
+ });
735
+
736
+ // src/models/format2QueryProjection.ts
737
+ var FORMAT2_QUERY_PROJECTION_DDL;
738
+ var init_format2QueryProjection = __esm({
739
+ "src/models/format2QueryProjection.ts"() {
740
+ "use strict";
741
+ FORMAT2_QUERY_PROJECTION_DDL = `
742
+ CREATE TABLE IF NOT EXISTS _query_projection (
743
+ doc_id TEXT NOT NULL,
744
+ model TEXT NOT NULL,
745
+ PRIMARY KEY (doc_id, model)
746
+ )`.trim();
747
+ }
748
+ });
749
+
750
+ // src/models/format2Observer.ts
751
+ var Y6;
752
+ var init_format2Observer = __esm({
753
+ "src/models/format2Observer.ts"() {
754
+ "use strict";
755
+ Y6 = __toESM(require("yjs"), 1);
756
+ init_largeDocuments();
757
+ }
758
+ });
759
+
760
+ // src/models/format2ClientWrite.ts
761
+ var init_format2ClientWrite = __esm({
762
+ "src/models/format2ClientWrite.ts"() {
763
+ "use strict";
764
+ }
765
+ });
766
+
767
+ // src/models/format2OfflineWindow.ts
768
+ var DAY_MS;
769
+ var init_format2OfflineWindow = __esm({
770
+ "src/models/format2OfflineWindow.ts"() {
771
+ "use strict";
772
+ DAY_MS = 24 * 60 * 60 * 1e3;
773
+ }
774
+ });
775
+
242
776
  // src/models/BaseModel.ts
243
777
  function generateULID() {
244
- return (0, import_ulid.ulid)();
778
+ return (0, import_ulid2.ulid)();
245
779
  }
246
- var Y3, import_ulid, Logger;
780
+ var Y7, import_ulid2, Logger;
247
781
  var init_BaseModel = __esm({
248
782
  "src/models/BaseModel.ts"() {
249
783
  "use strict";
250
- Y3 = __toESM(require("yjs"), 1);
251
- import_ulid = require("ulid");
784
+ Y7 = __toESM(require("yjs"), 1);
785
+ import_ulid2 = require("ulid");
252
786
  init_StringSet();
253
787
  init_documentTypes();
254
788
  init_DocumentQueryTranslator();
255
789
  init_CursorManager();
256
790
  init_sql();
257
791
  init_metaSync();
792
+ init_writeLifecycle();
258
793
  init_yjsValues();
794
+ init_uniqueIndex();
795
+ init_largeDocuments();
796
+ init_OrmContext();
797
+ init_format2QueryProjection();
798
+ init_format2Observer();
799
+ init_format2ClientWrite();
800
+ init_format2OfflineWindow();
259
801
  Logger = class {
260
802
  static _logLevel = 1 /* ERROR */;
261
803
  static _logCallback = null;
@@ -342,6 +884,7 @@ var init_ModelRegistry = __esm({
342
884
  "use strict";
343
885
  init_BaseModel();
344
886
  init_relationshipManager();
887
+ init_format2QueryProjection();
345
888
  }
346
889
  });
347
890
 
@@ -349,19 +892,37 @@ var init_ModelRegistry = __esm({
349
892
  var cloudflare_exports = {};
350
893
  __export(cloudflare_exports, {
351
894
  DOClientEngine: () => DOClientEngine,
895
+ applyAutoStampsFromSchema: () => applyAutoStampsFromSchema,
896
+ applyDefaultsFromSchema: () => applyDefaultsFromSchema,
897
+ applyStringSetChange: () => applyStringSetChange,
898
+ applyStringSetDelta: () => applyStringSetDelta,
899
+ assertNever: () => assertNever,
900
+ buildUniqueKey: () => buildUniqueKey,
352
901
  clearMetaSyncCache: () => clearMetaSyncCache,
353
902
  connectDoDb: () => connectDoDb,
903
+ decodeFunctionDefault: () => decodeFunctionDefault,
904
+ discoverDeclaredModel: () => discoverDeclaredModel,
354
905
  discoverModelNames: () => discoverModelNames,
355
906
  discoverSchema: () => discoverSchema,
907
+ ensureStringSetYMap: () => ensureStringSetYMap,
908
+ fieldUniqueConstraintName: () => fieldUniqueConstraintName,
909
+ fieldWriteKind: () => fieldWriteKind,
356
910
  getActiveDOEngine: () => getActiveDOEngine,
357
911
  inferFieldType: () => inferFieldType,
358
912
  initJsBaoDO: () => initJsBaoDO,
913
+ isKnownFieldType: () => isKnownFieldType,
359
914
  loadSchemaFromTomlString: () => loadSchemaFromTomlString,
915
+ parseStringSetOp: () => parseStringSetOp,
360
916
  registerFunctionDefault: () => registerFunctionDefault,
917
+ registerFunctionDefaultGenerator: () => registerFunctionDefaultGenerator,
918
+ replaceStringSet: () => replaceStringSet,
361
919
  resetJsBaoDO: () => resetJsBaoDO,
920
+ resolveDefaultValue: () => resolveDefaultValue,
362
921
  schemaToToml: () => schemaToToml,
363
922
  syncInferredMeta: () => syncInferredMeta,
364
- syncModelMeta: () => syncModelMeta
923
+ syncModelMeta: () => syncModelMeta,
924
+ uniqueIndexMapName: () => uniqueIndexMapName,
925
+ writeField: () => writeField
365
926
  });
366
927
  module.exports = __toCommonJS(cloudflare_exports);
367
928
 
@@ -400,6 +961,32 @@ var DatabaseEngine = class {
400
961
  getTableName(_modelName) {
401
962
  throw new Error("Method not implemented.");
402
963
  }
964
+ /**
965
+ * A SYNCHRONOUS SQL executor over this engine's database, used by the
966
+ * large-document (format 2) record store. That store commits a merged
967
+ * record row and its pending-operation entry as one transaction — the
968
+ * barrier a `save()` resolves behind — which the async `query()` API cannot
969
+ * express. Engines whose underlying SQLite is synchronous (better-sqlite3,
970
+ * SQL.js) provide it; anything else leaves it undefined and cannot host a
971
+ * large document.
972
+ */
973
+ createFormat2Sql;
974
+ /**
975
+ * Whether what {@link createFormat2Sql} writes survives this process.
976
+ *
977
+ * On a large document the merged record view IS the document: the Y.Doc
978
+ * holds one epoch's overlay, and everything else — including a record a
979
+ * `save()` has already resolved, and the pending write that still owes it to
980
+ * the server — lives only in this store. An engine whose database is in
981
+ * memory therefore cannot host one: a reload does not cost a cache, it costs
982
+ * the document and the writes nobody has acknowledged yet.
983
+ *
984
+ * The platform is asked separately about its storage quota; this answers the
985
+ * half only the engine knows. An engine that does not implement it is taken
986
+ * at the platform's word, which is what keeps hosts with storage of their
987
+ * own — a worker handed a file store, a test — working unchanged.
988
+ */
989
+ format2StorageIsDurable;
403
990
  // Transaction support
404
991
  async withTransaction(_callback) {
405
992
  throw new Error("Method not implemented.");
@@ -1242,7 +1829,7 @@ function connectDoDb(options) {
1242
1829
  }
1243
1830
 
1244
1831
  // src/utils/yDocSchema.ts
1245
- var Y4 = __toESM(require("yjs"), 1);
1832
+ var Y8 = __toESM(require("yjs"), 1);
1246
1833
  init_yjsValues();
1247
1834
  function discoverSchema(yDoc) {
1248
1835
  const models = {};
@@ -1265,6 +1852,12 @@ function discoverSchema(yDoc) {
1265
1852
  }
1266
1853
  return { models };
1267
1854
  }
1855
+ function discoverDeclaredModel(yDoc, modelName) {
1856
+ const key = `_meta_${modelName}`;
1857
+ if (!yDoc.share.has(key)) return void 0;
1858
+ const map = materializeMap(yDoc, key);
1859
+ return map ? readModelMeta(map) : void 0;
1860
+ }
1268
1861
  function discoverModelNames(yDoc) {
1269
1862
  const names = [];
1270
1863
  for (const key of yDoc.share.keys()) {
@@ -1277,7 +1870,7 @@ function discoverModelNames(yDoc) {
1277
1870
  function materializeMap(yDoc, key) {
1278
1871
  try {
1279
1872
  const map = yDoc.getMap(key);
1280
- return map instanceof Y4.Map ? map : null;
1873
+ return map instanceof Y8.Map ? map : null;
1281
1874
  } catch {
1282
1875
  return null;
1283
1876
  }
@@ -1287,11 +1880,11 @@ function readModelMeta(metaMap) {
1287
1880
  let constraints;
1288
1881
  let relationships;
1289
1882
  for (const [key, value] of metaMap.entries()) {
1290
- if (key === "_constraints" && value instanceof Y4.Map) {
1883
+ if (key === "_constraints" && value instanceof Y8.Map) {
1291
1884
  constraints = readConstraints(value);
1292
- } else if (key === "_relationships" && value instanceof Y4.Map) {
1885
+ } else if (key === "_relationships" && value instanceof Y8.Map) {
1293
1886
  relationships = readRelationships(value);
1294
- } else if (value instanceof Y4.Map) {
1887
+ } else if (value instanceof Y8.Map) {
1295
1888
  fields[key] = readFieldMeta(value);
1296
1889
  }
1297
1890
  }
@@ -1326,7 +1919,7 @@ function inferModelFromData(dataMap) {
1326
1919
  const fields = {};
1327
1920
  let sampled = 0;
1328
1921
  for (const [_recordId, recordValue] of dataMap.entries()) {
1329
- if (!(recordValue instanceof Y4.Map)) continue;
1922
+ if (!(recordValue instanceof Y8.Map)) continue;
1330
1923
  if (++sampled > 5) break;
1331
1924
  for (const [fieldName, value] of recordValue.entries()) {
1332
1925
  if (fieldName.startsWith("_")) continue;
@@ -1354,7 +1947,7 @@ function inferTypeFromValue(value) {
1354
1947
  function readConstraints(constraintsMap) {
1355
1948
  const out = {};
1356
1949
  for (const [name, value] of constraintsMap.entries()) {
1357
- if (!(value instanceof Y4.Map)) continue;
1950
+ if (!(value instanceof Y8.Map)) continue;
1358
1951
  let fields = [];
1359
1952
  const rawFields = value.get("fields");
1360
1953
  if (typeof rawFields === "string") {
@@ -1373,7 +1966,7 @@ function readConstraints(constraintsMap) {
1373
1966
  function readRelationships(relsMap) {
1374
1967
  const out = {};
1375
1968
  for (const [name, value] of relsMap.entries()) {
1376
- if (!(value instanceof Y4.Map)) continue;
1969
+ if (!(value instanceof Y8.Map)) continue;
1377
1970
  const rel = {};
1378
1971
  for (const [k, v] of value.entries()) {
1379
1972
  rel[k] = v;
@@ -1452,6 +2045,7 @@ var import_smol_toml = require("smol-toml");
1452
2045
  // src/models/schema.ts
1453
2046
  init_BaseModel();
1454
2047
  init_ModelRegistry();
2048
+ init_uniqueIndex();
1455
2049
  function defineModelSchema(input) {
1456
2050
  const { name, fields } = input;
1457
2051
  const options = {
@@ -1494,7 +2088,7 @@ function resolveUniqueConstraints(modelName, fields, customConstraints) {
1494
2088
  for (const [fieldName, options] of fields.entries()) {
1495
2089
  if (options.unique) {
1496
2090
  resolved.push({
1497
- name: `${modelName}_${fieldName}_unique`,
2091
+ name: fieldUniqueConstraintName(modelName, fieldName),
1498
2092
  fields: [fieldName]
1499
2093
  });
1500
2094
  }
@@ -1740,4 +2334,6 @@ function loadSchemaFromTomlString(tomlString, options = {}) {
1740
2334
  }
1741
2335
 
1742
2336
  // src/cloudflare.ts
2337
+ init_uniqueIndex();
1743
2338
  init_metaSync();
2339
+ init_writeLifecycle();