@voidhash/mimic-effect 0.0.9 → 1.0.0-beta.2

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 (227) hide show
  1. package/.turbo/turbo-build.log +136 -90
  2. package/README.md +385 -0
  3. package/dist/ColdStorage.cjs +60 -0
  4. package/dist/ColdStorage.d.cts +53 -0
  5. package/dist/ColdStorage.d.cts.map +1 -0
  6. package/dist/ColdStorage.d.mts +53 -0
  7. package/dist/ColdStorage.d.mts.map +1 -0
  8. package/dist/ColdStorage.mjs +60 -0
  9. package/dist/ColdStorage.mjs.map +1 -0
  10. package/dist/DocumentManager.cjs +263 -82
  11. package/dist/DocumentManager.d.cts +44 -22
  12. package/dist/DocumentManager.d.cts.map +1 -1
  13. package/dist/DocumentManager.d.mts +44 -22
  14. package/dist/DocumentManager.d.mts.map +1 -1
  15. package/dist/DocumentManager.mjs +259 -67
  16. package/dist/DocumentManager.mjs.map +1 -1
  17. package/dist/Errors.cjs +54 -0
  18. package/dist/Errors.d.cts +96 -0
  19. package/dist/Errors.d.cts.map +1 -0
  20. package/dist/Errors.d.mts +96 -0
  21. package/dist/Errors.d.mts.map +1 -0
  22. package/dist/Errors.mjs +48 -0
  23. package/dist/Errors.mjs.map +1 -0
  24. package/dist/HotStorage.cjs +100 -0
  25. package/dist/HotStorage.d.cts +70 -0
  26. package/dist/HotStorage.d.cts.map +1 -0
  27. package/dist/HotStorage.d.mts +70 -0
  28. package/dist/HotStorage.d.mts.map +1 -0
  29. package/dist/HotStorage.mjs +100 -0
  30. package/dist/HotStorage.mjs.map +1 -0
  31. package/dist/Metrics.cjs +143 -0
  32. package/dist/Metrics.d.cts +31 -0
  33. package/dist/Metrics.d.cts.map +1 -0
  34. package/dist/Metrics.d.mts +31 -0
  35. package/dist/Metrics.d.mts.map +1 -0
  36. package/dist/Metrics.mjs +126 -0
  37. package/dist/Metrics.mjs.map +1 -0
  38. package/dist/MimicAuthService.cjs +61 -45
  39. package/dist/MimicAuthService.d.cts +61 -48
  40. package/dist/MimicAuthService.d.cts.map +1 -1
  41. package/dist/MimicAuthService.d.mts +61 -48
  42. package/dist/MimicAuthService.d.mts.map +1 -1
  43. package/dist/MimicAuthService.mjs +60 -36
  44. package/dist/MimicAuthService.mjs.map +1 -1
  45. package/dist/MimicClusterServerEngine.cjs +521 -0
  46. package/dist/MimicClusterServerEngine.d.cts +17 -0
  47. package/dist/MimicClusterServerEngine.d.cts.map +1 -0
  48. package/dist/MimicClusterServerEngine.d.mts +17 -0
  49. package/dist/MimicClusterServerEngine.d.mts.map +1 -0
  50. package/dist/MimicClusterServerEngine.mjs +523 -0
  51. package/dist/MimicClusterServerEngine.mjs.map +1 -0
  52. package/dist/MimicServer.cjs +205 -96
  53. package/dist/MimicServer.d.cts +9 -110
  54. package/dist/MimicServer.d.cts.map +1 -1
  55. package/dist/MimicServer.d.mts +9 -110
  56. package/dist/MimicServer.d.mts.map +1 -1
  57. package/dist/MimicServer.mjs +206 -90
  58. package/dist/MimicServer.mjs.map +1 -1
  59. package/dist/MimicServerEngine.cjs +97 -0
  60. package/dist/MimicServerEngine.d.cts +78 -0
  61. package/dist/MimicServerEngine.d.cts.map +1 -0
  62. package/dist/MimicServerEngine.d.mts +78 -0
  63. package/dist/MimicServerEngine.d.mts.map +1 -0
  64. package/dist/MimicServerEngine.mjs +97 -0
  65. package/dist/MimicServerEngine.mjs.map +1 -0
  66. package/dist/PresenceManager.cjs +75 -91
  67. package/dist/PresenceManager.d.cts +17 -66
  68. package/dist/PresenceManager.d.cts.map +1 -1
  69. package/dist/PresenceManager.d.mts +17 -66
  70. package/dist/PresenceManager.d.mts.map +1 -1
  71. package/dist/PresenceManager.mjs +74 -78
  72. package/dist/PresenceManager.mjs.map +1 -1
  73. package/dist/Protocol.cjs +146 -0
  74. package/dist/Protocol.d.cts +203 -0
  75. package/dist/Protocol.d.cts.map +1 -0
  76. package/dist/Protocol.d.mts +203 -0
  77. package/dist/Protocol.d.mts.map +1 -0
  78. package/dist/Protocol.mjs +132 -0
  79. package/dist/Protocol.mjs.map +1 -0
  80. package/dist/Types.d.cts +172 -0
  81. package/dist/Types.d.cts.map +1 -0
  82. package/dist/Types.d.mts +172 -0
  83. package/dist/Types.d.mts.map +1 -0
  84. package/dist/_virtual/rolldown_runtime.cjs +1 -25
  85. package/dist/_virtual/rolldown_runtime.mjs +4 -1
  86. package/dist/index.cjs +37 -75
  87. package/dist/index.d.cts +13 -12
  88. package/dist/index.d.mts +13 -12
  89. package/dist/index.mjs +12 -12
  90. package/dist/testing/ColdStorageTestSuite.cjs +508 -0
  91. package/dist/testing/ColdStorageTestSuite.d.cts +36 -0
  92. package/dist/testing/ColdStorageTestSuite.d.cts.map +1 -0
  93. package/dist/testing/ColdStorageTestSuite.d.mts +36 -0
  94. package/dist/testing/ColdStorageTestSuite.d.mts.map +1 -0
  95. package/dist/testing/ColdStorageTestSuite.mjs +508 -0
  96. package/dist/testing/ColdStorageTestSuite.mjs.map +1 -0
  97. package/dist/testing/FailingStorage.cjs +135 -0
  98. package/dist/testing/FailingStorage.d.cts +43 -0
  99. package/dist/testing/FailingStorage.d.cts.map +1 -0
  100. package/dist/testing/FailingStorage.d.mts +43 -0
  101. package/dist/testing/FailingStorage.d.mts.map +1 -0
  102. package/dist/testing/FailingStorage.mjs +136 -0
  103. package/dist/testing/FailingStorage.mjs.map +1 -0
  104. package/dist/testing/HotStorageTestSuite.cjs +585 -0
  105. package/dist/testing/HotStorageTestSuite.d.cts +40 -0
  106. package/dist/testing/HotStorageTestSuite.d.cts.map +1 -0
  107. package/dist/testing/HotStorageTestSuite.d.mts +40 -0
  108. package/dist/testing/HotStorageTestSuite.d.mts.map +1 -0
  109. package/dist/testing/HotStorageTestSuite.mjs +585 -0
  110. package/dist/testing/HotStorageTestSuite.mjs.map +1 -0
  111. package/dist/testing/StorageIntegrationTestSuite.cjs +349 -0
  112. package/dist/testing/StorageIntegrationTestSuite.d.cts +35 -0
  113. package/dist/testing/StorageIntegrationTestSuite.d.cts.map +1 -0
  114. package/dist/testing/StorageIntegrationTestSuite.d.mts +35 -0
  115. package/dist/testing/StorageIntegrationTestSuite.d.mts.map +1 -0
  116. package/dist/testing/StorageIntegrationTestSuite.mjs +349 -0
  117. package/dist/testing/StorageIntegrationTestSuite.mjs.map +1 -0
  118. package/dist/testing/assertions.cjs +114 -0
  119. package/dist/testing/assertions.mjs +109 -0
  120. package/dist/testing/assertions.mjs.map +1 -0
  121. package/dist/testing/index.cjs +14 -0
  122. package/dist/testing/index.d.cts +6 -0
  123. package/dist/testing/index.d.mts +6 -0
  124. package/dist/testing/index.mjs +7 -0
  125. package/dist/testing/types.cjs +15 -0
  126. package/dist/testing/types.d.cts +90 -0
  127. package/dist/testing/types.d.cts.map +1 -0
  128. package/dist/testing/types.d.mts +90 -0
  129. package/dist/testing/types.d.mts.map +1 -0
  130. package/dist/testing/types.mjs +16 -0
  131. package/dist/testing/types.mjs.map +1 -0
  132. package/package.json +18 -3
  133. package/src/ColdStorage.ts +136 -0
  134. package/src/DocumentManager.ts +550 -190
  135. package/src/Errors.ts +114 -0
  136. package/src/HotStorage.ts +239 -0
  137. package/src/Metrics.ts +187 -0
  138. package/src/MimicAuthService.ts +126 -64
  139. package/src/MimicClusterServerEngine.ts +946 -0
  140. package/src/MimicServer.ts +448 -195
  141. package/src/MimicServerEngine.ts +276 -0
  142. package/src/PresenceManager.ts +169 -240
  143. package/src/Protocol.ts +350 -0
  144. package/src/Types.ts +231 -0
  145. package/src/index.ts +57 -23
  146. package/src/testing/ColdStorageTestSuite.ts +589 -0
  147. package/src/testing/FailingStorage.ts +286 -0
  148. package/src/testing/HotStorageTestSuite.ts +762 -0
  149. package/src/testing/StorageIntegrationTestSuite.ts +504 -0
  150. package/src/testing/assertions.ts +181 -0
  151. package/src/testing/index.ts +83 -0
  152. package/src/testing/types.ts +100 -0
  153. package/tests/ColdStorage.test.ts +24 -0
  154. package/tests/DocumentManager.test.ts +158 -287
  155. package/tests/HotStorage.test.ts +24 -0
  156. package/tests/MimicAuthService.test.ts +102 -134
  157. package/tests/MimicClusterServerEngine.test.ts +587 -0
  158. package/tests/MimicServer.test.ts +90 -226
  159. package/tests/MimicServerEngine.test.ts +521 -0
  160. package/tests/PresenceManager.test.ts +22 -63
  161. package/tests/Protocol.test.ts +190 -0
  162. package/tests/StorageIntegration.test.ts +259 -0
  163. package/tsconfig.json +1 -1
  164. package/tsdown.config.ts +1 -1
  165. package/dist/DocumentProtocol.cjs +0 -94
  166. package/dist/DocumentProtocol.d.cts +0 -113
  167. package/dist/DocumentProtocol.d.cts.map +0 -1
  168. package/dist/DocumentProtocol.d.mts +0 -113
  169. package/dist/DocumentProtocol.d.mts.map +0 -1
  170. package/dist/DocumentProtocol.mjs +0 -89
  171. package/dist/DocumentProtocol.mjs.map +0 -1
  172. package/dist/MimicConfig.cjs +0 -60
  173. package/dist/MimicConfig.d.cts +0 -141
  174. package/dist/MimicConfig.d.cts.map +0 -1
  175. package/dist/MimicConfig.d.mts +0 -141
  176. package/dist/MimicConfig.d.mts.map +0 -1
  177. package/dist/MimicConfig.mjs +0 -50
  178. package/dist/MimicConfig.mjs.map +0 -1
  179. package/dist/MimicDataStorage.cjs +0 -83
  180. package/dist/MimicDataStorage.d.cts +0 -113
  181. package/dist/MimicDataStorage.d.cts.map +0 -1
  182. package/dist/MimicDataStorage.d.mts +0 -113
  183. package/dist/MimicDataStorage.d.mts.map +0 -1
  184. package/dist/MimicDataStorage.mjs +0 -74
  185. package/dist/MimicDataStorage.mjs.map +0 -1
  186. package/dist/WebSocketHandler.cjs +0 -365
  187. package/dist/WebSocketHandler.d.cts +0 -34
  188. package/dist/WebSocketHandler.d.cts.map +0 -1
  189. package/dist/WebSocketHandler.d.mts +0 -34
  190. package/dist/WebSocketHandler.d.mts.map +0 -1
  191. package/dist/WebSocketHandler.mjs +0 -355
  192. package/dist/WebSocketHandler.mjs.map +0 -1
  193. package/dist/auth/NoAuth.cjs +0 -43
  194. package/dist/auth/NoAuth.d.cts +0 -22
  195. package/dist/auth/NoAuth.d.cts.map +0 -1
  196. package/dist/auth/NoAuth.d.mts +0 -22
  197. package/dist/auth/NoAuth.d.mts.map +0 -1
  198. package/dist/auth/NoAuth.mjs +0 -36
  199. package/dist/auth/NoAuth.mjs.map +0 -1
  200. package/dist/errors.cjs +0 -74
  201. package/dist/errors.d.cts +0 -89
  202. package/dist/errors.d.cts.map +0 -1
  203. package/dist/errors.d.mts +0 -89
  204. package/dist/errors.d.mts.map +0 -1
  205. package/dist/errors.mjs +0 -67
  206. package/dist/errors.mjs.map +0 -1
  207. package/dist/storage/InMemoryDataStorage.cjs +0 -57
  208. package/dist/storage/InMemoryDataStorage.d.cts +0 -19
  209. package/dist/storage/InMemoryDataStorage.d.cts.map +0 -1
  210. package/dist/storage/InMemoryDataStorage.d.mts +0 -19
  211. package/dist/storage/InMemoryDataStorage.d.mts.map +0 -1
  212. package/dist/storage/InMemoryDataStorage.mjs +0 -48
  213. package/dist/storage/InMemoryDataStorage.mjs.map +0 -1
  214. package/src/DocumentProtocol.ts +0 -112
  215. package/src/MimicConfig.ts +0 -211
  216. package/src/MimicDataStorage.ts +0 -157
  217. package/src/WebSocketHandler.ts +0 -735
  218. package/src/auth/NoAuth.ts +0 -46
  219. package/src/errors.ts +0 -113
  220. package/src/storage/InMemoryDataStorage.ts +0 -66
  221. package/tests/DocumentProtocol.test.ts +0 -113
  222. package/tests/InMemoryDataStorage.test.ts +0 -190
  223. package/tests/MimicConfig.test.ts +0 -290
  224. package/tests/MimicDataStorage.test.ts +0 -190
  225. package/tests/NoAuth.test.ts +0 -94
  226. package/tests/WebSocketHandler.test.ts +0 -321
  227. package/tests/errors.test.ts +0 -77
