@voidhash/mimic-effect 1.0.0-beta.16 → 1.0.0-beta.17

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 (99) hide show
  1. package/dist/ColdStorage.cjs +1 -1
  2. package/dist/ColdStorage.d.cts +2 -2
  3. package/dist/ColdStorage.d.cts.map +1 -1
  4. package/dist/ColdStorage.d.mts +2 -2
  5. package/dist/ColdStorage.d.mts.map +1 -1
  6. package/dist/ColdStorage.mjs +2 -2
  7. package/dist/ColdStorage.mjs.map +1 -1
  8. package/dist/DocumentInstance.cjs +13 -13
  9. package/dist/DocumentInstance.mjs +13 -13
  10. package/dist/DocumentInstance.mjs.map +1 -1
  11. package/dist/Errors.d.cts +8 -8
  12. package/dist/Errors.d.cts.map +1 -1
  13. package/dist/Errors.d.mts +8 -8
  14. package/dist/Errors.d.mts.map +1 -1
  15. package/dist/HotStorage.cjs +1 -1
  16. package/dist/HotStorage.d.cts +2 -2
  17. package/dist/HotStorage.d.mts +2 -2
  18. package/dist/HotStorage.mjs +2 -2
  19. package/dist/HotStorage.mjs.map +1 -1
  20. package/dist/Metrics.cjs +6 -6
  21. package/dist/Metrics.d.cts +21 -23
  22. package/dist/Metrics.d.cts.map +1 -1
  23. package/dist/Metrics.d.mts +21 -23
  24. package/dist/Metrics.d.mts.map +1 -1
  25. package/dist/Metrics.mjs +7 -7
  26. package/dist/Metrics.mjs.map +1 -1
  27. package/dist/MimicAuthService.cjs +1 -1
  28. package/dist/MimicAuthService.d.cts +2 -2
  29. package/dist/MimicAuthService.d.cts.map +1 -1
  30. package/dist/MimicAuthService.d.mts +2 -2
  31. package/dist/MimicAuthService.d.mts.map +1 -1
  32. package/dist/MimicAuthService.mjs +2 -2
  33. package/dist/MimicAuthService.mjs.map +1 -1
  34. package/dist/MimicClusterServerEngine.cjs +38 -41
  35. package/dist/MimicClusterServerEngine.d.cts +1 -1
  36. package/dist/MimicClusterServerEngine.d.mts +1 -1
  37. package/dist/MimicClusterServerEngine.mjs +31 -34
  38. package/dist/MimicClusterServerEngine.mjs.map +1 -1
  39. package/dist/MimicServer.cjs +23 -23
  40. package/dist/MimicServer.d.cts +3 -3
  41. package/dist/MimicServer.d.cts.map +1 -1
  42. package/dist/MimicServer.d.mts +3 -3
  43. package/dist/MimicServer.d.mts.map +1 -1
  44. package/dist/MimicServer.mjs +22 -22
  45. package/dist/MimicServer.mjs.map +1 -1
  46. package/dist/MimicServerEngine.cjs +13 -13
  47. package/dist/MimicServerEngine.d.cts +2 -2
  48. package/dist/MimicServerEngine.d.mts +2 -2
  49. package/dist/MimicServerEngine.mjs +14 -14
  50. package/dist/MimicServerEngine.mjs.map +1 -1
  51. package/dist/PresenceManager.cjs +4 -4
  52. package/dist/PresenceManager.d.cts +2 -2
  53. package/dist/PresenceManager.d.mts +2 -2
  54. package/dist/PresenceManager.mjs +5 -5
  55. package/dist/PresenceManager.mjs.map +1 -1
  56. package/dist/Types.d.cts +1 -1
  57. package/dist/Types.d.mts +1 -1
  58. package/dist/testing/ColdStorageTestSuite.cjs +3 -3
  59. package/dist/testing/ColdStorageTestSuite.mjs +3 -3
  60. package/dist/testing/ColdStorageTestSuite.mjs.map +1 -1
  61. package/dist/testing/HotStorageTestSuite.cjs +13 -13
  62. package/dist/testing/HotStorageTestSuite.mjs +13 -13
  63. package/dist/testing/HotStorageTestSuite.mjs.map +1 -1
  64. package/dist/testing/StorageIntegrationTestSuite.cjs +3 -3
  65. package/dist/testing/StorageIntegrationTestSuite.mjs +3 -3
  66. package/dist/testing/StorageIntegrationTestSuite.mjs.map +1 -1
  67. package/dist/testing/types.d.cts +1 -1
  68. package/dist/testing/types.d.cts.map +1 -1
  69. package/dist/testing/types.d.mts +3 -3
  70. package/dist/testing/types.d.mts.map +1 -1
  71. package/package.json +17 -21
  72. package/src/ColdStorage.ts +4 -5
  73. package/src/DocumentInstance.ts +13 -13
  74. package/src/HotStorage.ts +3 -3
  75. package/src/Metrics.ts +22 -16
  76. package/src/MimicAuthService.ts +3 -3
  77. package/src/MimicClusterServerEngine.ts +35 -35
  78. package/src/MimicServer.ts +26 -30
  79. package/src/MimicServerEngine.ts +15 -15
  80. package/src/PresenceManager.ts +6 -6
  81. package/src/Types.ts +1 -1
  82. package/src/testing/ColdStorageTestSuite.ts +3 -3
  83. package/src/testing/HotStorageTestSuite.ts +17 -17
  84. package/src/testing/StorageIntegrationTestSuite.ts +3 -3
  85. package/.turbo/turbo-build.log +0 -154
  86. package/tests/ColdStorage.test.ts +0 -24
  87. package/tests/DocumentInstance.test.ts +0 -669
  88. package/tests/HotStorage.test.ts +0 -24
  89. package/tests/MimicAuthService.test.ts +0 -153
  90. package/tests/MimicClusterServerEngine.test.ts +0 -587
  91. package/tests/MimicServer.test.ts +0 -142
  92. package/tests/MimicServerEngine.test.ts +0 -547
  93. package/tests/PresenceManager.test.ts +0 -380
  94. package/tests/Protocol.test.ts +0 -190
  95. package/tests/StorageIntegration.test.ts +0 -259
  96. package/tsconfig.build.json +0 -24
  97. package/tsconfig.json +0 -8
  98. package/tsdown.config.ts +0 -18
  99. package/vitest.mts +0 -11
