@voidhash/mimic-effect 1.0.0-beta.5 → 1.0.0-beta.7

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 (46) hide show
  1. package/.turbo/turbo-build.log +91 -91
  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/DocumentManager.cjs +14 -14
  8. package/dist/DocumentManager.d.cts.map +1 -1
  9. package/dist/DocumentManager.d.mts.map +1 -1
  10. package/dist/DocumentManager.mjs +14 -14
  11. package/dist/DocumentManager.mjs.map +1 -1
  12. package/dist/HotStorage.cjs +17 -13
  13. package/dist/HotStorage.d.cts.map +1 -1
  14. package/dist/HotStorage.d.mts.map +1 -1
  15. package/dist/HotStorage.mjs +17 -13
  16. package/dist/HotStorage.mjs.map +1 -1
  17. package/dist/MimicClusterServerEngine.cjs +17 -17
  18. package/dist/MimicClusterServerEngine.d.cts.map +1 -1
  19. package/dist/MimicClusterServerEngine.d.mts.map +1 -1
  20. package/dist/MimicClusterServerEngine.mjs +17 -17
  21. package/dist/MimicClusterServerEngine.mjs.map +1 -1
  22. package/dist/MimicServer.cjs +19 -19
  23. package/dist/MimicServer.d.cts.map +1 -1
  24. package/dist/MimicServer.d.mts.map +1 -1
  25. package/dist/MimicServer.mjs +19 -19
  26. package/dist/MimicServer.mjs.map +1 -1
  27. package/dist/MimicServerEngine.cjs +2 -2
  28. package/dist/MimicServerEngine.mjs +2 -2
  29. package/dist/MimicServerEngine.mjs.map +1 -1
  30. package/dist/PresenceManager.cjs +5 -5
  31. package/dist/PresenceManager.d.cts.map +1 -1
  32. package/dist/PresenceManager.d.mts.map +1 -1
  33. package/dist/PresenceManager.mjs +5 -5
  34. package/dist/PresenceManager.mjs.map +1 -1
  35. package/dist/testing/HotStorageTestSuite.cjs +7 -19
  36. package/dist/testing/HotStorageTestSuite.mjs +7 -19
  37. package/dist/testing/HotStorageTestSuite.mjs.map +1 -1
  38. package/package.json +3 -3
  39. package/src/ColdStorage.ts +21 -12
  40. package/src/DocumentManager.ts +49 -47
  41. package/src/HotStorage.ts +75 -58
  42. package/src/MimicClusterServerEngine.ts +61 -49
  43. package/src/MimicServer.ts +83 -75
  44. package/src/MimicServerEngine.ts +2 -2
  45. package/src/PresenceManager.ts +44 -34
  46. package/src/testing/HotStorageTestSuite.ts +3 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voidhash/mimic-effect",
3
- "version": "1.0.0-beta.5",
3
+ "version": "1.0.0-beta.7",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -26,14 +26,14 @@
26
26
  "typescript": "5.8.3",
27
27
  "vite-tsconfig-paths": "^5.1.4",
28
28
  "vitest": "^3.2.4",
29
- "@voidhash/tsconfig": "1.0.0-beta.5"
29
+ "@voidhash/tsconfig": "1.0.0-beta.7"
30
30
  },
31
31
  "peerDependencies": {
32
32
  "@effect/platform": "^0.93.8",
33
33
  "@effect/cluster": "^0.55.0",
34
34
  "@effect/rpc": "^0.72.2",
35
35
  "effect": "^3.19.12",
36
- "@voidhash/mimic": "1.0.0-beta.5"
36
+ "@voidhash/mimic": "1.0.0-beta.7"
37
37
  },
