@palantir/pack.state.core 0.0.1-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/.turbo/turbo-lint.log +4 -0
  2. package/.turbo/turbo-transpileBrowser.log +5 -0
  3. package/.turbo/turbo-transpileCjs.log +5 -0
  4. package/.turbo/turbo-transpileEsm.log +5 -0
  5. package/.turbo/turbo-transpileTypes.log +5 -0
  6. package/.turbo/turbo-typecheck.log +4 -0
  7. package/LICENSE.txt +13 -0
  8. package/README.md +55 -0
  9. package/build/browser/index.js +1257 -0
  10. package/build/browser/index.js.map +1 -0
  11. package/build/cjs/index.cjs +1298 -0
  12. package/build/cjs/index.cjs.map +1 -0
  13. package/build/cjs/index.d.cts +272 -0
  14. package/build/esm/index.js +1257 -0
  15. package/build/esm/index.js.map +1 -0
  16. package/build/types/DocumentServiceModule.d.ts +6 -0
  17. package/build/types/DocumentServiceModule.d.ts.map +1 -0
  18. package/build/types/__tests__/DocumentStatusTracking.test.d.ts +1 -0
  19. package/build/types/__tests__/DocumentStatusTracking.test.d.ts.map +1 -0
  20. package/build/types/__tests__/RefStability.test.d.ts +1 -0
  21. package/build/types/__tests__/RefStability.test.d.ts.map +1 -0
  22. package/build/types/__tests__/StateModule.integration.test.d.ts +1 -0
  23. package/build/types/__tests__/StateModule.integration.test.d.ts.map +1 -0
  24. package/build/types/__tests__/testUtils.d.ts +7 -0
  25. package/build/types/__tests__/testUtils.d.ts.map +1 -0
  26. package/build/types/index.d.ts +11 -0
  27. package/build/types/index.d.ts.map +1 -0
  28. package/build/types/service/BaseYjsDocumentService.d.ts +155 -0
  29. package/build/types/service/BaseYjsDocumentService.d.ts.map +1 -0
  30. package/build/types/service/InMemoryDocumentService.d.ts +12 -0
  31. package/build/types/service/InMemoryDocumentService.d.ts.map +1 -0
  32. package/build/types/service/YjsSchemaMapper.d.ts +9 -0
  33. package/build/types/service/YjsSchemaMapper.d.ts.map +1 -0
  34. package/build/types/types/DocumentRefImpl.d.ts +5 -0
  35. package/build/types/types/DocumentRefImpl.d.ts.map +1 -0
  36. package/build/types/types/DocumentService.d.ts +62 -0
  37. package/build/types/types/DocumentService.d.ts.map +1 -0
  38. package/build/types/types/DocumentServiceConfig.d.ts +5 -0
  39. package/build/types/types/DocumentServiceConfig.d.ts.map +1 -0
  40. package/build/types/types/RecordCollectionRefImpl.d.ts +5 -0
  41. package/build/types/types/RecordCollectionRefImpl.d.ts.map +1 -0
  42. package/build/types/types/RecordRefImpl.d.ts +5 -0
  43. package/build/types/types/RecordRefImpl.d.ts.map +1 -0
  44. package/build/types/types/StateModule.d.ts +59 -0
  45. package/build/types/types/StateModule.d.ts.map +1 -0
  46. package/package.json +71 -0
  47. package/src/DocumentServiceModule.ts +53 -0
  48. package/src/__tests__/DocumentStatusTracking.test.ts +229 -0
  49. package/src/__tests__/RefStability.test.ts +441 -0
  50. package/src/__tests__/StateModule.integration.test.ts +1187 -0
  51. package/src/__tests__/testUtils.ts +106 -0
  52. package/src/index.ts +38 -0
  53. package/src/service/BaseYjsDocumentService.ts +1277 -0
  54. package/src/service/InMemoryDocumentService.ts +162 -0
  55. package/src/service/YjsSchemaMapper.ts +194 -0
  56. package/src/types/DocumentRefImpl.ts +98 -0
  57. package/src/types/DocumentService.ts +210 -0
  58. package/src/types/DocumentServiceConfig.ts +22 -0
  59. package/src/types/RecordCollectionRefImpl.ts +124 -0
  60. package/src/types/RecordRefImpl.ts +106 -0
  61. package/src/types/StateModule.ts +329 -0
  62. package/tsconfig.json +21 -0
  63. package/vitest.config.mjs +26 -0