@@ -1 +1 @@
1
- {"version":3,"file":"MimicServerEngine.mjs","names":["Metrics.documentsEvicted","Metrics.documentsActive","Metrics.storageIdleSnapshots","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 HashMap,\n Layer,\n Metric,\n Ref,\n Schedule,\n Scope,\n Stream,\n} from \"effect\";\nimport type { 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 DocumentInstance,\n type SubmitResult,\n type DocumentInstance as DocumentInstanceType,\n} from \"./DocumentInstance\";\nimport {\n PresenceManagerTag,\n layer as presenceManagerLayer,\n} from \"./PresenceManager\";\nimport * as Metrics from \"./Metrics\";\nimport type { ColdStorageError, HotStorageError } from \"./Errors\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Error type for MimicServerEngine operations\n */\nexport type MimicServerEngineError = ColdStorageError | HotStorageError;\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 MimicServerEngineError if storage is unavailable.\n */\n readonly submit: (\n documentId: string,\n transaction: Transaction.Transaction\n ) => Effect.Effect<SubmitResult, MimicServerEngineError>;\n\n /**\n * Get document snapshot (current state and version).\n * Returns flat state format used for storage and synchronization.\n * May fail with MimicServerEngineError if storage is unavailable.\n */\n readonly getSnapshot: (\n documentId: string\n ) => Effect.Effect<{ state: unknown; version: number }, MimicServerEngineError>;\n\n /**\n * Get tree-like snapshot for rendering.\n * Returns a readonly structure where trees are converted from\n * flat state to nested/hierarchical structure suitable for UI rendering.\n * May fail with MimicServerEngineError if storage is unavailable.\n */\n readonly getTreeSnapshot: (\n documentId: string\n ) => Effect.Effect<unknown, MimicServerEngineError>;\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 MimicServerEngineError if storage is unavailable.\n */\n readonly subscribe: (\n documentId: string\n ) => Effect.Effect<Stream.Stream<Protocol.ServerMessage, never, never>, MimicServerEngineError, 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;\nconst DEFAULT_SNAPSHOT_IDLE_TIMEOUT = Duration.seconds(30);\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 idleTimeout: config.snapshot?.idleTimeout\n ? Duration.decode(config.snapshot.idleTimeout)\n : DEFAULT_SNAPSHOT_IDLE_TIMEOUT,\n },\n});\n\n// =============================================================================\n// Internal Types\n// =============================================================================\n\n/**\n * Store entry for a document instance with last activity time\n */\ninterface StoreEntry<TSchema extends Primitive.AnyPrimitive> {\n readonly instance: DocumentInstanceType<TSchema>;\n readonly lastActivityTime: Ref.Ref<number>;\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 return Layer.scoped(\n MimicServerEngineTag,\n Effect.gen(function* () {\n const coldStorage = yield* ColdStorageTag;\n const hotStorage = yield* HotStorageTag;\n const presenceManager = yield* PresenceManagerTag;\n\n // Store: documentId -> StoreEntry\n const store = yield* Ref.make(\n HashMap.empty<string, StoreEntry<TSchema>>()\n );\n\n /**\n * Get or create a document instance\n */\n const getOrCreateDocument = Effect.fn(\"engine.document.get-or-create\")(\n function* (documentId: string) {\n const current = yield* Ref.get(store);\n const existing = HashMap.get(current, documentId);\n\n if (existing._tag === \"Some\") {\n // Update activity time\n yield* Ref.set(existing.value.lastActivityTime, Date.now());\n return existing.value.instance;\n }\n\n // Create new document instance\n const instance = yield* DocumentInstance.make(\n documentId,\n {\n schema: config.schema,\n initial: config.initial,\n maxTransactionHistory: resolvedConfig.maxTransactionHistory,\n snapshot: resolvedConfig.snapshot,\n },\n coldStorage,\n hotStorage\n );\n\n const lastActivityTime = yield* Ref.make(Date.now());\n\n // Store it\n yield* Ref.update(store, (map) =>\n HashMap.set(map, documentId, { instance, lastActivityTime })\n );\n\n return instance;\n }\n );\n\n /**\n * Start background GC fiber\n */\n const startGCFiber = Effect.fn(\"engine.gc.start\")(function* () {\n const gcLoop = Effect.fn(\"engine.gc.loop\")(function* () {\n const current = yield* Ref.get(store);\n const now = Date.now();\n const maxIdleMs = Duration.toMillis(resolvedConfig.maxIdleTime);\n\n for (const [documentId, entry] of current) {\n const lastActivity = yield* Ref.get(entry.lastActivityTime);\n if (now - lastActivity >= maxIdleMs) {\n // Save final snapshot before eviction (best effort)\n yield* Effect.catchAll(entry.instance.saveSnapshot(), (e) =>\n Effect.logError(\"Failed to save snapshot during eviction\", {\n documentId,\n error: e,\n })\n );\n\n // Remove from store\n yield* Ref.update(store, (map) => HashMap.remove(map, documentId));\n\n // Track eviction metrics\n yield* Metric.increment(Metrics.documentsEvicted);\n yield* Metric.incrementBy(Metrics.documentsActive, -1);\n\n yield* Effect.logInfo(\"Document evicted due to idle timeout\", {\n documentId,\n });\n }\n }\n });\n\n // Run GC every minute\n yield* gcLoop().pipe(\n Effect.repeat(Schedule.spaced(\"1 minute\")),\n Effect.fork\n );\n });\n\n // Start GC fiber\n yield* startGCFiber();\n\n /**\n * Start background snapshot fiber for idle documents.\n * This ensures documents with unsnapshot transactions get persisted\n * even without new transaction activity.\n */\n const startSnapshotFiber = Effect.fn(\"engine.snapshot.fiber.start\")(function* () {\n const idleTimeoutMs = Duration.toMillis(resolvedConfig.snapshot.idleTimeout);\n\n // Skip if idle snapshots are disabled\n if (idleTimeoutMs <= 0) {\n return;\n }\n\n const snapshotLoop = Effect.fn(\"engine.snapshot.loop\")(function* () {\n const current = yield* Ref.get(store);\n const now = Date.now();\n\n for (const [documentId, entry] of current) {\n // Check if document has been idle long enough\n const lastActivity = yield* Ref.get(entry.lastActivityTime);\n const idleDuration = now - lastActivity;\n\n if (idleDuration < idleTimeoutMs) {\n // Document not idle long enough, skip\n continue;\n }\n\n // Check if document has unsnapshot transactions\n const needs = yield* entry.instance.needsSnapshot();\n if (!needs) {\n continue;\n }\n\n // Save snapshot (with error handling)\n yield* Effect.catchAll(entry.instance.saveSnapshot(), (e) =>\n Effect.logWarning(\"Periodic snapshot save failed\", {\n documentId,\n error: e,\n })\n );\n\n // Track metric\n yield* Metric.increment(Metrics.storageIdleSnapshots);\n }\n });\n\n // Run snapshot check every 10 seconds\n yield* snapshotLoop().pipe(\n Effect.repeat(Schedule.spaced(\"10 seconds\")),\n Effect.fork\n );\n });\n\n // Start snapshot fiber\n yield* startSnapshotFiber();\n\n // Cleanup on shutdown\n yield* Effect.addFinalizer(() =>\n Effect.fn(\"engine.shutdown\")(function* () {\n const current = yield* Ref.get(store);\n for (const [documentId, entry] of current) {\n // Best effort save - don't fail shutdown if storage is unavailable\n yield* Effect.catchAll(entry.instance.saveSnapshot(), (e) =>\n Effect.logError(\"Failed to save snapshot during shutdown\", {\n documentId,\n error: e,\n })\n );\n }\n yield* Effect.logInfo(\"MimicServerEngine shutdown complete\");\n })()\n );\n\n const engine: MimicServerEngine = {\n submit: (documentId, transaction) =>\n Effect.gen(function* () {\n const instance = yield* getOrCreateDocument(documentId);\n return yield* instance.submit(transaction);\n }),\n\n getSnapshot: (documentId) =>\n Effect.gen(function* () {\n const instance = yield* getOrCreateDocument(documentId);\n return instance.getSnapshot();\n }),\n\n getTreeSnapshot: (documentId) =>\n Effect.gen(function* () {\n const instance = yield* getOrCreateDocument(documentId);\n return instance.toSnapshot();\n }),\n\n subscribe: (documentId) =>\n Effect.gen(function* () {\n const instance = yield* getOrCreateDocument(documentId);\n return Stream.fromPubSub(instance.pubsub) as Stream.Stream<\n Protocol.ServerMessage,\n never,\n never\n >;\n }),\n\n touch: (documentId) =>\n Effect.gen(function* () {\n const current = yield* Ref.get(store);\n const existing = HashMap.get(current, documentId);\n if (existing._tag === \"Some\") {\n yield* Ref.set(existing.value.lastActivityTime, Date.now());\n }\n }),\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(presenceManagerLayer));\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":";;;;;;;;;;;;;;;;;;;AA0JA,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;AACnC,MAAM,gCAAgC,SAAS,QAAQ,GAAG;;;;AAK1D,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;GAC3C,mCAAa,OAAO,gFAAU,eAC1B,SAAS,OAAO,OAAO,SAAS,YAAY,GAC5C;GACL;EACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiDD,MAAa,QACX,WAKG;CACH,MAAM,iBAAiB,cAAc,OAAO;AAE5C,QAAO,MAAM,OACX,sBACA,OAAO,IAAI,aAAa;EACtB,MAAM,cAAc,OAAO;EAC3B,MAAM,aAAa,OAAO;EAC1B,MAAM,kBAAkB,OAAO;EAG/B,MAAM,QAAQ,OAAO,IAAI,KACvB,QAAQ,OAAoC,CAC7C;;;;EAKD,MAAM,sBAAsB,OAAO,GAAG,gCAAgC,CACpE,WAAW,YAAoB;GAC7B,MAAM,UAAU,OAAO,IAAI,IAAI,MAAM;GACrC,MAAM,WAAW,QAAQ,IAAI,SAAS,WAAW;AAEjD,OAAI,SAAS,SAAS,QAAQ;AAE5B,WAAO,IAAI,IAAI,SAAS,MAAM,kBAAkB,KAAK,KAAK,CAAC;AAC3D,WAAO,SAAS,MAAM;;GAIxB,MAAM,WAAW,OAAO,iBAAiB,KACvC,YACA;IACE,QAAQ,OAAO;IACf,SAAS,OAAO;IAChB,uBAAuB,eAAe;IACtC,UAAU,eAAe;IAC1B,EACD,aACA,WACD;GAED,MAAM,mBAAmB,OAAO,IAAI,KAAK,KAAK,KAAK,CAAC;AAGpD,UAAO,IAAI,OAAO,QAAQ,QACxB,QAAQ,IAAI,KAAK,YAAY;IAAE;IAAU;IAAkB,CAAC,CAC7D;AAED,UAAO;IAEV;AA4CD,SAvCqB,OAAO,GAAG,kBAAkB,CAAC,aAAa;AAgC7D,UA/Be,OAAO,GAAG,iBAAiB,CAAC,aAAa;IACtD,MAAM,UAAU,OAAO,IAAI,IAAI,MAAM;IACrC,MAAM,MAAM,KAAK,KAAK;IACtB,MAAM,YAAY,SAAS,SAAS,eAAe,YAAY;AAE/D,SAAK,MAAM,CAAC,YAAY,UAAU,QAEhC,KAAI,OADiB,OAAO,IAAI,IAAI,MAAM,iBAAiB,KACjC,WAAW;AAEnC,YAAO,OAAO,SAAS,MAAM,SAAS,cAAc,GAAG,MACrD,OAAO,SAAS,2CAA2C;MACzD;MACA,OAAO;MACR,CAAC,CACH;AAGD,YAAO,IAAI,OAAO,QAAQ,QAAQ,QAAQ,OAAO,KAAK,WAAW,CAAC;AAGlE,YAAO,OAAO,UAAUA,iBAAyB;AACjD,YAAO,OAAO,YAAYC,iBAAyB,GAAG;AAEtD,YAAO,OAAO,QAAQ,wCAAwC,EAC5D,YACD,CAAC;;KAGN,EAGa,CAAC,KACd,OAAO,OAAO,SAAS,OAAO,WAAW,CAAC,EAC1C,OAAO,KACR;IACD,EAGmB;AAwDrB,SAjD2B,OAAO,GAAG,8BAA8B,CAAC,aAAa;GAC/E,MAAM,gBAAgB,SAAS,SAAS,eAAe,SAAS,YAAY;AAG5E,OAAI,iBAAiB,EACnB;AAqCF,UAlCqB,OAAO,GAAG,uBAAuB,CAAC,aAAa;IAClE,MAAM,UAAU,OAAO,IAAI,IAAI,MAAM;IACrC,MAAM,MAAM,KAAK,KAAK;AAEtB,SAAK,MAAM,CAAC,YAAY,UAAU,SAAS;AAKzC,SAFqB,OADA,OAAO,IAAI,IAAI,MAAM,iBAAiB,IAGxC,cAEjB;AAKF,SAAI,EADU,OAAO,MAAM,SAAS,eAAe,EAEjD;AAIF,YAAO,OAAO,SAAS,MAAM,SAAS,cAAc,GAAG,MACrD,OAAO,WAAW,iCAAiC;MACjD;MACA,OAAO;MACR,CAAC,CACH;AAGD,YAAO,OAAO,UAAUC,qBAA6B;;KAEvD,EAGmB,CAAC,KACpB,OAAO,OAAO,SAAS,OAAO,aAAa,CAAC,EAC5C,OAAO,KACR;IACD,EAGyB;AAG3B,SAAO,OAAO,mBACZ,OAAO,GAAG,kBAAkB,CAAC,aAAa;GACxC,MAAM,UAAU,OAAO,IAAI,IAAI,MAAM;AACrC,QAAK,MAAM,CAAC,YAAY,UAAU,QAEhC,QAAO,OAAO,SAAS,MAAM,SAAS,cAAc,GAAG,MACrD,OAAO,SAAS,2CAA2C;IACzD;IACA,OAAO;IACR,CAAC,CACH;AAEH,UAAO,OAAO,QAAQ,sCAAsC;IAC5D,EAAE,CACL;AAuDD,SArDkC;GAChC,SAAS,YAAY,gBACnB,OAAO,IAAI,aAAa;AAEtB,WAAO,QADU,OAAO,oBAAoB,WAAW,EAChC,OAAO,YAAY;KAC1C;GAEJ,cAAc,eACZ,OAAO,IAAI,aAAa;AAEtB,YADiB,OAAO,oBAAoB,WAAW,EACvC,aAAa;KAC7B;GAEJ,kBAAkB,eAChB,OAAO,IAAI,aAAa;AAEtB,YADiB,OAAO,oBAAoB,WAAW,EACvC,YAAY;KAC5B;GAEJ,YAAY,eACV,OAAO,IAAI,aAAa;IACtB,MAAM,WAAW,OAAO,oBAAoB,WAAW;AACvD,WAAO,OAAO,WAAW,SAAS,OAAO;KAKzC;GAEJ,QAAQ,eACN,OAAO,IAAI,aAAa;IACtB,MAAM,UAAU,OAAO,IAAI,IAAI,MAAM;IACrC,MAAM,WAAW,QAAQ,IAAI,SAAS,WAAW;AACjD,QAAI,SAAS,SAAS,OACpB,QAAO,IAAI,IAAI,SAAS,MAAM,kBAAkB,KAAK,KAAK,CAAC;KAE7D;GAEJ,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,QAAQC,MAAqB,CAAC;;AAO7C,MAAa,oBAAoB;CAC/B,KAAK;CACL;CACD"}
1
+ {"version":3,"file":"MimicServerEngine.mjs","names":["Metrics.documentsEvicted","Metrics.documentsActive","Metrics.storageIdleSnapshots","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 ServiceMap,\n Duration,\n Effect,\n HashMap,\n Layer,\n Metric,\n Ref,\n Schedule,\n Scope,\n Stream,\n} from \"effect\";\nimport type { 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 DocumentInstance,\n type SubmitResult,\n type DocumentInstance as DocumentInstanceType,\n} from \"./DocumentInstance\";\nimport {\n PresenceManagerTag,\n layer as presenceManagerLayer,\n} from \"./PresenceManager\";\nimport * as Metrics from \"./Metrics\";\nimport type { ColdStorageError, HotStorageError } from \"./Errors\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Error type for MimicServerEngine operations\n */\nexport type MimicServerEngineError = ColdStorageError | HotStorageError;\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 MimicServerEngineError if storage is unavailable.\n */\n readonly submit: (\n documentId: string,\n transaction: Transaction.Transaction\n ) => Effect.Effect<SubmitResult, MimicServerEngineError>;\n\n /**\n * Get document snapshot (current state and version).\n * Returns flat state format used for storage and synchronization.\n * May fail with MimicServerEngineError if storage is unavailable.\n */\n readonly getSnapshot: (\n documentId: string\n ) => Effect.Effect<{ state: unknown; version: number }, MimicServerEngineError>;\n\n /**\n * Get tree-like snapshot for rendering.\n * Returns a readonly structure where trees are converted from\n * flat state to nested/hierarchical structure suitable for UI rendering.\n * May fail with MimicServerEngineError if storage is unavailable.\n */\n readonly getTreeSnapshot: (\n documentId: string\n ) => Effect.Effect<unknown, MimicServerEngineError>;\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 MimicServerEngineError if storage is unavailable.\n */\n readonly subscribe: (\n documentId: string\n ) => Effect.Effect<Stream.Stream<Protocol.ServerMessage, never, never>, MimicServerEngineError, 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 ServiceMap.Service<MimicServerEngineTag, MimicServerEngine>()(\n \"@voidhash/mimic-effect/MimicServerEngine\"\n) {}\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;\nconst DEFAULT_SNAPSHOT_IDLE_TIMEOUT = Duration.seconds(30);\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.fromInputUnsafe(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.fromInputUnsafe(config.snapshot.interval)\n : DEFAULT_SNAPSHOT_INTERVAL,\n transactionThreshold:\n config.snapshot?.transactionThreshold ?? DEFAULT_SNAPSHOT_THRESHOLD,\n idleTimeout: config.snapshot?.idleTimeout\n ? Duration.fromInputUnsafe(config.snapshot.idleTimeout)\n : DEFAULT_SNAPSHOT_IDLE_TIMEOUT,\n },\n});\n\n// =============================================================================\n// Internal Types\n// =============================================================================\n\n/**\n * Store entry for a document instance with last activity time\n */\ninterface StoreEntry<TSchema extends Primitive.AnyPrimitive> {\n readonly instance: DocumentInstanceType<TSchema>;\n readonly lastActivityTime: Ref.Ref<number>;\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 return Layer.effect(\n MimicServerEngineTag,\n Effect.gen(function* () {\n const coldStorage = yield* ColdStorageTag;\n const hotStorage = yield* HotStorageTag;\n const presenceManager = yield* PresenceManagerTag;\n\n // Store: documentId -> StoreEntry\n const store = yield* Ref.make(\n HashMap.empty<string, StoreEntry<TSchema>>()\n );\n\n /**\n * Get or create a document instance\n */\n const getOrCreateDocument = Effect.fn(\"engine.document.get-or-create\")(\n function* (documentId: string) {\n const current = yield* Ref.get(store);\n const existing = HashMap.get(current, documentId);\n\n if (existing._tag === \"Some\") {\n // Update activity time\n yield* Ref.set(existing.value.lastActivityTime, Date.now());\n return existing.value.instance;\n }\n\n // Create new document instance\n const instance = yield* DocumentInstance.make(\n documentId,\n {\n schema: config.schema,\n initial: config.initial,\n maxTransactionHistory: resolvedConfig.maxTransactionHistory,\n snapshot: resolvedConfig.snapshot,\n },\n coldStorage,\n hotStorage\n );\n\n const lastActivityTime = yield* Ref.make(Date.now());\n\n // Store it\n yield* Ref.update(store, (map) =>\n HashMap.set(map, documentId, { instance, lastActivityTime })\n );\n\n return instance;\n }\n );\n\n /**\n * Start background GC fiber\n */\n const startGCFiber = Effect.fn(\"engine.gc.start\")(function* () {\n const gcLoop = Effect.fn(\"engine.gc.loop\")(function* () {\n const current = yield* Ref.get(store);\n const now = Date.now();\n const maxIdleMs = Duration.toMillis(resolvedConfig.maxIdleTime);\n\n for (const [documentId, entry] of current) {\n const lastActivity = yield* Ref.get(entry.lastActivityTime);\n if (now - lastActivity >= maxIdleMs) {\n // Save final snapshot before eviction (best effort)\n yield* Effect[\"catch\"](entry.instance.saveSnapshot(), (e) =>\n Effect.logError(\"Failed to save snapshot during eviction\", {\n documentId,\n error: e,\n })\n );\n\n // Remove from store\n yield* Ref.update(store, (map) => HashMap.remove(map, documentId));\n\n // Track eviction metrics\n yield* Metric.update(Metrics.documentsEvicted, 1);\n yield* Metric.update(Metrics.documentsActive, -1);\n\n yield* Effect.logInfo(\"Document evicted due to idle timeout\", {\n documentId,\n });\n }\n }\n });\n\n // Run GC every minute\n yield* gcLoop().pipe(\n Effect.repeat(Schedule.spaced(\"1 minute\")),\n Effect.forkChild\n );\n });\n\n // Start GC fiber\n yield* startGCFiber();\n\n /**\n * Start background snapshot fiber for idle documents.\n * This ensures documents with unsnapshot transactions get persisted\n * even without new transaction activity.\n */\n const startSnapshotFiber = Effect.fn(\"engine.snapshot.fiber.start\")(function* () {\n const idleTimeoutMs = Duration.toMillis(resolvedConfig.snapshot.idleTimeout);\n\n // Skip if idle snapshots are disabled\n if (idleTimeoutMs <= 0) {\n return;\n }\n\n const snapshotLoop = Effect.fn(\"engine.snapshot.loop\")(function* () {\n const current = yield* Ref.get(store);\n const now = Date.now();\n\n for (const [documentId, entry] of current) {\n // Check if document has been idle long enough\n const lastActivity = yield* Ref.get(entry.lastActivityTime);\n const idleDuration = now - lastActivity;\n\n if (idleDuration < idleTimeoutMs) {\n // Document not idle long enough, skip\n continue;\n }\n\n // Check if document has unsnapshot transactions\n const needs = yield* entry.instance.needsSnapshot();\n if (!needs) {\n continue;\n }\n\n // Save snapshot (with error handling)\n yield* Effect[\"catch\"](entry.instance.saveSnapshot(), (e) =>\n Effect.logWarning(\"Periodic snapshot save failed\", {\n documentId,\n error: e,\n })\n );\n\n // Track metric\n yield* Metric.update(Metrics.storageIdleSnapshots, 1);\n }\n });\n\n // Run snapshot check every 10 seconds\n yield* snapshotLoop().pipe(\n Effect.repeat(Schedule.spaced(\"10 seconds\")),\n Effect.forkChild\n );\n });\n\n // Start snapshot fiber\n yield* startSnapshotFiber();\n\n // Cleanup on shutdown\n yield* Effect.addFinalizer(() =>\n Effect.fn(\"engine.shutdown\")(function* () {\n const current = yield* Ref.get(store);\n for (const [documentId, entry] of current) {\n // Best effort save - don't fail shutdown if storage is unavailable\n yield* Effect[\"catch\"](entry.instance.saveSnapshot(), (e) =>\n Effect.logError(\"Failed to save snapshot during shutdown\", {\n documentId,\n error: e,\n })\n );\n }\n yield* Effect.logInfo(\"MimicServerEngine shutdown complete\");\n })()\n );\n\n const engine: MimicServerEngine = {\n submit: (documentId, transaction) =>\n Effect.gen(function* () {\n const instance = yield* getOrCreateDocument(documentId);\n return yield* instance.submit(transaction);\n }),\n\n getSnapshot: (documentId) =>\n Effect.gen(function* () {\n const instance = yield* getOrCreateDocument(documentId);\n return instance.getSnapshot();\n }),\n\n getTreeSnapshot: (documentId) =>\n Effect.gen(function* () {\n const instance = yield* getOrCreateDocument(documentId);\n return instance.toSnapshot();\n }),\n\n subscribe: (documentId) =>\n Effect.gen(function* () {\n const instance = yield* getOrCreateDocument(documentId);\n return Stream.fromPubSub(instance.pubsub) as Stream.Stream<\n Protocol.ServerMessage,\n never,\n never\n >;\n }),\n\n touch: (documentId) =>\n Effect.gen(function* () {\n const current = yield* Ref.get(store);\n const existing = HashMap.get(current, documentId);\n if (existing._tag === \"Some\") {\n yield* Ref.set(existing.value.lastActivityTime, Date.now());\n }\n }),\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(presenceManagerLayer));\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":"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"}
@@ -11,7 +11,7 @@ let effect = require("effect");
11
11
  /**
12
12
  * Context tag for PresenceManager service
13
13
  */
14
- var PresenceManagerTag = class extends effect.Context.Tag("@voidhash/mimic-effect/PresenceManager")() {};
14
+ var PresenceManagerTag = class extends effect.ServiceMap.Service()("@voidhash/mimic-effect/PresenceManager") {};
15
15
  /**
16
16
  * Create the PresenceManager layer.
17
17
  */
@@ -48,8 +48,8 @@ const layer = effect.Layer.effect(PresenceManagerTag, effect.Effect.gen(function
48
48
  if (existing._tag === "None") return map;
49
49
  return effect.HashMap.set(map, documentId, require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({}, existing.value), {}, { presences: effect.HashMap.set(existing.value.presences, connectionId, entry) }));
50
50
  });
51
- yield* effect.Metric.increment(require_Metrics.presenceUpdates);
52
- yield* effect.Metric.incrementBy(require_Metrics.presenceActive, 1);
51
+ yield* effect.Metric.update(require_Metrics.presenceUpdates, 1);
52
+ yield* effect.Metric.update(require_Metrics.presenceActive, 1);
53
53
  const event = {
54
54
  type: "presence_update",
55
55
  id: connectionId,
@@ -68,7 +68,7 @@ const layer = effect.Layer.effect(PresenceManagerTag, effect.Effect.gen(function
68
68
  if (docState._tag === "None") return map;
69
69
  return effect.HashMap.set(map, documentId, require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({}, docState.value), {}, { presences: effect.HashMap.remove(docState.value.presences, connectionId) }));
70
70
  });
71
- yield* effect.Metric.incrementBy(require_Metrics.presenceActive, -1);
71
+ yield* effect.Metric.update(require_Metrics.presenceActive, -1);
72
72
  const event = {
73
73
  type: "presence_remove",
74
74
  id: connectionId
@@ -1,5 +1,5 @@
1
1
  import { PresenceEntry, PresenceEvent, PresenceSnapshot } from "./Types.cjs";
2
- import { Context, Effect, Layer, Scope, Stream } from "effect";
2
+ import { Effect, Layer, Scope, ServiceMap, Stream } from "effect";
3
3
 
4
4
  //#region src/PresenceManager.d.ts
5
5
 
@@ -28,7 +28,7 @@ interface PresenceManager {
28
28
  */
29
29
  readonly subscribe: (documentId: string) => Effect.Effect<Stream.Stream<PresenceEvent>, never, Scope.Scope>;
30
30
  }
31
- declare const PresenceManagerTag_base: Context.TagClass<PresenceManagerTag, "@voidhash/mimic-effect/PresenceManager", PresenceManager>;
31
+ declare const PresenceManagerTag_base: ServiceMap.ServiceClass<PresenceManagerTag, "@voidhash/mimic-effect/PresenceManager", PresenceManager>;
32
32
  /**
33
33
  * Context tag for PresenceManager service
34
34
  */
@@ -1,5 +1,5 @@
1
1
  import { PresenceEntry, PresenceEvent, PresenceSnapshot } from "./Types.mjs";
2
- import { Context, Effect, Layer, Scope, Stream } from "effect";
2
+ import { Effect, Layer, Scope, ServiceMap, Stream } from "effect";
3
3
 
4
4
  //#region src/PresenceManager.d.ts
5
5
 
@@ -28,7 +28,7 @@ interface PresenceManager {
28
28
  */
29
29
  readonly subscribe: (documentId: string) => Effect.Effect<Stream.Stream<PresenceEvent>, never, Scope.Scope>;
30
30
  }
31
- declare const PresenceManagerTag_base: Context.TagClass<PresenceManagerTag, "@voidhash/mimic-effect/PresenceManager", PresenceManager>;
31
+ declare const PresenceManagerTag_base: ServiceMap.ServiceClass<PresenceManagerTag, "@voidhash/mimic-effect/PresenceManager", PresenceManager>;
32
32
  /**
33
33
  * Context tag for PresenceManager service
34
34
  */
@@ -1,6 +1,6 @@
1
1
  import { presenceActive, presenceUpdates } from "./Metrics.mjs";
2
2
  import { _objectSpread2 } from "./_virtual/_@oxc-project_runtime@0.103.0/helpers/objectSpread2.mjs";
3
- import { Context, Effect, HashMap, Layer, Metric, PubSub, Ref, Stream } from "effect";
3
+ import { Effect, HashMap, Layer, Metric, PubSub, Ref, ServiceMap, Stream } from "effect";
4
4
 
5
5
  //#region src/PresenceManager.ts
6
6
  /**
@@ -11,7 +11,7 @@ import { Context, Effect, HashMap, Layer, Metric, PubSub, Ref, Stream } from "ef
11
11
  /**
12
12
  * Context tag for PresenceManager service
13
13
  */
14
- var PresenceManagerTag = class extends Context.Tag("@voidhash/mimic-effect/PresenceManager")() {};
14
+ var PresenceManagerTag = class extends ServiceMap.Service()("@voidhash/mimic-effect/PresenceManager") {};
15
15
  /**
16
16
  * Create the PresenceManager layer.
17
17
  */
@@ -48,8 +48,8 @@ const layer = Layer.effect(PresenceManagerTag, Effect.gen(function* () {
48
48
  if (existing._tag === "None") return map;
49
49
  return HashMap.set(map, documentId, _objectSpread2(_objectSpread2({}, existing.value), {}, { presences: HashMap.set(existing.value.presences, connectionId, entry) }));
50
50
  });
51
- yield* Metric.increment(presenceUpdates);
52
- yield* Metric.incrementBy(presenceActive, 1);
51
+ yield* Metric.update(presenceUpdates, 1);
52
+ yield* Metric.update(presenceActive, 1);
53
53
  const event = {
54
54
  type: "presence_update",
55
55
  id: connectionId,
@@ -68,7 +68,7 @@ const layer = Layer.effect(PresenceManagerTag, Effect.gen(function* () {
68
68
  if (docState._tag === "None") return map;
69
69
  return HashMap.set(map, documentId, _objectSpread2(_objectSpread2({}, docState.value), {}, { presences: HashMap.remove(docState.value.presences, connectionId) }));
70
70
  });
71
- yield* Metric.incrementBy(presenceActive, -1);
71
+ yield* Metric.update(presenceActive, -1);
72
72
  const event = {
73
73
  type: "presence_remove",
74
74
  id: connectionId
@@ -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 = 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
+ {"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 Effect,\n HashMap,\n Layer,\n Metric,\n PubSub,\n Ref,\n Scope,\n ServiceMap,\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 ServiceMap.Service<PresenceManagerTag, PresenceManager>()(\n \"@voidhash/mimic-effect/PresenceManager\"\n) {}\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.update(Metrics.presenceUpdates, 1);\n yield* Metric.update(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.update(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,WAAW,SAA8C,CAC/F,yCACD,CAAC;;;;AAqBF,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,OAAOC,iBAAyB,EAAE;AAChD,UAAO,OAAO,OAAOC,gBAAwB,EAAE;GAG/C,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,OAAOD,gBAAwB,GAAG;GAGhD,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"}
package/dist/Types.d.cts CHANGED
@@ -79,7 +79,7 @@ type PresenceEvent = PresenceUpdateEvent | PresenceRemoveEvent;
79
79
  /**
80
80
  * Duration input type - matches Effect's DurationInput
81
81
  */
82
- type DurationInput = Duration.DurationInput;
82
+ type DurationInput = Duration.Input;
83
83
  /**
84
84
  * Snapshot configuration
85
85
  */
package/dist/Types.d.mts CHANGED
@@ -79,7 +79,7 @@ type PresenceEvent = PresenceUpdateEvent | PresenceRemoveEvent;
79
79
  /**
80
80
  * Duration input type - matches Effect's DurationInput
81
81
  */
82
- type DurationInput = Duration.DurationInput;
82
+ type DurationInput = Duration.Input;
83
83
  /**
84
84
  * Snapshot configuration
85
85
  */
@@ -482,11 +482,11 @@ const runAll = () => effect.Effect.gen(function* () {
482
482
  const passed = [];
483
483
  const failed = [];
484
484
  for (const test of tests) {
485
- const result = yield* effect.Effect.either(test.run);
486
- if (result._tag === "Right") passed.push(test);
485
+ const result = yield* effect.Effect.result(test.run);
486
+ if (result._tag === "Success") passed.push(test);
487
487
  else failed.push({
488
488
  test,
489
- error: result.left
489
+ error: result.failure
490
490
  });
491
491
  }
492
492
  return {
@@ -482,11 +482,11 @@ const runAll = () => Effect.gen(function* () {
482
482
  const passed = [];
483
483
  const failed = [];
484
484
  for (const test of tests) {
485
- const result = yield* Effect.either(test.run);
486
- if (result._tag === "Right") passed.push(test);
485
+ const result = yield* Effect.result(test.run);
486
+ if (result._tag === "Success") passed.push(test);
487
487
  else failed.push({
488
488
  test,
489
- error: result.left
489
+ error: result.failure
490
490
  });
491
491
  }
492
492
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"ColdStorageTestSuite.mjs","names":["tests: StorageTestCase<ColdStorageTestError, ColdStorageTag>[]","doc: StoredDocument","passed: StorageTestCase<ColdStorageTestError, ColdStorageTag>[]","failed: Array<{\n test: StorageTestCase<ColdStorageTestError, ColdStorageTag>;\n error: ColdStorageTestError;\n }>"],"sources":["../../src/testing/ColdStorageTestSuite.ts"],"sourcesContent":["/**\n * @voidhash/mimic-effect/testing - ColdStorage Test Suite\n *\n * Comprehensive test suite for ColdStorage adapter implementations.\n * These tests verify that an adapter correctly implements the ColdStorage interface\n * and can reliably store/retrieve document snapshots without data loss.\n */\nimport { Effect } from \"effect\";\nimport { ColdStorageTag } from \"../ColdStorage\";\nimport type { ColdStorageError } from \"../Errors\";\nimport type { StoredDocument } from \"../Types\";\nimport type { StorageTestCase, TestResults } from \"./types\";\nimport { TestError } from \"./types\";\nimport {\n assertEqual,\n assertUndefined,\n assertDefined,\n} from \"./assertions\";\n\n/**\n * Error type for ColdStorage tests - can be either a TestError or a ColdStorageError\n */\nexport type ColdStorageTestError = TestError | ColdStorageError;\n\n// =============================================================================\n// Categories\n// =============================================================================\n\nexport const Categories = {\n BasicOperations: \"Basic Operations\",\n DataIntegrity: \"Data Integrity\",\n VersionHandling: \"Version Handling\",\n TimestampHandling: \"Timestamp Handling\",\n DocumentIdEdgeCases: \"Document ID Edge Cases\",\n DocumentIsolation: \"Document Isolation\",\n ConsistencyGuarantees: \"Consistency Guarantees\",\n} as const;\n\n// =============================================================================\n// Helper Functions\n// =============================================================================\n\nconst makeDoc = (overrides: Partial<StoredDocument> = {}): StoredDocument => ({\n state: { title: \"Test Document\" },\n version: 1,\n schemaVersion: 1,\n savedAt: Date.now(),\n ...overrides,\n});\n\nconst generateLargeState = (sizeKB: number): Record<string, unknown> => {\n const chars = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\";\n const targetBytes = sizeKB * 1024;\n let content = \"\";\n while (content.length < targetBytes) {\n content += chars[Math.floor(Math.random() * chars.length)];\n }\n return { content, padding: Array(100).fill(\"x\") };\n};\n\n// =============================================================================\n// Test Definitions\n// =============================================================================\n\nconst tests: StorageTestCase<ColdStorageTestError, ColdStorageTag>[] = [\n // ---------------------------------------------------------------------------\n // Basic Operations\n // ---------------------------------------------------------------------------\n {\n name: \"load returns undefined for non-existent document\",\n category: Categories.BasicOperations,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const result = yield* storage.load(\"non-existent-doc-id-12345\");\n yield* assertUndefined(result, \"Expected undefined for non-existent document\");\n }),\n },\n\n {\n name: \"save then load returns exact same document\",\n category: Categories.BasicOperations,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc = makeDoc();\n yield* storage.save(\"test-doc-1\", doc);\n const loaded = yield* storage.load(\"test-doc-1\");\n yield* assertEqual(loaded, doc, \"Loaded document should match saved document\");\n }),\n },\n\n {\n name: \"save overwrites existing document\",\n category: Categories.BasicOperations,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc1 = makeDoc({ state: { title: \"First\" }, version: 1 });\n const doc2 = makeDoc({ state: { title: \"Second\" }, version: 2 });\n yield* storage.save(\"test-doc-2\", doc1);\n yield* storage.save(\"test-doc-2\", doc2);\n const loaded = yield* storage.load(\"test-doc-2\");\n yield* assertEqual(loaded, doc2, \"Should return the second (overwritten) document\");\n }),\n },\n\n {\n name: \"delete removes document\",\n category: Categories.BasicOperations,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc = makeDoc();\n yield* storage.save(\"test-doc-3\", doc);\n yield* storage.delete(\"test-doc-3\");\n const loaded = yield* storage.load(\"test-doc-3\");\n yield* assertUndefined(loaded, \"Document should be undefined after deletion\");\n }),\n },\n\n {\n name: \"delete on non-existent document does not error\",\n category: Categories.BasicOperations,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n yield* storage.delete(\"definitely-non-existent-doc-xyz\");\n }),\n },\n\n // ---------------------------------------------------------------------------\n // Data Integrity & Serialization\n // ---------------------------------------------------------------------------\n {\n name: \"all StoredDocument fields are preserved\",\n category: Categories.DataIntegrity,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc: StoredDocument = {\n state: { nested: { value: 42 } },\n version: 123,\n schemaVersion: 2,\n savedAt: 1704067200000,\n };\n yield* storage.save(\"fields-test\", doc);\n const loaded = yield* storage.load(\"fields-test\");\n const definedLoaded = yield* assertDefined(loaded, \"Document should exist\");\n yield* assertEqual(definedLoaded.state, doc.state, \"state should be preserved\");\n yield* assertEqual(definedLoaded.version, doc.version, \"version should be preserved\");\n yield* assertEqual(definedLoaded.schemaVersion, doc.schemaVersion, \"schemaVersion should be preserved\");\n yield* assertEqual(definedLoaded.savedAt, doc.savedAt, \"savedAt should be preserved\");\n }),\n },\n\n {\n name: \"complex nested state objects survive roundtrip\",\n category: Categories.DataIntegrity,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const complexState = {\n level1: {\n level2: {\n level3: {\n value: \"deep\",\n array: [1, 2, { nested: true }],\n },\n },\n sibling: \"value\",\n },\n topLevel: 42,\n };\n const doc = makeDoc({ state: complexState });\n yield* storage.save(\"complex-state\", doc);\n const loaded = yield* storage.load(\"complex-state\");\n yield* assertEqual(loaded, doc, \"Complex nested state should survive roundtrip\");\n }),\n },\n\n {\n name: \"arrays in state survive roundtrip\",\n category: Categories.DataIntegrity,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc = makeDoc({\n state: {\n numbers: [1, 2, 3, 4, 5],\n strings: [\"a\", \"b\", \"c\"],\n mixed: [1, \"two\", { three: 3 }, [4]],\n empty: [],\n },\n });\n yield* storage.save(\"arrays-test\", doc);\n const loaded = yield* storage.load(\"arrays-test\");\n yield* assertEqual(loaded, doc, \"Arrays should survive roundtrip\");\n }),\n },\n\n {\n name: \"null values in state survive roundtrip\",\n category: Categories.DataIntegrity,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc = makeDoc({\n state: {\n nullValue: null,\n nested: { alsoNull: null },\n array: [null, 1, null],\n },\n });\n yield* storage.save(\"null-test\", doc);\n const loaded = yield* storage.load(\"null-test\");\n yield* assertEqual(loaded, doc, \"null values should survive roundtrip\");\n }),\n },\n\n {\n name: \"empty object survives roundtrip\",\n category: Categories.DataIntegrity,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc = makeDoc({ state: {} });\n yield* storage.save(\"empty-obj\", doc);\n const loaded = yield* storage.load(\"empty-obj\");\n yield* assertEqual(loaded, doc, \"Empty object should survive roundtrip\");\n }),\n },\n\n {\n name: \"empty array survives roundtrip\",\n category: Categories.DataIntegrity,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc = makeDoc({ state: [] });\n yield* storage.save(\"empty-arr\", doc);\n const loaded = yield* storage.load(\"empty-arr\");\n yield* assertEqual(loaded, doc, \"Empty array should survive roundtrip\");\n }),\n },\n\n {\n name: \"unicode strings in state survive roundtrip\",\n category: Categories.DataIntegrity,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc = makeDoc({\n state: {\n emoji: \"Hello! How are you?\",\n chinese: \"Chinese Characters\",\n arabic: \"Arabic Letters\",\n special: \"Combined: Cafe\",\n },\n });\n yield* storage.save(\"unicode-test\", doc);\n const loaded = yield* storage.load(\"unicode-test\");\n yield* assertEqual(loaded, doc, \"Unicode strings should survive roundtrip\");\n }),\n },\n\n {\n name: \"large state objects (100KB+) survive roundtrip\",\n category: Categories.DataIntegrity,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const largeState = generateLargeState(100);\n const doc = makeDoc({ state: largeState });\n yield* storage.save(\"large-doc\", doc);\n const loaded = yield* storage.load(\"large-doc\");\n yield* assertEqual(loaded, doc, \"Large documents should survive roundtrip\");\n }),\n },\n\n {\n name: \"special JSON characters survive roundtrip\",\n category: Categories.DataIntegrity,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc = makeDoc({\n state: {\n quotes: 'He said \"hello\"',\n backslash: \"path\\\\to\\\\file\",\n newline: \"line1\\nline2\",\n tab: \"col1\\tcol2\",\n mixed: 'All: \"\\\\\\n\\t',\n },\n });\n yield* storage.save(\"special-chars\", doc);\n const loaded = yield* storage.load(\"special-chars\");\n yield* assertEqual(loaded, doc, \"Special JSON characters should survive roundtrip\");\n }),\n },\n\n // ---------------------------------------------------------------------------\n // Version Number Edge Cases\n // ---------------------------------------------------------------------------\n {\n name: \"version 0 is preserved correctly\",\n category: Categories.VersionHandling,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc = makeDoc({ version: 0 });\n yield* storage.save(\"version-0\", doc);\n const loaded = yield* storage.load(\"version-0\");\n const definedLoaded = yield* assertDefined(loaded, \"Document should exist\");\n yield* assertEqual(definedLoaded.version, 0, \"Version 0 should be preserved\");\n }),\n },\n\n {\n name: \"version 1 is preserved correctly\",\n category: Categories.VersionHandling,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc = makeDoc({ version: 1 });\n yield* storage.save(\"version-1\", doc);\n const loaded = yield* storage.load(\"version-1\");\n const definedLoaded = yield* assertDefined(loaded, \"Document should exist\");\n yield* assertEqual(definedLoaded.version, 1, \"Version 1 should be preserved\");\n }),\n },\n\n {\n name: \"large version numbers are preserved\",\n category: Categories.VersionHandling,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const largeVersion = Number.MAX_SAFE_INTEGER;\n const doc = makeDoc({ version: largeVersion });\n yield* storage.save(\"large-version\", doc);\n const loaded = yield* storage.load(\"large-version\");\n const definedLoaded = yield* assertDefined(loaded, \"Document should exist\");\n yield* assertEqual(\n definedLoaded.version,\n largeVersion,\n \"Large version number should be preserved exactly\"\n );\n }),\n },\n\n {\n name: \"schema version is preserved correctly\",\n category: Categories.VersionHandling,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc = makeDoc({ schemaVersion: 42 });\n yield* storage.save(\"schema-version\", doc);\n const loaded = yield* storage.load(\"schema-version\");\n const definedLoaded = yield* assertDefined(loaded, \"Document should exist\");\n yield* assertEqual(definedLoaded.schemaVersion, 42, \"Schema version should be preserved\");\n }),\n },\n\n // ---------------------------------------------------------------------------\n // Timestamp Handling\n // ---------------------------------------------------------------------------\n {\n name: \"savedAt timestamp is preserved exactly\",\n category: Categories.TimestampHandling,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const timestamp = 1704067200000;\n const doc = makeDoc({ savedAt: timestamp });\n yield* storage.save(\"timestamp-exact\", doc);\n const loaded = yield* storage.load(\"timestamp-exact\");\n const definedLoaded = yield* assertDefined(loaded, \"Document should exist\");\n yield* assertEqual(definedLoaded.savedAt, timestamp, \"savedAt should be preserved exactly\");\n }),\n },\n\n {\n name: \"timestamp 0 is preserved correctly\",\n category: Categories.TimestampHandling,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc = makeDoc({ savedAt: 0 });\n yield* storage.save(\"timestamp-0\", doc);\n const loaded = yield* storage.load(\"timestamp-0\");\n const definedLoaded = yield* assertDefined(loaded, \"Document should exist\");\n yield* assertEqual(definedLoaded.savedAt, 0, \"Timestamp 0 should be preserved\");\n }),\n },\n\n {\n name: \"recent timestamps are preserved correctly\",\n category: Categories.TimestampHandling,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const now = Date.now();\n const doc = makeDoc({ savedAt: now });\n yield* storage.save(\"timestamp-recent\", doc);\n const loaded = yield* storage.load(\"timestamp-recent\");\n const definedLoaded = yield* assertDefined(loaded, \"Document should exist\");\n yield* assertEqual(definedLoaded.savedAt, now, \"Recent timestamp should be preserved\");\n }),\n },\n\n // ---------------------------------------------------------------------------\n // Document ID Edge Cases\n // ---------------------------------------------------------------------------\n {\n name: \"long documentId (1000+ chars) works\",\n category: Categories.DocumentIdEdgeCases,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const longId = \"x\".repeat(1000);\n const doc = makeDoc();\n yield* storage.save(longId, doc);\n const loaded = yield* storage.load(longId);\n yield* assertEqual(loaded, doc, \"Long documentId should work correctly\");\n }),\n },\n\n {\n name: \"unicode documentId works\",\n category: Categories.DocumentIdEdgeCases,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const unicodeId = \"doc-test-id\";\n const doc = makeDoc();\n yield* storage.save(unicodeId, doc);\n const loaded = yield* storage.load(unicodeId);\n yield* assertEqual(loaded, doc, \"Unicode documentId should work correctly\");\n }),\n },\n\n {\n name: \"documentId with special chars works\",\n category: Categories.DocumentIdEdgeCases,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const specialId = \"doc/path:to.file\";\n const doc = makeDoc();\n yield* storage.save(specialId, doc);\n const loaded = yield* storage.load(specialId);\n yield* assertEqual(loaded, doc, \"DocumentId with special chars should work\");\n }),\n },\n\n {\n name: \"documentId with spaces works\",\n category: Categories.DocumentIdEdgeCases,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const spacedId = \"doc with spaces\";\n const doc = makeDoc();\n yield* storage.save(spacedId, doc);\n const loaded = yield* storage.load(spacedId);\n yield* assertEqual(loaded, doc, \"DocumentId with spaces should work\");\n }),\n },\n\n // ---------------------------------------------------------------------------\n // Document Isolation\n // ---------------------------------------------------------------------------\n {\n name: \"different documents are stored independently\",\n category: Categories.DocumentIsolation,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc1 = makeDoc({ state: { id: 1 }, version: 1 });\n const doc2 = makeDoc({ state: { id: 2 }, version: 2 });\n yield* storage.save(\"iso-doc-1\", doc1);\n yield* storage.save(\"iso-doc-2\", doc2);\n const loaded1 = yield* storage.load(\"iso-doc-1\");\n const loaded2 = yield* storage.load(\"iso-doc-2\");\n yield* assertEqual(loaded1, doc1, \"First document should be unchanged\");\n yield* assertEqual(loaded2, doc2, \"Second document should be unchanged\");\n }),\n },\n\n {\n name: \"deleting one document does not affect others\",\n category: Categories.DocumentIsolation,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc1 = makeDoc({ state: { id: 1 } });\n const doc2 = makeDoc({ state: { id: 2 } });\n yield* storage.save(\"del-iso-1\", doc1);\n yield* storage.save(\"del-iso-2\", doc2);\n yield* storage.delete(\"del-iso-1\");\n const loaded1 = yield* storage.load(\"del-iso-1\");\n const loaded2 = yield* storage.load(\"del-iso-2\");\n yield* assertUndefined(loaded1, \"Deleted document should be undefined\");\n yield* assertEqual(loaded2, doc2, \"Other document should be unchanged\");\n }),\n },\n\n {\n name: \"updating one document does not affect others\",\n category: Categories.DocumentIsolation,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc1v1 = makeDoc({ state: { id: 1, v: 1 }, version: 1 });\n const doc1v2 = makeDoc({ state: { id: 1, v: 2 }, version: 2 });\n const doc2 = makeDoc({ state: { id: 2 } });\n yield* storage.save(\"upd-iso-1\", doc1v1);\n yield* storage.save(\"upd-iso-2\", doc2);\n yield* storage.save(\"upd-iso-1\", doc1v2);\n const loaded1 = yield* storage.load(\"upd-iso-1\");\n const loaded2 = yield* storage.load(\"upd-iso-2\");\n yield* assertEqual(loaded1, doc1v2, \"Updated document should have new value\");\n yield* assertEqual(loaded2, doc2, \"Other document should be unchanged\");\n }),\n },\n\n // ---------------------------------------------------------------------------\n // Consistency Guarantees\n // ---------------------------------------------------------------------------\n {\n name: \"multiple saves to same doc, load returns latest\",\n category: Categories.ConsistencyGuarantees,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc1 = makeDoc({ state: { v: 1 }, version: 1 });\n const doc2 = makeDoc({ state: { v: 2 }, version: 2 });\n const doc3 = makeDoc({ state: { v: 3 }, version: 3 });\n yield* storage.save(\"multi-save\", doc1);\n yield* storage.save(\"multi-save\", doc2);\n yield* storage.save(\"multi-save\", doc3);\n const loaded = yield* storage.load(\"multi-save\");\n yield* assertEqual(loaded, doc3, \"Should return the latest saved document\");\n }),\n },\n\n {\n name: \"save-delete-save sequence works correctly\",\n category: Categories.ConsistencyGuarantees,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc1 = makeDoc({ state: { phase: \"first\" }, version: 1 });\n const doc2 = makeDoc({ state: { phase: \"second\" }, version: 2 });\n yield* storage.save(\"sds-test\", doc1);\n yield* storage.delete(\"sds-test\");\n yield* storage.save(\"sds-test\", doc2);\n const loaded = yield* storage.load(\"sds-test\");\n yield* assertEqual(loaded, doc2, \"Should return document saved after delete\");\n }),\n },\n];\n\n// =============================================================================\n// Exports\n// =============================================================================\n\n/**\n * Get all ColdStorage test cases.\n *\n * @returns Array of test cases that require ColdStorageTag\n */\nexport const makeTests = (): StorageTestCase<\n ColdStorageTestError,\n ColdStorageTag\n>[] => tests;\n\n/**\n * Run all tests and collect results.\n *\n * @returns Effect that produces TestResults\n */\nexport const runAll = (): Effect.Effect<\n TestResults<ColdStorageTestError, ColdStorageTag>,\n never,\n ColdStorageTag\n> =>\n Effect.gen(function* () {\n const passed: StorageTestCase<ColdStorageTestError, ColdStorageTag>[] = [];\n const failed: Array<{\n test: StorageTestCase<ColdStorageTestError, ColdStorageTag>;\n error: ColdStorageTestError;\n }> = [];\n\n for (const test of tests) {\n const result = yield* Effect.either(test.run);\n if (result._tag === \"Right\") {\n passed.push(test);\n } else {\n failed.push({ test, error: result.left });\n }\n }\n\n return {\n passed,\n failed,\n total: tests.length,\n passCount: passed.length,\n failCount: failed.length,\n };\n });\n\nexport const ColdStorageTestSuite = {\n Categories,\n makeTests,\n runAll,\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"ColdStorageTestSuite.mjs","names":["tests: StorageTestCase<ColdStorageTestError, ColdStorageTag>[]","doc: StoredDocument","passed: StorageTestCase<ColdStorageTestError, ColdStorageTag>[]","failed: Array<{\n test: StorageTestCase<ColdStorageTestError, ColdStorageTag>;\n error: ColdStorageTestError;\n }>"],"sources":["../../src/testing/ColdStorageTestSuite.ts"],"sourcesContent":["/**\n * @voidhash/mimic-effect/testing - ColdStorage Test Suite\n *\n * Comprehensive test suite for ColdStorage adapter implementations.\n * These tests verify that an adapter correctly implements the ColdStorage interface\n * and can reliably store/retrieve document snapshots without data loss.\n */\nimport { Effect } from \"effect\";\nimport { ColdStorageTag } from \"../ColdStorage\";\nimport type { ColdStorageError } from \"../Errors\";\nimport type { StoredDocument } from \"../Types\";\nimport type { StorageTestCase, TestResults } from \"./types\";\nimport { TestError } from \"./types\";\nimport {\n assertEqual,\n assertUndefined,\n assertDefined,\n} from \"./assertions\";\n\n/**\n * Error type for ColdStorage tests - can be either a TestError or a ColdStorageError\n */\nexport type ColdStorageTestError = TestError | ColdStorageError;\n\n// =============================================================================\n// Categories\n// =============================================================================\n\nexport const Categories = {\n BasicOperations: \"Basic Operations\",\n DataIntegrity: \"Data Integrity\",\n VersionHandling: \"Version Handling\",\n TimestampHandling: \"Timestamp Handling\",\n DocumentIdEdgeCases: \"Document ID Edge Cases\",\n DocumentIsolation: \"Document Isolation\",\n ConsistencyGuarantees: \"Consistency Guarantees\",\n} as const;\n\n// =============================================================================\n// Helper Functions\n// =============================================================================\n\nconst makeDoc = (overrides: Partial<StoredDocument> = {}): StoredDocument => ({\n state: { title: \"Test Document\" },\n version: 1,\n schemaVersion: 1,\n savedAt: Date.now(),\n ...overrides,\n});\n\nconst generateLargeState = (sizeKB: number): Record<string, unknown> => {\n const chars = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\";\n const targetBytes = sizeKB * 1024;\n let content = \"\";\n while (content.length < targetBytes) {\n content += chars[Math.floor(Math.random() * chars.length)];\n }\n return { content, padding: Array(100).fill(\"x\") };\n};\n\n// =============================================================================\n// Test Definitions\n// =============================================================================\n\nconst tests: StorageTestCase<ColdStorageTestError, ColdStorageTag>[] = [\n // ---------------------------------------------------------------------------\n // Basic Operations\n // ---------------------------------------------------------------------------\n {\n name: \"load returns undefined for non-existent document\",\n category: Categories.BasicOperations,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const result = yield* storage.load(\"non-existent-doc-id-12345\");\n yield* assertUndefined(result, \"Expected undefined for non-existent document\");\n }),\n },\n\n {\n name: \"save then load returns exact same document\",\n category: Categories.BasicOperations,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc = makeDoc();\n yield* storage.save(\"test-doc-1\", doc);\n const loaded = yield* storage.load(\"test-doc-1\");\n yield* assertEqual(loaded, doc, \"Loaded document should match saved document\");\n }),\n },\n\n {\n name: \"save overwrites existing document\",\n category: Categories.BasicOperations,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc1 = makeDoc({ state: { title: \"First\" }, version: 1 });\n const doc2 = makeDoc({ state: { title: \"Second\" }, version: 2 });\n yield* storage.save(\"test-doc-2\", doc1);\n yield* storage.save(\"test-doc-2\", doc2);\n const loaded = yield* storage.load(\"test-doc-2\");\n yield* assertEqual(loaded, doc2, \"Should return the second (overwritten) document\");\n }),\n },\n\n {\n name: \"delete removes document\",\n category: Categories.BasicOperations,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc = makeDoc();\n yield* storage.save(\"test-doc-3\", doc);\n yield* storage.delete(\"test-doc-3\");\n const loaded = yield* storage.load(\"test-doc-3\");\n yield* assertUndefined(loaded, \"Document should be undefined after deletion\");\n }),\n },\n\n {\n name: \"delete on non-existent document does not error\",\n category: Categories.BasicOperations,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n yield* storage.delete(\"definitely-non-existent-doc-xyz\");\n }),\n },\n\n // ---------------------------------------------------------------------------\n // Data Integrity & Serialization\n // ---------------------------------------------------------------------------\n {\n name: \"all StoredDocument fields are preserved\",\n category: Categories.DataIntegrity,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc: StoredDocument = {\n state: { nested: { value: 42 } },\n version: 123,\n schemaVersion: 2,\n savedAt: 1704067200000,\n };\n yield* storage.save(\"fields-test\", doc);\n const loaded = yield* storage.load(\"fields-test\");\n const definedLoaded = yield* assertDefined(loaded, \"Document should exist\");\n yield* assertEqual(definedLoaded.state, doc.state, \"state should be preserved\");\n yield* assertEqual(definedLoaded.version, doc.version, \"version should be preserved\");\n yield* assertEqual(definedLoaded.schemaVersion, doc.schemaVersion, \"schemaVersion should be preserved\");\n yield* assertEqual(definedLoaded.savedAt, doc.savedAt, \"savedAt should be preserved\");\n }),\n },\n\n {\n name: \"complex nested state objects survive roundtrip\",\n category: Categories.DataIntegrity,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const complexState = {\n level1: {\n level2: {\n level3: {\n value: \"deep\",\n array: [1, 2, { nested: true }],\n },\n },\n sibling: \"value\",\n },\n topLevel: 42,\n };\n const doc = makeDoc({ state: complexState });\n yield* storage.save(\"complex-state\", doc);\n const loaded = yield* storage.load(\"complex-state\");\n yield* assertEqual(loaded, doc, \"Complex nested state should survive roundtrip\");\n }),\n },\n\n {\n name: \"arrays in state survive roundtrip\",\n category: Categories.DataIntegrity,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc = makeDoc({\n state: {\n numbers: [1, 2, 3, 4, 5],\n strings: [\"a\", \"b\", \"c\"],\n mixed: [1, \"two\", { three: 3 }, [4]],\n empty: [],\n },\n });\n yield* storage.save(\"arrays-test\", doc);\n const loaded = yield* storage.load(\"arrays-test\");\n yield* assertEqual(loaded, doc, \"Arrays should survive roundtrip\");\n }),\n },\n\n {\n name: \"null values in state survive roundtrip\",\n category: Categories.DataIntegrity,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc = makeDoc({\n state: {\n nullValue: null,\n nested: { alsoNull: null },\n array: [null, 1, null],\n },\n });\n yield* storage.save(\"null-test\", doc);\n const loaded = yield* storage.load(\"null-test\");\n yield* assertEqual(loaded, doc, \"null values should survive roundtrip\");\n }),\n },\n\n {\n name: \"empty object survives roundtrip\",\n category: Categories.DataIntegrity,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc = makeDoc({ state: {} });\n yield* storage.save(\"empty-obj\", doc);\n const loaded = yield* storage.load(\"empty-obj\");\n yield* assertEqual(loaded, doc, \"Empty object should survive roundtrip\");\n }),\n },\n\n {\n name: \"empty array survives roundtrip\",\n category: Categories.DataIntegrity,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc = makeDoc({ state: [] });\n yield* storage.save(\"empty-arr\", doc);\n const loaded = yield* storage.load(\"empty-arr\");\n yield* assertEqual(loaded, doc, \"Empty array should survive roundtrip\");\n }),\n },\n\n {\n name: \"unicode strings in state survive roundtrip\",\n category: Categories.DataIntegrity,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc = makeDoc({\n state: {\n emoji: \"Hello! How are you?\",\n chinese: \"Chinese Characters\",\n arabic: \"Arabic Letters\",\n special: \"Combined: Cafe\",\n },\n });\n yield* storage.save(\"unicode-test\", doc);\n const loaded = yield* storage.load(\"unicode-test\");\n yield* assertEqual(loaded, doc, \"Unicode strings should survive roundtrip\");\n }),\n },\n\n {\n name: \"large state objects (100KB+) survive roundtrip\",\n category: Categories.DataIntegrity,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const largeState = generateLargeState(100);\n const doc = makeDoc({ state: largeState });\n yield* storage.save(\"large-doc\", doc);\n const loaded = yield* storage.load(\"large-doc\");\n yield* assertEqual(loaded, doc, \"Large documents should survive roundtrip\");\n }),\n },\n\n {\n name: \"special JSON characters survive roundtrip\",\n category: Categories.DataIntegrity,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc = makeDoc({\n state: {\n quotes: 'He said \"hello\"',\n backslash: \"path\\\\to\\\\file\",\n newline: \"line1\\nline2\",\n tab: \"col1\\tcol2\",\n mixed: 'All: \"\\\\\\n\\t',\n },\n });\n yield* storage.save(\"special-chars\", doc);\n const loaded = yield* storage.load(\"special-chars\");\n yield* assertEqual(loaded, doc, \"Special JSON characters should survive roundtrip\");\n }),\n },\n\n // ---------------------------------------------------------------------------\n // Version Number Edge Cases\n // ---------------------------------------------------------------------------\n {\n name: \"version 0 is preserved correctly\",\n category: Categories.VersionHandling,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc = makeDoc({ version: 0 });\n yield* storage.save(\"version-0\", doc);\n const loaded = yield* storage.load(\"version-0\");\n const definedLoaded = yield* assertDefined(loaded, \"Document should exist\");\n yield* assertEqual(definedLoaded.version, 0, \"Version 0 should be preserved\");\n }),\n },\n\n {\n name: \"version 1 is preserved correctly\",\n category: Categories.VersionHandling,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc = makeDoc({ version: 1 });\n yield* storage.save(\"version-1\", doc);\n const loaded = yield* storage.load(\"version-1\");\n const definedLoaded = yield* assertDefined(loaded, \"Document should exist\");\n yield* assertEqual(definedLoaded.version, 1, \"Version 1 should be preserved\");\n }),\n },\n\n {\n name: \"large version numbers are preserved\",\n category: Categories.VersionHandling,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const largeVersion = Number.MAX_SAFE_INTEGER;\n const doc = makeDoc({ version: largeVersion });\n yield* storage.save(\"large-version\", doc);\n const loaded = yield* storage.load(\"large-version\");\n const definedLoaded = yield* assertDefined(loaded, \"Document should exist\");\n yield* assertEqual(\n definedLoaded.version,\n largeVersion,\n \"Large version number should be preserved exactly\"\n );\n }),\n },\n\n {\n name: \"schema version is preserved correctly\",\n category: Categories.VersionHandling,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc = makeDoc({ schemaVersion: 42 });\n yield* storage.save(\"schema-version\", doc);\n const loaded = yield* storage.load(\"schema-version\");\n const definedLoaded = yield* assertDefined(loaded, \"Document should exist\");\n yield* assertEqual(definedLoaded.schemaVersion, 42, \"Schema version should be preserved\");\n }),\n },\n\n // ---------------------------------------------------------------------------\n // Timestamp Handling\n // ---------------------------------------------------------------------------\n {\n name: \"savedAt timestamp is preserved exactly\",\n category: Categories.TimestampHandling,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const timestamp = 1704067200000;\n const doc = makeDoc({ savedAt: timestamp });\n yield* storage.save(\"timestamp-exact\", doc);\n const loaded = yield* storage.load(\"timestamp-exact\");\n const definedLoaded = yield* assertDefined(loaded, \"Document should exist\");\n yield* assertEqual(definedLoaded.savedAt, timestamp, \"savedAt should be preserved exactly\");\n }),\n },\n\n {\n name: \"timestamp 0 is preserved correctly\",\n category: Categories.TimestampHandling,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc = makeDoc({ savedAt: 0 });\n yield* storage.save(\"timestamp-0\", doc);\n const loaded = yield* storage.load(\"timestamp-0\");\n const definedLoaded = yield* assertDefined(loaded, \"Document should exist\");\n yield* assertEqual(definedLoaded.savedAt, 0, \"Timestamp 0 should be preserved\");\n }),\n },\n\n {\n name: \"recent timestamps are preserved correctly\",\n category: Categories.TimestampHandling,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const now = Date.now();\n const doc = makeDoc({ savedAt: now });\n yield* storage.save(\"timestamp-recent\", doc);\n const loaded = yield* storage.load(\"timestamp-recent\");\n const definedLoaded = yield* assertDefined(loaded, \"Document should exist\");\n yield* assertEqual(definedLoaded.savedAt, now, \"Recent timestamp should be preserved\");\n }),\n },\n\n // ---------------------------------------------------------------------------\n // Document ID Edge Cases\n // ---------------------------------------------------------------------------\n {\n name: \"long documentId (1000+ chars) works\",\n category: Categories.DocumentIdEdgeCases,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const longId = \"x\".repeat(1000);\n const doc = makeDoc();\n yield* storage.save(longId, doc);\n const loaded = yield* storage.load(longId);\n yield* assertEqual(loaded, doc, \"Long documentId should work correctly\");\n }),\n },\n\n {\n name: \"unicode documentId works\",\n category: Categories.DocumentIdEdgeCases,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const unicodeId = \"doc-test-id\";\n const doc = makeDoc();\n yield* storage.save(unicodeId, doc);\n const loaded = yield* storage.load(unicodeId);\n yield* assertEqual(loaded, doc, \"Unicode documentId should work correctly\");\n }),\n },\n\n {\n name: \"documentId with special chars works\",\n category: Categories.DocumentIdEdgeCases,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const specialId = \"doc/path:to.file\";\n const doc = makeDoc();\n yield* storage.save(specialId, doc);\n const loaded = yield* storage.load(specialId);\n yield* assertEqual(loaded, doc, \"DocumentId with special chars should work\");\n }),\n },\n\n {\n name: \"documentId with spaces works\",\n category: Categories.DocumentIdEdgeCases,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const spacedId = \"doc with spaces\";\n const doc = makeDoc();\n yield* storage.save(spacedId, doc);\n const loaded = yield* storage.load(spacedId);\n yield* assertEqual(loaded, doc, \"DocumentId with spaces should work\");\n }),\n },\n\n // ---------------------------------------------------------------------------\n // Document Isolation\n // ---------------------------------------------------------------------------\n {\n name: \"different documents are stored independently\",\n category: Categories.DocumentIsolation,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc1 = makeDoc({ state: { id: 1 }, version: 1 });\n const doc2 = makeDoc({ state: { id: 2 }, version: 2 });\n yield* storage.save(\"iso-doc-1\", doc1);\n yield* storage.save(\"iso-doc-2\", doc2);\n const loaded1 = yield* storage.load(\"iso-doc-1\");\n const loaded2 = yield* storage.load(\"iso-doc-2\");\n yield* assertEqual(loaded1, doc1, \"First document should be unchanged\");\n yield* assertEqual(loaded2, doc2, \"Second document should be unchanged\");\n }),\n },\n\n {\n name: \"deleting one document does not affect others\",\n category: Categories.DocumentIsolation,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc1 = makeDoc({ state: { id: 1 } });\n const doc2 = makeDoc({ state: { id: 2 } });\n yield* storage.save(\"del-iso-1\", doc1);\n yield* storage.save(\"del-iso-2\", doc2);\n yield* storage.delete(\"del-iso-1\");\n const loaded1 = yield* storage.load(\"del-iso-1\");\n const loaded2 = yield* storage.load(\"del-iso-2\");\n yield* assertUndefined(loaded1, \"Deleted document should be undefined\");\n yield* assertEqual(loaded2, doc2, \"Other document should be unchanged\");\n }),\n },\n\n {\n name: \"updating one document does not affect others\",\n category: Categories.DocumentIsolation,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc1v1 = makeDoc({ state: { id: 1, v: 1 }, version: 1 });\n const doc1v2 = makeDoc({ state: { id: 1, v: 2 }, version: 2 });\n const doc2 = makeDoc({ state: { id: 2 } });\n yield* storage.save(\"upd-iso-1\", doc1v1);\n yield* storage.save(\"upd-iso-2\", doc2);\n yield* storage.save(\"upd-iso-1\", doc1v2);\n const loaded1 = yield* storage.load(\"upd-iso-1\");\n const loaded2 = yield* storage.load(\"upd-iso-2\");\n yield* assertEqual(loaded1, doc1v2, \"Updated document should have new value\");\n yield* assertEqual(loaded2, doc2, \"Other document should be unchanged\");\n }),\n },\n\n // ---------------------------------------------------------------------------\n // Consistency Guarantees\n // ---------------------------------------------------------------------------\n {\n name: \"multiple saves to same doc, load returns latest\",\n category: Categories.ConsistencyGuarantees,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc1 = makeDoc({ state: { v: 1 }, version: 1 });\n const doc2 = makeDoc({ state: { v: 2 }, version: 2 });\n const doc3 = makeDoc({ state: { v: 3 }, version: 3 });\n yield* storage.save(\"multi-save\", doc1);\n yield* storage.save(\"multi-save\", doc2);\n yield* storage.save(\"multi-save\", doc3);\n const loaded = yield* storage.load(\"multi-save\");\n yield* assertEqual(loaded, doc3, \"Should return the latest saved document\");\n }),\n },\n\n {\n name: \"save-delete-save sequence works correctly\",\n category: Categories.ConsistencyGuarantees,\n run: Effect.gen(function* () {\n const storage = yield* ColdStorageTag;\n const doc1 = makeDoc({ state: { phase: \"first\" }, version: 1 });\n const doc2 = makeDoc({ state: { phase: \"second\" }, version: 2 });\n yield* storage.save(\"sds-test\", doc1);\n yield* storage.delete(\"sds-test\");\n yield* storage.save(\"sds-test\", doc2);\n const loaded = yield* storage.load(\"sds-test\");\n yield* assertEqual(loaded, doc2, \"Should return document saved after delete\");\n }),\n },\n];\n\n// =============================================================================\n// Exports\n// =============================================================================\n\n/**\n * Get all ColdStorage test cases.\n *\n * @returns Array of test cases that require ColdStorageTag\n */\nexport const makeTests = (): StorageTestCase<\n ColdStorageTestError,\n ColdStorageTag\n>[] => tests;\n\n/**\n * Run all tests and collect results.\n *\n * @returns Effect that produces TestResults\n */\nexport const runAll = (): Effect.Effect<\n TestResults<ColdStorageTestError, ColdStorageTag>,\n never,\n ColdStorageTag\n> =>\n Effect.gen(function* () {\n const passed: StorageTestCase<ColdStorageTestError, ColdStorageTag>[] = [];\n const failed: Array<{\n test: StorageTestCase<ColdStorageTestError, ColdStorageTag>;\n error: ColdStorageTestError;\n }> = [];\n\n for (const test of tests) {\n const result = yield* Effect.result(test.run);\n if (result._tag === \"Success\") {\n passed.push(test);\n } else {\n failed.push({ test, error: result.failure });\n }\n }\n\n return {\n passed,\n failed,\n total: tests.length,\n passCount: passed.length,\n failCount: failed.length,\n };\n });\n\nexport const ColdStorageTestSuite = {\n Categories,\n makeTests,\n runAll,\n};\n"],"mappings":"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"}
@@ -507,9 +507,9 @@ const tests = [
507
507
  run: effect.Effect.gen(function* () {
508
508
  const storage = yield* require_HotStorage.HotStorageTag;
509
509
  yield* storage.appendWithCheck("gap-check-fail", makeEntry(1), 1);
510
- const result = yield* effect.Effect.either(storage.appendWithCheck("gap-check-fail", makeEntry(3), 3));
511
- yield* require_assertions.assertTrue(result._tag === "Left", "appendWithCheck should fail when there's a version gap");
512
- if (result._tag === "Left") yield* require_assertions.assertTrue(result.left._tag === "WalVersionGapError", "Error should be WalVersionGapError");
510
+ const result = yield* effect.Effect.result(storage.appendWithCheck("gap-check-fail", makeEntry(3), 3));
511
+ yield* require_assertions.assertTrue(result._tag === "Failure", "appendWithCheck should fail when there's a version gap");
512
+ if (result._tag === "Failure") yield* require_assertions.assertTrue(result.failure._tag === "WalVersionGapError", "Error should be WalVersionGapError");
513
513
  const entries = yield* storage.getEntries("gap-check-fail", 0);
514
514
  yield* require_assertions.assertLength(entries, 1, "Should only have version 1");
515
515
  yield* require_assertions.assertEqual(entries[0].version, 1, "Only version 1 should exist");
@@ -520,9 +520,9 @@ const tests = [
520
520
  category: Categories.GapChecking,
521
521
  run: effect.Effect.gen(function* () {
522
522
  const storage = yield* require_HotStorage.HotStorageTag;
523
- const result = yield* effect.Effect.either(storage.appendWithCheck("gap-check-not-first", makeEntry(2), 2));
524
- yield* require_assertions.assertTrue(result._tag === "Left", "appendWithCheck should fail when first entry is not version 1");
525
- if (result._tag === "Left") yield* require_assertions.assertTrue(result.left._tag === "WalVersionGapError", "Error should be WalVersionGapError");
523
+ const result = yield* effect.Effect.result(storage.appendWithCheck("gap-check-not-first", makeEntry(2), 2));
524
+ yield* require_assertions.assertTrue(result._tag === "Failure", "appendWithCheck should fail when first entry is not version 1");
525
+ if (result._tag === "Failure") yield* require_assertions.assertTrue(result.failure._tag === "WalVersionGapError", "Error should be WalVersionGapError");
526
526
  yield* require_assertions.assertEmpty(yield* storage.getEntries("gap-check-not-first", 0), "No entries should exist after failed append");
527
527
  })
528
528
  },
@@ -532,7 +532,7 @@ const tests = [
532
532
  run: effect.Effect.gen(function* () {
533
533
  const storage = yield* require_HotStorage.HotStorageTag;
534
534
  yield* storage.appendWithCheck("gap-check-duplicate", makeEntry(1), 1);
535
- yield* require_assertions.assertTrue((yield* effect.Effect.either(storage.appendWithCheck("gap-check-duplicate", makeEntry(1), 1)))._tag === "Left", "appendWithCheck should fail when version already exists");
535
+ yield* require_assertions.assertTrue((yield* effect.Effect.result(storage.appendWithCheck("gap-check-duplicate", makeEntry(1), 1)))._tag === "Failure", "appendWithCheck should fail when version already exists");
536
536
  yield* require_assertions.assertLength(yield* storage.getEntries("gap-check-duplicate", 0), 1, "Should still only have one entry");
537
537
  })
538
538
  },
@@ -574,9 +574,9 @@ const tests = [
574
574
  run: effect.Effect.gen(function* () {
575
575
  const storage = yield* require_HotStorage.HotStorageTag;
576
576
  yield* storage.truncate("gap-base-detect", 5);
577
- const result = yield* effect.Effect.either(storage.appendWithCheck("gap-base-detect", makeEntry(7), 7, 5));
578
- yield* require_assertions.assertTrue(result._tag === "Left", "Should fail when skipping version 6");
579
- if (result._tag === "Left") yield* require_assertions.assertTrue(result.left._tag === "WalVersionGapError", "Error should be WalVersionGapError");
577
+ const result = yield* effect.Effect.result(storage.appendWithCheck("gap-base-detect", makeEntry(7), 7, 5));
578
+ yield* require_assertions.assertTrue(result._tag === "Failure", "Should fail when skipping version 6");
579
+ if (result._tag === "Failure") yield* require_assertions.assertTrue(result.failure._tag === "WalVersionGapError", "Error should be WalVersionGapError");
580
580
  })
581
581
  },
582
582
  {
@@ -832,11 +832,11 @@ const runAll = () => effect.Effect.gen(function* () {
832
832
  const passed = [];
833
833
  const failed = [];
834
834
  for (const test of tests) {
835
- const result = yield* effect.Effect.either(test.run);
836
- if (result._tag === "Right") passed.push(test);
835
+ const result = yield* effect.Effect.result(test.run);
836
+ if (result._tag === "Success") passed.push(test);
837
837
  else failed.push({
838
838
  test,
839
- error: result.left
839
+ error: result.failure
840
840
  });
841
841
  }
842
842
  return {
@@ -507,9 +507,9 @@ const tests = [
507
507
  run: Effect.gen(function* () {
508
508
  const storage = yield* HotStorageTag;
509
509
  yield* storage.appendWithCheck("gap-check-fail", makeEntry(1), 1);
510
- const result = yield* Effect.either(storage.appendWithCheck("gap-check-fail", makeEntry(3), 3));
511
- yield* assertTrue(result._tag === "Left", "appendWithCheck should fail when there's a version gap");
512
- if (result._tag === "Left") yield* assertTrue(result.left._tag === "WalVersionGapError", "Error should be WalVersionGapError");
510
+ const result = yield* Effect.result(storage.appendWithCheck("gap-check-fail", makeEntry(3), 3));
511
+ yield* assertTrue(result._tag === "Failure", "appendWithCheck should fail when there's a version gap");
512
+ if (result._tag === "Failure") yield* assertTrue(result.failure._tag === "WalVersionGapError", "Error should be WalVersionGapError");
513
513
  const entries = yield* storage.getEntries("gap-check-fail", 0);
514
514
  yield* assertLength(entries, 1, "Should only have version 1");
515
515
  yield* assertEqual(entries[0].version, 1, "Only version 1 should exist");
@@ -520,9 +520,9 @@ const tests = [
520
520
  category: Categories.GapChecking,
521
521
  run: Effect.gen(function* () {
522
522
  const storage = yield* HotStorageTag;
523
- const result = yield* Effect.either(storage.appendWithCheck("gap-check-not-first", makeEntry(2), 2));
524
- yield* assertTrue(result._tag === "Left", "appendWithCheck should fail when first entry is not version 1");
525
- if (result._tag === "Left") yield* assertTrue(result.left._tag === "WalVersionGapError", "Error should be WalVersionGapError");
523
+ const result = yield* Effect.result(storage.appendWithCheck("gap-check-not-first", makeEntry(2), 2));
524
+ yield* assertTrue(result._tag === "Failure", "appendWithCheck should fail when first entry is not version 1");
525
+ if (result._tag === "Failure") yield* assertTrue(result.failure._tag === "WalVersionGapError", "Error should be WalVersionGapError");
526
526
  yield* assertEmpty(yield* storage.getEntries("gap-check-not-first", 0), "No entries should exist after failed append");
527
527
  })
528
528
  },
@@ -532,7 +532,7 @@ const tests = [
532
532
  run: Effect.gen(function* () {
533
533
  const storage = yield* HotStorageTag;
534
534
  yield* storage.appendWithCheck("gap-check-duplicate", makeEntry(1), 1);
535
- yield* assertTrue((yield* Effect.either(storage.appendWithCheck("gap-check-duplicate", makeEntry(1), 1)))._tag === "Left", "appendWithCheck should fail when version already exists");
535
+ yield* assertTrue((yield* Effect.result(storage.appendWithCheck("gap-check-duplicate", makeEntry(1), 1)))._tag === "Failure", "appendWithCheck should fail when version already exists");
536
536
  yield* assertLength(yield* storage.getEntries("gap-check-duplicate", 0), 1, "Should still only have one entry");
537
537
  })
538
538
  },
@@ -574,9 +574,9 @@ const tests = [
574
574
  run: Effect.gen(function* () {
575
575
  const storage = yield* HotStorageTag;
576
576
  yield* storage.truncate("gap-base-detect", 5);
577
- const result = yield* Effect.either(storage.appendWithCheck("gap-base-detect", makeEntry(7), 7, 5));
578
- yield* assertTrue(result._tag === "Left", "Should fail when skipping version 6");
579
- if (result._tag === "Left") yield* assertTrue(result.left._tag === "WalVersionGapError", "Error should be WalVersionGapError");
577
+ const result = yield* Effect.result(storage.appendWithCheck("gap-base-detect", makeEntry(7), 7, 5));
578
+ yield* assertTrue(result._tag === "Failure", "Should fail when skipping version 6");
579
+ if (result._tag === "Failure") yield* assertTrue(result.failure._tag === "WalVersionGapError", "Error should be WalVersionGapError");
580
580
  })
581
581
  },
582
582
  {
@@ -832,11 +832,11 @@ const runAll = () => Effect.gen(function* () {
832
832
  const passed = [];
833
833
  const failed = [];
834
834
  for (const test of tests) {
835
- const result = yield* Effect.either(test.run);
836
- if (result._tag === "Right") passed.push(test);
835
+ const result = yield* Effect.result(test.run);
836
+ if (result._tag === "Success") passed.push(test);
837
837
  else failed.push({
838
838
  test,
839
- error: result.left
839
+ error: result.failure
840
840
  });
841
841
  }
842
842
  return {