@@ -1,118 +1,299 @@
1
- const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
2
- const require_MimicConfig = require('./MimicConfig.cjs');
3
- const require_MimicDataStorage = require('./MimicDataStorage.cjs');
4
- let effect_Effect = require("effect/Effect");
5
- effect_Effect = require_rolldown_runtime.__toESM(effect_Effect);
6
- let effect_Layer = require("effect/Layer");
7
- effect_Layer = require_rolldown_runtime.__toESM(effect_Layer);
8
- let effect_PubSub = require("effect/PubSub");
9
- effect_PubSub = require_rolldown_runtime.__toESM(effect_PubSub);
10
- let effect_Ref = require("effect/Ref");
11
- effect_Ref = require_rolldown_runtime.__toESM(effect_Ref);
12
- let effect_HashMap = require("effect/HashMap");
13
- effect_HashMap = require_rolldown_runtime.__toESM(effect_HashMap);
14
- let effect_Context = require("effect/Context");
15
- effect_Context = require_rolldown_runtime.__toESM(effect_Context);
16
- let effect_Stream = require("effect/Stream");
17
- effect_Stream = require_rolldown_runtime.__toESM(effect_Stream);
1
+ const require_ColdStorage = require('./ColdStorage.cjs');
2
+ const require_HotStorage = require('./HotStorage.cjs');
3
+ const require_Metrics = require('./Metrics.cjs');
4
+ let effect = require("effect");
5
+ let _voidhash_mimic = require("@voidhash/mimic");
18
6
  let _voidhash_mimic_server = require("@voidhash/mimic/server");