38
38
  "peerDependenciesMeta": {
39
39
  "@effect/cluster": {
@@ -104,24 +104,33 @@ export namespace InMemory {
104
104
  export const make = (): Layer.Layer<ColdStorageTag> =>
105
105
  Layer.effect(
106
106
  ColdStorageTag,
107
- Effect.gen(function* () {
107
+ Effect.fn("cold-storage.in-memory.create")(function* () {
108
108
  const store = yield* Ref.make(HashMap.empty<string, StoredDocument>());
109
109
 
110
110
  return {
111
- load: (documentId) =>
112
- Effect.gen(function* () {
113
- const current = yield* Ref.get(store);
114
- const result = HashMap.get(current, documentId);
115
- return result._tag === "Some" ? result.value : undefined;
116
- }),
111
+ load: Effect.fn("cold-storage.load")(function* (documentId: string) {
112
+ const current = yield* Ref.get(store);
113
+ const result = HashMap.get(current, documentId);
114
+ return result._tag === "Some" ? result.value : undefined;
115
+ }),
117
116
 
118
- save: (documentId, document) =>
119
- Ref.update(store, (map) => HashMap.set(map, documentId, document)),
117
+ save: Effect.fn("cold-storage.save")(
118
+ function* (documentId: string, document: StoredDocument) {
119
+ yield* Ref.update(store, (map) =>
120
+ HashMap.set(map, documentId, document)
121
+ );
122
+ }
123
+ ),
120
124
 
121
- delete: (documentId) =>
122
- Ref.update(store, (map) => HashMap.remove(map, documentId)),
125
+ delete: Effect.fn("cold-storage.delete")(
126
+ function* (documentId: string) {
127
+ yield* Ref.update(store, (map) =>
128
+ HashMap.remove(map, documentId)
129
+ );
130
+ }
131
+ ),
123
132
  };
124
- })
133
+ })()
125
134
  );
126
135
  }
127
136
 
@@ -176,10 +176,8 @@ export const layer = Layer.scoped(
176
176
  /**
177
177
  * Restore a document from storage
178
178
  */
179
- const restoreDocument = (
180
- documentId: string
181
- ): Effect.Effect<DocumentInstance<typeof config.schema>, ColdStorageError | HotStorageError> =>
182
- Effect.gen(function* () {
179
+ const restoreDocument = Effect.fn("document.restore")(
180
+ function* (documentId: string) {
183
181
  // 1. Load snapshot from ColdStorage (errors propagate - do not silently fallback)
184
182
  const storedDoc = yield* coldStorage.load(documentId);
185
183
 
@@ -294,15 +292,14 @@ export const layer = Layer.scoped(
294
292
  yield* Metric.incrementBy(Metrics.documentsActive, 1);
295
293
 
296
294
  return instance;
297
- });
295
+ }
296
+ );
298
297
 
299
298
  /**
300
299
  * Get or create a document instance
301
300
  */
302
- const getOrCreateDocument = (
303
- documentId: string
304
- ): Effect.Effect<DocumentInstance<typeof config.schema>, ColdStorageError | HotStorageError> =>
305
- Effect.gen(function* () {
301
+ const getOrCreateDocument = Effect.fn("document.get-or-create")(
302
+ function* (documentId: string) {
306
303
  const current = yield* Ref.get(store);
307
304
  const existing = HashMap.get(current, documentId);
308
305
 
@@ -321,7 +318,8 @@ export const layer = Layer.scoped(
321
318
  );
322
319
 
323
320
  return instance;
324
- });
321
+ }
322
+ );
325
323
 
326
324
  /**
327
325
  * Save a snapshot to ColdStorage derived from WAL entries.
@@ -329,12 +327,12 @@ export const layer = Layer.scoped(
329
327
  * Idempotent: skips save if already snapshotted at target version.
330
328
  * Truncate failures are non-fatal and will be retried on next snapshot.
331
329
  */
332
- const saveSnapshot = (
333
- documentId: string,
334
- instance: DocumentInstance<typeof config.schema>,
335
- targetVersion: number
336
- ): Effect.Effect<void, ColdStorageError | HotStorageError> =>
337
- Effect.gen(function* () {
330
+ const saveSnapshot = Effect.fn("document.snapshot.save")(
331
+ function* (
332
+ documentId: string,
333
+ instance: DocumentInstance<typeof config.schema>,
334
+ targetVersion: number
335
+ ) {
338
336
  const lastSnapshotVersion = yield* Ref.get(instance.lastSnapshotVersion);
339
337
 
340
338
  // Idempotency check: skip if already snapshotted at this version
@@ -410,16 +408,17 @@ export const layer = Layer.scoped(
410
408
  error: e,
411
409
  })
412
410
  );
413
- });
411
+ }
412
+ );
414
413
 
415
414
  /**
416
415
  * Check if snapshot should be triggered
417
416
  */
418
- const checkSnapshotTriggers = (
419
- documentId: string,
420
- instance: DocumentInstance<typeof config.schema>
421
- ): Effect.Effect<void, ColdStorageError | HotStorageError> =>
422
- Effect.gen(function* () {
417
+ const checkSnapshotTriggers = Effect.fn("document.snapshot.check-triggers")(
418
+ function* (
419
+ documentId: string,
420
+ instance: DocumentInstance<typeof config.schema>
421
+ ) {
423
422
  const txCount = yield* Ref.get(instance.transactionsSinceSnapshot);
424
423
  const lastTime = yield* Ref.get(instance.lastSnapshotTime);
425
424
  const now = Date.now();
@@ -439,13 +438,14 @@ export const layer = Layer.scoped(
439
438
  yield* saveSnapshot(documentId, instance, currentVersion);
440
439
  return;
441
440
  }
442
- });
441
+ }
442
+ );
443
443
 
444
444
  /**
445
445
  * Start background GC fiber
446
446
  */
447
- const startGCFiber = Effect.gen(function* () {
448
- const gcLoop = Effect.gen(function* () {
447
+ const startGCFiber = Effect.fn("document.gc.start")(function* () {
448
+ const gcLoop = Effect.fn("document.gc.loop")(function* () {
449
449
  const current = yield* Ref.get(store);
450
450
  const now = Date.now();
451
451
  const maxIdleMs = Duration.toMillis(config.maxIdleTime);
@@ -477,18 +477,18 @@ export const layer = Layer.scoped(
477
477
  });
478
478
 
479
479
  // Run GC every minute
480
- yield* gcLoop.pipe(
480
+ yield* gcLoop().pipe(
481
481
  Effect.repeat(Schedule.spaced("1 minute")),
482
482
  Effect.fork
483
483
  );
484
484
  });
485
485
 
486
486
  // Start GC fiber
487
- yield* startGCFiber;
487
+ yield* startGCFiber();
488
488
 
489
489
  // Cleanup on shutdown
490
490
  yield* Effect.addFinalizer(() =>
491
- Effect.gen(function* () {
491
+ Effect.fn("document-manager.shutdown")(function* () {
492
492
  const current = yield* Ref.get(store);
493
493
  for (const [documentId, instance] of current) {
494
494
  // Best effort save - don't fail shutdown if storage is unavailable
@@ -501,12 +501,12 @@ export const layer = Layer.scoped(
501
501
  );
502
502
  }
503
503
  yield* Effect.logInfo("DocumentManager shutdown complete");
504
- })
504
+ })()
505
505
  );
506
506
 
507
507
  return {
508
- submit: (documentId, transaction) =>
509
- Effect.gen(function* () {
508
+ submit: Effect.fn("document.transaction.submit")(
509
+ function* (documentId: string, transaction: Transaction.Transaction) {
510
510
  const instance = yield* getOrCreateDocument(documentId);
511
511
  const submitStartTime = Date.now();
512
512
 
@@ -577,28 +577,30 @@ export const layer = Layer.scoped(
577
577
  success: true as const,
578
578
  version: validation.nextVersion,
579
579
  };
580
- }),
580
+ }
581
+ ),
581
582
 
582
- getSnapshot: (documentId) =>
583
- Effect.gen(function* () {
583
+ getSnapshot: Effect.fn("document.snapshot.get")(
584
+ function* (documentId: string) {
584
585
  const instance = yield* getOrCreateDocument(documentId);
585
586
  return instance.document.getSnapshot();
586
- }),
587
+ }
588
+ ),
587
589
 
588
- subscribe: (documentId) =>
589
- Effect.gen(function* () {
590
+ subscribe: Effect.fn("document.subscribe")(
591
+ function* (documentId: string) {
590
592
  const instance = yield* getOrCreateDocument(documentId);
591
593
  return Stream.fromPubSub(instance.pubsub);
592
- }),
593
-
594
- touch: (documentId) =>
595
- Effect.gen(function* () {
596
- const current = yield* Ref.get(store);
597
- const existing = HashMap.get(current, documentId);
598
- if (existing._tag === "Some") {
599
- yield* Ref.set(existing.value.lastActivityTime, Date.now());
600
- }
601
- }),
594
+ }
595
+ ),
596
+
597
+ touch: Effect.fn("document.touch")(function* (documentId: string) {
598
+ const current = yield* Ref.get(store);
599
+ const existing = HashMap.get(current, documentId);
600
+ if (existing._tag === "Some") {
601
+ yield* Ref.set(existing.value.lastActivityTime, Date.now());
602
+ }
603
+ }),
602
604
  };
603
605
  })
604
606
  );
package/src/HotStorage.ts CHANGED
@@ -136,59 +136,71 @@ export namespace InMemory {
136
136
  export const make = (): Layer.Layer<HotStorageTag> =>
137
137
  Layer.effect(
138
138
  HotStorageTag,
139
- Effect.gen(function* () {
139
+ Effect.fn("hot-storage.in-memory.create")(function* () {
140
140
  const store = yield* Ref.make(HashMap.empty<string, WalEntry[]>());
141
141
 
142
142
  return {
143
- append: (documentId, entry) =>
144
- Ref.update(store, (map) => {
145
- const existing = HashMap.get(map, documentId);
146
- const entries =
147
- existing._tag === "Some" ? existing.value : [];
148
- return HashMap.set(map, documentId, [...entries, entry]);
149
- }),
143
+ append: Effect.fn("hot-storage.append")(
144
+ function* (documentId: string, entry: WalEntry) {
145
+ yield* Ref.update(store, (map) => {
146
+ const existing = HashMap.get(map, documentId);
147
+ const entries =
148
+ existing._tag === "Some" ? existing.value : [];
149
+ return HashMap.set(map, documentId, [...entries, entry]);
150
+ });
151
+ }
152
+ ),
150
153
 
151
- appendWithCheck: (documentId, entry, expectedVersion) =>
152
- Effect.gen(function* () {
154
+ appendWithCheck: Effect.fn("hot-storage.append-with-check")(
155
+ function* (
156
+ documentId: string,
157
+ entry: WalEntry,
158
+ expectedVersion: number
159
+ ) {
153
160
  type CheckResult =
154
161
  | { type: "ok" }
155
162
  | { type: "gap"; lastVersion: number | undefined };
156
163
 
157
164
  // Use Ref.modify for atomic check + update
158
- const result: CheckResult = yield* Ref.modify(store, (map): [CheckResult, HashMap.HashMap<string, WalEntry[]>] => {
159
- const existing = HashMap.get(map, documentId);
160
- const entries = existing._tag === "Some" ? existing.value : [];
161
-
162
- // Find the highest version in existing entries
163
- const lastVersion = entries.length > 0
164
- ? Math.max(...entries.map((e) => e.version))
165
- : 0;
166
-
167
- // Gap check
168
- if (expectedVersion === 1) {
169
- // First entry: should have no entries with version >= 1
170
- if (lastVersion >= 1) {
171
- return [
172
- { type: "gap", lastVersion },
173
- map,
174
- ];
175
- }
176
- } else {
177
- // Not first: last entry should have version = expectedVersion - 1
178
- if (lastVersion !== expectedVersion - 1) {
179
- return [
180
- { type: "gap", lastVersion: lastVersion > 0 ? lastVersion : undefined },
181
- map,
182
- ];
165
+ const result: CheckResult = yield* Ref.modify(
166
+ store,
167
+ (map): [CheckResult, HashMap.HashMap<string, WalEntry[]>] => {
168
+ const existing = HashMap.get(map, documentId);
169
+ const entries =
170
+ existing._tag === "Some" ? existing.value : [];
171
+
172
+ // Find the highest version in existing entries
173
+ const lastVersion =
174
+ entries.length > 0
175
+ ? Math.max(...entries.map((e) => e.version))
176
+ : 0;
177
+
178
+ // Gap check
179
+ if (expectedVersion === 1) {
180
+ // First entry: should have no entries with version >= 1
181
+ if (lastVersion >= 1) {
182
+ return [{ type: "gap", lastVersion }, map];
183
+ }
184
+ } else {
185
+ // Not first: last entry should have version = expectedVersion - 1
186
+ if (lastVersion !== expectedVersion - 1) {
187
+ return [
188
+ {
189
+ type: "gap",
190
+ lastVersion: lastVersion > 0 ? lastVersion : undefined,
191
+ },
192
+ map,
193
+ ];
194
+ }
183
195
  }
184
- }
185
196
 
186
- // No gap: append and return success
187
- return [
188
- { type: "ok" },
189
- HashMap.set(map, documentId, [...entries, entry]),
190
- ];
191
- });
197
+ // No gap: append and return success
198
+ return [
199
+ { type: "ok" },
200
+ HashMap.set(map, documentId, [...entries, entry]),
201
+ ];
202
+ }
203
+ );
192
204
 
193
205
  if (result.type === "gap") {
194
206
  return yield* Effect.fail(
@@ -199,10 +211,11 @@ export namespace InMemory {
199
211
  })
200
212
  );
201
213
  }
202
- }),
214
+ }
215
+ ),
203
216
 
204
- getEntries: (documentId, sinceVersion) =>
205
- Effect.gen(function* () {
217
+ getEntries: Effect.fn("hot-storage.get-entries")(
218
+ function* (documentId: string, sinceVersion: number) {
206
219
  const current = yield* Ref.get(store);
207
220
  const existing = HashMap.get(current, documentId);
208
221
  const entries =
@@ -210,21 +223,25 @@ export namespace InMemory {
210
223
  return entries
211
224
  .filter((e) => e.version > sinceVersion)
212
225
  .sort((a, b) => a.version - b.version);
213
- }),
226
+ }
227
+ ),
214
228
 
215
- truncate: (documentId, upToVersion) =>
216
- Ref.update(store, (map) => {
217
- const existing = HashMap.get(map, documentId);
218
- if (existing._tag === "None") {
219
- return map;
220
- }
221
- const filtered = existing.value.filter(
222
- (e) => e.version > upToVersion
223
- );
224
- return HashMap.set(map, documentId, filtered);
225
- }),
229
+ truncate: Effect.fn("hot-storage.truncate")(
230
+ function* (documentId: string, upToVersion: number) {
231
+ yield* Ref.update(store, (map) => {
232
+ const existing = HashMap.get(map, documentId);
233
+ if (existing._tag === "None") {
234
+ return map;
235
+ }
236
+ const filtered = existing.value.filter(
237
+ (e) => e.version > upToVersion
238
+ );
239
+ return HashMap.set(map, documentId, filtered);
240
+ });
241
+ }
242
+ ),
226
243
  };
227
- })
244
+ })()
228
245
  );
229
246
  }
230
247
 
@@ -257,7 +257,7 @@ const createEntityHandler = <TSchema extends Primitive.AnyPrimitive>(
257
257
  coldStorage: ColdStorage,
258
258
  hotStorage: HotStorage
259
259
  ) =>
260
- Effect.gen(function* () {
260
+ Effect.fn("cluster.entity.handler.create")(function* () {
261
261
  // Get entity address to determine documentId
262
262
  const address = yield* Entity.CurrentAddress;
263
263
  const documentId = address.entityId;
@@ -406,7 +406,8 @@ const createEntityHandler = <TSchema extends Primitive.AnyPrimitive>(
406
406
  * Idempotent: skips save if already snapshotted at target version.
407
407
  * Truncate failures are non-fatal and will be retried on next snapshot.
408
408
  */
409
- const saveSnapshot = (targetVersion: number) => Effect.gen(function* () {
409
+ const saveSnapshot = Effect.fn("cluster.document.snapshot.save")(
410
+ function* (targetVersion: number) {
410
411
  const state = yield* Ref.get(stateRef);
411
412
 
412
413
  // Idempotency check: skip if already snapshotted at this version
@@ -504,12 +505,15 @@ const createEntityHandler = <TSchema extends Primitive.AnyPrimitive>(
504
505
  error: e,
505
506
  })
506
507
  );
507
- });
508
+ }
509
+ );
508
510
 
509
511
  /**
510
512
  * Check if snapshot should be triggered
511
513
  */
512
- const checkSnapshotTriggers = Effect.gen(function* () {
514
+ const checkSnapshotTriggers = Effect.fn(
515
+ "cluster.document.snapshot.check-triggers"
516
+ )(function* () {
513
517
  const state = yield* Ref.get(stateRef);
514
518
  const now = Date.now();
515
519
  const currentVersion = state.document.getVersion();
@@ -530,7 +534,7 @@ const createEntityHandler = <TSchema extends Primitive.AnyPrimitive>(
530
534
 
531
535
  // Cleanup on entity finalization
532
536
  yield* Effect.addFinalizer(() =>
533
- Effect.gen(function* () {
537
+ Effect.fn("cluster.entity.finalize")(function* () {
534
538
  // Save final snapshot before entity is garbage collected
535
539
  const state = yield* Ref.get(stateRef);
536
540
  const currentVersion = state.document.getVersion();
@@ -538,12 +542,14 @@ const createEntityHandler = <TSchema extends Primitive.AnyPrimitive>(
538
542
  yield* Metric.incrementBy(Metrics.documentsActive, -1);
539
543
  yield* Metric.increment(Metrics.documentsEvicted);
540
544
  yield* Effect.logDebug("Entity finalized", { documentId });
541
- })
545
+ })()
542
546
  );
543
547
 
544
548
  // Return RPC handlers
545
549
  return {
546
- Submit: Effect.fnUntraced(function* ({ payload }) {
550
+ Submit: Effect.fn("cluster.document.transaction.submit")(function* ({
551
+ payload,
552
+ }) {
547
553
  const submitStartTime = Date.now();
548
554
  const state = yield* Ref.get(stateRef);
549
555
 
@@ -611,7 +617,7 @@ const createEntityHandler = <TSchema extends Primitive.AnyPrimitive>(
611
617
  }));
612
618
 
613
619
  // Check snapshot triggers
614
- yield* checkSnapshotTriggers;
620
+ yield* checkSnapshotTriggers();
615
621
 
616
622
  return {
617
623
  success: true as const,
@@ -619,18 +625,18 @@ const createEntityHandler = <TSchema extends Primitive.AnyPrimitive>(
619
625
  };
620
626
  }),
621
627
 
622
- GetSnapshot: Effect.fnUntraced(function* () {
628
+ GetSnapshot: Effect.fn("cluster.document.snapshot.get")(function* () {
623
629
  const state = yield* Ref.get(stateRef);
624
630
  return state.document.getSnapshot();
625
631
  }),
626
632
 
627
- Touch: Effect.fnUntraced(function* () {
633
+ Touch: Effect.fn("cluster.document.touch")(function* () {
628
634
  // Entity touch is handled automatically by the cluster framework
629
635
  // Just update last activity time conceptually
630
636
  return void 0;
631
637
  }),
632
638
 
633
- SetPresence: Effect.fnUntraced(function* ({ payload }) {
639
+ SetPresence: Effect.fn("cluster.presence.set")(function* ({ payload }) {
634
640
  const { connectionId, entry } = payload;
635
641
 
636
642
  yield* Ref.update(stateRef, (s) => ({
@@ -651,7 +657,9 @@ const createEntityHandler = <TSchema extends Primitive.AnyPrimitive>(
651
657
  yield* PubSub.publish(state.presencePubSub, event);
652
658
  }),
653
659
 
654
- RemovePresence: Effect.fnUntraced(function* ({ payload }) {
660
+ RemovePresence: Effect.fn("cluster.presence.remove")(function* ({
661
+ payload,
662
+ }) {
655
663
  const { connectionId } = payload;
656
664
  const state = yield* Ref.get(stateRef);
657
665
 
@@ -673,16 +681,18 @@ const createEntityHandler = <TSchema extends Primitive.AnyPrimitive>(
673
681
  yield* PubSub.publish(state.presencePubSub, event);
674
682
  }),
675
683
 
676
- GetPresenceSnapshot: Effect.fnUntraced(function* () {
677
- const state = yield* Ref.get(stateRef);
678
- const presences: Record<string, PresenceEntry> = {};
679
- for (const [id, entry] of state.presences) {
680
- presences[id] = entry;
684
+ GetPresenceSnapshot: Effect.fn("cluster.presence.snapshot.get")(
685
+ function* () {
686
+ const state = yield* Ref.get(stateRef);
687
+ const presences: Record<string, PresenceEntry> = {};
688
+ for (const [id, entry] of state.presences) {
689
+ presences[id] = entry;
690
+ }
691
+ return { presences };
681
692
  }
682
- return { presences };
683
- }),
693
+ ),
684
694
  };
685
- });
695
+ })();
686
696
 
687
697
  // =============================================================================
688
698
  // Subscription Store (for managing subscriptions at the gateway level)
@@ -707,7 +717,7 @@ class SubscriptionStoreTag extends Context.Tag(
707
717
 
708
718
  const subscriptionStoreLayer = Layer.effect(
709
719
  SubscriptionStoreTag,
710
- Effect.gen(function* () {
720
+ Effect.fn("cluster.subscriptions.layer.create")(function* () {
711
721
  const documentPubSubs = yield* Ref.make(
712
722
  HashMap.empty<string, PubSub.PubSub<Protocol.ServerMessage>>()
713
723
  );
@@ -716,37 +726,39 @@ const subscriptionStoreLayer = Layer.effect(
716
726
  );
717
727
 
718
728
  return {
719
- getOrCreatePubSub: (documentId: string) =>
720
- Effect.gen(function* () {
721
- const current = yield* Ref.get(documentPubSubs);
722
- const existing = HashMap.get(current, documentId);
723
- if (existing._tag === "Some") {
724
- return existing.value;
725
- }
729
+ getOrCreatePubSub: Effect.fn(
730
+ "cluster.subscriptions.pubsub.get-or-create"
731
+ )(function* (documentId: string) {
732
+ const current = yield* Ref.get(documentPubSubs);
733
+ const existing = HashMap.get(current, documentId);
734
+ if (existing._tag === "Some") {
735
+ return existing.value;
736
+ }
726
737
 
727
- const pubsub = yield* PubSub.unbounded<Protocol.ServerMessage>();
728
- yield* Ref.update(documentPubSubs, (map) =>
729
- HashMap.set(map, documentId, pubsub)
730
- );
731
- return pubsub;
732
- }),
733
-
734
- getOrCreatePresencePubSub: (documentId: string) =>
735
- Effect.gen(function* () {
736
- const current = yield* Ref.get(presencePubSubs);
737
- const existing = HashMap.get(current, documentId);
738
- if (existing._tag === "Some") {
739
- return existing.value;
740
- }
738
+ const pubsub = yield* PubSub.unbounded<Protocol.ServerMessage>();
739
+ yield* Ref.update(documentPubSubs, (map) =>
740
+ HashMap.set(map, documentId, pubsub)
741
+ );
742
+ return pubsub;
743
+ }),
744
+
745
+ getOrCreatePresencePubSub: Effect.fn(
746
+ "cluster.subscriptions.presence-pubsub.get-or-create"
747
+ )(function* (documentId: string) {
748
+ const current = yield* Ref.get(presencePubSubs);
749
+ const existing = HashMap.get(current, documentId);
750
+ if (existing._tag === "Some") {
751
+ return existing.value;
752
+ }
741
753
 
742
- const pubsub = yield* PubSub.unbounded<PresenceEvent>();
743
- yield* Ref.update(presencePubSubs, (map) =>
744
- HashMap.set(map, documentId, pubsub)
745
- );
746
- return pubsub;
747
- }),
754
+ const pubsub = yield* PubSub.unbounded<PresenceEvent>();
755
+ yield* Ref.update(presencePubSubs, (map) =>
756
+ HashMap.set(map, documentId, pubsub)
757
+ );
758
+ return pubsub;
759
+ }),
748
760
  };
749
- })
761
+ })()
750
762
  );
751
763
 
752
764
  // =============================================================================