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.
@@ -72,8 +72,203 @@ var init_yjsValues = __esm({
72
72
  }
73
73
  });
74
74
 
75
- // src/models/metaSync.ts
75
+ // src/models/writeLifecycle.ts
76
76
  import * as Y2 from "yjs";
77
+ import { ulid } from "ulid";
78
+ function assertNever(x) {
79
+ throw new Error(`Unhandled field type: ${String(x)}`);
80
+ }
81
+ function fieldWriteKind(type) {
82
+ switch (type) {
83
+ case "string":
84
+ case "number":
85
+ case "boolean":
86
+ case "date":
87
+ case "id":
88
+ return "scalar";
89
+ case "stringset":
90
+ return "stringset";
91
+ default:
92
+ return assertNever(type);
93
+ }
94
+ }
95
+ function isKnownFieldType(type) {
96
+ return KNOWN_FIELD_TYPES.has(type);
97
+ }
98
+ function ensureStringSetYMap(rm, fieldName) {
99
+ const nested = rm.get(fieldName);
100
+ if (nested instanceof Y2.Map) return nested;
101
+ const fresh = new Y2.Map();
102
+ if (nested && typeof nested === "object") {
103
+ for (const [member, marker] of Object.entries(
104
+ nested
105
+ )) {
106
+ if (Boolean(marker)) fresh.set(member, true);
107
+ }
108
+ }
109
+ rm.set(fieldName, fresh);
110
+ return fresh;
111
+ }
112
+ function applyStringSetDelta(target, delta) {
113
+ if (delta.clear) {
114
+ for (const member of Array.from(target.keys())) target.delete(member);
115
+ }
116
+ if (delta.remove) {
117
+ for (const member of delta.remove) target.delete(String(member));
118
+ }
119
+ if (delta.add) {
120
+ for (const member of delta.add) {
121
+ const key = String(member);
122
+ if (target.get(key) !== true) target.set(key, true);
123
+ }
124
+ }
125
+ }
126
+ function replaceStringSet(rm, fieldName, members) {
127
+ const target = ensureStringSetYMap(rm, fieldName);
128
+ const desired = /* @__PURE__ */ new Set();
129
+ for (const member of members) desired.add(String(member));
130
+ for (const existing of Array.from(target.keys())) {
131
+ if (!desired.has(existing)) target.delete(existing);
132
+ }
133
+ for (const member of desired) {
134
+ if (target.get(member) !== true) target.set(member, true);
135
+ }
136
+ }
137
+ function applyStringSetChange(recordYMap, fieldName, change) {
138
+ const target = ensureStringSetYMap(recordYMap, fieldName);
139
+ applyStringSetDelta(target, {
140
+ add: change.additions,
141
+ remove: change.removals
142
+ });
143
+ }
144
+ function parseStringSetOp(value) {
145
+ if (value === null || typeof value !== "object" || Array.isArray(value) || value instanceof Y2.AbstractType) {
146
+ return null;
147
+ }
148
+ const keys = Object.keys(value);
149
+ const opKeys = keys.filter((k) => k.startsWith("$"));
150
+ if (opKeys.length === 0) return null;
151
+ const known = /* @__PURE__ */ new Set(["$add", "$remove", "$clear"]);
152
+ const unknown = keys.filter((k) => !known.has(k));
153
+ if (unknown.length > 0) {
154
+ throw new Error(
155
+ `Invalid stringset op: unexpected key(s) [${unknown.join(", ")}]. Only $add, $remove and $clear are supported.`
156
+ );
157
+ }
158
+ const obj = value;
159
+ const toMembers = (raw, opName) => {
160
+ if (raw === void 0) return [];
161
+ if (!Array.isArray(raw)) {
162
+ throw new Error(`Invalid stringset op: ${opName} must be an array.`);
163
+ }
164
+ return raw.map((m) => String(m));
165
+ };
166
+ return {
167
+ add: toMembers(obj.$add, "$add"),
168
+ remove: toMembers(obj.$remove, "$remove"),
169
+ clear: obj.$clear === true
170
+ };
171
+ }
172
+ function writeField(rm, declaredType, key, value, modelName) {
173
+ const kind = declaredType && isKnownFieldType(declaredType) ? fieldWriteKind(declaredType) : "scalar";
174
+ if (Array.isArray(value)) {
175
+ if (kind !== "stringset") {
176
+ throw new Error(
177
+ `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.`
178
+ );
179
+ }
180
+ replaceStringSet(rm, key, value);
181
+ return;
182
+ }
183
+ const op = parseStringSetOp(value);
184
+ if (op) {
185
+ if (kind !== "stringset") {
186
+ throw new Error(
187
+ `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}).`
188
+ );
189
+ }
190
+ applyStringSetDelta(ensureStringSetYMap(rm, key), op);
191
+ return;
192
+ }
193
+ if (kind === "stringset") {
194
+ throw new Error(
195
+ `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}.`
196
+ );
197
+ }
198
+ rm.set(key, value);
199
+ }
200
+ function registerFunctionDefaultGenerator(name, fn) {
201
+ FUNCTION_DEFAULT_GENERATORS.set(name, fn);
202
+ }
203
+ function decodeFunctionDefault(name) {
204
+ return FUNCTION_DEFAULT_GENERATORS.get(name);
205
+ }
206
+ function resolveDefaultValue(rawDefault, opts = {}) {
207
+ if (typeof rawDefault === "function") return rawDefault();
208
+ if (typeof rawDefault === "string" && rawDefault.startsWith("$")) {
209
+ if (rawDefault.startsWith("$$")) return rawDefault.slice(1);
210
+ const name = rawDefault.slice(1);
211
+ const gen = opts.decode?.(name) ?? decodeFunctionDefault(name);
212
+ if (!gen) {
213
+ throw new Error(
214
+ `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.`
215
+ );
216
+ }
217
+ return gen();
218
+ }
219
+ return rawDefault;
220
+ }
221
+ function applyDefaultsFromSchema(fields, data, opts = {}) {
222
+ for (const [fieldName, field] of Object.entries(fields)) {
223
+ if (fieldName === "id") continue;
224
+ if (!field || field.default === void 0) continue;
225
+ if (field.default === UNKNOWN_FUNCTION_DEFAULT) {
226
+ continue;
227
+ }
228
+ if (Object.prototype.hasOwnProperty.call(data, fieldName) && data[fieldName] !== void 0) {
229
+ continue;
230
+ }
231
+ data[fieldName] = resolveDefaultValue(field.default, {
232
+ fieldName,
233
+ modelName: opts.modelName,
234
+ decode: opts.decodeFunctionDefault
235
+ });
236
+ }
237
+ return data;
238
+ }
239
+ function applyAutoStampsFromSchema(fields, data, opts) {
240
+ const { isNew, now } = opts;
241
+ for (const [fieldName, field] of Object.entries(fields)) {
242
+ const stamp = field?.autoStamp;
243
+ if (!stamp) continue;
244
+ const explicit = Object.prototype.hasOwnProperty.call(data, fieldName) && data[fieldName] !== null && data[fieldName] !== void 0;
245
+ if (explicit) continue;
246
+ if (stamp === "create" && !isNew) continue;
247
+ data[fieldName] = now;
248
+ }
249
+ return data;
250
+ }
251
+ var KNOWN_FIELD_TYPE_MAP, KNOWN_FIELD_TYPES, FUNCTION_DEFAULT_GENERATORS, UNKNOWN_FUNCTION_DEFAULT;
252
+ var init_writeLifecycle = __esm({
253
+ "src/models/writeLifecycle.ts"() {
254
+ "use strict";
255
+ KNOWN_FIELD_TYPE_MAP = {
256
+ string: true,
257
+ number: true,
258
+ boolean: true,
259
+ date: true,
260
+ id: true,
261
+ stringset: true
262
+ };
263
+ KNOWN_FIELD_TYPES = new Set(Object.keys(KNOWN_FIELD_TYPE_MAP));
264
+ FUNCTION_DEFAULT_GENERATORS = /* @__PURE__ */ new Map();
265
+ registerFunctionDefaultGenerator("generate_ulid", () => ulid());
266
+ UNKNOWN_FUNCTION_DEFAULT = "$unknown_function";
267
+ }
268
+ });
269
+
270
+ // src/models/metaSync.ts
271
+ import * as Y3 from "yjs";
77
272
  function inferFieldType(value) {
78
273
  if (isStringSetYMap(value)) return "stringset";
79
274
  switch (typeof value) {
@@ -94,7 +289,10 @@ function encodeDefault(value) {
94
289
  if (value === void 0 || value === null) return void 0;
95
290
  if (typeof value === "function") {
96
291
  const name = KNOWN_FUNCTION_DEFAULTS.get(value);
97
- return name ? `$${name}` : "$unknown_function";
292
+ return name ? `$${name}` : UNKNOWN_FUNCTION_DEFAULT;
293
+ }
294
+ if (typeof value === "string" && value.startsWith("$")) {
295
+ return `$${value}`;
98
296
  }
99
297
  return value;
100
298
  }
@@ -120,7 +318,7 @@ function syncModelMeta(yDoc, modelName, schema) {
120
318
  if (compoundConstraints.length > 0) {
121
319
  let constraints = meta.get("_constraints");
122
320
  if (!constraints) {
123
- constraints = new Y2.Map();
321
+ constraints = new Y3.Map();
124
322
  meta.set("_constraints", constraints);
125
323
  }
126
324
  for (const constraint of compoundConstraints) {
@@ -131,7 +329,7 @@ function syncModelMeta(yDoc, modelName, schema) {
131
329
  if (relationships && Object.keys(relationships).length > 0) {
132
330
  let rels = meta.get("_relationships");
133
331
  if (!rels) {
134
- rels = new Y2.Map();
332
+ rels = new Y3.Map();
135
333
  meta.set("_relationships", rels);
136
334
  }
137
335
  for (const [relName, relConfig] of Object.entries(relationships)) {
@@ -143,7 +341,7 @@ function syncModelMeta(yDoc, modelName, schema) {
143
341
  function syncFieldMeta(metaMap, fieldName, fieldOpts) {
144
342
  let fieldMeta = metaMap.get(fieldName);
145
343
  if (!fieldMeta) {
146
- fieldMeta = new Y2.Map();
344
+ fieldMeta = new Y3.Map();
147
345
  metaMap.set(fieldName, fieldMeta);
148
346
  }
149
347
  setIfChanged(fieldMeta, "type", fieldOpts.type);
@@ -160,7 +358,7 @@ function syncFieldMeta(metaMap, fieldName, fieldOpts) {
160
358
  function syncConstraintMeta(constraintsMap, constraint) {
161
359
  let cMeta = constraintsMap.get(constraint.name);
162
360
  if (!cMeta) {
163
- cMeta = new Y2.Map();
361
+ cMeta = new Y3.Map();
164
362
  constraintsMap.set(constraint.name, cMeta);
165
363
  }
166
364
  setIfChanged(cMeta, "type", "unique");
@@ -170,7 +368,7 @@ function syncConstraintMeta(constraintsMap, constraint) {
170
368
  function syncRelationshipMeta(relsMap, relName, relConfig) {
171
369
  let relMeta = relsMap.get(relName);
172
370
  if (!relMeta) {
173
- relMeta = new Y2.Map();
371
+ relMeta = new Y3.Map();
174
372
  relsMap.set(relName, relMeta);
175
373
  }
176
374
  for (const [key, value] of Object.entries(relConfig)) {
@@ -185,7 +383,7 @@ function syncInferredMeta(yDoc, modelName, recordData) {
185
383
  if (fieldName.startsWith("_")) continue;
186
384
  let fieldMeta = meta.get(fieldName);
187
385
  if (!fieldMeta) {
188
- fieldMeta = new Y2.Map();
386
+ fieldMeta = new Y3.Map();
189
387
  meta.set(fieldName, fieldMeta);
190
388
  }
191
389
  if (!fieldMeta.has("type")) {
@@ -206,16 +404,349 @@ var init_metaSync = __esm({
206
404
  "src/models/metaSync.ts"() {
207
405
  "use strict";
208
406
  init_yjsValues();
407
+ init_writeLifecycle();
209
408
  KNOWN_FUNCTION_DEFAULTS = /* @__PURE__ */ new WeakMap();
210
409
  _syncedCache = /* @__PURE__ */ new WeakMap();
211
410
  }
212
411
  });
213
412
 
413
+ // src/utils/uniqueIndex.ts
414
+ import * as Y4 from "yjs";
415
+ function buildUniqueKey(recordData, fields) {
416
+ const keyParts = [];
417
+ for (const field of fields) {
418
+ const value = recordData instanceof Y4.Map ? recordData.get(field) : recordData[field];
419
+ if (value === null || value === void 0) {
420
+ return null;
421
+ }
422
+ keyParts.push(String(value));
423
+ }
424
+ return keyParts.join("|");
425
+ }
426
+ function uniqueIndexMapName(modelName, constraintName) {
427
+ return `_uniqueIdx_${modelName}_${constraintName}`;
428
+ }
429
+ function fieldUniqueConstraintName(modelName, fieldName) {
430
+ return `${modelName}_${fieldName}_unique`;
431
+ }
432
+ var init_uniqueIndex = __esm({
433
+ "src/utils/uniqueIndex.ts"() {
434
+ "use strict";
435
+ }
436
+ });
437
+
438
+ // src/utils/largeDocuments.ts
439
+ import * as Y5 from "yjs";
440
+ var init_largeDocuments = __esm({
441
+ "src/utils/largeDocuments.ts"() {
442
+ "use strict";
443
+ init_yjsValues();
444
+ }
445
+ });
446
+
447
+ // src/models/OrmContext.ts
448
+ var OrmContext, defaultOrmContext, liveOrmContexts;
449
+ var init_OrmContext = __esm({
450
+ "src/models/OrmContext.ts"() {
451
+ "use strict";
452
+ OrmContext = class _OrmContext {
453
+ /** Declared before the field that reads it: static initialization is ordered. */
454
+ static instanceCounter = 0;
455
+ /**
456
+ * Which instance this is, for diagnostics. A process holding several clients
457
+ * needs a way to say WHICH one a warning is about, so every context takes the
458
+ * next number as it is created and keeps it for its lifetime.
459
+ */
460
+ instanceId = ++_OrmContext.instanceCounter;
461
+ /** The engine this instance's models read and write through. */
462
+ dbEngine = null;
463
+ connectedDocuments = /* @__PURE__ */ new Map();
464
+ /** `docId_modelName` → the Y.Map that document holds for that model. */
465
+ documentYMaps = /* @__PURE__ */ new Map();
466
+ /** `subscribe()` callbacks, by model name. */
467
+ listenersMap = /* @__PURE__ */ new Map();
468
+ // Default-document resolution
469
+ modelNameToDefaultDocId = /* @__PURE__ */ new Map();
470
+ globalDefaultDocId = void 0;
471
+ defaultDocChangedListeners = /* @__PURE__ */ new Set();
472
+ modelDocMappingChangedListeners = /* @__PURE__ */ new Set();
473
+ /** Format-2 ("large document", #2816) record stores by document id. */
474
+ format2Documents = /* @__PURE__ */ new Map();
475
+ /** Format-2 epoch moves in flight (#2816). */
476
+ format2Transitions = /* @__PURE__ */ new Map();
477
+ /**
478
+ * Nested-Y.Map stringset fields this context has already observed. Per
479
+ * context, not global: a Y.Doc reconnected to a fresh instance has to be
480
+ * observed again by that instance, and a global guard would silently
481
+ * suppress it.
482
+ */
483
+ observedStringSetMaps = /* @__PURE__ */ new WeakSet();
484
+ /**
485
+ * The registry this instance's schema lookups use. `null` on the standing
486
+ * default context, where callers fall back to the global singleton.
487
+ */
488
+ modelRegistry = null;
489
+ /** Model classes routed to this context. */
490
+ boundModels = /* @__PURE__ */ new Set();
491
+ /** True once {@link destroy} has run; bound classes keep pointing here. */
492
+ destroyed = false;
493
+ /** Yjs `unobserve` thunks by owning document id. */
494
+ observerDisposers = /* @__PURE__ */ new Map();
495
+ /** Teardown the owner (the document manager) registers for `destroy()`. */
496
+ teardownHooks = /* @__PURE__ */ new Set();
497
+ static NO_DOCUMENT_KEY = "__no_document__";
498
+ /**
499
+ * Record how to remove an observer this context installed. Every `observe()`
500
+ * in the ORM pairs with one of these: without it a torn-down instance's
501
+ * callbacks keep firing on a Y.Doc that outlives it.
502
+ */
503
+ registerObserverDisposer(docId, dispose) {
504
+ const key = docId ?? _OrmContext.NO_DOCUMENT_KEY;
505
+ const list = this.observerDisposers.get(key);
506
+ if (list) list.push(dispose);
507
+ else this.observerDisposers.set(key, [dispose]);
508
+ }
509
+ /** Remove every observer this context installed for `docId`. */
510
+ disposeObserversForDocument(docId) {
511
+ const list = this.observerDisposers.get(docId);
512
+ if (!list) return;
513
+ this.observerDisposers.delete(docId);
514
+ for (const dispose of list) {
515
+ try {
516
+ dispose();
517
+ } catch {
518
+ }
519
+ }
520
+ }
521
+ disposeAllObservers() {
522
+ const buckets = Array.from(this.observerDisposers.values());
523
+ this.observerDisposers.clear();
524
+ for (const list of buckets) {
525
+ for (const dispose of list) {
526
+ try {
527
+ dispose();
528
+ } catch {
529
+ }
530
+ }
531
+ }
532
+ }
533
+ /** Run `hook` as part of `destroy()` (the format-2 binding registers here). */
534
+ onDestroy(hook) {
535
+ this.teardownHooks.add(hook);
536
+ }
537
+ /** The record store for `docId`, or `null` when the document is format 1. */
538
+ getFormat2Store(docId) {
539
+ if (!docId) return null;
540
+ return this.format2Documents.get(docId) ?? null;
541
+ }
542
+ /**
543
+ * The epoch move a write to `docId` has to wait out (#2816, behavior 10), or
544
+ * `null` when none is in flight.
545
+ */
546
+ format2WritesHeld(docId) {
547
+ if (!docId) return null;
548
+ return this.format2Transitions.get(docId) ?? null;
549
+ }
550
+ /** Run an epoch move with `docId`'s local writes held back (#2816). */
551
+ async holdFormat2Writes(docId, run) {
552
+ const inFlight = this.format2Transitions.get(docId);
553
+ if (inFlight) await inFlight.catch(() => {
554
+ });
555
+ let release;
556
+ const held = new Promise((resolve) => {
557
+ release = resolve;
558
+ });
559
+ this.format2Transitions.set(docId, held);
560
+ try {
561
+ return await run();
562
+ } finally {
563
+ if (this.format2Transitions.get(docId) === held) {
564
+ this.format2Transitions.delete(docId);
565
+ }
566
+ release();
567
+ }
568
+ }
569
+ /** Notify `subscribe()` callbacks registered on this context for `modelName`. */
570
+ notifyListeners(modelName, log, onError) {
571
+ const listeners = this.listenersMap.get(modelName);
572
+ if (!listeners) return;
573
+ log?.(`Notifying ${listeners.size} listeners for ${modelName}`);
574
+ listeners.forEach((callback) => {
575
+ try {
576
+ callback();
577
+ } catch (e) {
578
+ onError?.(e);
579
+ }
580
+ });
581
+ }
582
+ // ---- Default-document resolution (the `BaseModel` statics delegate here) ----
583
+ setModelDefaultDocumentId(modelName, docId) {
584
+ const previous = this.modelNameToDefaultDocId.get(modelName);
585
+ this.modelNameToDefaultDocId.set(modelName, docId);
586
+ this.announceModelDocMapping(modelName, previous, docId);
587
+ }
588
+ removeModelDefaultDocumentId(modelName) {
589
+ const previous = this.modelNameToDefaultDocId.get(modelName);
590
+ this.modelNameToDefaultDocId.delete(modelName);
591
+ this.announceModelDocMapping(modelName, previous, void 0);
592
+ }
593
+ clearModelDefaultDocumentIds() {
594
+ const entries = Array.from(this.modelNameToDefaultDocId.entries());
595
+ this.modelNameToDefaultDocId.clear();
596
+ for (const [modelName, previous] of entries) {
597
+ this.announceModelDocMapping(modelName, previous, void 0);
598
+ }
599
+ }
600
+ setGlobalDefaultDocumentId(docId) {
601
+ const previous = this.globalDefaultDocId;
602
+ this.globalDefaultDocId = docId;
603
+ this.announceDefaultDoc(previous, docId);
604
+ }
605
+ clearGlobalDefaultDocumentId() {
606
+ const previous = this.globalDefaultDocId;
607
+ this.globalDefaultDocId = void 0;
608
+ this.announceDefaultDoc(previous, void 0);
609
+ }
610
+ getModelDefaultDocumentMapping() {
611
+ return Object.fromEntries(this.modelNameToDefaultDocId.entries());
612
+ }
613
+ getDocumentIdForModel(modelName) {
614
+ return this.modelNameToDefaultDocId.get(modelName);
615
+ }
616
+ getGlobalDefaultDocumentId() {
617
+ return this.globalDefaultDocId;
618
+ }
619
+ onDefaultDocChanged(listener) {
620
+ this.defaultDocChangedListeners.add(listener);
621
+ return () => this.defaultDocChangedListeners.delete(listener);
622
+ }
623
+ onModelDocMappingChanged(listener) {
624
+ this.modelDocMappingChangedListeners.add(listener);
625
+ return () => this.modelDocMappingChangedListeners.delete(listener);
626
+ }
627
+ /** Drop the model→document defaults that point at a disconnected document. */
628
+ clearMappingsForDocId(docId) {
629
+ const toRemove = [];
630
+ for (const [modelName, mappedDocId] of this.modelNameToDefaultDocId) {
631
+ if (mappedDocId === docId) toRemove.push(modelName);
632
+ }
633
+ for (const modelName of toRemove) {
634
+ this.removeModelDefaultDocumentId(modelName);
635
+ }
636
+ if (this.globalDefaultDocId === docId) {
637
+ this.clearGlobalDefaultDocumentId();
638
+ }
639
+ }
640
+ announceDefaultDoc(previous, current) {
641
+ for (const listener of this.defaultDocChangedListeners) {
642
+ try {
643
+ listener({ previous, current });
644
+ } catch {
645
+ }
646
+ }
647
+ }
648
+ announceModelDocMapping(modelName, previous, current) {
649
+ for (const listener of this.modelDocMappingChangedListeners) {
650
+ try {
651
+ listener({ modelName, previous, current });
652
+ } catch {
653
+ }
654
+ }
655
+ }
656
+ /**
657
+ * Tear this instance down: observers off, engine destroyed, state cleared.
658
+ *
659
+ * Deliberately does NOT unbind its model classes. They keep resolving this
660
+ * context, whose engine is now null, so an operation through them throws the
661
+ * ordinary not-initialized error instead of silently landing on whatever
662
+ * other instance happens to be live (#3039, behavior 7). Only a later
663
+ * `initJsBao` may rebind a class whose context is destroyed.
664
+ */
665
+ async destroy() {
666
+ if (this.destroyed) return;
667
+ this.destroyed = true;
668
+ this.disposeAllObservers();
669
+ const hooks = Array.from(this.teardownHooks);
670
+ this.teardownHooks.clear();
671
+ for (const hook of hooks) {
672
+ try {
673
+ await hook();
674
+ } catch {
675
+ }
676
+ }
677
+ const engine = this.dbEngine;
678
+ this.dbEngine = null;
679
+ if (engine) {
680
+ try {
681
+ await engine.destroy();
682
+ } catch {
683
+ }
684
+ }
685
+ this.connectedDocuments.clear();
686
+ this.documentYMaps.clear();
687
+ this.listenersMap.clear();
688
+ this.modelNameToDefaultDocId.clear();
689
+ this.globalDefaultDocId = void 0;
690
+ this.defaultDocChangedListeners.clear();
691
+ this.modelDocMappingChangedListeners.clear();
692
+ this.format2Documents.clear();
693
+ this.format2Transitions.clear();
694
+ this.observedStringSetMaps = /* @__PURE__ */ new WeakSet();
695
+ liveOrmContexts.delete(this);
696
+ if (defaultOrmContext === this) {
697
+ defaultOrmContext = new _OrmContext();
698
+ }
699
+ }
700
+ };
701
+ defaultOrmContext = new OrmContext();
702
+ liveOrmContexts = /* @__PURE__ */ new Set();
703
+ }
704
+ });
705
+
706
+ // src/models/format2QueryProjection.ts
707
+ var FORMAT2_QUERY_PROJECTION_DDL;
708
+ var init_format2QueryProjection = __esm({
709
+ "src/models/format2QueryProjection.ts"() {
710
+ "use strict";
711
+ FORMAT2_QUERY_PROJECTION_DDL = `
712
+ CREATE TABLE IF NOT EXISTS _query_projection (
713
+ doc_id TEXT NOT NULL,
714
+ model TEXT NOT NULL,
715
+ PRIMARY KEY (doc_id, model)
716
+ )`.trim();
717
+ }
718
+ });
719
+
720
+ // src/models/format2Observer.ts
721
+ import * as Y6 from "yjs";
722
+ var init_format2Observer = __esm({
723
+ "src/models/format2Observer.ts"() {
724
+ "use strict";
725
+ init_largeDocuments();
726
+ }
727
+ });
728
+
729
+ // src/models/format2ClientWrite.ts
730
+ var init_format2ClientWrite = __esm({
731
+ "src/models/format2ClientWrite.ts"() {
732
+ "use strict";
733
+ }
734
+ });
735
+
736
+ // src/models/format2OfflineWindow.ts
737
+ var DAY_MS;
738
+ var init_format2OfflineWindow = __esm({
739
+ "src/models/format2OfflineWindow.ts"() {
740
+ "use strict";
741
+ DAY_MS = 24 * 60 * 60 * 1e3;
742
+ }
743
+ });
744
+
214
745
  // src/models/BaseModel.ts
215
- import * as Y3 from "yjs";
216
- import { ulid } from "ulid";
746
+ import * as Y7 from "yjs";
747
+ import { ulid as ulid2 } from "ulid";
217
748
  function generateULID() {
218
- return ulid();
749
+ return ulid2();
219
750
  }
220
751
  var Logger;
221
752
  var init_BaseModel = __esm({
@@ -227,7 +758,15 @@ var init_BaseModel = __esm({
227
758
  init_CursorManager();
228
759
  init_sql();
229
760
  init_metaSync();
761
+ init_writeLifecycle();
230
762
  init_yjsValues();
763
+ init_uniqueIndex();
764
+ init_largeDocuments();
765
+ init_OrmContext();
766
+ init_format2QueryProjection();
767
+ init_format2Observer();
768
+ init_format2ClientWrite();
769
+ init_format2OfflineWindow();
231
770
  Logger = class {
232
771
  static _logLevel = 1 /* ERROR */;
233
772
  static _logCallback = null;
@@ -314,6 +853,7 @@ var init_ModelRegistry = __esm({
314
853
  "use strict";
315
854
  init_BaseModel();
316
855
  init_relationshipManager();
856
+ init_format2QueryProjection();
317
857
  }
318
858
  });
319
859
 
@@ -352,6 +892,32 @@ var DatabaseEngine = class {
352
892
  getTableName(_modelName) {
353
893
  throw new Error("Method not implemented.");
354
894
  }
895
+ /**
896
+ * A SYNCHRONOUS SQL executor over this engine's database, used by the
897
+ * large-document (format 2) record store. That store commits a merged
898
+ * record row and its pending-operation entry as one transaction — the
899
+ * barrier a `save()` resolves behind — which the async `query()` API cannot
900
+ * express. Engines whose underlying SQLite is synchronous (better-sqlite3,
901
+ * SQL.js) provide it; anything else leaves it undefined and cannot host a
902
+ * large document.
903
+ */
904
+ createFormat2Sql;
905
+ /**
906
+ * Whether what {@link createFormat2Sql} writes survives this process.
907
+ *
908
+ * On a large document the merged record view IS the document: the Y.Doc
909
+ * holds one epoch's overlay, and everything else — including a record a
910
+ * `save()` has already resolved, and the pending write that still owes it to
911
+ * the server — lives only in this store. An engine whose database is in
912
+ * memory therefore cannot host one: a reload does not cost a cache, it costs
913
+ * the document and the writes nobody has acknowledged yet.
914
+ *
915
+ * The platform is asked separately about its storage quota; this answers the
916
+ * half only the engine knows. An engine that does not implement it is taken
917
+ * at the platform's word, which is what keeps hosts with storage of their
918
+ * own — a worker handed a file store, a test — working unchanged.
919
+ */
920
+ format2StorageIsDurable;
355
921
  // Transaction support
356
922
  async withTransaction(_callback) {
357
923
  throw new Error("Method not implemented.");
@@ -1195,7 +1761,7 @@ function connectDoDb(options) {
1195
1761
 
1196
1762
  // src/utils/yDocSchema.ts
1197
1763
  init_yjsValues();
1198
- import * as Y4 from "yjs";
1764
+ import * as Y8 from "yjs";
1199
1765
  function discoverSchema(yDoc) {
1200
1766
  const models = {};
1201
1767
  const metaNames = /* @__PURE__ */ new Set();
@@ -1217,6 +1783,12 @@ function discoverSchema(yDoc) {
1217
1783
  }
1218
1784
  return { models };
1219
1785
  }
1786
+ function discoverDeclaredModel(yDoc, modelName) {
1787
+ const key = `_meta_${modelName}`;
1788
+ if (!yDoc.share.has(key)) return void 0;
1789
+ const map = materializeMap(yDoc, key);
1790
+ return map ? readModelMeta(map) : void 0;
1791
+ }
1220
1792
  function discoverModelNames(yDoc) {
1221
1793
  const names = [];
1222
1794
  for (const key of yDoc.share.keys()) {
@@ -1229,7 +1801,7 @@ function discoverModelNames(yDoc) {
1229
1801
  function materializeMap(yDoc, key) {
1230
1802
  try {
1231
1803
  const map = yDoc.getMap(key);
1232
- return map instanceof Y4.Map ? map : null;
1804
+ return map instanceof Y8.Map ? map : null;
1233
1805
  } catch {
1234
1806
  return null;
1235
1807
  }
@@ -1239,11 +1811,11 @@ function readModelMeta(metaMap) {
1239
1811
  let constraints;
1240
1812
  let relationships;
1241
1813
  for (const [key, value] of metaMap.entries()) {
1242
- if (key === "_constraints" && value instanceof Y4.Map) {
1814
+ if (key === "_constraints" && value instanceof Y8.Map) {
1243
1815
  constraints = readConstraints(value);
1244
- } else if (key === "_relationships" && value instanceof Y4.Map) {
1816
+ } else if (key === "_relationships" && value instanceof Y8.Map) {
1245
1817
  relationships = readRelationships(value);
1246
- } else if (value instanceof Y4.Map) {
1818
+ } else if (value instanceof Y8.Map) {
1247
1819
  fields[key] = readFieldMeta(value);
1248
1820
  }
1249
1821
  }
@@ -1278,7 +1850,7 @@ function inferModelFromData(dataMap) {
1278
1850
  const fields = {};
1279
1851
  let sampled = 0;
1280
1852
  for (const [_recordId, recordValue] of dataMap.entries()) {
1281
- if (!(recordValue instanceof Y4.Map)) continue;
1853
+ if (!(recordValue instanceof Y8.Map)) continue;
1282
1854
  if (++sampled > 5) break;
1283
1855
  for (const [fieldName, value] of recordValue.entries()) {
1284
1856
  if (fieldName.startsWith("_")) continue;
@@ -1306,7 +1878,7 @@ function inferTypeFromValue(value) {
1306
1878
  function readConstraints(constraintsMap) {
1307
1879
  const out = {};
1308
1880
  for (const [name, value] of constraintsMap.entries()) {
1309
- if (!(value instanceof Y4.Map)) continue;
1881
+ if (!(value instanceof Y8.Map)) continue;
1310
1882
  let fields = [];
1311
1883
  const rawFields = value.get("fields");
1312
1884
  if (typeof rawFields === "string") {
@@ -1325,7 +1897,7 @@ function readConstraints(constraintsMap) {
1325
1897
  function readRelationships(relsMap) {
1326
1898
  const out = {};
1327
1899
  for (const [name, value] of relsMap.entries()) {
1328
- if (!(value instanceof Y4.Map)) continue;
1900
+ if (!(value instanceof Y8.Map)) continue;
1329
1901
  const rel = {};
1330
1902
  for (const [k, v] of value.entries()) {
1331
1903
  rel[k] = v;
@@ -1404,6 +1976,7 @@ import { parse as parseToml } from "smol-toml";
1404
1976
  // src/models/schema.ts
1405
1977
  init_BaseModel();
1406
1978
  init_ModelRegistry();
1979
+ init_uniqueIndex();
1407
1980
  function defineModelSchema(input) {
1408
1981
  const { name, fields } = input;
1409
1982
  const options = {
@@ -1446,7 +2019,7 @@ function resolveUniqueConstraints(modelName, fields, customConstraints) {
1446
2019
  for (const [fieldName, options] of fields.entries()) {
1447
2020
  if (options.unique) {
1448
2021
  resolved.push({
1449
- name: `${modelName}_${fieldName}_unique`,
2022
+ name: fieldUniqueConstraintName(modelName, fieldName),
1450
2023
  fields: [fieldName]
1451
2024
  });
1452
2025
  }
@@ -1692,20 +2265,40 @@ function loadSchemaFromTomlString(tomlString, options = {}) {
1692
2265
  }
1693
2266
 
1694
2267
  // src/cloudflare.ts
2268
+ init_uniqueIndex();
1695
2269
  init_metaSync();
2270
+ init_writeLifecycle();
1696
2271
  export {
1697
2272
  DOClientEngine,
2273
+ applyAutoStampsFromSchema,
2274
+ applyDefaultsFromSchema,
2275
+ applyStringSetChange,
2276
+ applyStringSetDelta,
2277
+ assertNever,
2278
+ buildUniqueKey,
1698
2279
  clearMetaSyncCache,
1699
2280
  connectDoDb,
2281
+ decodeFunctionDefault,
2282
+ discoverDeclaredModel,
1700
2283
  discoverModelNames,
1701
2284
  discoverSchema,
2285
+ ensureStringSetYMap,
2286
+ fieldUniqueConstraintName,
2287
+ fieldWriteKind,
1702
2288
  getActiveDOEngine,
1703
2289
  inferFieldType,
1704
2290
  initJsBaoDO,
2291
+ isKnownFieldType,
1705
2292
  loadSchemaFromTomlString,
2293
+ parseStringSetOp,
1706
2294
  registerFunctionDefault,
2295
+ registerFunctionDefaultGenerator,
2296
+ replaceStringSet,
1707
2297
  resetJsBaoDO,
2298
+ resolveDefaultValue,
1708
2299
  schemaToToml,
1709
2300
  syncInferredMeta,
1710
- syncModelMeta
2301
+ syncModelMeta,
2302
+ uniqueIndexMapName,
2303
+ writeField
1711
2304
  };