@voidhash/mimic-effect 1.0.0-beta.1 → 1.0.0-beta.10

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 (142) hide show
  1. package/.turbo/turbo-build.log +116 -74
  2. package/dist/ColdStorage.cjs +9 -5
  3. package/dist/ColdStorage.d.cts.map +1 -1
  4. package/dist/ColdStorage.d.mts.map +1 -1
  5. package/dist/ColdStorage.mjs +9 -5
  6. package/dist/ColdStorage.mjs.map +1 -1
  7. package/dist/DocumentInstance.cjs +263 -0
  8. package/dist/DocumentInstance.d.cts +78 -0
  9. package/dist/DocumentInstance.d.cts.map +1 -0
  10. package/dist/DocumentInstance.d.mts +78 -0
  11. package/dist/DocumentInstance.d.mts.map +1 -0
  12. package/dist/DocumentInstance.mjs +264 -0
  13. package/dist/DocumentInstance.mjs.map +1 -0
  14. package/dist/Errors.cjs +10 -1
  15. package/dist/Errors.d.cts +18 -3
  16. package/dist/Errors.d.cts.map +1 -1
  17. package/dist/Errors.d.mts +18 -3
  18. package/dist/Errors.d.mts.map +1 -1
  19. package/dist/Errors.mjs +9 -1
  20. package/dist/Errors.mjs.map +1 -1
  21. package/dist/HotStorage.cjs +39 -12
  22. package/dist/HotStorage.d.cts +17 -1
  23. package/dist/HotStorage.d.cts.map +1 -1
  24. package/dist/HotStorage.d.mts +17 -1
  25. package/dist/HotStorage.d.mts.map +1 -1
  26. package/dist/HotStorage.mjs +39 -12
  27. package/dist/HotStorage.mjs.map +1 -1
  28. package/dist/Metrics.cjs +29 -1
  29. package/dist/Metrics.d.cts +5 -0
  30. package/dist/Metrics.d.cts.map +1 -1
  31. package/dist/Metrics.d.mts +5 -0
  32. package/dist/Metrics.d.mts.map +1 -1
  33. package/dist/Metrics.mjs +26 -1
  34. package/dist/Metrics.mjs.map +1 -1
  35. package/dist/MimicClusterServerEngine.cjs +44 -139
  36. package/dist/MimicClusterServerEngine.d.cts.map +1 -1
  37. package/dist/MimicClusterServerEngine.d.mts +1 -1
  38. package/dist/MimicClusterServerEngine.d.mts.map +1 -1
  39. package/dist/MimicClusterServerEngine.mjs +46 -141
  40. package/dist/MimicClusterServerEngine.mjs.map +1 -1
  41. package/dist/MimicServer.cjs +20 -20
  42. package/dist/MimicServer.d.cts.map +1 -1
  43. package/dist/MimicServer.d.mts.map +1 -1
  44. package/dist/MimicServer.mjs +20 -20
  45. package/dist/MimicServer.mjs.map +1 -1
  46. package/dist/MimicServerEngine.cjs +92 -11
  47. package/dist/MimicServerEngine.d.cts +12 -4
  48. package/dist/MimicServerEngine.d.cts.map +1 -1
  49. package/dist/MimicServerEngine.d.mts +12 -4
  50. package/dist/MimicServerEngine.d.mts.map +1 -1
  51. package/dist/MimicServerEngine.mjs +94 -13
  52. package/dist/MimicServerEngine.mjs.map +1 -1
  53. package/dist/PresenceManager.cjs +5 -5
  54. package/dist/PresenceManager.d.cts.map +1 -1
  55. package/dist/PresenceManager.d.mts.map +1 -1
  56. package/dist/PresenceManager.mjs +5 -5
  57. package/dist/PresenceManager.mjs.map +1 -1
  58. package/dist/Protocol.d.cts +1 -1
  59. package/dist/Protocol.d.mts +1 -1
  60. package/dist/Types.d.cts +9 -2
  61. package/dist/Types.d.cts.map +1 -1
  62. package/dist/Types.d.mts +9 -2
  63. package/dist/Types.d.mts.map +1 -1
  64. package/dist/index.cjs +5 -6
  65. package/dist/index.d.cts +3 -3
  66. package/dist/index.d.mts +3 -3
  67. package/dist/index.mjs +3 -3
  68. package/dist/testing/ColdStorageTestSuite.cjs +508 -0
  69. package/dist/testing/ColdStorageTestSuite.d.cts +36 -0
  70. package/dist/testing/ColdStorageTestSuite.d.cts.map +1 -0
  71. package/dist/testing/ColdStorageTestSuite.d.mts +36 -0
  72. package/dist/testing/ColdStorageTestSuite.d.mts.map +1 -0
  73. package/dist/testing/ColdStorageTestSuite.mjs +508 -0
  74. package/dist/testing/ColdStorageTestSuite.mjs.map +1 -0
  75. package/dist/testing/FailingStorage.cjs +162 -0
  76. package/dist/testing/FailingStorage.d.cts +43 -0
  77. package/dist/testing/FailingStorage.d.cts.map +1 -0
  78. package/dist/testing/FailingStorage.d.mts +43 -0
  79. package/dist/testing/FailingStorage.d.mts.map +1 -0
  80. package/dist/testing/FailingStorage.mjs +163 -0
  81. package/dist/testing/FailingStorage.mjs.map +1 -0
  82. package/dist/testing/HotStorageTestSuite.cjs +820 -0
  83. package/dist/testing/HotStorageTestSuite.d.cts +42 -0
  84. package/dist/testing/HotStorageTestSuite.d.cts.map +1 -0
  85. package/dist/testing/HotStorageTestSuite.d.mts +42 -0
  86. package/dist/testing/HotStorageTestSuite.d.mts.map +1 -0
  87. package/dist/testing/HotStorageTestSuite.mjs +820 -0
  88. package/dist/testing/HotStorageTestSuite.mjs.map +1 -0
  89. package/dist/testing/StorageIntegrationTestSuite.cjs +487 -0
  90. package/dist/testing/StorageIntegrationTestSuite.d.cts +37 -0
  91. package/dist/testing/StorageIntegrationTestSuite.d.cts.map +1 -0
  92. package/dist/testing/StorageIntegrationTestSuite.d.mts +37 -0
  93. package/dist/testing/StorageIntegrationTestSuite.d.mts.map +1 -0
  94. package/dist/testing/StorageIntegrationTestSuite.mjs +487 -0
  95. package/dist/testing/StorageIntegrationTestSuite.mjs.map +1 -0
  96. package/dist/testing/assertions.cjs +117 -0
  97. package/dist/testing/assertions.mjs +112 -0
  98. package/dist/testing/assertions.mjs.map +1 -0
  99. package/dist/testing/index.cjs +14 -0
  100. package/dist/testing/index.d.cts +6 -0
  101. package/dist/testing/index.d.mts +6 -0
  102. package/dist/testing/index.mjs +7 -0
  103. package/dist/testing/types.cjs +15 -0
  104. package/dist/testing/types.d.cts +90 -0
  105. package/dist/testing/types.d.cts.map +1 -0
  106. package/dist/testing/types.d.mts +90 -0
  107. package/dist/testing/types.d.mts.map +1 -0
  108. package/dist/testing/types.mjs +16 -0
  109. package/dist/testing/types.mjs.map +1 -0
  110. package/package.json +8 -3
  111. package/src/ColdStorage.ts +21 -12
  112. package/src/DocumentInstance.ts +527 -0
  113. package/src/Errors.ts +15 -1
  114. package/src/HotStorage.ts +115 -24
  115. package/src/Metrics.ts +30 -0
  116. package/src/MimicClusterServerEngine.ts +120 -275
  117. package/src/MimicServer.ts +83 -75
  118. package/src/MimicServerEngine.ts +230 -30
  119. package/src/PresenceManager.ts +44 -34
  120. package/src/Types.ts +9 -2
  121. package/src/index.ts +5 -35
  122. package/src/testing/ColdStorageTestSuite.ts +589 -0
  123. package/src/testing/FailingStorage.ts +338 -0
  124. package/src/testing/HotStorageTestSuite.ts +1105 -0
  125. package/src/testing/StorageIntegrationTestSuite.ts +736 -0
  126. package/src/testing/assertions.ts +188 -0
  127. package/src/testing/index.ts +83 -0
  128. package/src/testing/types.ts +100 -0
  129. package/tests/ColdStorage.test.ts +8 -120
  130. package/tests/DocumentInstance.test.ts +669 -0
  131. package/tests/HotStorage.test.ts +7 -126
  132. package/tests/StorageIntegration.test.ts +259 -0
  133. package/tsdown.config.ts +1 -1
  134. package/dist/DocumentManager.cjs +0 -229
  135. package/dist/DocumentManager.d.cts +0 -59
  136. package/dist/DocumentManager.d.cts.map +0 -1
  137. package/dist/DocumentManager.d.mts +0 -59
  138. package/dist/DocumentManager.d.mts.map +0 -1
  139. package/dist/DocumentManager.mjs +0 -227
  140. package/dist/DocumentManager.mjs.map +0 -1
  141. package/src/DocumentManager.ts +0 -506
  142. package/tests/DocumentManager.test.ts +0 -335
