@voidhash/mimic-effect 1.0.0-beta.6 → 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 (43) hide show
  1. package/.turbo/turbo-build.log +90 -90
  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/types.d.cts +3 -3
  36. package/package.json +3 -3
  37. package/src/ColdStorage.ts +21 -12
  38. package/src/DocumentManager.ts +49 -47
  39. package/src/HotStorage.ts +75 -58
  40. package/src/MimicClusterServerEngine.ts +61 -49
  41. package/src/MimicServer.ts +83 -75
  42. package/src/MimicServerEngine.ts +2 -2
  43. package/src/PresenceManager.ts +44 -34
@@ -1 +1 @@
1
- {"version":3,"file":"MimicServerEngine.mjs","names":["documentManagerLayer","presenceManagerLayer"],"sources":["../src/MimicServerEngine.ts"],"sourcesContent":["/**\n * @voidhash/mimic-effect - MimicServerEngine\n *\n * Core document management service for Mimic real-time collaboration.\n * Handles document lifecycle, storage, presence, and transaction processing.\n *\n * This is the engine layer - for WebSocket routes, use MimicServer.layerHttpLayerRouter().\n */\nimport {\n Context,\n Duration,\n Effect,\n Layer,\n Scope,\n Stream,\n} from \"effect\";\nimport type { Presence, Primitive, Transaction } from \"@voidhash/mimic\";\nimport type {\n MimicServerEngineConfig,\n PresenceEntry,\n PresenceEvent,\n PresenceSnapshot,\n ResolvedConfig,\n} from \"./Types\";\nimport type * as Protocol from \"./Protocol\";\nimport { ColdStorageTag } from \"./ColdStorage\";\nimport { HotStorageTag } from \"./HotStorage\";\nimport { MimicAuthServiceTag } from \"./MimicAuthService\";\nimport {\n DocumentManagerTag,\n DocumentManagerConfigTag,\n layer as documentManagerLayer,\n type SubmitResult,\n type DocumentManagerError,\n} from \"./DocumentManager\";\nimport {\n PresenceManagerTag,\n layer as presenceManagerLayer,\n} from \"./PresenceManager\";\n\n// =============================================================================\n// MimicServerEngine Interface\n// =============================================================================\n\n/**\n * MimicServerEngine service interface.\n *\n * Provides document management operations for Mimic collaboration.\n * Use MimicServer.layerHttpLayerRouter() to create WebSocket routes.\n */\nexport interface MimicServerEngine {\n /**\n * Submit a transaction to a document.\n * Authorization is checked against the auth service.\n * May fail with DocumentManagerError if storage is unavailable.\n */\n readonly submit: (\n documentId: string,\n transaction: Transaction.Transaction\n ) => Effect.Effect<SubmitResult, DocumentManagerError>;\n\n /**\n * Get document snapshot (current state and version).\n * May fail with DocumentManagerError if storage is unavailable.\n */\n readonly getSnapshot: (\n documentId: string\n ) => Effect.Effect<{ state: unknown; version: number }, DocumentManagerError>;\n\n /**\n * Subscribe to document broadcasts (transactions).\n * Returns a stream of server messages.\n * Requires a Scope for cleanup when the subscription ends.\n * May fail with DocumentManagerError if storage is unavailable.\n */\n readonly subscribe: (\n documentId: string\n ) => Effect.Effect<Stream.Stream<Protocol.ServerMessage, never, never>, DocumentManagerError, Scope.Scope>;\n\n /**\n * Touch document to prevent idle garbage collection.\n */\n readonly touch: (documentId: string) => Effect.Effect<void, never>;\n\n /**\n * Get presence snapshot for a document.\n */\n readonly getPresenceSnapshot: (\n documentId: string\n ) => Effect.Effect<PresenceSnapshot, never>;\n\n /**\n * Set presence for a connection.\n */\n readonly setPresence: (\n documentId: string,\n connectionId: string,\n entry: PresenceEntry\n ) => Effect.Effect<void, never>;\n\n /**\n * Remove presence for a connection.\n */\n readonly removePresence: (\n documentId: string,\n connectionId: string\n ) => Effect.Effect<void, never>;\n\n /**\n * Subscribe to presence events for a document.\n * Requires a Scope for cleanup when the subscription ends.\n */\n readonly subscribePresence: (\n documentId: string\n ) => Effect.Effect<Stream.Stream<PresenceEvent, never, never>, never, Scope.Scope>;\n\n /**\n * Resolved engine configuration.\n * Used by route layer to access schema, presence config, etc.\n */\n readonly config: ResolvedConfig<Primitive.AnyPrimitive>;\n}\n\n// =============================================================================\n// Context Tag\n// =============================================================================\n\n/**\n * Context tag for MimicServerEngine\n */\nexport class MimicServerEngineTag extends Context.Tag(\n \"@voidhash/mimic-effect/MimicServerEngine\"\n)<MimicServerEngineTag, MimicServerEngine>() {}\n\n// =============================================================================\n// Default Configuration\n// =============================================================================\n\nconst DEFAULT_MAX_IDLE_TIME = Duration.minutes(5);\nconst DEFAULT_MAX_TRANSACTION_HISTORY = 1000;\nconst DEFAULT_SNAPSHOT_INTERVAL = Duration.minutes(5);\nconst DEFAULT_SNAPSHOT_THRESHOLD = 100;\n\n/**\n * Resolve configuration with defaults\n */\nconst resolveConfig = <TSchema extends Primitive.AnyPrimitive>(\n config: MimicServerEngineConfig<TSchema>\n): ResolvedConfig<TSchema> => ({\n schema: config.schema,\n initial: config.initial,\n presence: config.presence,\n maxIdleTime: config.maxIdleTime\n ? Duration.decode(config.maxIdleTime)\n : DEFAULT_MAX_IDLE_TIME,\n maxTransactionHistory:\n config.maxTransactionHistory ?? DEFAULT_MAX_TRANSACTION_HISTORY,\n snapshot: {\n interval: config.snapshot?.interval\n ? Duration.decode(config.snapshot.interval)\n : DEFAULT_SNAPSHOT_INTERVAL,\n transactionThreshold:\n config.snapshot?.transactionThreshold ?? DEFAULT_SNAPSHOT_THRESHOLD,\n },\n});\n\n// =============================================================================\n// Factory\n// =============================================================================\n\n/**\n * Create a MimicServerEngine layer.\n *\n * This creates the core document management service. To expose it via WebSocket,\n * use MimicServer.layerHttpLayerRouter().\n *\n * @example\n * ```typescript\n * // 1. Create the engine\n * const Engine = MimicServerEngine.make({\n * schema: DocSchema,\n * initial: { title: \"Untitled\" },\n * presence: CursorPresence,\n * maxIdleTime: \"5 minutes\",\n * snapshot: { interval: \"5 minutes\", transactionThreshold: 100 },\n * })\n *\n * // 2. Create the WebSocket route\n * const MimicRoute = MimicServer.layerHttpLayerRouter({\n * path: \"/mimic\",\n * })\n *\n * // 3. Wire together\n * const MimicLive = MimicRoute.pipe(\n * Layer.provide(Engine),\n * Layer.provide(ColdStorage.InMemory.make()),\n * Layer.provide(HotStorage.InMemory.make()),\n * Layer.provide(MimicAuthService.NoAuth.make()),\n * )\n * ```\n */\nexport const make = <TSchema extends Primitive.AnyPrimitive>(\n config: MimicServerEngineConfig<TSchema>\n): Layer.Layer<\n MimicServerEngineTag,\n never,\n ColdStorageTag | HotStorageTag | MimicAuthServiceTag\n> => {\n const resolvedConfig = resolveConfig(config);\n\n // Create config layer for DocumentManager\n const configLayer = Layer.succeed(\n DocumentManagerConfigTag,\n resolvedConfig as ResolvedConfig<Primitive.AnyPrimitive>\n );\n\n // Create internal layers\n const internalLayers = Layer.mergeAll(\n documentManagerLayer.pipe(Layer.provide(configLayer)),\n presenceManagerLayer\n );\n\n return Layer.scoped(\n MimicServerEngineTag,\n Effect.gen(function* () {\n const documentManager = yield* DocumentManagerTag;\n const presenceManager = yield* PresenceManagerTag;\n\n const engine: MimicServerEngine = {\n submit: (documentId, transaction) =>\n documentManager.submit(documentId, transaction),\n\n getSnapshot: (documentId) => documentManager.getSnapshot(documentId),\n\n subscribe: (documentId) =>\n documentManager.subscribe(documentId) as Effect.Effect<\n Stream.Stream<Protocol.ServerMessage, never, never>,\n never\n >,\n\n touch: (documentId) => documentManager.touch(documentId),\n\n getPresenceSnapshot: (documentId) =>\n presenceManager.getSnapshot(documentId),\n\n setPresence: (documentId, connectionId, entry) =>\n presenceManager.set(documentId, connectionId, entry),\n\n removePresence: (documentId, connectionId) =>\n presenceManager.remove(documentId, connectionId),\n\n subscribePresence: (documentId) =>\n presenceManager.subscribe(documentId),\n\n config: resolvedConfig as ResolvedConfig<Primitive.AnyPrimitive>,\n };\n\n return engine;\n })\n ).pipe(Layer.provide(internalLayers));\n};\n\n// =============================================================================\n// Re-export namespace\n// =============================================================================\n\nexport const MimicServerEngine = {\n Tag: MimicServerEngineTag,\n make,\n};\n\n// =============================================================================\n// Re-export SubmitResult type\n// =============================================================================\n\nexport type { SubmitResult };\n"],"mappings":";;;;;;;;;;;;;;;;AAkIA,IAAa,uBAAb,cAA0C,QAAQ,IAChD,2CACD,EAA2C,CAAC;AAM7C,MAAM,wBAAwB,SAAS,QAAQ,EAAE;AACjD,MAAM,kCAAkC;AACxC,MAAM,4BAA4B,SAAS,QAAQ,EAAE;AACrD,MAAM,6BAA6B;;;;AAKnC,MAAM,iBACJ,WAC4B;;QAAC;EAC7B,QAAQ,OAAO;EACf,SAAS,OAAO;EAChB,UAAU,OAAO;EACjB,aAAa,OAAO,cAChB,SAAS,OAAO,OAAO,YAAY,GACnC;EACJ,gDACE,OAAO,8FAAyB;EAClC,UAAU;GACR,+BAAU,OAAO,8EAAU,YACvB,SAAS,OAAO,OAAO,SAAS,SAAS,GACzC;GACJ,oEACE,OAAO,gFAAU,6FAAwB;GAC5C;EACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCD,MAAa,QACX,WAKG;CACH,MAAM,iBAAiB,cAAc,OAAO;CAG5C,MAAM,cAAc,MAAM,QACxB,0BACA,eACD;CAGD,MAAM,iBAAiB,MAAM,SAC3BA,MAAqB,KAAK,MAAM,QAAQ,YAAY,CAAC,EACrDC,QACD;AAED,QAAO,MAAM,OACX,sBACA,OAAO,IAAI,aAAa;EACtB,MAAM,kBAAkB,OAAO;EAC/B,MAAM,kBAAkB,OAAO;AA+B/B,SA7BkC;GAChC,SAAS,YAAY,gBACnB,gBAAgB,OAAO,YAAY,YAAY;GAEjD,cAAc,eAAe,gBAAgB,YAAY,WAAW;GAEpE,YAAY,eACV,gBAAgB,UAAU,WAAW;GAKvC,QAAQ,eAAe,gBAAgB,MAAM,WAAW;GAExD,sBAAsB,eACpB,gBAAgB,YAAY,WAAW;GAEzC,cAAc,YAAY,cAAc,UACtC,gBAAgB,IAAI,YAAY,cAAc,MAAM;GAEtD,iBAAiB,YAAY,iBAC3B,gBAAgB,OAAO,YAAY,aAAa;GAElD,oBAAoB,eAClB,gBAAgB,UAAU,WAAW;GAEvC,QAAQ;GACT;GAGD,CACH,CAAC,KAAK,MAAM,QAAQ,eAAe,CAAC;;AAOvC,MAAa,oBAAoB;CAC/B,KAAK;CACL;CACD"}
1
+ {"version":3,"file":"MimicServerEngine.mjs","names":["documentManagerLayer","presenceManagerLayer"],"sources":["../src/MimicServerEngine.ts"],"sourcesContent":["/**\n * @voidhash/mimic-effect - MimicServerEngine\n *\n * Core document management service for Mimic real-time collaboration.\n * Handles document lifecycle, storage, presence, and transaction processing.\n *\n * This is the engine layer - for WebSocket routes, use MimicServer.layerHttpLayerRouter().\n */\nimport {\n Context,\n Duration,\n Effect,\n Layer,\n Scope,\n Stream,\n} from \"effect\";\nimport type { Presence, Primitive, Transaction } from \"@voidhash/mimic\";\nimport type {\n MimicServerEngineConfig,\n PresenceEntry,\n PresenceEvent,\n PresenceSnapshot,\n ResolvedConfig,\n} from \"./Types\";\nimport type * as Protocol from \"./Protocol\";\nimport { ColdStorageTag } from \"./ColdStorage\";\nimport { HotStorageTag } from \"./HotStorage\";\nimport { MimicAuthServiceTag } from \"./MimicAuthService\";\nimport {\n DocumentManagerTag,\n DocumentManagerConfigTag,\n layer as documentManagerLayer,\n type SubmitResult,\n type DocumentManagerError,\n} from \"./DocumentManager\";\nimport {\n PresenceManagerTag,\n layer as presenceManagerLayer,\n} from \"./PresenceManager\";\n\n// =============================================================================\n// MimicServerEngine Interface\n// =============================================================================\n\n/**\n * MimicServerEngine service interface.\n *\n * Provides document management operations for Mimic collaboration.\n * Use MimicServer.layerHttpLayerRouter() to create WebSocket routes.\n */\nexport interface MimicServerEngine {\n /**\n * Submit a transaction to a document.\n * Authorization is checked against the auth service.\n * May fail with DocumentManagerError if storage is unavailable.\n */\n readonly submit: (\n documentId: string,\n transaction: Transaction.Transaction\n ) => Effect.Effect<SubmitResult, DocumentManagerError>;\n\n /**\n * Get document snapshot (current state and version).\n * May fail with DocumentManagerError if storage is unavailable.\n */\n readonly getSnapshot: (\n documentId: string\n ) => Effect.Effect<{ state: unknown; version: number }, DocumentManagerError>;\n\n /**\n * Subscribe to document broadcasts (transactions).\n * Returns a stream of server messages.\n * Requires a Scope for cleanup when the subscription ends.\n * May fail with DocumentManagerError if storage is unavailable.\n */\n readonly subscribe: (\n documentId: string\n ) => Effect.Effect<Stream.Stream<Protocol.ServerMessage, never, never>, DocumentManagerError, Scope.Scope>;\n\n /**\n * Touch document to prevent idle garbage collection.\n */\n readonly touch: (documentId: string) => Effect.Effect<void, never>;\n\n /**\n * Get presence snapshot for a document.\n */\n readonly getPresenceSnapshot: (\n documentId: string\n ) => Effect.Effect<PresenceSnapshot, never>;\n\n /**\n * Set presence for a connection.\n */\n readonly setPresence: (\n documentId: string,\n connectionId: string,\n entry: PresenceEntry\n ) => Effect.Effect<void, never>;\n\n /**\n * Remove presence for a connection.\n */\n readonly removePresence: (\n documentId: string,\n connectionId: string\n ) => Effect.Effect<void, never>;\n\n /**\n * Subscribe to presence events for a document.\n * Requires a Scope for cleanup when the subscription ends.\n */\n readonly subscribePresence: (\n documentId: string\n ) => Effect.Effect<Stream.Stream<PresenceEvent, never, never>, never, Scope.Scope>;\n\n /**\n * Resolved engine configuration.\n * Used by route layer to access schema, presence config, etc.\n */\n readonly config: ResolvedConfig<Primitive.AnyPrimitive>;\n}\n\n// =============================================================================\n// Context Tag\n// =============================================================================\n\n/**\n * Context tag for MimicServerEngine\n */\nexport class MimicServerEngineTag extends Context.Tag(\n \"@voidhash/mimic-effect/MimicServerEngine\"\n)<MimicServerEngineTag, MimicServerEngine>() {}\n\n// =============================================================================\n// Default Configuration\n// =============================================================================\n\nconst DEFAULT_MAX_IDLE_TIME = Duration.minutes(5);\nconst DEFAULT_MAX_TRANSACTION_HISTORY = 1000;\nconst DEFAULT_SNAPSHOT_INTERVAL = Duration.minutes(5);\nconst DEFAULT_SNAPSHOT_THRESHOLD = 100;\n\n/**\n * Resolve configuration with defaults\n */\nconst resolveConfig = <TSchema extends Primitive.AnyPrimitive>(\n config: MimicServerEngineConfig<TSchema>\n): ResolvedConfig<TSchema> => ({\n schema: config.schema,\n initial: config.initial,\n presence: config.presence,\n maxIdleTime: config.maxIdleTime\n ? Duration.decode(config.maxIdleTime)\n : DEFAULT_MAX_IDLE_TIME,\n maxTransactionHistory:\n config.maxTransactionHistory ?? DEFAULT_MAX_TRANSACTION_HISTORY,\n snapshot: {\n interval: config.snapshot?.interval\n ? Duration.decode(config.snapshot.interval)\n : DEFAULT_SNAPSHOT_INTERVAL,\n transactionThreshold:\n config.snapshot?.transactionThreshold ?? DEFAULT_SNAPSHOT_THRESHOLD,\n },\n});\n\n// =============================================================================\n// Factory\n// =============================================================================\n\n/**\n * Create a MimicServerEngine layer.\n *\n * This creates the core document management service. To expose it via WebSocket,\n * use MimicServer.layerHttpLayerRouter().\n *\n * @example\n * ```typescript\n * // 1. Create the engine\n * const Engine = MimicServerEngine.make({\n * schema: DocSchema,\n * initial: { title: \"Untitled\" },\n * presence: CursorPresence,\n * maxIdleTime: \"5 minutes\",\n * snapshot: { interval: \"5 minutes\", transactionThreshold: 100 },\n * })\n *\n * // 2. Create the WebSocket route\n * const MimicRoute = MimicServer.layerHttpLayerRouter({\n * path: \"/mimic\",\n * })\n *\n * // 3. Wire together\n * const MimicLive = MimicRoute.pipe(\n * Layer.provide(Engine),\n * Layer.provide(ColdStorage.InMemory.make()),\n * Layer.provide(HotStorage.InMemory.make()),\n * Layer.provide(MimicAuthService.NoAuth.make()),\n * )\n * ```\n */\nexport const make = <TSchema extends Primitive.AnyPrimitive>(\n config: MimicServerEngineConfig<TSchema>\n): Layer.Layer<\n MimicServerEngineTag,\n never,\n ColdStorageTag | HotStorageTag | MimicAuthServiceTag\n> => {\n const resolvedConfig = resolveConfig(config);\n\n // Create config layer for DocumentManager\n const configLayer = Layer.succeed(\n DocumentManagerConfigTag,\n resolvedConfig as ResolvedConfig<Primitive.AnyPrimitive>\n );\n\n // Create internal layers\n const internalLayers = Layer.mergeAll(\n documentManagerLayer.pipe(Layer.provide(configLayer)),\n presenceManagerLayer\n );\n\n return Layer.scoped(\n MimicServerEngineTag,\n Effect.fn(\"mimic-server-engine.make\")(function* () {\n const documentManager = yield* DocumentManagerTag;\n const presenceManager = yield* PresenceManagerTag;\n\n const engine: MimicServerEngine = {\n submit: (documentId, transaction) =>\n documentManager.submit(documentId, transaction),\n\n getSnapshot: (documentId) => documentManager.getSnapshot(documentId),\n\n subscribe: (documentId) =>\n documentManager.subscribe(documentId) as Effect.Effect<\n Stream.Stream<Protocol.ServerMessage, never, never>,\n never\n >,\n\n touch: (documentId) => documentManager.touch(documentId),\n\n getPresenceSnapshot: (documentId) =>\n presenceManager.getSnapshot(documentId),\n\n setPresence: (documentId, connectionId, entry) =>\n presenceManager.set(documentId, connectionId, entry),\n\n removePresence: (documentId, connectionId) =>\n presenceManager.remove(documentId, connectionId),\n\n subscribePresence: (documentId) =>\n presenceManager.subscribe(documentId),\n\n config: resolvedConfig as ResolvedConfig<Primitive.AnyPrimitive>,\n };\n\n return engine;\n })()\n ).pipe(Layer.provide(internalLayers));\n};\n\n// =============================================================================\n// Re-export namespace\n// =============================================================================\n\nexport const MimicServerEngine = {\n Tag: MimicServerEngineTag,\n make,\n};\n\n// =============================================================================\n// Re-export SubmitResult type\n// =============================================================================\n\nexport type { SubmitResult };\n"],"mappings":";;;;;;;;;;;;;;;;AAkIA,IAAa,uBAAb,cAA0C,QAAQ,IAChD,2CACD,EAA2C,CAAC;AAM7C,MAAM,wBAAwB,SAAS,QAAQ,EAAE;AACjD,MAAM,kCAAkC;AACxC,MAAM,4BAA4B,SAAS,QAAQ,EAAE;AACrD,MAAM,6BAA6B;;;;AAKnC,MAAM,iBACJ,WAC4B;;QAAC;EAC7B,QAAQ,OAAO;EACf,SAAS,OAAO;EAChB,UAAU,OAAO;EACjB,aAAa,OAAO,cAChB,SAAS,OAAO,OAAO,YAAY,GACnC;EACJ,gDACE,OAAO,8FAAyB;EAClC,UAAU;GACR,+BAAU,OAAO,8EAAU,YACvB,SAAS,OAAO,OAAO,SAAS,SAAS,GACzC;GACJ,oEACE,OAAO,gFAAU,6FAAwB;GAC5C;EACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCD,MAAa,QACX,WAKG;CACH,MAAM,iBAAiB,cAAc,OAAO;CAG5C,MAAM,cAAc,MAAM,QACxB,0BACA,eACD;CAGD,MAAM,iBAAiB,MAAM,SAC3BA,MAAqB,KAAK,MAAM,QAAQ,YAAY,CAAC,EACrDC,QACD;AAED,QAAO,MAAM,OACX,sBACA,OAAO,GAAG,2BAA2B,CAAC,aAAa;EACjD,MAAM,kBAAkB,OAAO;EAC/B,MAAM,kBAAkB,OAAO;AA+B/B,SA7BkC;GAChC,SAAS,YAAY,gBACnB,gBAAgB,OAAO,YAAY,YAAY;GAEjD,cAAc,eAAe,gBAAgB,YAAY,WAAW;GAEpE,YAAY,eACV,gBAAgB,UAAU,WAAW;GAKvC,QAAQ,eAAe,gBAAgB,MAAM,WAAW;GAExD,sBAAsB,eACpB,gBAAgB,YAAY,WAAW;GAEzC,cAAc,YAAY,cAAc,UACtC,gBAAgB,IAAI,YAAY,cAAc,MAAM;GAEtD,iBAAiB,YAAY,iBAC3B,gBAAgB,OAAO,YAAY,aAAa;GAElD,oBAAoB,eAClB,gBAAgB,UAAU,WAAW;GAEvC,QAAQ;GACT;GAGD,EAAE,CACL,CAAC,KAAK,MAAM,QAAQ,eAAe,CAAC;;AAOvC,MAAa,oBAAoB;CAC/B,KAAK;CACL;CACD"}
@@ -20,7 +20,7 @@ const layer = effect.Layer.effect(PresenceManagerTag, effect.Effect.gen(function
20
20
  /**
21
21
  * Get or create presence state for a document
22
22
  */
23
- const getOrCreateDocumentState = (documentId) => effect.Effect.gen(function* () {
23
+ const getOrCreateDocumentState = effect.Effect.fn("presence.document-state.get-or-create")(function* (documentId) {
24
24
  const current = yield* effect.Ref.get(store);
25
25
  const existing = effect.HashMap.get(current, documentId);
26
26
  if (existing._tag === "Some") return existing.value;
@@ -33,7 +33,7 @@ const layer = effect.Layer.effect(PresenceManagerTag, effect.Effect.gen(function
33
33
  return state;
34
34
  });
35
35
  return {
36
- getSnapshot: (documentId) => effect.Effect.gen(function* () {
36
+ getSnapshot: effect.Effect.fn("presence.snapshot.get")(function* (documentId) {
37
37
  const current = yield* effect.Ref.get(store);
38
38
  const existing = effect.HashMap.get(current, documentId);
39
39
  if (existing._tag === "None") return { presences: {} };
@@ -41,7 +41,7 @@ const layer = effect.Layer.effect(PresenceManagerTag, effect.Effect.gen(function
41
41
  for (const [id, entry] of existing.value.presences) presences[id] = entry;
42
42
  return { presences };
43
43
  }),
44
- set: (documentId, connectionId, entry) => effect.Effect.gen(function* () {
44
+ set: effect.Effect.fn("presence.set")(function* (documentId, connectionId, entry) {
45
45
  const state = yield* getOrCreateDocumentState(documentId);
46
46
  yield* effect.Ref.update(store, (map) => {
47
47
  const existing = effect.HashMap.get(map, documentId);
@@ -58,7 +58,7 @@ const layer = effect.Layer.effect(PresenceManagerTag, effect.Effect.gen(function
58
58
  };
59
59
  yield* effect.PubSub.publish(state.pubsub, event);
60
60
  }),
61
- remove: (documentId, connectionId) => effect.Effect.gen(function* () {
61
+ remove: effect.Effect.fn("presence.remove")(function* (documentId, connectionId) {
62
62
  const current = yield* effect.Ref.get(store);
63
63
  const existing = effect.HashMap.get(current, documentId);
64
64
  if (existing._tag === "None") return;
@@ -75,7 +75,7 @@ const layer = effect.Layer.effect(PresenceManagerTag, effect.Effect.gen(function
75
75
  };
76
76
  yield* effect.PubSub.publish(existing.value.pubsub, event);
77
77
  }),
78
- subscribe: (documentId) => effect.Effect.gen(function* () {
78
+ subscribe: effect.Effect.fn("presence.subscribe")(function* (documentId) {
79
79
  const state = yield* getOrCreateDocumentState(documentId);
80
80
  return effect.Stream.fromPubSub(state.pubsub);
81
81
  })
@@ -1 +1 @@
1
- {"version":3,"file":"PresenceManager.d.cts","names":[],"sources":["../src/PresenceManager.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;AAgEO,UA/BU,eAAA,CA+BH;EAAM;AACnB;;gDA1BM,MAAA,CAAO,OAAO;;;AAmCrB;EAoJa,SAAA,GAAA,EAAA,CAAA,UAGZ,EAAA,MAAA,EAAA,YAAA,EAAA,MAAA,EAAA,KAAA,EAlLU,aAkLV,EAAA,GAjLM,MAAA,CAAO,MAiLb,CAAA,IAAA,CAAA;;;;iEAzKM,MAAA,CAAO;;;;;8CAQP,MAAA,CAAO,OAAO,MAAA,CAAO,OAAO,uBAAuB,KAAA,CAAM;;cAC/D;;;;cASY,kBAAA,SAA2B,uBAAA;cAoJ3B"}
1
+ {"version":3,"file":"PresenceManager.d.cts","names":[],"sources":["../src/PresenceManager.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;AAgEO,UA/BU,eAAA,CA+BH;EAAM;AACnB;;gDA1BM,MAAA,CAAO,OAAO;;;AAmCrB;EA8Ja,SAAA,GAAA,EAAA,CAAA,UAGZ,EAAA,MAAA,EAAA,YAAA,EAAA,MAAA,EAAA,KAAA,EA5LU,aA4LV,EAAA,GA3LM,MAAA,CAAO,MA2Lb,CAAA,IAAA,CAAA;;;;iEAnLM,MAAA,CAAO;;;;;8CAQP,MAAA,CAAO,OAAO,MAAA,CAAO,OAAO,uBAAuB,KAAA,CAAM;;cAC/D;;;;cASY,kBAAA,SAA2B,uBAAA;cA8J3B"}
@@ -1 +1 @@
1
- {"version":3,"file":"PresenceManager.d.mts","names":[],"sources":["../src/PresenceManager.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;AAgEO,UA/BU,eAAA,CA+BH;EAAM;AACnB;;gDA1BM,MAAA,CAAO,OAAO;;;AAmCrB;EAoJa,SAAA,GAAA,EAAA,CAAA,UAGZ,EAAA,MAAA,EAAA,YAAA,EAAA,MAAA,EAAA,KAAA,EAlLU,aAkLV,EAAA,GAjLM,MAAA,CAAO,MAiLb,CAAA,IAAA,CAAA;;;;iEAzKM,MAAA,CAAO;;;;;8CAQP,MAAA,CAAO,OAAO,MAAA,CAAO,OAAO,uBAAuB,KAAA,CAAM;;cAC/D;;;;cASY,kBAAA,SAA2B,uBAAA;cAoJ3B"}
1
+ {"version":3,"file":"PresenceManager.d.mts","names":[],"sources":["../src/PresenceManager.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;AAgEO,UA/BU,eAAA,CA+BH;EAAM;AACnB;;gDA1BM,MAAA,CAAO,OAAO;;;AAmCrB;EA8Ja,SAAA,GAAA,EAAA,CAAA,UAGZ,EAAA,MAAA,EAAA,YAAA,EAAA,MAAA,EAAA,KAAA,EA5LU,aA4LV,EAAA,GA3LM,MAAA,CAAO,MA2Lb,CAAA,IAAA,CAAA;;;;iEAnLM,MAAA,CAAO;;;;;8CAQP,MAAA,CAAO,OAAO,MAAA,CAAO,OAAO,uBAAuB,KAAA,CAAM;;cAC/D;;;;cASY,kBAAA,SAA2B,uBAAA;cA8J3B"}
@@ -20,7 +20,7 @@ const layer = Layer.effect(PresenceManagerTag, Effect.gen(function* () {
20
20
  /**
21
21
  * Get or create presence state for a document
22
22
  */
23
- const getOrCreateDocumentState = (documentId) => Effect.gen(function* () {
23
+ const getOrCreateDocumentState = Effect.fn("presence.document-state.get-or-create")(function* (documentId) {
24
24
  const current = yield* Ref.get(store);
25
25
  const existing = HashMap.get(current, documentId);
26
26
  if (existing._tag === "Some") return existing.value;
@@ -33,7 +33,7 @@ const layer = Layer.effect(PresenceManagerTag, Effect.gen(function* () {
33
33
  return state;
34
34
  });
35
35
  return {
36
- getSnapshot: (documentId) => Effect.gen(function* () {
36
+ getSnapshot: Effect.fn("presence.snapshot.get")(function* (documentId) {
37
37
  const current = yield* Ref.get(store);
38
38
  const existing = HashMap.get(current, documentId);
39
39
  if (existing._tag === "None") return { presences: {} };
@@ -41,7 +41,7 @@ const layer = Layer.effect(PresenceManagerTag, Effect.gen(function* () {
41
41
  for (const [id, entry] of existing.value.presences) presences[id] = entry;
42
42
  return { presences };
43
43
  }),
44
- set: (documentId, connectionId, entry) => Effect.gen(function* () {
44
+ set: Effect.fn("presence.set")(function* (documentId, connectionId, entry) {
45
45
  const state = yield* getOrCreateDocumentState(documentId);
46
46
  yield* Ref.update(store, (map) => {
47
47
  const existing = HashMap.get(map, documentId);
@@ -58,7 +58,7 @@ const layer = Layer.effect(PresenceManagerTag, Effect.gen(function* () {
58
58
  };
59
59
  yield* PubSub.publish(state.pubsub, event);
60
60
  }),
61
- remove: (documentId, connectionId) => Effect.gen(function* () {
61
+ remove: Effect.fn("presence.remove")(function* (documentId, connectionId) {
62
62
  const current = yield* Ref.get(store);
63
63
  const existing = HashMap.get(current, documentId);
64
64
  if (existing._tag === "None") return;
@@ -75,7 +75,7 @@ const layer = Layer.effect(PresenceManagerTag, Effect.gen(function* () {
75
75
  };
76
76
  yield* PubSub.publish(existing.value.pubsub, event);
77
77
  }),
78
- subscribe: (documentId) => Effect.gen(function* () {
78
+ subscribe: Effect.fn("presence.subscribe")(function* (documentId) {
79
79
  const state = yield* getOrCreateDocumentState(documentId);
80
80
  return Stream.fromPubSub(state.pubsub);
81
81
  })
@@ -1 +1 @@
1
- {"version":3,"file":"PresenceManager.mjs","names":["layer: Layer.Layer<PresenceManagerTag>","state: DocumentPresenceState","presences: Record<string, PresenceEntry>","Metrics.presenceUpdates","Metrics.presenceActive","event: PresenceEvent"],"sources":["../src/PresenceManager.ts"],"sourcesContent":["/**\n * @voidhash/mimic-effect - PresenceManager\n *\n * Internal service for managing presence state per document.\n */\nimport {\n Context,\n Effect,\n HashMap,\n Layer,\n Metric,\n PubSub,\n Ref,\n Scope,\n Stream,\n} from \"effect\";\nimport type {\n PresenceEntry,\n PresenceEvent,\n PresenceSnapshot,\n} from \"./Types\";\nimport * as Metrics from \"./Metrics\";\n\n// =============================================================================\n// PresenceManager Interface\n// =============================================================================\n\n/**\n * Internal service for managing presence state per document.\n *\n * Presence is ephemeral state associated with connections, not persisted.\n * Each document has its own set of presences, keyed by connectionId.\n */\nexport interface PresenceManager {\n /**\n * Get snapshot of all presences for a document.\n */\n readonly getSnapshot: (\n documentId: string\n ) => Effect.Effect<PresenceSnapshot>;\n\n /**\n * Set/update presence for a connection.\n */\n readonly set: (\n documentId: string,\n connectionId: string,\n entry: PresenceEntry\n ) => Effect.Effect<void>;\n\n /**\n * Remove presence for a connection (on disconnect).\n */\n readonly remove: (\n documentId: string,\n connectionId: string\n ) => Effect.Effect<void>;\n\n /**\n * Subscribe to presence events for a document.\n * Returns a stream of presence update/remove events.\n */\n readonly subscribe: (\n documentId: string\n ) => Effect.Effect<Stream.Stream<PresenceEvent>, never, Scope.Scope>;\n}\n\n// =============================================================================\n// Context Tag\n// =============================================================================\n\n/**\n * Context tag for PresenceManager service\n */\nexport class PresenceManagerTag extends Context.Tag(\n \"@voidhash/mimic-effect/PresenceManager\"\n)<PresenceManagerTag, PresenceManager>() {}\n\n// =============================================================================\n// Internal Types\n// =============================================================================\n\n/**\n * Per-document presence state\n */\ninterface DocumentPresenceState {\n readonly presences: HashMap.HashMap<string, PresenceEntry>;\n readonly pubsub: PubSub.PubSub<PresenceEvent>;\n}\n\n// =============================================================================\n// Layer Implementation\n// =============================================================================\n\n/**\n * Create the PresenceManager layer.\n */\nexport const layer: Layer.Layer<PresenceManagerTag> = Layer.effect(\n PresenceManagerTag,\n Effect.gen(function* () {\n // Store: documentId -> DocumentPresenceState\n const store = yield* Ref.make(\n HashMap.empty<string, DocumentPresenceState>()\n );\n\n /**\n * Get or create presence state for a document\n */\n const getOrCreateDocumentState = (\n documentId: string\n ): Effect.Effect<DocumentPresenceState> =>\n Effect.gen(function* () {\n const current = yield* Ref.get(store);\n const existing = HashMap.get(current, documentId);\n if (existing._tag === \"Some\") {\n return existing.value;\n }\n\n // Create new state for this document\n const pubsub = yield* PubSub.unbounded<PresenceEvent>();\n const state: DocumentPresenceState = {\n presences: HashMap.empty(),\n pubsub,\n };\n\n yield* Ref.update(store, (map) => HashMap.set(map, documentId, state));\n return state;\n });\n\n return {\n getSnapshot: (documentId) =>\n Effect.gen(function* () {\n const current = yield* Ref.get(store);\n const existing = HashMap.get(current, documentId);\n if (existing._tag === \"None\") {\n return { presences: {} };\n }\n\n // Convert HashMap to Record\n const presences: Record<string, PresenceEntry> = {};\n for (const [id, entry] of existing.value.presences) {\n presences[id] = entry;\n }\n return { presences };\n }),\n\n set: (documentId, connectionId, entry) =>\n Effect.gen(function* () {\n const state = yield* getOrCreateDocumentState(documentId);\n\n // Update presence in store\n yield* Ref.update(store, (map) => {\n const existing = HashMap.get(map, documentId);\n if (existing._tag === \"None\") return map;\n return HashMap.set(map, documentId, {\n ...existing.value,\n presences: HashMap.set(\n existing.value.presences,\n connectionId,\n entry\n ),\n });\n });\n\n // Track metrics\n yield* Metric.increment(Metrics.presenceUpdates);\n yield* Metric.incrementBy(Metrics.presenceActive, 1);\n\n // Broadcast update event\n const event: PresenceEvent = {\n type: \"presence_update\",\n id: connectionId,\n data: entry.data,\n userId: entry.userId,\n };\n yield* PubSub.publish(state.pubsub, event);\n }),\n\n remove: (documentId, connectionId) =>\n Effect.gen(function* () {\n const current = yield* Ref.get(store);\n const existing = HashMap.get(current, documentId);\n if (existing._tag === \"None\") return;\n\n // Check if presence exists before removing\n const hasPresence = HashMap.has(existing.value.presences, connectionId);\n if (!hasPresence) return;\n\n // Remove presence from store\n yield* Ref.update(store, (map) => {\n const docState = HashMap.get(map, documentId);\n if (docState._tag === \"None\") return map;\n return HashMap.set(map, documentId, {\n ...docState.value,\n presences: HashMap.remove(docState.value.presences, connectionId),\n });\n });\n\n // Track metrics\n yield* Metric.incrementBy(Metrics.presenceActive, -1);\n\n // Broadcast remove event\n const event: PresenceEvent = {\n type: \"presence_remove\",\n id: connectionId,\n };\n yield* PubSub.publish(existing.value.pubsub, event);\n }),\n\n subscribe: (documentId) =>\n Effect.gen(function* () {\n const state = yield* getOrCreateDocumentState(documentId);\n return Stream.fromPubSub(state.pubsub);\n }),\n };\n })\n);\n\n// =============================================================================\n// Re-export namespace\n// =============================================================================\n\nexport const PresenceManager = {\n Tag: PresenceManagerTag,\n layer,\n};\n"],"mappings":";;;;;;;;;;;;;AA0EA,IAAa,qBAAb,cAAwC,QAAQ,IAC9C,yCACD,EAAuC,CAAC;;;;AAqBzC,MAAaA,QAAyC,MAAM,OAC1D,oBACA,OAAO,IAAI,aAAa;CAEtB,MAAM,QAAQ,OAAO,IAAI,KACvB,QAAQ,OAAsC,CAC/C;;;;CAKD,MAAM,4BACJ,eAEA,OAAO,IAAI,aAAa;EACtB,MAAM,UAAU,OAAO,IAAI,IAAI,MAAM;EACrC,MAAM,WAAW,QAAQ,IAAI,SAAS,WAAW;AACjD,MAAI,SAAS,SAAS,OACpB,QAAO,SAAS;EAIlB,MAAM,SAAS,OAAO,OAAO,WAA0B;EACvD,MAAMC,QAA+B;GACnC,WAAW,QAAQ,OAAO;GAC1B;GACD;AAED,SAAO,IAAI,OAAO,QAAQ,QAAQ,QAAQ,IAAI,KAAK,YAAY,MAAM,CAAC;AACtE,SAAO;GACP;AAEJ,QAAO;EACL,cAAc,eACZ,OAAO,IAAI,aAAa;GACtB,MAAM,UAAU,OAAO,IAAI,IAAI,MAAM;GACrC,MAAM,WAAW,QAAQ,IAAI,SAAS,WAAW;AACjD,OAAI,SAAS,SAAS,OACpB,QAAO,EAAE,WAAW,EAAE,EAAE;GAI1B,MAAMC,YAA2C,EAAE;AACnD,QAAK,MAAM,CAAC,IAAI,UAAU,SAAS,MAAM,UACvC,WAAU,MAAM;AAElB,UAAO,EAAE,WAAW;IACpB;EAEJ,MAAM,YAAY,cAAc,UAC9B,OAAO,IAAI,aAAa;GACtB,MAAM,QAAQ,OAAO,yBAAyB,WAAW;AAGzD,UAAO,IAAI,OAAO,QAAQ,QAAQ;IAChC,MAAM,WAAW,QAAQ,IAAI,KAAK,WAAW;AAC7C,QAAI,SAAS,SAAS,OAAQ,QAAO;AACrC,WAAO,QAAQ,IAAI,KAAK,8CACnB,SAAS,cACZ,WAAW,QAAQ,IACjB,SAAS,MAAM,WACf,cACA,MACD,IACD;KACF;AAGF,UAAO,OAAO,UAAUC,gBAAwB;AAChD,UAAO,OAAO,YAAYC,gBAAwB,EAAE;GAGpD,MAAMC,QAAuB;IAC3B,MAAM;IACN,IAAI;IACJ,MAAM,MAAM;IACZ,QAAQ,MAAM;IACf;AACD,UAAO,OAAO,QAAQ,MAAM,QAAQ,MAAM;IAC1C;EAEJ,SAAS,YAAY,iBACnB,OAAO,IAAI,aAAa;GACtB,MAAM,UAAU,OAAO,IAAI,IAAI,MAAM;GACrC,MAAM,WAAW,QAAQ,IAAI,SAAS,WAAW;AACjD,OAAI,SAAS,SAAS,OAAQ;AAI9B,OAAI,CADgB,QAAQ,IAAI,SAAS,MAAM,WAAW,aAAa,CACrD;AAGlB,UAAO,IAAI,OAAO,QAAQ,QAAQ;IAChC,MAAM,WAAW,QAAQ,IAAI,KAAK,WAAW;AAC7C,QAAI,SAAS,SAAS,OAAQ,QAAO;AACrC,WAAO,QAAQ,IAAI,KAAK,8CACnB,SAAS,cACZ,WAAW,QAAQ,OAAO,SAAS,MAAM,WAAW,aAAa,IACjE;KACF;AAGF,UAAO,OAAO,YAAYD,gBAAwB,GAAG;GAGrD,MAAMC,QAAuB;IAC3B,MAAM;IACN,IAAI;IACL;AACD,UAAO,OAAO,QAAQ,SAAS,MAAM,QAAQ,MAAM;IACnD;EAEJ,YAAY,eACV,OAAO,IAAI,aAAa;GACtB,MAAM,QAAQ,OAAO,yBAAyB,WAAW;AACzD,UAAO,OAAO,WAAW,MAAM,OAAO;IACtC;EACL;EACD,CACH;AAMD,MAAa,kBAAkB;CAC7B,KAAK;CACL;CACD"}
1
+ {"version":3,"file":"PresenceManager.mjs","names":["layer: Layer.Layer<PresenceManagerTag>","state: DocumentPresenceState","presences: Record<string, PresenceEntry>","Metrics.presenceUpdates","Metrics.presenceActive","event: PresenceEvent"],"sources":["../src/PresenceManager.ts"],"sourcesContent":["/**\n * @voidhash/mimic-effect - PresenceManager\n *\n * Internal service for managing presence state per document.\n */\nimport {\n Context,\n Effect,\n HashMap,\n Layer,\n Metric,\n PubSub,\n Ref,\n Scope,\n Stream,\n} from \"effect\";\nimport type {\n PresenceEntry,\n PresenceEvent,\n PresenceSnapshot,\n} from \"./Types\";\nimport * as Metrics from \"./Metrics\";\n\n// =============================================================================\n// PresenceManager Interface\n// =============================================================================\n\n/**\n * Internal service for managing presence state per document.\n *\n * Presence is ephemeral state associated with connections, not persisted.\n * Each document has its own set of presences, keyed by connectionId.\n */\nexport interface PresenceManager {\n /**\n * Get snapshot of all presences for a document.\n */\n readonly getSnapshot: (\n documentId: string\n ) => Effect.Effect<PresenceSnapshot>;\n\n /**\n * Set/update presence for a connection.\n */\n readonly set: (\n documentId: string,\n connectionId: string,\n entry: PresenceEntry\n ) => Effect.Effect<void>;\n\n /**\n * Remove presence for a connection (on disconnect).\n */\n readonly remove: (\n documentId: string,\n connectionId: string\n ) => Effect.Effect<void>;\n\n /**\n * Subscribe to presence events for a document.\n * Returns a stream of presence update/remove events.\n */\n readonly subscribe: (\n documentId: string\n ) => Effect.Effect<Stream.Stream<PresenceEvent>, never, Scope.Scope>;\n}\n\n// =============================================================================\n// Context Tag\n// =============================================================================\n\n/**\n * Context tag for PresenceManager service\n */\nexport class PresenceManagerTag extends Context.Tag(\n \"@voidhash/mimic-effect/PresenceManager\"\n)<PresenceManagerTag, PresenceManager>() {}\n\n// =============================================================================\n// Internal Types\n// =============================================================================\n\n/**\n * Per-document presence state\n */\ninterface DocumentPresenceState {\n readonly presences: HashMap.HashMap<string, PresenceEntry>;\n readonly pubsub: PubSub.PubSub<PresenceEvent>;\n}\n\n// =============================================================================\n// Layer Implementation\n// =============================================================================\n\n/**\n * Create the PresenceManager layer.\n */\nexport const layer: Layer.Layer<PresenceManagerTag> = Layer.effect(\n PresenceManagerTag,\n Effect.gen(function* () {\n // Store: documentId -> DocumentPresenceState\n const store = yield* Ref.make(\n HashMap.empty<string, DocumentPresenceState>()\n );\n\n /**\n * Get or create presence state for a document\n */\n const getOrCreateDocumentState = Effect.fn(\n \"presence.document-state.get-or-create\"\n )(function* (documentId: string) {\n const current = yield* Ref.get(store);\n const existing = HashMap.get(current, documentId);\n if (existing._tag === \"Some\") {\n return existing.value;\n }\n\n // Create new state for this document\n const pubsub = yield* PubSub.unbounded<PresenceEvent>();\n const state: DocumentPresenceState = {\n presences: HashMap.empty(),\n pubsub,\n };\n\n yield* Ref.update(store, (map) => HashMap.set(map, documentId, state));\n return state;\n });\n\n return {\n getSnapshot: Effect.fn(\"presence.snapshot.get\")(\n function* (documentId: string) {\n const current = yield* Ref.get(store);\n const existing = HashMap.get(current, documentId);\n if (existing._tag === \"None\") {\n return { presences: {} };\n }\n\n // Convert HashMap to Record\n const presences: Record<string, PresenceEntry> = {};\n for (const [id, entry] of existing.value.presences) {\n presences[id] = entry;\n }\n return { presences };\n }\n ),\n\n set: Effect.fn(\"presence.set\")(\n function* (\n documentId: string,\n connectionId: string,\n entry: PresenceEntry\n ) {\n const state = yield* getOrCreateDocumentState(documentId);\n\n // Update presence in store\n yield* Ref.update(store, (map) => {\n const existing = HashMap.get(map, documentId);\n if (existing._tag === \"None\") return map;\n return HashMap.set(map, documentId, {\n ...existing.value,\n presences: HashMap.set(\n existing.value.presences,\n connectionId,\n entry\n ),\n });\n });\n\n // Track metrics\n yield* Metric.increment(Metrics.presenceUpdates);\n yield* Metric.incrementBy(Metrics.presenceActive, 1);\n\n // Broadcast update event\n const event: PresenceEvent = {\n type: \"presence_update\",\n id: connectionId,\n data: entry.data,\n userId: entry.userId,\n };\n yield* PubSub.publish(state.pubsub, event);\n }\n ),\n\n remove: Effect.fn(\"presence.remove\")(\n function* (documentId: string, connectionId: string) {\n const current = yield* Ref.get(store);\n const existing = HashMap.get(current, documentId);\n if (existing._tag === \"None\") return;\n\n // Check if presence exists before removing\n const hasPresence = HashMap.has(\n existing.value.presences,\n connectionId\n );\n if (!hasPresence) return;\n\n // Remove presence from store\n yield* Ref.update(store, (map) => {\n const docState = HashMap.get(map, documentId);\n if (docState._tag === \"None\") return map;\n return HashMap.set(map, documentId, {\n ...docState.value,\n presences: HashMap.remove(docState.value.presences, connectionId),\n });\n });\n\n // Track metrics\n yield* Metric.incrementBy(Metrics.presenceActive, -1);\n\n // Broadcast remove event\n const event: PresenceEvent = {\n type: \"presence_remove\",\n id: connectionId,\n };\n yield* PubSub.publish(existing.value.pubsub, event);\n }\n ),\n\n subscribe: Effect.fn(\"presence.subscribe\")(\n function* (documentId: string) {\n const state = yield* getOrCreateDocumentState(documentId);\n return Stream.fromPubSub(state.pubsub);\n }\n ),\n };\n })\n);\n\n// =============================================================================\n// Re-export namespace\n// =============================================================================\n\nexport const PresenceManager = {\n Tag: PresenceManagerTag,\n layer,\n};\n"],"mappings":";;;;;;;;;;;;;AA0EA,IAAa,qBAAb,cAAwC,QAAQ,IAC9C,yCACD,EAAuC,CAAC;;;;AAqBzC,MAAaA,QAAyC,MAAM,OAC1D,oBACA,OAAO,IAAI,aAAa;CAEtB,MAAM,QAAQ,OAAO,IAAI,KACvB,QAAQ,OAAsC,CAC/C;;;;CAKD,MAAM,2BAA2B,OAAO,GACtC,wCACD,CAAC,WAAW,YAAoB;EAC/B,MAAM,UAAU,OAAO,IAAI,IAAI,MAAM;EACrC,MAAM,WAAW,QAAQ,IAAI,SAAS,WAAW;AACjD,MAAI,SAAS,SAAS,OACpB,QAAO,SAAS;EAIlB,MAAM,SAAS,OAAO,OAAO,WAA0B;EACvD,MAAMC,QAA+B;GACnC,WAAW,QAAQ,OAAO;GAC1B;GACD;AAED,SAAO,IAAI,OAAO,QAAQ,QAAQ,QAAQ,IAAI,KAAK,YAAY,MAAM,CAAC;AACtE,SAAO;GACP;AAEF,QAAO;EACL,aAAa,OAAO,GAAG,wBAAwB,CAC7C,WAAW,YAAoB;GAC7B,MAAM,UAAU,OAAO,IAAI,IAAI,MAAM;GACrC,MAAM,WAAW,QAAQ,IAAI,SAAS,WAAW;AACjD,OAAI,SAAS,SAAS,OACpB,QAAO,EAAE,WAAW,EAAE,EAAE;GAI1B,MAAMC,YAA2C,EAAE;AACnD,QAAK,MAAM,CAAC,IAAI,UAAU,SAAS,MAAM,UACvC,WAAU,MAAM;AAElB,UAAO,EAAE,WAAW;IAEvB;EAED,KAAK,OAAO,GAAG,eAAe,CAC5B,WACE,YACA,cACA,OACA;GACA,MAAM,QAAQ,OAAO,yBAAyB,WAAW;AAGzD,UAAO,IAAI,OAAO,QAAQ,QAAQ;IAChC,MAAM,WAAW,QAAQ,IAAI,KAAK,WAAW;AAC7C,QAAI,SAAS,SAAS,OAAQ,QAAO;AACrC,WAAO,QAAQ,IAAI,KAAK,8CACnB,SAAS,cACZ,WAAW,QAAQ,IACjB,SAAS,MAAM,WACf,cACA,MACD,IACD;KACF;AAGF,UAAO,OAAO,UAAUC,gBAAwB;AAChD,UAAO,OAAO,YAAYC,gBAAwB,EAAE;GAGpD,MAAMC,QAAuB;IAC3B,MAAM;IACN,IAAI;IACJ,MAAM,MAAM;IACZ,QAAQ,MAAM;IACf;AACD,UAAO,OAAO,QAAQ,MAAM,QAAQ,MAAM;IAE7C;EAED,QAAQ,OAAO,GAAG,kBAAkB,CAClC,WAAW,YAAoB,cAAsB;GACnD,MAAM,UAAU,OAAO,IAAI,IAAI,MAAM;GACrC,MAAM,WAAW,QAAQ,IAAI,SAAS,WAAW;AACjD,OAAI,SAAS,SAAS,OAAQ;AAO9B,OAAI,CAJgB,QAAQ,IAC1B,SAAS,MAAM,WACf,aACD,CACiB;AAGlB,UAAO,IAAI,OAAO,QAAQ,QAAQ;IAChC,MAAM,WAAW,QAAQ,IAAI,KAAK,WAAW;AAC7C,QAAI,SAAS,SAAS,OAAQ,QAAO;AACrC,WAAO,QAAQ,IAAI,KAAK,8CACnB,SAAS,cACZ,WAAW,QAAQ,OAAO,SAAS,MAAM,WAAW,aAAa,IACjE;KACF;AAGF,UAAO,OAAO,YAAYD,gBAAwB,GAAG;GAGrD,MAAMC,QAAuB;IAC3B,MAAM;IACN,IAAI;IACL;AACD,UAAO,OAAO,QAAQ,SAAS,MAAM,QAAQ,MAAM;IAEtD;EAED,WAAW,OAAO,GAAG,qBAAqB,CACxC,WAAW,YAAoB;GAC7B,MAAM,QAAQ,OAAO,yBAAyB,WAAW;AACzD,UAAO,OAAO,WAAW,MAAM,OAAO;IAEzC;EACF;EACD,CACH;AAMD,MAAa,kBAAkB;CAC7B,KAAK;CACL;CACD"}
@@ -1,10 +1,10 @@
1
1
  import { Effect } from "effect";
2
- import * as effect_Types0 from "effect/Types";
3
- import * as effect_Cause0 from "effect/Cause";
2
+ import * as effect_Types7 from "effect/Types";
3
+ import * as effect_Cause7 from "effect/Cause";
4
4
 
5
5
  //#region src/testing/types.d.ts
6
6
 
7
- declare const TestError_base: new <A extends Record<string, any> = {}>(args: effect_Types0.Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }) => effect_Cause0.YieldableError & {
7
+ declare const TestError_base: new <A extends Record<string, any> = {}>(args: effect_Types7.Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }) => effect_Cause7.YieldableError & {
8
8
  readonly _tag: "TestError";
9
9
  } & Readonly<A>;
10
10
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voidhash/mimic-effect",
3
- "version": "1.0.0-beta.6",
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.6"
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.6"
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
  );