19
7
 
20
8
  //#region src/DocumentManager.ts
21
9
  /**
22
- * @since 0.0.1
23
- * Document manager that handles multiple document instances.
10
+ * @voidhash/mimic-effect - DocumentManager
11
+ *
12
+ * Internal service for managing document lifecycle, including:
13
+ * - Document creation and restoration
14
+ * - Transaction processing
15
+ * - WAL management
16
+ * - Snapshot scheduling
17
+ * - Idle document GC
24
18
  */
25
- var DocumentManager_exports = /* @__PURE__ */ require_rolldown_runtime.__export({
26
- DocumentManagerTag: () => DocumentManagerTag,
27
- layer: () => layer
28
- });
29
19
  /**
30
- * Context tag for DocumentManager.
20
+ * Context tag for DocumentManager service
31
21
  */
32
- var DocumentManagerTag = class extends effect_Context.Tag("@voidhash/mimic-server-effect/DocumentManager")() {};
22
+ var DocumentManagerTag = class extends effect.Context.Tag("@voidhash/mimic-effect/DocumentManager")() {};
33
23
  /**
34
- * Create the DocumentManager service.
24
+ * Context tag for DocumentManager configuration
35
25
  */
36
- const makeDocumentManager = effect_Effect.gen(function* () {
37
- const config = yield* require_MimicConfig.MimicServerConfigTag;
38
- const storage = yield* require_MimicDataStorage.MimicDataStorageTag;
39
- const documents = yield* effect_Ref.make(effect_HashMap.empty());
40
- const getOrCreateDocument = (documentId) => effect_Effect.gen(function* () {
41
- const current = yield* effect_Ref.get(documents);
42
- const existing = effect_HashMap.get(current, documentId);
43
- if (existing._tag === "Some") {
44
- yield* effect_Ref.update(existing.value.refCount, (n) => n + 1);
45
- return existing.value;
46
- }
47
- const rawState = yield* effect_Effect.catchAll(storage.load(documentId), () => effect_Effect.succeed(void 0));
48
- const initialState = rawState !== void 0 ? yield* storage.onLoad(rawState) : config.initial !== void 0 ? yield* config.initial({ documentId }) : void 0;
49
- const pubsub = yield* effect_PubSub.unbounded();
50
- const serverDocument = _voidhash_mimic_server.ServerDocument.make({
26
+ var DocumentManagerConfigTag = class extends effect.Context.Tag("@voidhash/mimic-effect/DocumentManagerConfig")() {};
27
+ /**
28
+ * Create the DocumentManager layer.
29
+ * Requires ColdStorage, HotStorage, and DocumentManagerConfig.
30
+ */
31
+ const layer = effect.Layer.scoped(DocumentManagerTag, effect.Effect.gen(function* () {
32
+ const coldStorage = yield* require_ColdStorage.ColdStorageTag;
33
+ const hotStorage = yield* require_HotStorage.HotStorageTag;
34
+ const config = yield* DocumentManagerConfigTag;
35
+ const store = yield* effect.Ref.make(effect.HashMap.empty());
36
+ const SCHEMA_VERSION = 1;
37
+ /**
38
+ * Compute initial state for a new document
39
+ */
40
+ const computeInitialState = (documentId) => {
41
+ if (config.initial === void 0) return effect.Effect.succeed(void 0);
42
+ if (typeof config.initial === "function") return config.initial({ documentId });
43
+ return effect.Effect.succeed(config.initial);
44
+ };
45
+ /**
46
+ * Restore a document from storage
47
+ */
48
+ const restoreDocument = (documentId) => effect.Effect.gen(function* () {
49
+ const storedDoc = yield* coldStorage.load(documentId);
50
+ let initialState;
51
+ let initialVersion = 0;
52
+ if (storedDoc) {
53
+ initialState = storedDoc.state;
54
+ initialVersion = storedDoc.version;
55
+ } else initialState = yield* computeInitialState(documentId);
56
+ const pubsub = yield* effect.PubSub.unbounded();
57
+ const lastSnapshotVersion = yield* effect.Ref.make(initialVersion);
58
+ const lastSnapshotTime = yield* effect.Ref.make(Date.now());
59
+ const transactionsSinceSnapshot = yield* effect.Ref.make(0);
60
+ const lastActivityTime = yield* effect.Ref.make(Date.now());
61
+ const document = _voidhash_mimic_server.ServerDocument.make({
51
62
  schema: config.schema,
52
63
  initialState,
64
+ initialVersion,
53
65
  maxTransactionHistory: config.maxTransactionHistory,
54
- onBroadcast: (transactionMessage) => {
55
- const currentState = serverDocument.get();
56
- effect_Effect.runFork(effect_Effect.gen(function* () {
57
- if (currentState !== void 0) {
58
- const transformedState = yield* storage.onSave(currentState);
59
- yield* effect_Effect.catchAll(storage.save(documentId, transformedState), (error) => effect_Effect.logError("Failed to save document", error));
60
- }
61
- }));
62
- effect_Effect.runSync(effect_PubSub.publish(pubsub, {
66
+ onBroadcast: (message) => {
67
+ effect.Effect.runSync(effect.PubSub.publish(pubsub, {
63
68
  type: "transaction",
64
- transaction: transactionMessage.transaction,
65
- version: transactionMessage.version
69
+ transaction: message.transaction,
70
+ version: message.version
66
71
  }));
67
72
  },
68
73
  onRejection: (transactionId, reason) => {
69
- effect_Effect.runSync(effect_PubSub.publish(pubsub, {
74
+ effect.Effect.runSync(effect.PubSub.publish(pubsub, {
70
75
  type: "error",
71
76
  transactionId,
72
77
  reason
73
78
  }));
74
79
  }
75
80
  });
81
+ const walEntries = yield* hotStorage.getEntries(documentId, initialVersion);
82
+ if (walEntries.length > 0) {
83
+ const firstWalVersion = walEntries[0].version;
84
+ const expectedFirst = initialVersion + 1;
85
+ if (firstWalVersion !== expectedFirst) {
86
+ yield* effect.Effect.logWarning("WAL version gap detected", {
87
+ documentId,
88
+ snapshotVersion: initialVersion,
89
+ firstWalVersion,
90
+ expectedFirst
91
+ });
92
+ yield* effect.Metric.increment(require_Metrics.storageVersionGaps);
93
+ }
94
+ for (let i = 1; i < walEntries.length; i++) {
95
+ const prev = walEntries[i - 1].version;
96
+ const curr = walEntries[i].version;
97
+ if (curr !== prev + 1) yield* effect.Effect.logWarning("WAL internal gap detected", {
98
+ documentId,
99
+ previousVersion: prev,
100
+ currentVersion: curr
101
+ });
102
+ }
103
+ }
104
+ for (const entry of walEntries) {
105
+ const result = document.submit(entry.transaction);
106
+ if (!result.success) yield* effect.Effect.logWarning("Skipping corrupted WAL entry", {
107
+ documentId,
108
+ version: entry.version,
109
+ reason: result.reason
110
+ });
111
+ }
76
112
  const instance = {
77
- document: serverDocument,
113
+ document,
78
114
  pubsub,
79
- refCount: yield* effect_Ref.make(1)
115
+ lastSnapshotVersion,
116
+ lastSnapshotTime,
117
+ transactionsSinceSnapshot,
118
+ lastActivityTime
80
119
  };
81
- yield* effect_Ref.update(documents, (map) => effect_HashMap.set(map, documentId, instance));
120
+ if (storedDoc) yield* effect.Metric.increment(require_Metrics.documentsRestored);
121
+ else yield* effect.Metric.increment(require_Metrics.documentsCreated);
122
+ yield* effect.Metric.incrementBy(require_Metrics.documentsActive, 1);
82
123
  return instance;
83
124
  });
84
- const submit = (documentId, transaction) => effect_Effect.gen(function* () {
85
- return (yield* getOrCreateDocument(documentId)).document.submit(transaction);
86
- });
87
- const getSnapshot = (documentId) => effect_Effect.gen(function* () {
88
- return (yield* getOrCreateDocument(documentId)).document.getSnapshot();
125
+ /**
126
+ * Get or create a document instance
127
+ */
128
+ const getOrCreateDocument = (documentId) => effect.Effect.gen(function* () {
129
+ const current = yield* effect.Ref.get(store);
130
+ const existing = effect.HashMap.get(current, documentId);
131
+ if (existing._tag === "Some") {
132
+ yield* effect.Ref.set(existing.value.lastActivityTime, Date.now());
133
+ return existing.value;
134
+ }
135
+ const instance = yield* restoreDocument(documentId);
136
+ yield* effect.Ref.update(store, (map) => effect.HashMap.set(map, documentId, instance));
137
+ return instance;
89
138
  });
90
- const subscribe = (documentId) => effect_Effect.gen(function* () {
91
- const instance = yield* getOrCreateDocument(documentId);
92
- const queue = yield* effect_PubSub.subscribe(instance.pubsub);
93
- yield* effect_Effect.addFinalizer(() => effect_Effect.gen(function* () {
94
- yield* effect_Ref.updateAndGet(instance.refCount, (n) => n - 1);
139
+ /**
140
+ * Save a snapshot to ColdStorage derived from WAL entries.
141
+ * This ensures snapshots are always based on durable WAL data.
142
+ * Idempotent: skips save if already snapshotted at target version.
143
+ * Truncate failures are non-fatal and will be retried on next snapshot.
144
+ */
145
+ const saveSnapshot = (documentId, instance, targetVersion) => effect.Effect.gen(function* () {
146
+ var _baseSnapshot$version;
147
+ if (targetVersion <= (yield* effect.Ref.get(instance.lastSnapshotVersion))) return;
148
+ const snapshotStartTime = Date.now();
149
+ const baseSnapshot = yield* coldStorage.load(documentId);
150
+ const baseVersion = (_baseSnapshot$version = baseSnapshot === null || baseSnapshot === void 0 ? void 0 : baseSnapshot.version) !== null && _baseSnapshot$version !== void 0 ? _baseSnapshot$version : 0;
151
+ const baseState = baseSnapshot === null || baseSnapshot === void 0 ? void 0 : baseSnapshot.state;
152
+ const relevantEntries = (yield* hotStorage.getEntries(documentId, baseVersion)).filter((e) => e.version <= targetVersion);
153
+ if (relevantEntries.length === 0 && !baseSnapshot) return;
154
+ let snapshotState = baseState;
155
+ for (const entry of relevantEntries) {
156
+ const tempDoc = _voidhash_mimic.Document.make(config.schema, { initialState: snapshotState });
157
+ tempDoc.apply(entry.transaction.ops);
158
+ snapshotState = tempDoc.get();
159
+ }
160
+ if (snapshotState === void 0) return;
161
+ const snapshotVersion = relevantEntries.length > 0 ? relevantEntries[relevantEntries.length - 1].version : baseVersion;
162
+ if (snapshotVersion <= (yield* effect.Ref.get(instance.lastSnapshotVersion))) return;
163
+ const storedDoc = {
164
+ state: snapshotState,
165
+ version: snapshotVersion,
166
+ schemaVersion: SCHEMA_VERSION,
167
+ savedAt: Date.now()
168
+ };
169
+ yield* coldStorage.save(documentId, storedDoc);
170
+ const snapshotDuration = Date.now() - snapshotStartTime;
171
+ yield* effect.Metric.increment(require_Metrics.storageSnapshots);
172
+ yield* effect.Metric.update(require_Metrics.storageSnapshotLatency, snapshotDuration);
173
+ yield* effect.Ref.set(instance.lastSnapshotVersion, snapshotVersion);
174
+ yield* effect.Ref.set(instance.lastSnapshotTime, Date.now());
175
+ yield* effect.Ref.set(instance.transactionsSinceSnapshot, 0);
176
+ yield* effect.Effect.catchAll(hotStorage.truncate(documentId, snapshotVersion), (e) => effect.Effect.logWarning("WAL truncate failed - will retry on next snapshot", {
177
+ documentId,
178
+ version: snapshotVersion,
179
+ error: e
95
180
  }));
96
- return effect_Stream.fromQueue(queue);
97
181
  });
182
+ /**
183
+ * Check if snapshot should be triggered
184
+ */
185
+ const checkSnapshotTriggers = (documentId, instance) => effect.Effect.gen(function* () {
186
+ const txCount = yield* effect.Ref.get(instance.transactionsSinceSnapshot);
187
+ const lastTime = yield* effect.Ref.get(instance.lastSnapshotTime);
188
+ const now = Date.now();
189
+ const currentVersion = instance.document.getVersion();
190
+ const intervalMs = effect.Duration.toMillis(config.snapshot.interval);
191
+ if (txCount >= config.snapshot.transactionThreshold) {
192
+ yield* saveSnapshot(documentId, instance, currentVersion);
193
+ return;
194
+ }
195
+ if (now - lastTime >= intervalMs) {
196
+ yield* saveSnapshot(documentId, instance, currentVersion);
197
+ return;
198
+ }
199
+ });
200
+ yield* effect.Effect.gen(function* () {
201
+ yield* effect.Effect.gen(function* () {
202
+ const current = yield* effect.Ref.get(store);
203
+ const now = Date.now();
204
+ const maxIdleMs = effect.Duration.toMillis(config.maxIdleTime);
205
+ for (const [documentId, instance] of current) if (now - (yield* effect.Ref.get(instance.lastActivityTime)) >= maxIdleMs) {
206
+ const currentVersion = instance.document.getVersion();
207
+ yield* effect.Effect.catchAll(saveSnapshot(documentId, instance, currentVersion), (e) => effect.Effect.logError("Failed to save snapshot during eviction", {
208
+ documentId,
209
+ error: e
210
+ }));
211
+ yield* effect.Ref.update(store, (map) => effect.HashMap.remove(map, documentId));
212
+ yield* effect.Metric.increment(require_Metrics.documentsEvicted);
213
+ yield* effect.Metric.incrementBy(require_Metrics.documentsActive, -1);
214
+ yield* effect.Effect.logInfo("Document evicted due to idle timeout", { documentId });
215
+ }
216
+ }).pipe(effect.Effect.repeat(effect.Schedule.spaced("1 minute")), effect.Effect.fork);
217
+ });
218
+ yield* effect.Effect.addFinalizer(() => effect.Effect.gen(function* () {
219
+ const current = yield* effect.Ref.get(store);
220
+ for (const [documentId, instance] of current) {
221
+ const currentVersion = instance.document.getVersion();
222
+ yield* effect.Effect.catchAll(saveSnapshot(documentId, instance, currentVersion), (e) => effect.Effect.logError("Failed to save snapshot during shutdown", {
223
+ documentId,
224
+ error: e
225
+ }));
226
+ }
227
+ yield* effect.Effect.logInfo("DocumentManager shutdown complete");
228
+ }));
98
229
  return {
99
- submit,
100
- getSnapshot,
101
- subscribe
230
+ submit: (documentId, transaction) => effect.Effect.gen(function* () {
231
+ const instance = yield* getOrCreateDocument(documentId);
232
+ const submitStartTime = Date.now();
233
+ const validation = instance.document.validate(transaction);
234
+ if (!validation.valid) {
235
+ yield* effect.Metric.increment(require_Metrics.transactionsRejected);
236
+ const latency$1 = Date.now() - submitStartTime;
237
+ yield* effect.Metric.update(require_Metrics.transactionsLatency, latency$1);
238
+ return {
239
+ success: false,
240
+ reason: validation.reason
241
+ };
242
+ }
243
+ const walEntry = {
244
+ transaction,
245
+ version: validation.nextVersion,
246
+ timestamp: Date.now()
247
+ };
248
+ const appendResult = yield* effect.Effect.either(hotStorage.appendWithCheck(documentId, walEntry, validation.nextVersion));
249
+ if (appendResult._tag === "Left") {
250
+ yield* effect.Effect.logError("WAL append failed", {
251
+ documentId,
252
+ version: validation.nextVersion,
253
+ error: appendResult.left
254
+ });
255
+ yield* effect.Metric.increment(require_Metrics.walAppendFailures);
256
+ const latency$1 = Date.now() - submitStartTime;
257
+ yield* effect.Metric.update(require_Metrics.transactionsLatency, latency$1);
258
+ return {
259
+ success: false,
260
+ reason: "Storage unavailable. Please retry."
261
+ };
262
+ }
263
+ instance.document.apply(transaction);
264
+ const latency = Date.now() - submitStartTime;
265
+ yield* effect.Metric.update(require_Metrics.transactionsLatency, latency);
266
+ yield* effect.Metric.increment(require_Metrics.transactionsProcessed);
267
+ yield* effect.Metric.increment(require_Metrics.storageWalAppends);
268
+ yield* effect.Ref.update(instance.transactionsSinceSnapshot, (n) => n + 1);
269
+ yield* checkSnapshotTriggers(documentId, instance);
270
+ return {
271
+ success: true,
272
+ version: validation.nextVersion
273
+ };
274
+ }),
275
+ getSnapshot: (documentId) => effect.Effect.gen(function* () {
276
+ return (yield* getOrCreateDocument(documentId)).document.getSnapshot();
277
+ }),
278
+ subscribe: (documentId) => effect.Effect.gen(function* () {
279
+ const instance = yield* getOrCreateDocument(documentId);
280
+ return effect.Stream.fromPubSub(instance.pubsub);
281
+ }),
282
+ touch: (documentId) => effect.Effect.gen(function* () {
283
+ const current = yield* effect.Ref.get(store);
284
+ const existing = effect.HashMap.get(current, documentId);
285
+ if (existing._tag === "Some") yield* effect.Ref.set(existing.value.lastActivityTime, Date.now());
286
+ })
102
287
  };
103
- });
104
- /**
105
- * Layer that provides DocumentManager.
106
- * Requires MimicServerConfigTag and MimicDataStorageTag.
107
- */
108
- const layer = effect_Layer.effect(DocumentManagerTag, makeDocumentManager);
288
+ }));
289
+ const DocumentManager = {
290
+ Tag: DocumentManagerTag,
291
+ ConfigTag: DocumentManagerConfigTag,
292
+ layer
293
+ };
109
294
 
110
295
  //#endregion
296
+ exports.DocumentManager = DocumentManager;
297
+ exports.DocumentManagerConfigTag = DocumentManagerConfigTag;
111
298
  exports.DocumentManagerTag = DocumentManagerTag;
112
- Object.defineProperty(exports, 'DocumentManager_exports', {
113
- enumerable: true,
114
- get: function () {
115
- return DocumentManager_exports;
116
- }
117
- });
118
299
  exports.layer = layer;
@@ -1,45 +1,67 @@
1
- import { ServerBroadcast, SnapshotMessage, SubmitResult } from "./DocumentProtocol.cjs";
2
- import { MimicServerConfigTag } from "./MimicConfig.cjs";
3
- import { MimicDataStorageTag } from "./MimicDataStorage.cjs";
4
- import * as Effect from "effect/Effect";
5
- import * as Layer from "effect/Layer";
1
+ import { ResolvedConfig } from "./Types.cjs";
2
+ import { ColdStorageError, HotStorageError } from "./Errors.cjs";
3
+ import { ServerBroadcast, SnapshotMessage } from "./Protocol.cjs";
4
+ import { ColdStorageTag } from "./ColdStorage.cjs";
5
+ import { HotStorageTag } from "./HotStorage.cjs";
6
+ import { Context, Effect, Layer, Scope, Stream } from "effect";
6
7
  import { Transaction } from "@voidhash/mimic";
7
- import * as Context from "effect/Context";
8
- import * as Scope from "effect/Scope";
9
- import * as Stream from "effect/Stream";
10
8
 
11
9
  //#region src/DocumentManager.d.ts
12
- declare namespace DocumentManager_d_exports {
13
- export { DocumentManager, DocumentManagerTag, layer };
14
- }
10
+
11
+ /**
12
+ * Result of submitting a transaction
13
+ */
14
+ type SubmitResult = {
15
+ readonly success: true;
16
+ readonly version: number;
17
+ } | {
18
+ readonly success: false;
19
+ readonly reason: string;
20
+ };
15
21
  /**
16
- * Service interface for the DocumentManager.
22
+ * Error type for DocumentManager operations
23
+ */
24
+ type DocumentManagerError = ColdStorageError | HotStorageError;
25
+ /**
26
+ * Internal service for managing document lifecycle.
17
27
  */
18
28
  interface DocumentManager {
19
29
  /**
20
30
  * Submit a transaction to a document.
31
+ * May fail with ColdStorageError or HotStorageError if storage is unavailable.
21
32
  */
22
- readonly submit: (documentId: string, transaction: Transaction.Transaction) => Effect.Effect<SubmitResult>;
33
+ readonly submit: (documentId: string, transaction: Transaction.Transaction) => Effect.Effect<SubmitResult, DocumentManagerError>;
23
34
  /**
24
35
  * Get a snapshot of a document.
36
+ * May fail with ColdStorageError or HotStorageError if storage is unavailable.
25
37
  */
26
- readonly getSnapshot: (documentId: string) => Effect.Effect<SnapshotMessage>;
38
+ readonly getSnapshot: (documentId: string) => Effect.Effect<SnapshotMessage, DocumentManagerError>;
27
39
  /**
28
40
  * Subscribe to broadcasts for a document.
29
- * Returns a Stream of server broadcasts.
41
+ * May fail with ColdStorageError or HotStorageError if storage is unavailable.
42
+ */
43
+ readonly subscribe: (documentId: string) => Effect.Effect<Stream.Stream<ServerBroadcast>, DocumentManagerError, Scope.Scope>;
44
+ /**
45
+ * Touch a document to update its last activity time.
46
+ * Call this on any client activity to prevent idle GC.
30
47
  */
31
- readonly subscribe: (documentId: string) => Effect.Effect<Stream.Stream<ServerBroadcast>, never, Scope.Scope>;
48
+ readonly touch: (documentId: string) => Effect.Effect<void>;
32
49
  }
33
- declare const DocumentManagerTag_base: Context.TagClass<DocumentManagerTag, "@voidhash/mimic-server-effect/DocumentManager", DocumentManager>;
50
+ declare const DocumentManagerTag_base: Context.TagClass<DocumentManagerTag, "@voidhash/mimic-effect/DocumentManager", DocumentManager>;
34
51
  /**
35
- * Context tag for DocumentManager.
52
+ * Context tag for DocumentManager service
36
53
  */
37
54
  declare class DocumentManagerTag extends DocumentManagerTag_base {}
55
+ declare const DocumentManagerConfigTag_base: Context.TagClass<DocumentManagerConfigTag, "@voidhash/mimic-effect/DocumentManagerConfig", ResolvedConfig<Primitive.AnyPrimitive>>;
38
56
  /**
39
- * Layer that provides DocumentManager.
40
- * Requires MimicServerConfigTag and MimicDataStorageTag.
57
+ * Context tag for DocumentManager configuration
41
58
  */
42
- declare const layer: Layer.Layer<DocumentManagerTag, never, MimicServerConfigTag | MimicDataStorageTag>;
59
+ declare class DocumentManagerConfigTag extends DocumentManagerConfigTag_base {}
60
+ declare const DocumentManager: {
61
+ Tag: typeof DocumentManagerTag;
62
+ ConfigTag: typeof DocumentManagerConfigTag;
63
+ layer: Layer.Layer<DocumentManagerTag, never, ColdStorageTag | HotStorageTag | DocumentManagerConfigTag>;
64
+ };
43
65
  //#endregion
44
- export { DocumentManagerTag, DocumentManager_d_exports };
66
+ export { DocumentManager, DocumentManagerConfigTag, DocumentManagerError, DocumentManagerTag, SubmitResult };
45
67
  //# sourceMappingURL=DocumentManager.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DocumentManager.d.cts","names":[],"sources":["../src/DocumentManager.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;;UA2CiB,eAAA;EAAA;;;EAOV,SAAO,MAAA,EAAA,CAAA,UAAA,EAAA,MAAA,EAAA,WAAA,EADG,WAAA,CAAY,WACf,EAAA,GAAP,MAAA,CAAO,MAAA,CAAO,YAAP,CAAA;EAOO;;;EASjB,SAAO,WAAA,EAAA,CAAA,UAAA,EAAA,MAAA,EAAA,GATJ,MAAA,CAAO,MASH,CATU,eASV,CAAA;EAEP;;;AAEH;8CALM,MAAA,CAAO,OACV,MAAA,CAAO,OAAO,yBAEd,KAAA,CAAM;;cAET;;AAKD;AA8KA;AACE,cA/KW,kBAAA,SAA2B,uBAAA,CA+KtC;;;;;cADW,OAAO,KAAA,CAAM,MACxB,2BAEA,uBAAuB"}
1
+ {"version":3,"file":"DocumentManager.d.cts","names":[],"sources":["../src/DocumentManager.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;AAiFqB,KAtCT,YAAA,GAsCgB;EAAyB,SAAA,OAAA,EAAA,IAAA;EAAsB,SAAM,OAAA,EAAA,MAAA;CAA1E,GAAA;EAMmC,SAAO,OAAA,EAAA,KAAA;EAAM,SAAA,MAAA,EAAA,MAAA;AACtD,CAAA;;;;KAlCW,oBAAA,GAAuB,mBAAmB;AA2CtD;AAE2C;;UAxC1B,eAAA;;;;AAuEjB;EA+da,SAAA,MAAA,EAAA,CAIZ,UAAA,EAAA,MAAA,EAAA,WAAA,EAniBgB,WAAA,CAAY,WAmiB5B,EAAA,GAliBM,MAAA,CAAO,MAkiBb,CAliBoB,YAkiBpB,EAliBkC,oBAkiBlC,CAAA;;;;;gDA5hB+C,MAAA,CAAO,OAAO,iBAAiB;;;;;8CAQxE,MAAA,CAAO,OAAO,MAAA,CAAO,OAAO,kBAAkB,sBAAsB,KAAA,CAAM;;;;;0CAMvC,MAAA,CAAO;;cAChD;;;;cASY,kBAAA,SAA2B,uBAAA;cAEG;;;;cA+B9B,wBAAA,SAAiC,6BAAA;cA+djC"}
@@ -1,45 +1,67 @@
1
- import { ServerBroadcast, SnapshotMessage, SubmitResult } from "./DocumentProtocol.mjs";
2
- import { MimicServerConfigTag } from "./MimicConfig.mjs";
3
- import { MimicDataStorageTag } from "./MimicDataStorage.mjs";
4
- import * as Effect from "effect/Effect";
5
- import * as Layer from "effect/Layer";
6
- import * as Context from "effect/Context";
7
- import * as Stream from "effect/Stream";
1
+ import { ResolvedConfig } from "./Types.mjs";
2
+ import { ColdStorageError, HotStorageError } from "./Errors.mjs";
3
+ import { ServerBroadcast, SnapshotMessage } from "./Protocol.mjs";
4
+ import { ColdStorageTag } from "./ColdStorage.mjs";
5
+ import { HotStorageTag } from "./HotStorage.mjs";
6
+ import { Context, Effect, Layer, Scope, Stream } from "effect";
8
7
  import { Transaction } from "@voidhash/mimic";
9
- import * as Scope from "effect/Scope";
10
8
 
11
9
  //#region src/DocumentManager.d.ts
12
- declare namespace DocumentManager_d_exports {
13
- export { DocumentManager, DocumentManagerTag, layer };
14
- }
10
+
11
+ /**
12
+ * Result of submitting a transaction
13
+ */
14
+ type SubmitResult = {
15
+ readonly success: true;
16
+ readonly version: number;
17
+ } | {
18
+ readonly success: false;
19
+ readonly reason: string;
20
+ };
15
21
  /**
16
- * Service interface for the DocumentManager.
22
+ * Error type for DocumentManager operations
23
+ */
24
+ type DocumentManagerError = ColdStorageError | HotStorageError;
25
+ /**
26
+ * Internal service for managing document lifecycle.
17
27
  */
18
28
  interface DocumentManager {
19
29
  /**
20
30
  * Submit a transaction to a document.
31
+ * May fail with ColdStorageError or HotStorageError if storage is unavailable.
21
32
  */
22
- readonly submit: (documentId: string, transaction: Transaction.Transaction) => Effect.Effect<SubmitResult>;
33
+ readonly submit: (documentId: string, transaction: Transaction.Transaction) => Effect.Effect<SubmitResult, DocumentManagerError>;
23
34
  /**
24
35
  * Get a snapshot of a document.
36
+ * May fail with ColdStorageError or HotStorageError if storage is unavailable.
25
37
  */
26
- readonly getSnapshot: (documentId: string) => Effect.Effect<SnapshotMessage>;
38
+ readonly getSnapshot: (documentId: string) => Effect.Effect<SnapshotMessage, DocumentManagerError>;
27
39
  /**
28
40
  * Subscribe to broadcasts for a document.
29
- * Returns a Stream of server broadcasts.
41
+ * May fail with ColdStorageError or HotStorageError if storage is unavailable.
42
+ */
43
+ readonly subscribe: (documentId: string) => Effect.Effect<Stream.Stream<ServerBroadcast>, DocumentManagerError, Scope.Scope>;
44
+ /**
45
+ * Touch a document to update its last activity time.
46
+ * Call this on any client activity to prevent idle GC.
30
47
  */
31
- readonly subscribe: (documentId: string) => Effect.Effect<Stream.Stream<ServerBroadcast>, never, Scope.Scope>;
48
+ readonly touch: (documentId: string) => Effect.Effect<void>;
32
49
  }
33
- declare const DocumentManagerTag_base: Context.TagClass<DocumentManagerTag, "@voidhash/mimic-server-effect/DocumentManager", DocumentManager>;
50
+ declare const DocumentManagerTag_base: Context.TagClass<DocumentManagerTag, "@voidhash/mimic-effect/DocumentManager", DocumentManager>;
34
51
  /**
35
- * Context tag for DocumentManager.
52
+ * Context tag for DocumentManager service
36
53
  */
37
54
  declare class DocumentManagerTag extends DocumentManagerTag_base {}
55
+ declare const DocumentManagerConfigTag_base: Context.TagClass<DocumentManagerConfigTag, "@voidhash/mimic-effect/DocumentManagerConfig", ResolvedConfig<Primitive.AnyPrimitive>>;
38
56
  /**
39
- * Layer that provides DocumentManager.
40
- * Requires MimicServerConfigTag and MimicDataStorageTag.
57
+ * Context tag for DocumentManager configuration
41
58
  */
42
- declare const layer: Layer.Layer<DocumentManagerTag, never, MimicServerConfigTag | MimicDataStorageTag>;
59
+ declare class DocumentManagerConfigTag extends DocumentManagerConfigTag_base {}
60
+ declare const DocumentManager: {
61
+ Tag: typeof DocumentManagerTag;
62
+ ConfigTag: typeof DocumentManagerConfigTag;
63
+ layer: Layer.Layer<DocumentManagerTag, never, ColdStorageTag | HotStorageTag | DocumentManagerConfigTag>;
64
+ };
43
65
  //#endregion
44
- export { DocumentManagerTag, DocumentManager_d_exports };
66
+ export { DocumentManager, DocumentManagerConfigTag, DocumentManagerError, DocumentManagerTag, SubmitResult };
45
67
  //# sourceMappingURL=DocumentManager.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DocumentManager.d.mts","names":[],"sources":["../src/DocumentManager.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;;UA2CiB,eAAA;EAAA;;;EAOV,SAAO,MAAA,EAAA,CAAA,UAAA,EAAA,MAAA,EAAA,WAAA,EADG,WAAA,CAAY,WACf,EAAA,GAAP,MAAA,CAAO,MAAA,CAAO,YAAP,CAAA;EAOO;;;EASjB,SAAO,WAAA,EAAA,CAAA,UAAA,EAAA,MAAA,EAAA,GATJ,MAAA,CAAO,MASH,CATU,eASV,CAAA;EAEP;;;AAEH;8CALM,MAAA,CAAO,OACV,MAAA,CAAO,OAAO,yBAEd,KAAA,CAAM;;cAET;;AAKD;AA8KA;AACE,cA/KW,kBAAA,SAA2B,uBAAA,CA+KtC;;;;;cADW,OAAO,KAAA,CAAM,MACxB,2BAEA,uBAAuB"}
1
+ {"version":3,"file":"DocumentManager.d.mts","names":[],"sources":["../src/DocumentManager.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;AAiFqB,KAtCT,YAAA,GAsCgB;EAAyB,SAAA,OAAA,EAAA,IAAA;EAAsB,SAAM,OAAA,EAAA,MAAA;CAA1E,GAAA;EAMmC,SAAO,OAAA,EAAA,KAAA;EAAM,SAAA,MAAA,EAAA,MAAA;AACtD,CAAA;;;;KAlCW,oBAAA,GAAuB,mBAAmB;AA2CtD;AAE2C;;UAxC1B,eAAA;;;;AAuEjB;EA+da,SAAA,MAAA,EAAA,CAIZ,UAAA,EAAA,MAAA,EAAA,WAAA,EAniBgB,WAAA,CAAY,WAmiB5B,EAAA,GAliBM,MAAA,CAAO,MAkiBb,CAliBoB,YAkiBpB,EAliBkC,oBAkiBlC,CAAA;;;;;gDA5hB+C,MAAA,CAAO,OAAO,iBAAiB;;;;;8CAQxE,MAAA,CAAO,OAAO,MAAA,CAAO,OAAO,kBAAkB,sBAAsB,KAAA,CAAM;;;;;0CAMvC,MAAA,CAAO;;cAChD;;;;cASY,kBAAA,SAA2B,uBAAA;cAEG;;;;cA+B9B,wBAAA,SAAiC,6BAAA;cA+djC"}