@@ -1,506 +0,0 @@
1
- /**
2
- * @voidhash/mimic-effect - DocumentManager
3
- *
4
- * Internal service for managing document lifecycle, including:
5
- * - Document creation and restoration
6
- * - Transaction processing
7
- * - WAL management
8
- * - Snapshot scheduling
9
- * - Idle document GC
10
- */
11
- import {
12
- Context,
13
- Duration,
14
- Effect,
15
- Fiber,
16
- HashMap,
17
- Layer,
18
- Metric,
19
- PubSub,
20
- Ref,
21
- Schedule,
22
- Scope,
23
- Stream,
24
- } from "effect";
25
- import { Primitive, Transaction } from "@voidhash/mimic";
26
- import { ServerDocument } from "@voidhash/mimic/server";
27
- import type {
28
- Initial,
29
- ResolvedConfig,
30
- StoredDocument,
31
- WalEntry,
32
- } from "./Types";
33
- import type { SnapshotMessage, ServerBroadcast } from "./Protocol";
34
- import { ColdStorageTag, type ColdStorage } from "./ColdStorage";
35
- import { HotStorageTag, type HotStorage } from "./HotStorage";
36
- import * as Metrics from "./Metrics";
37
-
38
- // =============================================================================
39
- // Submit Result Types
40
- // =============================================================================
41
-
42
- /**
43
- * Result of submitting a transaction
44
- */
45
- export type SubmitResult =
46
- | { readonly success: true; readonly version: number }
47
- | { readonly success: false; readonly reason: string };
48
-
49
- // =============================================================================
50
- // DocumentManager Interface
51
- // =============================================================================
52
-
53
- /**
54
- * Internal service for managing document lifecycle.
55
- */
56
- export interface DocumentManager {
57
- /**
58
- * Submit a transaction to a document.
59
- */
60
- readonly submit: (
61
- documentId: string,
62
- transaction: Transaction.Transaction
63
- ) => Effect.Effect<SubmitResult>;
64
-
65
- /**
66
- * Get a snapshot of a document.
67
- */
68
- readonly getSnapshot: (documentId: string) => Effect.Effect<SnapshotMessage>;
69
-
70
- /**
71
- * Subscribe to broadcasts for a document.
72
- */
73
- readonly subscribe: (
74
- documentId: string
75
- ) => Effect.Effect<Stream.Stream<ServerBroadcast>, never, Scope.Scope>;
76
-
77
- /**
78
- * Touch a document to update its last activity time.
79
- * Call this on any client activity to prevent idle GC.
80
- */
81
- readonly touch: (documentId: string) => Effect.Effect<void>;
82
- }
83
-
84
- // =============================================================================
85
- // Context Tag
86
- // =============================================================================
87
-
88
- /**
89
- * Context tag for DocumentManager service
90
- */
91
- export class DocumentManagerTag extends Context.Tag(
92
- "@voidhash/mimic-effect/DocumentManager"
93
- )<DocumentManagerTag, DocumentManager>() {}
94
-
95
- // =============================================================================
96
- // Internal Types
97
- // =============================================================================
98
-
99
- /**
100
- * Document instance state
101
- */
102
- interface DocumentInstance<TSchema extends Primitive.AnyPrimitive> {
103
- /** The underlying ServerDocument */
104
- readonly document: ServerDocument.ServerDocument<TSchema>;
105
- /** PubSub for broadcasting messages */
106
- readonly pubsub: PubSub.PubSub<ServerBroadcast>;
107
- /** Version at last snapshot */
108
- readonly lastSnapshotVersion: Ref.Ref<number>;
109
- /** Timestamp of last snapshot (ms) */
110
- readonly lastSnapshotTime: Ref.Ref<number>;
111
- /** Transactions since last snapshot */
112
- readonly transactionsSinceSnapshot: Ref.Ref<number>;
113
- /** Last activity timestamp (ms) */
114
- readonly lastActivityTime: Ref.Ref<number>;
115
- }
116
-
117
- // =============================================================================
118
- // Config Context Tag
119
- // =============================================================================
120
-
121
- /**
122
- * Context tag for DocumentManager configuration
123
- */
124
- export class DocumentManagerConfigTag extends Context.Tag(
125
- "@voidhash/mimic-effect/DocumentManagerConfig"
126
- )<DocumentManagerConfigTag, ResolvedConfig<Primitive.AnyPrimitive>>() {}
127
-
128
- // =============================================================================
129
- // Layer Implementation
130
- // =============================================================================
131
-
132
- /**
133
- * Create the DocumentManager layer.
134
- * Requires ColdStorage, HotStorage, and DocumentManagerConfig.
135
- */
136
- export const layer: Layer.Layer<
137
- DocumentManagerTag,
138
- never,
139
- ColdStorageTag | HotStorageTag | DocumentManagerConfigTag
140
- > = Layer.scoped(
141
- DocumentManagerTag,
142
- Effect.gen(function* () {
143
- const coldStorage = yield* ColdStorageTag;
144
- const hotStorage = yield* HotStorageTag;
145
- const config = yield* DocumentManagerConfigTag;
146
-
147
- // Store: documentId -> DocumentInstance
148
- const store = yield* Ref.make(
149
- HashMap.empty<string, DocumentInstance<Primitive.AnyPrimitive>>()
150
- );
151
-
152
- // Current schema version (hard-coded to 1 for now)
153
- const SCHEMA_VERSION = 1;
154
-
155
- /**
156
- * Compute initial state for a new document
157
- */
158
- const computeInitialState = (
159
- documentId: string
160
- ): Effect.Effect<Primitive.InferSetInput<typeof config.schema> | undefined> => {
161
- if (config.initial === undefined) {
162
- return Effect.succeed(undefined);
163
- }
164
-
165
- // Check if it's a function or static value
166
- if (typeof config.initial === "function") {
167
- return (config.initial as (ctx: { documentId: string }) => Effect.Effect<Primitive.InferSetInput<typeof config.schema>>)({ documentId });
168
- }
169
-
170
- return Effect.succeed(config.initial as Primitive.InferSetInput<typeof config.schema>);
171
- };
172
-
173
- /**
174
- * Restore a document from storage
175
- */
176
- const restoreDocument = (
177
- documentId: string
178
- ): Effect.Effect<DocumentInstance<typeof config.schema>> =>
179
- Effect.gen(function* () {
180
- // 1. Load snapshot from ColdStorage
181
- const storedDoc = yield* Effect.catchAll(
182
- coldStorage.load(documentId),
183
- () => Effect.succeed(undefined)
184
- );
185
-
186
- let initialState: Primitive.InferSetInput<typeof config.schema> | undefined;
187
- let initialVersion = 0;
188
-
189
- if (storedDoc) {
190
- // Use stored state
191
- initialState = storedDoc.state as Primitive.InferSetInput<typeof config.schema>;
192
- initialVersion = storedDoc.version;
193
- } else {
194
- // Compute initial state
195
- initialState = yield* computeInitialState(documentId);
196
- }
197
-
198
- // 2. Create PubSub for broadcasting
199
- const pubsub = yield* PubSub.unbounded<ServerBroadcast>();
200
-
201
- // 3. Create refs for tracking
202
- const lastSnapshotVersion = yield* Ref.make(initialVersion);
203
- const lastSnapshotTime = yield* Ref.make(Date.now());
204
- const transactionsSinceSnapshot = yield* Ref.make(0);
205
- const lastActivityTime = yield* Ref.make(Date.now());
206
-
207
- // 4. Create ServerDocument with callbacks
208
- const document = ServerDocument.make({
209
- schema: config.schema,
210
- initialState,
211
- initialVersion,
212
- maxTransactionHistory: config.maxTransactionHistory,
213
- onBroadcast: (message: ServerDocument.TransactionMessage) => {
214
- // This is called synchronously by ServerDocument
215
- // We need to publish to PubSub
216
- Effect.runSync(
217
- PubSub.publish(pubsub, {
218
- type: "transaction",
219
- transaction: message.transaction,
220
- version: message.version,
221
- })
222
- );
223
- },
224
- onRejection: (transactionId: string, reason: string) => {
225
- Effect.runSync(
226
- PubSub.publish(pubsub, {
227
- type: "error",
228
- transactionId,
229
- reason,
230
- })
231
- );
232
- },
233
- });
234
-
235
- // 5. Load and replay WAL entries
236
- const walEntries = yield* Effect.catchAll(
237
- hotStorage.getEntries(documentId, initialVersion),
238
- () => Effect.succeed([] as WalEntry[])
239
- );
240
-
241
- for (const entry of walEntries) {
242
- const result = document.submit(entry.transaction);
243
- if (!result.success) {
244
- yield* Effect.logWarning("Skipping corrupted WAL entry", {
245
- documentId,
246
- version: entry.version,
247
- reason: result.reason,
248
- });
249
- }
250
- }
251
-
252
- const instance: DocumentInstance<typeof config.schema> = {
253
- document,
254
- pubsub,
255
- lastSnapshotVersion,
256
- lastSnapshotTime,
257
- transactionsSinceSnapshot,
258
- lastActivityTime,
259
- };
260
-
261
- // Track metrics - determine if restored or created
262
- if (storedDoc) {
263
- yield* Metric.increment(Metrics.documentsRestored);
264
- } else {
265
- yield* Metric.increment(Metrics.documentsCreated);
266
- }
267
- yield* Metric.incrementBy(Metrics.documentsActive, 1);
268
-
269
- return instance;
270
- });
271
-
272
- /**
273
- * Get or create a document instance
274
- */
275
- const getOrCreateDocument = (
276
- documentId: string
277
- ): Effect.Effect<DocumentInstance<typeof config.schema>> =>
278
- Effect.gen(function* () {
279
- const current = yield* Ref.get(store);
280
- const existing = HashMap.get(current, documentId);
281
-
282
- if (existing._tag === "Some") {
283
- // Update activity time
284
- yield* Ref.set(existing.value.lastActivityTime, Date.now());
285
- return existing.value as DocumentInstance<typeof config.schema>;
286
- }
287
-
288
- // Restore document
289
- const instance = yield* restoreDocument(documentId);
290
-
291
- // Store it
292
- yield* Ref.update(store, (map) =>
293
- HashMap.set(map, documentId, instance)
294
- );
295
-
296
- return instance;
297
- });
298
-
299
- /**
300
- * Save a snapshot to ColdStorage and truncate WAL
301
- */
302
- const saveSnapshot = (
303
- documentId: string,
304
- instance: DocumentInstance<typeof config.schema>
305
- ): Effect.Effect<void> =>
306
- Effect.gen(function* () {
307
- const state = instance.document.get();
308
- const version = instance.document.getVersion();
309
-
310
- if (state === undefined) {
311
- return;
312
- }
313
-
314
- const storedDoc: StoredDocument = {
315
- state,
316
- version,
317
- schemaVersion: SCHEMA_VERSION,
318
- savedAt: Date.now(),
319
- };
320
-
321
- const snapshotStartTime = Date.now();
322
-
323
- // Save to ColdStorage
324
- yield* Effect.catchAll(coldStorage.save(documentId, storedDoc), (e) =>
325
- Effect.logError("Failed to save snapshot", { documentId, error: e })
326
- );
327
-
328
- // Track snapshot metrics
329
- const snapshotDuration = Date.now() - snapshotStartTime;
330
- yield* Metric.increment(Metrics.storageSnapshots);
331
- yield* Metric.update(Metrics.storageSnapshotLatency, snapshotDuration);
332
-
333
- // Truncate WAL
334
- yield* Effect.catchAll(hotStorage.truncate(documentId, version), (e) =>
335
- Effect.logError("Failed to truncate WAL", { documentId, error: e })
336
- );
337
-
338
- // Update tracking
339
- yield* Ref.set(instance.lastSnapshotVersion, version);
340
- yield* Ref.set(instance.lastSnapshotTime, Date.now());
341
- yield* Ref.set(instance.transactionsSinceSnapshot, 0);
342
- });
343
-
344
- /**
345
- * Check if snapshot should be triggered
346
- */
347
- const checkSnapshotTriggers = (
348
- documentId: string,
349
- instance: DocumentInstance<typeof config.schema>
350
- ): Effect.Effect<void> =>
351
- Effect.gen(function* () {
352
- const txCount = yield* Ref.get(instance.transactionsSinceSnapshot);
353
- const lastTime = yield* Ref.get(instance.lastSnapshotTime);
354
- const now = Date.now();
355
-
356
- const intervalMs = Duration.toMillis(config.snapshot.interval);
357
- const threshold = config.snapshot.transactionThreshold;
358
-
359
- // Check transaction threshold
360
- if (txCount >= threshold) {
361
- yield* saveSnapshot(documentId, instance);
362
- return;
363
- }
364
-
365
- // Check time interval
366
- if (now - lastTime >= intervalMs) {
367
- yield* saveSnapshot(documentId, instance);
368
- return;
369
- }
370
- });
371
-
372
- /**
373
- * Start background GC fiber
374
- */
375
- const startGCFiber = Effect.gen(function* () {
376
- const gcLoop = Effect.gen(function* () {
377
- const current = yield* Ref.get(store);
378
- const now = Date.now();
379
- const maxIdleMs = Duration.toMillis(config.maxIdleTime);
380
-
381
- for (const [documentId, instance] of current) {
382
- const lastActivity = yield* Ref.get(instance.lastActivityTime);
383
- if (now - lastActivity >= maxIdleMs) {
384
- // Save final snapshot before eviction
385
- yield* saveSnapshot(documentId, instance);
386
-
387
- // Remove from store
388
- yield* Ref.update(store, (map) => HashMap.remove(map, documentId));
389
-
390
- // Track eviction metrics
391
- yield* Metric.increment(Metrics.documentsEvicted);
392
- yield* Metric.incrementBy(Metrics.documentsActive, -1);
393
-
394
- yield* Effect.logInfo("Document evicted due to idle timeout", {
395
- documentId,
396
- });
397
- }
398
- }
399
- });
400
-
401
- // Run GC every minute
402
- yield* gcLoop.pipe(
403
- Effect.repeat(Schedule.spaced("1 minute")),
404
- Effect.fork
405
- );
406
- });
407
-
408
- // Start GC fiber
409
- yield* startGCFiber;
410
-
411
- // Cleanup on shutdown
412
- yield* Effect.addFinalizer(() =>
413
- Effect.gen(function* () {
414
- const current = yield* Ref.get(store);
415
- for (const [documentId, instance] of current) {
416
- yield* saveSnapshot(documentId, instance);
417
- }
418
- yield* Effect.logInfo("DocumentManager shutdown complete");
419
- })
420
- );
421
-
422
- return {
423
- submit: (documentId, transaction) =>
424
- Effect.gen(function* () {
425
- const instance = yield* getOrCreateDocument(documentId);
426
- const submitStartTime = Date.now();
427
-
428
- // Submit to ServerDocument
429
- const result = instance.document.submit(transaction);
430
-
431
- // Track latency
432
- const latency = Date.now() - submitStartTime;
433
- yield* Metric.update(Metrics.transactionsLatency, latency);
434
-
435
- if (result.success) {
436
- // Track success
437
- yield* Metric.increment(Metrics.transactionsProcessed);
438
-
439
- // Append to WAL
440
- const walEntry: WalEntry = {
441
- transaction,
442
- version: result.version,
443
- timestamp: Date.now(),
444
- };
445
-
446
- yield* Effect.catchAll(
447
- hotStorage.append(documentId, walEntry),
448
- (e) =>
449
- Effect.logError("Failed to append to WAL", {
450
- documentId,
451
- error: e,
452
- })
453
- );
454
-
455
- // Track WAL append
456
- yield* Metric.increment(Metrics.storageWalAppends);
457
-
458
- // Increment transaction count
459
- yield* Ref.update(
460
- instance.transactionsSinceSnapshot,
461
- (n) => n + 1
462
- );
463
-
464
- // Check snapshot triggers
465
- yield* checkSnapshotTriggers(documentId, instance);
466
- } else {
467
- // Track rejection
468
- yield* Metric.increment(Metrics.transactionsRejected);
469
- }
470
-
471
- return result;
472
- }),
473
-
474
- getSnapshot: (documentId) =>
475
- Effect.gen(function* () {
476
- const instance = yield* getOrCreateDocument(documentId);
477
- return instance.document.getSnapshot();
478
- }),
479
-
480
- subscribe: (documentId) =>
481
- Effect.gen(function* () {
482
- const instance = yield* getOrCreateDocument(documentId);
483
- return Stream.fromPubSub(instance.pubsub);
484
- }),
485
-
486
- touch: (documentId) =>
487
- Effect.gen(function* () {
488
- const current = yield* Ref.get(store);
489
- const existing = HashMap.get(current, documentId);
490
- if (existing._tag === "Some") {
491
- yield* Ref.set(existing.value.lastActivityTime, Date.now());
492
- }
493
- }),
494
- };
495
- })
496
- );
497
-
498
- // =============================================================================
499
- // Re-export namespace
500
- // =============================================================================
501
-
502
- export const DocumentManager = {
503
- Tag: DocumentManagerTag,
504
- ConfigTag: DocumentManagerConfigTag,
505
- layer,
506
- };