@@ -0,0 +1,1298 @@
1
+ 'use strict';
2
+
3
+ var pack_documentSchema_modelTypes = require('@palantir/pack.document-schema.model-types');
4
+ var remeda = require('remeda');
5
+ var invariant = require('tiny-invariant');
6
+ var Y = require('yjs');
7
+ var pack_core = require('@palantir/pack.core');
8
+
9
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
10
+
11
+ function _interopNamespace(e) {
12
+ if (e && e.__esModule) return e;
13
+ var n = Object.create(null);
14
+ if (e) {
15
+ Object.keys(e).forEach(function (k) {
16
+ if (k !== 'default') {
17
+ var d = Object.getOwnPropertyDescriptor(e, k);
18
+ Object.defineProperty(n, k, d.get ? d : {
19
+ enumerable: true,
20
+ get: function () { return e[k]; }
21
+ });
22
+ }
23
+ });
24
+ }
25
+ n.default = e;
26
+ return Object.freeze(n);
27
+ }
28
+
29
+ var invariant__default = /*#__PURE__*/_interopDefault(invariant);
30
+ var Y__namespace = /*#__PURE__*/_interopNamespace(Y);
31
+
32
+ // src/DocumentServiceModule.ts
33
+ var DOCUMENT_SERVICE_MODULE_KEY = {
34
+ key: Symbol("DocumentService"),
35
+ initModule: initDocumentService
36
+ };
37
+ function getDocumentService(app) {
38
+ return app.getModule(DOCUMENT_SERVICE_MODULE_KEY);
39
+ }
40
+ function createDocumentServiceConfig(init, config) {
41
+ return [DOCUMENT_SERVICE_MODULE_KEY, {
42
+ ...config,
43
+ init
44
+ }];
45
+ }
46
+ function initDocumentService(app, config) {
47
+ if (config == null) {
48
+ throw new Error("DocumentServiceConfig is required to initialize DocumentService");
49
+ }
50
+ return config.init(app, config);
51
+ }
52
+ var STATE_MODULE_ACCESSOR = "state";
53
+ var STATE_MODULE_KEY = {
54
+ appMemberName: STATE_MODULE_ACCESSOR,
55
+ key: Symbol.for("pack.state"),
56
+ initModule: (app) => {
57
+ const documentService = app.getModule(DOCUMENT_SERVICE_MODULE_KEY);
58
+ return new StateModuleImpl(documentService);
59
+ }
60
+ };
61
+ var StateModuleImpl = class {
62
+ constructor(documentService) {
63
+ this.documentService = documentService;
64
+ }
65
+ createDocRef(id, schema) {
66
+ return this.documentService.createDocRef(id, schema);
67
+ }
68
+ createRecordRef(docRef, id, model) {
69
+ return this.documentService.getCreateRecordRef(docRef, id, model);
70
+ }
71
+ async createDocument(metadata, schema) {
72
+ return this.documentService.createDocument(metadata, schema);
73
+ }
74
+ async getDocumentSnapshot(docRef) {
75
+ return this.documentService.getDocumentSnapshot(docRef);
76
+ }
77
+ onMetadataChange(docRef, cb) {
78
+ return this.documentService.onMetadataChange(docRef, cb);
79
+ }
80
+ onStateChange(docRef, cb) {
81
+ return this.documentService.onStateChange(docRef, cb);
82
+ }
83
+ async getRecordSnapshot(recordRef) {
84
+ return this.documentService.getRecordSnapshot(recordRef);
85
+ }
86
+ async setRecord(recordRef, state) {
87
+ return this.documentService.setRecord(recordRef, state);
88
+ }
89
+ async updateRecord(recordRef, partialState) {
90
+ return this.documentService.updateRecord(recordRef, partialState);
91
+ }
92
+ // Collection methods
93
+ getCreateRecordCollectionRef(docRef, model) {
94
+ return this.documentService.getCreateRecordCollectionRef(docRef, model);
95
+ }
96
+ // FIXME: confusing vs createRecordRef
97
+ getRecord(collection, id) {
98
+ return this.documentService.getRecord(collection, id);
99
+ }
100
+ hasRecord(collection, id) {
101
+ return this.documentService.hasRecord(collection, id);
102
+ }
103
+ async setCollectionRecord(collection, id, state) {
104
+ return this.documentService.setCollectionRecord(collection, id, state);
105
+ }
106
+ getCollectionSize(collection) {
107
+ return this.documentService.getCollectionSize(collection);
108
+ }
109
+ getCollectionRecords(collection) {
110
+ return this.documentService.getCollectionRecords(collection);
111
+ }
112
+ onRecordChanged(record, callback) {
113
+ return this.documentService.onRecordChanged(record, callback);
114
+ }
115
+ onRecordDeleted(record, callback) {
116
+ return this.documentService.onRecordDeleted(record, callback);
117
+ }
118
+ onCollectionItemsAdded(collection, callback) {
119
+ return this.documentService.onCollectionItemsAdded(collection, callback);
120
+ }
121
+ onCollectionItemsChanged(collection, callback) {
122
+ return this.documentService.onCollectionItemsChanged(collection, callback);
123
+ }
124
+ onCollectionItemsDeleted(collection, callback) {
125
+ return this.documentService.onCollectionItemsDeleted(collection, callback);
126
+ }
127
+ // Status methods implementation
128
+ getDocumentStatus(docRef) {
129
+ return this.documentService.getDocumentStatus(docRef);
130
+ }
131
+ onStatusChange(docRef, callback) {
132
+ return this.documentService.onStatusChange(docRef, callback);
133
+ }
134
+ async waitForMetadataLoad(docRef) {
135
+ return this.documentService.waitForMetadataLoad(docRef);
136
+ }
137
+ async waitForDataLoad(docRef) {
138
+ return this.documentService.waitForDataLoad(docRef);
139
+ }
140
+ async deleteRecord(record) {
141
+ return this.documentService.deleteRecord(record);
142
+ }
143
+ };
144
+ function getStateModule(app) {
145
+ pack_core.assertIsAppInternal(app);
146
+ return app.getModule(STATE_MODULE_KEY);
147
+ }
148
+
149
+ // src/types/DocumentRefImpl.ts
150
+ var INVALID_DOC_REF_ID = "INVALID_DOC_REF";
151
+ var INVALID_DOC_REF = Object.freeze({
152
+ id: INVALID_DOC_REF_ID,
153
+ schema: {},
154
+ [pack_documentSchema_modelTypes.DocumentRefBrand]: pack_documentSchema_modelTypes.DocumentRefBrand,
155
+ getDocSnapshot: () => Promise.reject(new Error("Invalid document reference")),
156
+ getRecords: () => {
157
+ throw new Error("Invalid document reference");
158
+ },
159
+ onMetadataChange: () => () => {
160
+ },
161
+ onStateChange: () => () => {
162
+ }
163
+ });
164
+ var createDocRef = (app, id, schema) => {
165
+ return new DocumentRefImpl(app, id, schema);
166
+ };
167
+ function invalidDocRef() {
168
+ return INVALID_DOC_REF;
169
+ }
170
+ function isValidDocRef(docRef) {
171
+ return docRef.id !== INVALID_DOC_REF_ID && docRef.id !== "";
172
+ }
173
+ var DocumentRefImpl = class {
174
+ id;
175
+ schema;
176
+ #stateModule;
177
+ constructor(app, id, schema) {
178
+ this.#stateModule = getStateModule(app);
179
+ this.id = id;
180
+ this.schema = schema;
181
+ }
182
+ async getDocSnapshot() {
183
+ return this.#stateModule.getDocumentSnapshot(this);
184
+ }
185
+ getRecords(model) {
186
+ return this.#stateModule.getCreateRecordCollectionRef(this, model);
187
+ }
188
+ onMetadataChange(cb) {
189
+ return this.#stateModule.onMetadataChange(this, cb);
190
+ }
191
+ onStateChange(callback) {
192
+ return this.#stateModule.onStateChange(this, callback);
193
+ }
194
+ };
195
+
196
+ // src/types/DocumentService.ts
197
+ var DocumentLoadStatus = {
198
+ UNLOADED: "unloaded",
199
+ // Not yet loaded
200
+ LOADING: "loading",
201
+ // Initial load in progress
202
+ LOADED: "loaded",
203
+ // Successfully loaded
204
+ ERROR: "error"
205
+ // Load failed
206
+ };
207
+ var DocumentLiveStatus = {
208
+ DISCONNECTED: "disconnected",
209
+ // Not syncing
210
+ CONNECTING: "connecting",
211
+ // Establishing connection
212
+ CONNECTED: "connected",
213
+ // Live syncing active
214
+ ERROR: "error"
215
+ // Connection error
216
+ };
217
+ var INVALID_RECORD_COLLECTION_REF = Object.freeze({
218
+ docRef: invalidDocRef(),
219
+ model: {},
220
+ [pack_documentSchema_modelTypes.RecordCollectionRefBrand]: pack_documentSchema_modelTypes.RecordCollectionRefBrand,
221
+ get: () => void 0,
222
+ has: () => false,
223
+ set: () => Promise.reject(new Error("Invalid record collection reference")),
224
+ delete: () => Promise.reject(new Error("Invalid record collection reference")),
225
+ size: 0,
226
+ [Symbol.iterator]: () => ({
227
+ next: () => ({
228
+ done: true,
229
+ value: void 0
230
+ })
231
+ }),
232
+ onItemsAdded: () => () => {
233
+ },
234
+ onItemsChanged: () => () => {
235
+ },
236
+ onItemsDeleted: () => () => {
237
+ }
238
+ });
239
+ var createRecordCollectionRef = (documentService, docRef, model) => {
240
+ return new RecordCollectionRefImpl(documentService, docRef, model);
241
+ };
242
+ function invalidRecordCollectionRef() {
243
+ return INVALID_RECORD_COLLECTION_REF;
244
+ }
245
+ function isValidRecordCollectionRef(collectionRef) {
246
+ return collectionRef !== INVALID_RECORD_COLLECTION_REF;
247
+ }
248
+ var RecordCollectionRefImpl = class {
249
+ docRef;
250
+ model;
251
+ #documentService;
252
+ constructor(documentService, docRef, model) {
253
+ this.docRef = docRef;
254
+ this.model = model;
255
+ this.#documentService = documentService;
256
+ }
257
+ get(id) {
258
+ return this.#documentService.getRecord(this, id);
259
+ }
260
+ has(id) {
261
+ return this.#documentService.hasRecord(this, id);
262
+ }
263
+ async set(id, state) {
264
+ return this.#documentService.setCollectionRecord(this, id, state);
265
+ }
266
+ delete(id) {
267
+ const recordRefInstance = this.#documentService.getRecord(this, id);
268
+ if (recordRefInstance) {
269
+ return this.#documentService.deleteRecord(recordRefInstance);
270
+ }
271
+ return Promise.reject(new Error(`Unknown record`));
272
+ }
273
+ get size() {
274
+ return this.#documentService.getCollectionSize(this);
275
+ }
276
+ [Symbol.iterator]() {
277
+ return this.#documentService.getCollectionRecords(this)[Symbol.iterator]();
278
+ }
279
+ onItemsAdded = (callback) => {
280
+ return this.#documentService.onCollectionItemsAdded(this, callback);
281
+ };
282
+ onItemsChanged = (callback) => {
283
+ return this.#documentService.onCollectionItemsChanged(this, callback);
284
+ };
285
+ onItemsDeleted = (callback) => {
286
+ return this.#documentService.onCollectionItemsDeleted(this, callback);
287
+ };
288
+ };
289
+ var INVALID_RECORD_ID = "INVALID_RECORD_REF";
290
+ var INVALID_RECORD_REF = Object.freeze({
291
+ docRef: invalidDocRef(),
292
+ id: INVALID_RECORD_ID,
293
+ model: {},
294
+ [pack_documentSchema_modelTypes.RecordRefBrand]: pack_documentSchema_modelTypes.RecordRefBrand,
295
+ getSnapshot: () => Promise.reject(new Error("Invalid record reference")),
296
+ set: () => Promise.reject(new Error("Invalid record reference")),
297
+ onChange: () => () => {
298
+ },
299
+ onDeleted: () => () => {
300
+ }
301
+ });
302
+ var createRecordRef = (documentService, docRef, id, model) => {
303
+ return new RecordRefImpl(documentService, docRef, id, model);
304
+ };
305
+ function invalidRecordRef() {
306
+ return INVALID_RECORD_REF;
307
+ }
308
+ function isValidRecordRef(recordRef) {
309
+ return recordRef.id !== INVALID_RECORD_ID;
310
+ }
311
+ var RecordRefImpl = class {
312
+ docRef;
313
+ id;
314
+ model;
315
+ #documentService;
316
+ constructor(documentService, docRef, id, model) {
317
+ this.#documentService = documentService;
318
+ this.docRef = docRef;
319
+ this.id = id;
320
+ this.model = model;
321
+ }
322
+ async getSnapshot() {
323
+ return this.#documentService.getRecordSnapshot(this);
324
+ }
325
+ onChange(callback) {
326
+ return this.#documentService.onRecordChanged(this, callback);
327
+ }
328
+ onDeleted(callback) {
329
+ return this.#documentService.onRecordDeleted(this, callback);
330
+ }
331
+ delete() {
332
+ return this.#documentService.deleteRecord(this);
333
+ }
334
+ set(record) {
335
+ return this.#documentService.setRecord(this, record);
336
+ }
337
+ update(partialRecord) {
338
+ return this.#documentService.updateRecord(this, partialRecord);
339
+ }
340
+ };
341
+ function initializeDocumentStructure(yDoc, schema) {
342
+ Object.values(schema).forEach((modelEntry) => {
343
+ yDoc.getMap(pack_documentSchema_modelTypes.getMetadata(modelEntry).name);
344
+ });
345
+ }
346
+ function getRecordsMap(yDoc, storageName) {
347
+ return yDoc.getMap(storageName);
348
+ }
349
+ function getRecordData(yDoc, storageName, recordId) {
350
+ const recordsCollection = getRecordsMap(yDoc, storageName);
351
+ return recordsCollection.get(recordId);
352
+ }
353
+ function setRecord(yDoc, storageName, recordId, state) {
354
+ const recordsCollection = getRecordsMap(yDoc, storageName);
355
+ const currentRecord = recordsCollection.get(recordId);
356
+ const wasExisting = currentRecord != null;
357
+ yDoc.transact(() => {
358
+ if (currentRecord != null) {
359
+ currentRecord.clear();
360
+ populateYMapFromState(currentRecord, state);
361
+ } else {
362
+ const newRecord = new Y__namespace.Map();
363
+ populateYMapFromState(newRecord, state);
364
+ recordsCollection.set(recordId, newRecord);
365
+ }
366
+ });
367
+ return wasExisting;
368
+ }
369
+ function getRecordSnapshot(yDoc, storageName, recordId) {
370
+ const data = getRecordData(yDoc, storageName, recordId);
371
+ if (!data) {
372
+ return void 0;
373
+ }
374
+ return yMapToState(data);
375
+ }
376
+ function updateRecord(yDoc, storageName, recordId, partialState) {
377
+ const recordsCollection = getRecordsMap(yDoc, storageName);
378
+ const currentRecord = recordsCollection.get(recordId);
379
+ if (currentRecord == null) {
380
+ return false;
381
+ }
382
+ yDoc.transact(() => {
383
+ updateYMapFromPartialState(currentRecord, partialState);
384
+ });
385
+ return true;
386
+ }
387
+ function getAllRecordIds(yDoc, storageName) {
388
+ const recordsCollection = getRecordsMap(yDoc, storageName);
389
+ return Array.from(recordsCollection.keys()).map((key) => key);
390
+ }
391
+ function populateYMapFromState(yMap, state) {
392
+ if (state != null && typeof state === "object") {
393
+ Object.entries(state).forEach(([key, value]) => {
394
+ if (value === void 0) return;
395
+ if (Array.isArray(value)) {
396
+ const yArray = new Y__namespace.Array();
397
+ value.forEach((item) => {
398
+ yArray.push([item]);
399
+ });
400
+ yMap.set(key, yArray);
401
+ } else if (typeof value === "object" && value != null) {
402
+ const nestedMap = new Y__namespace.Map();
403
+ populateYMapFromState(nestedMap, value);
404
+ yMap.set(key, nestedMap);
405
+ } else {
406
+ yMap.set(key, value);
407
+ }
408
+ });
409
+ }
410
+ }
411
+ function updateYMapFromPartialState(yMap, partialState) {
412
+ if (typeof partialState === "object") {
413
+ Object.entries(partialState).forEach(([key, value]) => {
414
+ if (value === void 0) {
415
+ yMap.delete(key);
416
+ return;
417
+ }
418
+ if (Array.isArray(value)) {
419
+ const yArray = new Y__namespace.Array();
420
+ value.forEach((item) => {
421
+ yArray.push([item]);
422
+ });
423
+ yMap.set(key, yArray);
424
+ } else if (typeof value === "object" && value != null) {
425
+ const existingValue = yMap.get(key);
426
+ if (existingValue instanceof Y__namespace.Map) {
427
+ updateYMapFromPartialState(existingValue, value);
428
+ } else {
429
+ const nestedMap = new Y__namespace.Map();
430
+ populateYMapFromState(nestedMap, value);
431
+ yMap.set(key, nestedMap);
432
+ }
433
+ } else {
434
+ yMap.set(key, value);
435
+ }
436
+ });
437
+ }
438
+ }
439
+ function yMapToState(yMap) {
440
+ const state = {};
441
+ yMap.forEach((value, key) => {
442
+ if (value instanceof Y__namespace.Array) {
443
+ state[key] = value.toArray();
444
+ } else if (value instanceof Y__namespace.Map) {
445
+ state[key] = yMapToState(value);
446
+ } else {
447
+ state[key] = value;
448
+ }
449
+ });
450
+ return state;
451
+ }
452
+
453
+ // src/service/BaseYjsDocumentService.ts
454
+ var BaseYjsDocumentService = class {
455
+ documents = /* @__PURE__ */ new Map();
456
+ constructor(app, logger) {
457
+ this.app = app;
458
+ this.logger = logger;
459
+ }
460
+ createDocRef = (id, schema) => {
461
+ const temporaryRef = createDocRef(this.app, id, schema);
462
+ const {
463
+ internalDocRef
464
+ } = this.getCreateInternalDoc(temporaryRef);
465
+ return internalDocRef;
466
+ };
467
+ getCreateRecordCollectionRef = (docRef, model) => {
468
+ const {
469
+ internalDoc
470
+ } = this.getCreateInternalDoc(docRef);
471
+ const modelName = pack_documentSchema_modelTypes.getMetadata(model).name;
472
+ const existingRef = internalDoc.collectionRefs.get(modelName)?.deref();
473
+ if (existingRef != null) {
474
+ return existingRef;
475
+ }
476
+ const newRef = createRecordCollectionRef(this, docRef, model);
477
+ internalDoc.collectionRefs.set(modelName, new WeakRef(newRef));
478
+ return newRef;
479
+ };
480
+ getCreateRecordRef = (docRef, id, model) => {
481
+ const {
482
+ internalDoc
483
+ } = this.getCreateInternalDoc(docRef);
484
+ const modelName = pack_documentSchema_modelTypes.getMetadata(model).name;
485
+ let modelMap = internalDoc.recordRefs.get(modelName);
486
+ if (!modelMap) {
487
+ modelMap = /* @__PURE__ */ new Map();
488
+ internalDoc.recordRefs.set(modelName, modelMap);
489
+ }
490
+ const existingRef = modelMap.get(id)?.deref();
491
+ if (existingRef != null) {
492
+ return existingRef;
493
+ }
494
+ const newRef = createRecordRef(this, docRef, id, model);
495
+ modelMap.set(id, new WeakRef(newRef));
496
+ return newRef;
497
+ };
498
+ /**
499
+ * Called when the first metadata subscription is opened for a document.
500
+ * Implementation must:
501
+ * - Set status to LOADING immediately
502
+ * - Load/validate metadata asynchronously
503
+ * - Set status to LOADED or ERROR when complete
504
+ * - Handle all errors internally (never throw/reject)
505
+ */
506
+ /**
507
+ * Called when the first data subscription is opened for a document.
508
+ * Implementation must:
509
+ * - Set status to LOADING immediately
510
+ * - Set up data synchronization asynchronously
511
+ * - Set status to LOADED or ERROR when ready
512
+ * - Handle all errors internally (never throw/reject)
513
+ */
514
+ /**
515
+ * Called when the last metadata subscription is closed for a document.
516
+ * Implementation should clean up any resources related to metadata loading.
517
+ */
518
+ /**
519
+ * Called when the last data subscription is closed for a document.
520
+ * Implementation should clean up any resources related to data synchronization.
521
+ */
522
+ createBaseInternalDoc = (ref, metadata, yDoc) => {
523
+ const schema = ref.schema;
524
+ return {
525
+ ref: new WeakRef(ref),
526
+ metadata,
527
+ schema,
528
+ metadataStatus: {
529
+ load: metadata ? DocumentLoadStatus.LOADED : DocumentLoadStatus.UNLOADED,
530
+ live: DocumentLiveStatus.DISCONNECTED
531
+ },
532
+ dataStatus: {
533
+ load: DocumentLoadStatus.UNLOADED,
534
+ live: DocumentLiveStatus.DISCONNECTED
535
+ },
536
+ metadataError: void 0,
537
+ dataError: void 0,
538
+ statusSubscribers: /* @__PURE__ */ new Set(),
539
+ hasMetadataSubscriptions: false,
540
+ hasDataSubscriptions: false,
541
+ collectionRefs: /* @__PURE__ */ new Map(),
542
+ recordRefs: /* @__PURE__ */ new Map(),
543
+ collectionSubscriptions: /* @__PURE__ */ new Map(),
544
+ docStateSubscribers: /* @__PURE__ */ new Set(),
545
+ metadataSubscribers: /* @__PURE__ */ new Set(),
546
+ recordSubscriptions: /* @__PURE__ */ new Map(),
547
+ yDoc: yDoc || this.initializeYDoc(schema),
548
+ yDocUpdateHandler: void 0,
549
+ yjsCollectionHandlers: /* @__PURE__ */ new Map()
550
+ };
551
+ };
552
+ hasSubscriptions(internalDoc) {
553
+ if (internalDoc.metadataSubscribers.size > 0 || internalDoc.docStateSubscribers.size > 0) {
554
+ return true;
555
+ }
556
+ for (const subs of internalDoc.recordSubscriptions.values()) {
557
+ if (!subs.changed?.size || !subs.deleted?.size) {
558
+ return true;
559
+ }
560
+ }
561
+ return false;
562
+ }
563
+ // Status helper methods
564
+ notifyStatusSubscribers(internalDoc, docRef) {
565
+ const status = {
566
+ metadata: internalDoc.metadataStatus,
567
+ data: internalDoc.dataStatus,
568
+ metadataError: internalDoc.metadataError,
569
+ dataError: internalDoc.dataError
570
+ };
571
+ for (const callback of internalDoc.statusSubscribers) {
572
+ callback(docRef, status);
573
+ }
574
+ }
575
+ updateMetadataStatus(internalDoc, docRef, update) {
576
+ if (update.load != null || update.live != null) {
577
+ internalDoc.metadataStatus = {
578
+ load: update.load ?? internalDoc.metadataStatus.load,
579
+ live: update.live ?? internalDoc.metadataStatus.live
580
+ };
581
+ }
582
+ if (update.error != null) {
583
+ internalDoc.metadataError = update.error;
584
+ } else if (update.load === DocumentLoadStatus.LOADED) {
585
+ internalDoc.metadataError = void 0;
586
+ }
587
+ this.notifyStatusSubscribers(internalDoc, docRef);
588
+ }
589
+ updateDataStatus(internalDoc, docRef, update) {
590
+ if (update.load != null || update.live != null) {
591
+ internalDoc.dataStatus = {
592
+ load: update.load ?? internalDoc.dataStatus.load,
593
+ live: update.live ?? internalDoc.dataStatus.live
594
+ };
595
+ }
596
+ if (update.error != null) {
597
+ internalDoc.dataError = update.error;
598
+ } else if (update.load === DocumentLoadStatus.LOADED) {
599
+ internalDoc.dataError = void 0;
600
+ }
601
+ this.notifyStatusSubscribers(internalDoc, docRef);
602
+ }
603
+ /**
604
+ * Hook method called after a record is set. Subclasses can override to handle
605
+ * backend synchronization, logging, or other side effects.
606
+ */
607
+ /**
608
+ * Initialize a Y.Doc with the given schema
609
+ */
610
+ initializeYDoc(schema) {
611
+ const yDoc = new Y__namespace.Doc();
612
+ initializeDocumentStructure(yDoc, schema);
613
+ return yDoc;
614
+ }
615
+ /**
616
+ * Get existing internal doc or create one with placeholder metadata for lazy initialization
617
+ */
618
+ getCreateInternalDoc(ref, metadata, initialYDoc) {
619
+ const {
620
+ id,
621
+ schema
622
+ } = ref;
623
+ const existingDoc = this.documents.get(id);
624
+ if (existingDoc != null) {
625
+ !(existingDoc.schema === schema || remeda.isDeepEqual(existingDoc.schema, schema)) ? process.env.NODE_ENV !== "production" ? invariant__default.default(false, "Schema mismatch for existing document") : invariant__default.default(false) : void 0;
626
+ const existingRef = existingDoc.ref.deref();
627
+ if (existingRef == null) {
628
+ existingDoc.ref = new WeakRef(ref);
629
+ }
630
+ return {
631
+ internalDocRef: existingRef ?? ref,
632
+ internalDoc: existingDoc,
633
+ wasExisting: true
634
+ };
635
+ }
636
+ const internalDoc = this.createInternalDoc(ref, metadata, initialYDoc);
637
+ this.documents.set(id, internalDoc);
638
+ return {
639
+ internalDocRef: ref,
640
+ internalDoc,
641
+ wasExisting: false
642
+ };
643
+ }
644
+ getDocumentSnapshot = (docRef) => {
645
+ const {
646
+ internalDoc
647
+ } = this.getCreateInternalDoc(docRef);
648
+ return Promise.resolve(internalDoc.yDoc);
649
+ };
650
+ getRecordSnapshot = (recordRef) => {
651
+ const {
652
+ internalDoc
653
+ } = this.getCreateInternalDoc(recordRef.docRef);
654
+ const snapshot = this.getRecordSnapshotInternal(internalDoc, recordRef);
655
+ if (snapshot == null) {
656
+ return Promise.reject(new Error(`Record not found: ${recordRef.id}`));
657
+ }
658
+ return Promise.resolve(snapshot);
659
+ };
660
+ getRecordSnapshotInternal(internalDoc, recordRef) {
661
+ return getRecordSnapshot(internalDoc.yDoc, pack_documentSchema_modelTypes.getMetadata(recordRef.model).name, recordRef.id);
662
+ }
663
+ setRecord = (recordRef, state) => {
664
+ const internalDoc = this.documents.get(recordRef.docRef.id);
665
+ !(internalDoc != null) ? process.env.NODE_ENV !== "production" ? invariant__default.default(false, `Cannot set record as document not found: ${recordRef.docRef.id}`) : invariant__default.default(false) : void 0;
666
+ setRecord(internalDoc.yDoc, pack_documentSchema_modelTypes.getMetadata(recordRef.model).name, recordRef.id, state);
667
+ this.onRecordSet?.(recordRef, state);
668
+ return Promise.resolve();
669
+ };
670
+ updateRecord = (recordRef, partialState) => {
671
+ const internalDoc = this.documents.get(recordRef.docRef.id);
672
+ !(internalDoc != null) ? process.env.NODE_ENV !== "production" ? invariant__default.default(false, `Cannot update record as document not found: ${recordRef.docRef.id}`) : invariant__default.default(false) : void 0;
673
+ const wasUpdated = updateRecord(internalDoc.yDoc, pack_documentSchema_modelTypes.getMetadata(recordRef.model).name, recordRef.id, partialState);
674
+ if (!wasUpdated) {
675
+ return Promise.reject(new Error(`Record not found for update: ${recordRef.id}`));
676
+ }
677
+ this.onRecordSet?.(recordRef, partialState);
678
+ return Promise.resolve();
679
+ };
680
+ onMetadataChange(docRef, callback) {
681
+ const {
682
+ internalDoc,
683
+ internalDocRef
684
+ } = this.getCreateInternalDoc(docRef);
685
+ const isFirstSubscription = !internalDoc.hasMetadataSubscriptions;
686
+ internalDoc.metadataSubscribers.add(callback);
687
+ internalDoc.hasMetadataSubscriptions = true;
688
+ if (isFirstSubscription && internalDoc.metadataStatus.load === DocumentLoadStatus.UNLOADED) {
689
+ this.onMetadataSubscriptionOpened(internalDoc, internalDocRef);
690
+ }
691
+ if (internalDoc.metadata != null) {
692
+ callback(docRef, internalDoc.metadata);
693
+ }
694
+ return () => {
695
+ const currentDoc = this.documents.get(docRef.id);
696
+ if (currentDoc) {
697
+ currentDoc.metadataSubscribers.delete(callback);
698
+ if (currentDoc.metadataSubscribers.size === 0) {
699
+ currentDoc.hasMetadataSubscriptions = false;
700
+ this.onMetadataSubscriptionClosed(currentDoc, internalDocRef);
701
+ }
702
+ }
703
+ };
704
+ }
705
+ onStateChange = (docRef, callback) => {
706
+ const {
707
+ internalDoc,
708
+ internalDocRef
709
+ } = this.getCreateInternalDoc(docRef);
710
+ const isFirstDataSubscription = !internalDoc.hasDataSubscriptions;
711
+ const isFirstStateSubscription = internalDoc.docStateSubscribers.size === 0;
712
+ internalDoc.docStateSubscribers.add(callback);
713
+ internalDoc.hasDataSubscriptions = true;
714
+ if (isFirstDataSubscription && internalDoc.dataStatus.load === DocumentLoadStatus.UNLOADED) {
715
+ this.onDataSubscriptionOpened(internalDoc, internalDocRef);
716
+ }
717
+ if (isFirstStateSubscription && !internalDoc.yDocUpdateHandler) {
718
+ const updateHandler = () => {
719
+ this.notifyStateSubscribers(internalDoc, docRef);
720
+ };
721
+ internalDoc.yDoc.on("update", updateHandler);
722
+ internalDoc.yDocUpdateHandler = updateHandler;
723
+ }
724
+ callback(internalDocRef);
725
+ return () => {
726
+ const currentDoc = this.documents.get(docRef.id);
727
+ if (!currentDoc) return;
728
+ currentDoc.docStateSubscribers.delete(callback);
729
+ if (currentDoc.docStateSubscribers.size === 0 && currentDoc.yDocUpdateHandler) {
730
+ currentDoc.yDoc.off("update", currentDoc.yDocUpdateHandler);
731
+ currentDoc.yDocUpdateHandler = void 0;
732
+ }
733
+ const hasDataSubs = currentDoc.docStateSubscribers.size > 0 || currentDoc.recordSubscriptions.size > 0 || Array.from(currentDoc.collectionSubscriptions.values()).some((subs) => subs.added?.size || subs.changed?.size || subs.deleted?.size);
734
+ if (!hasDataSubs) {
735
+ currentDoc.hasDataSubscriptions = false;
736
+ this.onDataSubscriptionClosed(currentDoc, internalDocRef);
737
+ }
738
+ };
739
+ };
740
+ getDocumentRef(docId) {
741
+ const internalDoc = this.documents.get(docId);
742
+ if (!internalDoc) return null;
743
+ return createDocRef(this.app, docId, internalDoc.schema);
744
+ }
745
+ notifyMetadataSubscribers(internalDoc, docRef, metadata) {
746
+ for (const callback of internalDoc.metadataSubscribers) {
747
+ callback(docRef, metadata);
748
+ }
749
+ }
750
+ notifyStateSubscribers(internalDoc, docRef) {
751
+ for (const callback of internalDoc.docStateSubscribers) {
752
+ callback(docRef);
753
+ }
754
+ }
755
+ updateMetadata(docId, metadata) {
756
+ const internalDoc = this.documents.get(docId);
757
+ if (internalDoc) {
758
+ internalDoc.metadata = metadata;
759
+ const docRef = this.getDocumentRef(docId);
760
+ if (docRef) {
761
+ this.notifyMetadataSubscribers(internalDoc, docRef, metadata);
762
+ }
763
+ }
764
+ }
765
+ notifyCollectionSubscribers(internalDoc, collection, recordId, changeType) {
766
+ const storageName = pack_documentSchema_modelTypes.getMetadata(collection.model).name;
767
+ const subs = internalDoc.collectionSubscriptions.get(storageName);
768
+ if (!subs) {
769
+ return;
770
+ }
771
+ const subscribers = subs[changeType];
772
+ if (subscribers == null || subscribers.size === 0) {
773
+ return;
774
+ }
775
+ const recordRefInstance = this.getCreateRecordRef(collection.docRef, recordId, collection.model);
776
+ const records = [recordRefInstance];
777
+ for (const callback of subscribers) {
778
+ callback(records);
779
+ }
780
+ }
781
+ notifyRecordSubscribers(recordRef, changeType) {
782
+ const internalDoc = this.documents.get(recordRef.docRef.id);
783
+ !(internalDoc != null) ? process.env.NODE_ENV !== "production" ? invariant__default.default(false, "Document not found for record notifications") : invariant__default.default(false) : void 0;
784
+ const recordSubs = internalDoc.recordSubscriptions.get(recordRef.id);
785
+ if (recordSubs == null) {
786
+ return;
787
+ }
788
+ !(pack_documentSchema_modelTypes.getMetadata(recordSubs.ref.model).name === pack_documentSchema_modelTypes.getMetadata(recordRef.model).name) ? process.env.NODE_ENV !== "production" ? invariant__default.default(false, `Model mismatch when notifying record subscribers for ${recordRef.id}: expected ${pack_documentSchema_modelTypes.getMetadata(recordSubs.ref.model).name}, got ${pack_documentSchema_modelTypes.getMetadata(recordRef.model).name}`) : invariant__default.default(false) : void 0;
789
+ switch (changeType) {
790
+ case "changed": {
791
+ const snapshot = this.getRecordSnapshotInternal(internalDoc, recordRef);
792
+ for (const callback of recordSubs.changed ?? []) {
793
+ try {
794
+ callback(snapshot, recordRef);
795
+ } catch (e) {
796
+ console.error("Record onChanged callback threw unhandled error", e, {
797
+ model: pack_documentSchema_modelTypes.getMetadata(recordRef.model).name,
798
+ id: recordRef.id
799
+ });
800
+ }
801
+ }
802
+ break;
803
+ }
804
+ case "deleted": {
805
+ for (const callback of recordSubs.deleted ?? []) {
806
+ try {
807
+ callback(recordRef);
808
+ } catch (e) {
809
+ console.error("Record onDeleted callback threw unhandled error", e, {
810
+ model: pack_documentSchema_modelTypes.getMetadata(recordRef.model).name,
811
+ id: recordRef.id
812
+ });
813
+ }
814
+ }
815
+ break;
816
+ }
817
+ }
818
+ }
819
+ // Collection methods implementation
820
+ getRecord = (collection, id) => {
821
+ const internalDoc = this.documents.get(collection.docRef.id);
822
+ if (!internalDoc) return void 0;
823
+ const storageName = pack_documentSchema_modelTypes.getMetadata(collection.model).name;
824
+ const recordExists = getRecordData(internalDoc.yDoc, storageName, id);
825
+ return recordExists ? this.getCreateRecordRef(collection.docRef, id, collection.model) : void 0;
826
+ };
827
+ hasRecord = (collection, id) => {
828
+ const internalDoc = this.documents.get(collection.docRef.id);
829
+ if (!internalDoc) return false;
830
+ const storageName = pack_documentSchema_modelTypes.getMetadata(collection.model).name;
831
+ return getRecordData(internalDoc.yDoc, storageName, id) != null;
832
+ };
833
+ setCollectionRecord = (collection, id, state) => {
834
+ const recordRefInstance = this.getCreateRecordRef(collection.docRef, id, collection.model);
835
+ return this.setRecord(recordRefInstance, state);
836
+ };
837
+ deleteRecord = (record) => {
838
+ const internalDoc = this.documents.get(record.docRef.id);
839
+ if (!internalDoc) {
840
+ return Promise.resolve();
841
+ }
842
+ const storageName = pack_documentSchema_modelTypes.getMetadata(record.model).name;
843
+ const recordsCollection = getRecordsMap(internalDoc.yDoc, storageName);
844
+ const existed = recordsCollection.has(record.id);
845
+ if (existed) {
846
+ recordsCollection.delete(record.id);
847
+ }
848
+ return Promise.resolve();
849
+ };
850
+ getCollectionSize = (collection) => {
851
+ const internalDoc = this.documents.get(collection.docRef.id);
852
+ if (!internalDoc) return 0;
853
+ const storageName = pack_documentSchema_modelTypes.getMetadata(collection.model).name;
854
+ return getAllRecordIds(internalDoc.yDoc, storageName).length;
855
+ };
856
+ getCollectionRecords = (collection) => {
857
+ const internalDoc = this.documents.get(collection.docRef.id);
858
+ if (!internalDoc) return [];
859
+ const storageName = pack_documentSchema_modelTypes.getMetadata(collection.model).name;
860
+ const recordIds = getAllRecordIds(internalDoc.yDoc, storageName);
861
+ return recordIds.map((id) => this.getCreateRecordRef(collection.docRef, id, collection.model));
862
+ };
863
+ onCollectionItemsAdded = (collection, callback) => {
864
+ const {
865
+ internalDoc,
866
+ internalDocRef
867
+ } = this.getCreateInternalDoc(collection.docRef);
868
+ return this.subscribeToCollectionChanges(internalDoc, internalDocRef, collection, "added", callback);
869
+ };
870
+ onCollectionItemsChanged = (collection, callback) => {
871
+ const {
872
+ internalDoc,
873
+ internalDocRef
874
+ } = this.getCreateInternalDoc(collection.docRef);
875
+ return this.subscribeToCollectionChanges(internalDoc, internalDocRef, collection, "changed", callback);
876
+ };
877
+ onCollectionItemsDeleted = (collection, callback) => {
878
+ const {
879
+ internalDoc,
880
+ internalDocRef
881
+ } = this.getCreateInternalDoc(collection.docRef);
882
+ return this.subscribeToCollectionChanges(internalDoc, internalDocRef, collection, "deleted", callback);
883
+ };
884
+ // TODO: clearer naming of subscription vs handlers etc.
885
+ onRecordChanged = (record, callback) => {
886
+ const {
887
+ internalDoc,
888
+ internalDocRef
889
+ } = this.getCreateInternalDoc(record.docRef);
890
+ const isFirstDataSubscription = !internalDoc.hasDataSubscriptions;
891
+ const storageName = pack_documentSchema_modelTypes.getMetadata(record.model).name;
892
+ const collectionRef = this.getCreateRecordCollectionRef(record.docRef, record.model);
893
+ const needsCollectionListener = !internalDoc.yjsCollectionHandlers.has(storageName);
894
+ const recordSubs = this.getCreateRecordSubscriptions(internalDoc, record);
895
+ (recordSubs.changed ??= /* @__PURE__ */ new Set()).add(callback);
896
+ internalDoc.hasDataSubscriptions = true;
897
+ if (isFirstDataSubscription && internalDoc.dataStatus.load === DocumentLoadStatus.UNLOADED) {
898
+ this.onDataSubscriptionOpened(internalDoc, internalDocRef);
899
+ }
900
+ if (needsCollectionListener) {
901
+ this.getCreateCollectionSubscriptions(internalDoc, collectionRef);
902
+ this.setupCollectionListener(internalDoc, collectionRef);
903
+ }
904
+ const snapshot = this.getRecordSnapshotInternal(internalDoc, record);
905
+ if (snapshot != null) {
906
+ callback(snapshot, record);
907
+ }
908
+ return () => {
909
+ recordSubs.changed?.delete(callback);
910
+ const currentDoc = this.documents.get(record.docRef.id);
911
+ if (!currentDoc) return;
912
+ if (isRecordSubscriptionsEmpty(recordSubs)) {
913
+ currentDoc.recordSubscriptions.delete(record.id);
914
+ }
915
+ this.cleanupCollectionListenerIfUnused(currentDoc, record.docRef.id, storageName);
916
+ const hasDataSubs = currentDoc.docStateSubscribers.size > 0 || currentDoc.recordSubscriptions.size > 0 || Array.from(currentDoc.collectionSubscriptions.values()).some((subs) => subs.added?.size || subs.changed?.size || subs.deleted?.size);
917
+ if (!hasDataSubs) {
918
+ currentDoc.hasDataSubscriptions = false;
919
+ this.onDataSubscriptionClosed(currentDoc, internalDocRef);
920
+ }
921
+ };
922
+ };
923
+ onRecordDeleted = (record, callback) => {
924
+ const {
925
+ internalDoc,
926
+ internalDocRef
927
+ } = this.getCreateInternalDoc(record.docRef);
928
+ const isFirstDataSubscription = !internalDoc.hasDataSubscriptions;
929
+ const storageName = pack_documentSchema_modelTypes.getMetadata(record.model).name;
930
+ const collectionRef = this.getCreateRecordCollectionRef(record.docRef, record.model);
931
+ const needsCollectionListener = !internalDoc.yjsCollectionHandlers.has(storageName);
932
+ const recordSubs = this.getCreateRecordSubscriptions(internalDoc, record);
933
+ (recordSubs.deleted ??= /* @__PURE__ */ new Set()).add(callback);
934
+ internalDoc.hasDataSubscriptions = true;
935
+ if (isFirstDataSubscription && internalDoc.dataStatus.load === DocumentLoadStatus.UNLOADED) {
936
+ this.onDataSubscriptionOpened(internalDoc, internalDocRef);
937
+ }
938
+ if (needsCollectionListener) {
939
+ this.getCreateCollectionSubscriptions(internalDoc, collectionRef);
940
+ this.setupCollectionListener(internalDoc, collectionRef);
941
+ }
942
+ return () => {
943
+ recordSubs.deleted?.delete(callback);
944
+ const currentDoc = this.documents.get(record.docRef.id);
945
+ if (!currentDoc) return;
946
+ if (isRecordSubscriptionsEmpty(recordSubs)) {
947
+ currentDoc.recordSubscriptions.delete(record.id);
948
+ }
949
+ this.cleanupCollectionListenerIfUnused(currentDoc, record.docRef.id, storageName);
950
+ const hasDataSubs = currentDoc.docStateSubscribers.size > 0 || currentDoc.recordSubscriptions.size > 0 || Array.from(currentDoc.collectionSubscriptions.values()).some((subs) => subs.added?.size || subs.changed?.size || subs.deleted?.size);
951
+ if (!hasDataSubs) {
952
+ currentDoc.hasDataSubscriptions = false;
953
+ this.onDataSubscriptionClosed(currentDoc, internalDocRef);
954
+ }
955
+ };
956
+ };
957
+ getCreateCollectionSubscriptions(internalDoc, collection) {
958
+ const storageName = pack_documentSchema_modelTypes.getMetadata(collection.model).name;
959
+ const docCollectionSubs = internalDoc.collectionSubscriptions.get(storageName) ?? internalDoc.collectionSubscriptions.set(storageName, {}).get(storageName);
960
+ return docCollectionSubs;
961
+ }
962
+ getCreateRecordSubscriptions(internalDoc, record) {
963
+ let recordSubs = internalDoc.recordSubscriptions.get(record.id);
964
+ if (!recordSubs) {
965
+ recordSubs = {
966
+ ref: record
967
+ };
968
+ internalDoc.recordSubscriptions.set(record.id, recordSubs);
969
+ } else {
970
+ if (pack_documentSchema_modelTypes.getMetadata(recordSubs.ref.model).name !== pack_documentSchema_modelTypes.getMetadata(record.model).name) {
971
+ throw new Error(`Model mismatch for record ${record.id}: expected ${pack_documentSchema_modelTypes.getMetadata(recordSubs.ref.model).name}, got ${pack_documentSchema_modelTypes.getMetadata(record.model).name}`);
972
+ }
973
+ }
974
+ return recordSubs;
975
+ }
976
+ subscribeToCollectionChanges(internalDoc, internalDocRef, collection, changeType, callback) {
977
+ const isFirstDataSubscription = !internalDoc.hasDataSubscriptions;
978
+ const modelSubscriptions = this.getCreateCollectionSubscriptions(internalDoc, collection);
979
+ const wasEmpty = isCollectionSubscriptionsEmpty(modelSubscriptions);
980
+ (modelSubscriptions[changeType] ??= /* @__PURE__ */ new Set()).add(callback);
981
+ internalDoc.hasDataSubscriptions = true;
982
+ if (isFirstDataSubscription && internalDoc.dataStatus.load === DocumentLoadStatus.UNLOADED) {
983
+ this.onDataSubscriptionOpened(internalDoc, internalDocRef);
984
+ }
985
+ if (wasEmpty) {
986
+ this.setupCollectionListener(internalDoc, collection);
987
+ }
988
+ return () => {
989
+ modelSubscriptions[changeType]?.delete(callback);
990
+ const currentDoc = this.documents.get(collection.docRef.id);
991
+ if (!currentDoc) return;
992
+ const storageName = pack_documentSchema_modelTypes.getMetadata(collection.model).name;
993
+ this.cleanupCollectionListenerIfUnused(currentDoc, collection.docRef.id, storageName);
994
+ const hasDataSubs = currentDoc.docStateSubscribers.size > 0 || currentDoc.recordSubscriptions.size > 0 || Array.from(currentDoc.collectionSubscriptions.values()).some((subs) => subs.added?.size || subs.changed?.size || subs.deleted?.size);
995
+ if (!hasDataSubs) {
996
+ currentDoc.hasDataSubscriptions = false;
997
+ this.onDataSubscriptionClosed(currentDoc, internalDocRef);
998
+ }
999
+ };
1000
+ }
1001
+ setupCollectionListener(internalDoc, collection) {
1002
+ const docId = collection.docRef.id;
1003
+ const storageName = pack_documentSchema_modelTypes.getMetadata(collection.model).name;
1004
+ const yCollection = internalDoc.yDoc.getMap(storageName);
1005
+ this.logger.debug("Setting up collection listener", {
1006
+ docId,
1007
+ storageName,
1008
+ existingKeys: Array.from(yCollection.keys()),
1009
+ allDocMaps: Array.from(internalDoc.yDoc.share.keys())
1010
+ });
1011
+ const eventHandler = (events) => {
1012
+ this.logger.debug("Y.Map observeDeep fired", {
1013
+ docId,
1014
+ storageName,
1015
+ eventCount: events.length
1016
+ });
1017
+ const currentDoc = this.documents.get(docId);
1018
+ if (!currentDoc) return;
1019
+ const subs = currentDoc.collectionSubscriptions.get(storageName);
1020
+ if (!subs) return;
1021
+ const addedKeys = /* @__PURE__ */ new Set();
1022
+ const changedKeys = /* @__PURE__ */ new Set();
1023
+ const deletedKeys = /* @__PURE__ */ new Set();
1024
+ for (const event of events) {
1025
+ if (event.target === yCollection) {
1026
+ for (const [key, change] of event.changes.keys) {
1027
+ switch (change.action) {
1028
+ case "add":
1029
+ addedKeys.add(key);
1030
+ break;
1031
+ case "update":
1032
+ changedKeys.add(key);
1033
+ break;
1034
+ case "delete":
1035
+ deletedKeys.add(key);
1036
+ break;
1037
+ }
1038
+ }
1039
+ } else {
1040
+ const recordId = event.path[0];
1041
+ if (recordId != null) {
1042
+ changedKeys.add(recordId);
1043
+ }
1044
+ }
1045
+ }
1046
+ if (addedKeys.size > 0) {
1047
+ const addedRecords = Array.from(addedKeys).map((id) => this.getCreateRecordRef(collection.docRef, id, collection.model));
1048
+ if (subs.added != null) {
1049
+ for (const callback of subs.added) {
1050
+ callback(addedRecords);
1051
+ }
1052
+ }
1053
+ for (const record of addedRecords) {
1054
+ this.notifyRecordSubscribers(record, "changed");
1055
+ }
1056
+ }
1057
+ if (changedKeys.size > 0) {
1058
+ const changedRecords = Array.from(changedKeys).map((id) => this.getCreateRecordRef(collection.docRef, id, collection.model));
1059
+ if (subs.changed != null) {
1060
+ for (const callback of subs.changed) {
1061
+ callback(changedRecords);
1062
+ }
1063
+ }
1064
+ for (const record of changedRecords) {
1065
+ this.notifyRecordSubscribers(record, "changed");
1066
+ }
1067
+ }
1068
+ if (deletedKeys.size > 0) {
1069
+ const deletedRecords = Array.from(deletedKeys).map((id) => this.getCreateRecordRef(collection.docRef, id, collection.model));
1070
+ if (subs.deleted?.size) {
1071
+ for (const callback of subs.deleted) {
1072
+ callback(deletedRecords);
1073
+ }
1074
+ }
1075
+ for (const record of deletedRecords) {
1076
+ this.notifyRecordSubscribers(record, "deleted");
1077
+ }
1078
+ }
1079
+ };
1080
+ yCollection.observeDeep(eventHandler);
1081
+ internalDoc.yjsCollectionHandlers.set(storageName, () => {
1082
+ yCollection.unobserveDeep(eventHandler);
1083
+ });
1084
+ }
1085
+ cleanupCollectionListener(docId, storageName) {
1086
+ const internalDoc = this.documents.get(docId);
1087
+ if (internalDoc == null) {
1088
+ return;
1089
+ }
1090
+ const cleanup = internalDoc.yjsCollectionHandlers.get(storageName);
1091
+ if (cleanup != null) {
1092
+ cleanup();
1093
+ internalDoc.yjsCollectionHandlers.delete(storageName);
1094
+ }
1095
+ }
1096
+ cleanupCollectionListenerIfUnused(internalDoc, docId, storageName) {
1097
+ const collectionSubs = internalDoc.collectionSubscriptions.get(storageName);
1098
+ const hasCollectionSubs = collectionSubs != null && (collectionSubs.added?.size || collectionSubs.changed?.size || collectionSubs.deleted?.size);
1099
+ const hasRecordSubs = Array.from(internalDoc.recordSubscriptions.values()).some((recordSubs) => pack_documentSchema_modelTypes.getMetadata(recordSubs.ref.model).name === storageName && !isRecordSubscriptionsEmpty(recordSubs));
1100
+ if (!hasCollectionSubs && !hasRecordSubs) {
1101
+ this.cleanupCollectionListener(docId, storageName);
1102
+ if (collectionSubs != null) {
1103
+ internalDoc.collectionSubscriptions.delete(storageName);
1104
+ }
1105
+ }
1106
+ }
1107
+ // DocumentService status methods implementation
1108
+ getDocumentStatus = (docRef) => {
1109
+ const {
1110
+ internalDoc
1111
+ } = this.getCreateInternalDoc(docRef);
1112
+ return {
1113
+ metadata: internalDoc.metadataStatus,
1114
+ data: internalDoc.dataStatus,
1115
+ metadataError: internalDoc.metadataError,
1116
+ dataError: internalDoc.dataError
1117
+ };
1118
+ };
1119
+ onStatusChange = (docRef, callback) => {
1120
+ const {
1121
+ internalDoc
1122
+ } = this.getCreateInternalDoc(docRef);
1123
+ internalDoc.statusSubscribers.add(callback);
1124
+ const status = {
1125
+ metadata: internalDoc.metadataStatus,
1126
+ data: internalDoc.dataStatus,
1127
+ metadataError: internalDoc.metadataError,
1128
+ dataError: internalDoc.dataError
1129
+ };
1130
+ callback(docRef, status);
1131
+ return () => {
1132
+ const currentDoc = this.documents.get(docRef.id);
1133
+ if (currentDoc) {
1134
+ currentDoc.statusSubscribers.delete(callback);
1135
+ }
1136
+ };
1137
+ };
1138
+ waitForMetadataLoad = async (docRef) => {
1139
+ const {
1140
+ internalDoc
1141
+ } = this.getCreateInternalDoc(docRef);
1142
+ if (internalDoc.metadataStatus.load === DocumentLoadStatus.LOADED) {
1143
+ return Promise.resolve();
1144
+ }
1145
+ if (internalDoc.metadataStatus.load === DocumentLoadStatus.ERROR) {
1146
+ return Promise.reject(new Error("Metadata load error", {
1147
+ cause: internalDoc.metadataError
1148
+ }));
1149
+ }
1150
+ return new Promise((resolve, reject) => {
1151
+ const unsubscribe = this.onStatusChange(docRef, (_, status) => {
1152
+ if (status.metadata.load === DocumentLoadStatus.LOADED) {
1153
+ unsubscribe();
1154
+ resolve();
1155
+ } else if (status.metadata.load === DocumentLoadStatus.ERROR) {
1156
+ unsubscribe();
1157
+ reject(new Error("Metadata load error", {
1158
+ cause: status.metadataError
1159
+ }));
1160
+ }
1161
+ });
1162
+ });
1163
+ };
1164
+ waitForDataLoad = async (docRef) => {
1165
+ const {
1166
+ internalDoc
1167
+ } = this.getCreateInternalDoc(docRef);
1168
+ if (internalDoc.dataStatus.load === DocumentLoadStatus.LOADED) {
1169
+ return Promise.resolve();
1170
+ }
1171
+ if (internalDoc.dataStatus.load === DocumentLoadStatus.ERROR) {
1172
+ return Promise.reject(new Error("Data load error", {
1173
+ cause: internalDoc.dataError
1174
+ }));
1175
+ }
1176
+ return new Promise((resolve, reject) => {
1177
+ const unsubscribe = this.onStatusChange(docRef, (_, status) => {
1178
+ if (status.data.load === DocumentLoadStatus.LOADED) {
1179
+ unsubscribe();
1180
+ resolve();
1181
+ } else if (status.data.load === DocumentLoadStatus.ERROR) {
1182
+ unsubscribe();
1183
+ reject(new Error("Data load error", {
1184
+ cause: status.dataError
1185
+ }));
1186
+ }
1187
+ });
1188
+ });
1189
+ };
1190
+ // FIXME: don't expose in production builds
1191
+ /**
1192
+ * @internal
1193
+ */
1194
+ getYDocForTesting(docId) {
1195
+ const internalDoc = this.documents.get(docId);
1196
+ return internalDoc ? internalDoc.yDoc : null;
1197
+ }
1198
+ };
1199
+ function isCollectionSubscriptionsEmpty(subs) {
1200
+ return !subs.added?.size && !subs.changed?.size && !subs.deleted?.size;
1201
+ }
1202
+ function isRecordSubscriptionsEmpty(subs) {
1203
+ return !subs.changed?.size && !subs.deleted?.size;
1204
+ }
1205
+ function createInMemoryDocumentServiceConfig({
1206
+ autoCreateDocuments = true
1207
+ } = {}) {
1208
+ const config = {
1209
+ autoCreateDocuments
1210
+ };
1211
+ return createDocumentServiceConfig(internalCreateInMemoryDocumentService, config);
1212
+ }
1213
+ function internalCreateInMemoryDocumentService(app, options) {
1214
+ return new InMemoryDocumentService(app, options);
1215
+ }
1216
+ var InMemoryDocumentService = class extends BaseYjsDocumentService {
1217
+ constructor(app, config) {
1218
+ super(app, app.config.logger.child({}, {
1219
+ level: "debug",
1220
+ msgPrefix: "InMemoryDocumentService"
1221
+ }));
1222
+ this.config = config;
1223
+ }
1224
+ createInternalDoc(ref, metadata, yDoc) {
1225
+ return this.createBaseInternalDoc(ref, metadata, yDoc);
1226
+ }
1227
+ get hasMetadataSubscriptions() {
1228
+ return Array.from(this.documents.values()).some((doc) => this.hasSubscriptions(doc) && doc.metadataSubscribers.size > 0);
1229
+ }
1230
+ get hasStateSubscriptions() {
1231
+ return Array.from(this.documents.values()).some((doc) => this.hasSubscriptions(doc) && doc.docStateSubscribers.size > 0);
1232
+ }
1233
+ createDocument = (metadata, schema) => {
1234
+ const id = generateDocumentId();
1235
+ const docRef = createDocRef(this.app, id, schema);
1236
+ const yDoc = this.initializeYDoc(schema);
1237
+ this.getCreateInternalDoc(docRef, metadata, yDoc);
1238
+ return Promise.resolve(docRef);
1239
+ };
1240
+ // Lifecycle method implementations
1241
+ onMetadataSubscriptionOpened(internalDoc, docRef) {
1242
+ this.updateMetadataStatus(internalDoc, docRef, {
1243
+ load: DocumentLoadStatus.LOADING
1244
+ });
1245
+ if (this.config.autoCreateDocuments === false && internalDoc.metadata == null) {
1246
+ this.updateMetadataStatus(internalDoc, docRef, {
1247
+ error: new Error("Document not found and autoCreateDocuments is disabled"),
1248
+ load: DocumentLoadStatus.ERROR
1249
+ });
1250
+ return;
1251
+ }
1252
+ this.updateMetadataStatus(internalDoc, docRef, {
1253
+ load: DocumentLoadStatus.LOADED
1254
+ });
1255
+ }
1256
+ onDataSubscriptionOpened(internalDoc, docRef) {
1257
+ this.updateDataStatus(internalDoc, docRef, {
1258
+ load: DocumentLoadStatus.LOADING
1259
+ });
1260
+ if (this.config.autoCreateDocuments === false && internalDoc.metadata == null) {
1261
+ this.updateDataStatus(internalDoc, docRef, {
1262
+ error: new Error("Document not found and autoCreateDocuments is disabled"),
1263
+ load: DocumentLoadStatus.ERROR
1264
+ });
1265
+ return;
1266
+ }
1267
+ this.updateDataStatus(internalDoc, docRef, {
1268
+ load: DocumentLoadStatus.LOADED
1269
+ });
1270
+ }
1271
+ onMetadataSubscriptionClosed(_internalDoc, _docRef) {
1272
+ }
1273
+ onDataSubscriptionClosed(_internalDoc, _docRef) {
1274
+ }
1275
+ };
1276
+ function generateDocumentId() {
1277
+ return pack_core.generateId();
1278
+ }
1279
+
1280
+ exports.BaseYjsDocumentService = BaseYjsDocumentService;
1281
+ exports.DocumentLiveStatus = DocumentLiveStatus;
1282
+ exports.DocumentLoadStatus = DocumentLoadStatus;
1283
+ exports.STATE_MODULE_ACCESSOR = STATE_MODULE_ACCESSOR;
1284
+ exports.createDocRef = createDocRef;
1285
+ exports.createDocumentServiceConfig = createDocumentServiceConfig;
1286
+ exports.createInMemoryDocumentServiceConfig = createInMemoryDocumentServiceConfig;
1287
+ exports.createRecordCollectionRef = createRecordCollectionRef;
1288
+ exports.createRecordRef = createRecordRef;
1289
+ exports.getDocumentService = getDocumentService;
1290
+ exports.getStateModule = getStateModule;
1291
+ exports.invalidDocRef = invalidDocRef;
1292
+ exports.invalidRecordCollectionRef = invalidRecordCollectionRef;
1293
+ exports.invalidRecordRef = invalidRecordRef;
1294
+ exports.isValidDocRef = isValidDocRef;
1295
+ exports.isValidRecordCollectionRef = isValidRecordCollectionRef;
1296
+ exports.isValidRecordRef = isValidRecordRef;
1297
+ //# sourceMappingURL=index.cjs.map
1298
+ //# sourceMappingURL=index.cjs.map