@voidhash/mimic-effect 0.0.1-alpha.1 → 0.0.1-alpha.3

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.
package/dist/index.mjs ADDED
@@ -0,0 +1,1162 @@
1
+ import { t as __export } from "./chunk-C6wwvPpM.mjs";
2
+ import * as Effect from "effect/Effect";
3
+ import * as Layer from "effect/Layer";
4
+ import * as Context from "effect/Context";
5
+ import { SocketServer } from "@effect/platform/SocketServer";
6
+ import * as PubSub from "effect/PubSub";
7
+ import * as Ref from "effect/Ref";
8
+ import * as HashMap from "effect/HashMap";
9
+ import * as Stream from "effect/Stream";
10
+ import { ServerDocument } from "@voidhash/mimic/server";
11
+ import * as Duration from "effect/Duration";
12
+ import * as Data from "effect/Data";
13
+ import * as Fiber from "effect/Fiber";
14
+ import { Presence, Transaction } from "@voidhash/mimic";
15
+ import { HttpLayerRouter, HttpServerRequest, HttpServerResponse } from "@effect/platform";
16
+ import * as Schema from "effect/Schema";
17
+
18
+ //#region src/MimicConfig.ts
19
+ /**
20
+ * @since 0.0.1
21
+ * Configuration types for the Mimic server.
22
+ */
23
+ var MimicConfig_exports = /* @__PURE__ */ __export({
24
+ MimicServerConfigTag: () => MimicServerConfigTag,
25
+ layer: () => layer$7,
26
+ make: () => make$2
27
+ });
28
+ /**
29
+ * Create a MimicServerConfig from options.
30
+ */
31
+ const make$2 = (options) => {
32
+ var _options$maxIdleTime, _options$maxTransacti, _options$heartbeatInt, _options$heartbeatTim;
33
+ return {
34
+ schema: options.schema,
35
+ maxIdleTime: Duration.decode((_options$maxIdleTime = options.maxIdleTime) !== null && _options$maxIdleTime !== void 0 ? _options$maxIdleTime : "5 minutes"),
36
+ maxTransactionHistory: (_options$maxTransacti = options.maxTransactionHistory) !== null && _options$maxTransacti !== void 0 ? _options$maxTransacti : 1e3,
37
+ heartbeatInterval: Duration.decode((_options$heartbeatInt = options.heartbeatInterval) !== null && _options$heartbeatInt !== void 0 ? _options$heartbeatInt : "30 seconds"),
38
+ heartbeatTimeout: Duration.decode((_options$heartbeatTim = options.heartbeatTimeout) !== null && _options$heartbeatTim !== void 0 ? _options$heartbeatTim : "10 seconds"),
39
+ presence: options.presence
40
+ };
41
+ };
42
+ /**
43
+ * Context tag for MimicServerConfig.
44
+ */
45
+ var MimicServerConfigTag = class extends Context.Tag("@voidhash/mimic-server-effect/MimicServerConfig")() {};
46
+ /**
47
+ * Create a Layer that provides MimicServerConfig.
48
+ */
49
+ const layer$7 = (options) => Layer.succeed(MimicServerConfigTag, make$2(options));
50
+
51
+ //#endregion
52
+ //#region src/MimicDataStorage.ts
53
+ /**
54
+ * @since 0.0.1
55
+ * Data storage service interface for Mimic documents.
56
+ * Provides pluggable storage adapters with load/save hooks for data transformation.
57
+ */
58
+ var MimicDataStorage_exports = /* @__PURE__ */ __export({
59
+ MimicDataStorageTag: () => MimicDataStorageTag,
60
+ StorageDeleteError: () => StorageDeleteError,
61
+ StorageLoadError: () => StorageLoadError,
62
+ StorageSaveError: () => StorageSaveError,
63
+ layer: () => layer$6,
64
+ layerEffect: () => layerEffect$1,
65
+ make: () => make$1
66
+ });
67
+ /**
68
+ * Error when loading a document from storage fails.
69
+ */
70
+ var StorageLoadError = class extends Data.TaggedError("StorageLoadError") {
71
+ get message() {
72
+ return `Failed to load document ${this.documentId}: ${String(this.cause)}`;
73
+ }
74
+ };
75
+ /**
76
+ * Error when saving a document to storage fails.
77
+ */
78
+ var StorageSaveError = class extends Data.TaggedError("StorageSaveError") {
79
+ get message() {
80
+ return `Failed to save document ${this.documentId}: ${String(this.cause)}`;
81
+ }
82
+ };
83
+ /**
84
+ * Error when deleting a document from storage fails.
85
+ */
86
+ var StorageDeleteError = class extends Data.TaggedError("StorageDeleteError") {
87
+ get message() {
88
+ return `Failed to delete document ${this.documentId}: ${String(this.cause)}`;
89
+ }
90
+ };
91
+ /**
92
+ * Context tag for MimicDataStorage service.
93
+ */
94
+ var MimicDataStorageTag = class extends Context.Tag("@voidhash/mimic-server-effect/MimicDataStorage")() {};
95
+ /**
96
+ * Create a MimicDataStorage layer from a storage implementation.
97
+ */
98
+ const layer$6 = (storage) => Layer.succeed(MimicDataStorageTag, storage);
99
+ /**
100
+ * Create a MimicDataStorage layer from an Effect that produces a storage implementation.
101
+ */
102
+ const layerEffect$1 = (effect) => Layer.effect(MimicDataStorageTag, effect);
103
+ /**
104
+ * Create a simple storage implementation with minimal configuration.
105
+ */
106
+ const make$1 = (options) => {
107
+ var _options$delete, _options$onLoad, _options$onSave;
108
+ return {
109
+ load: options.load,
110
+ save: options.save,
111
+ delete: (_options$delete = options.delete) !== null && _options$delete !== void 0 ? _options$delete : (() => Effect.void),
112
+ onLoad: (_options$onLoad = options.onLoad) !== null && _options$onLoad !== void 0 ? _options$onLoad : ((state) => Effect.succeed(state)),
113
+ onSave: (_options$onSave = options.onSave) !== null && _options$onSave !== void 0 ? _options$onSave : ((state) => Effect.succeed(state))
114
+ };
115
+ };
116
+
117
+ //#endregion
118
+ //#region src/DocumentManager.ts
119
+ /**
120
+ * @since 0.0.1
121
+ * Document manager that handles multiple document instances.
122
+ */
123
+ var DocumentManager_exports = /* @__PURE__ */ __export({
124
+ DocumentManagerTag: () => DocumentManagerTag,
125
+ layer: () => layer$5
126
+ });
127
+ /**
128
+ * Context tag for DocumentManager.
129
+ */
130
+ var DocumentManagerTag = class extends Context.Tag("@voidhash/mimic-server-effect/DocumentManager")() {};
131
+ /**
132
+ * Create the DocumentManager service.
133
+ */
134
+ const makeDocumentManager = Effect.gen(function* () {
135
+ const config = yield* MimicServerConfigTag;
136
+ const storage = yield* MimicDataStorageTag;
137
+ const documents = yield* Ref.make(HashMap.empty());
138
+ const getOrCreateDocument = (documentId) => Effect.gen(function* () {
139
+ const current = yield* Ref.get(documents);
140
+ const existing = HashMap.get(current, documentId);
141
+ if (existing._tag === "Some") {
142
+ yield* Ref.update(existing.value.refCount, (n) => n + 1);
143
+ return existing.value;
144
+ }
145
+ const rawState = yield* Effect.catchAll(storage.load(documentId), () => Effect.succeed(void 0));
146
+ const initialState = rawState !== void 0 ? yield* storage.onLoad(rawState) : void 0;
147
+ const pubsub = yield* PubSub.unbounded();
148
+ const serverDocument = ServerDocument.make({
149
+ schema: config.schema,
150
+ initialState,
151
+ maxTransactionHistory: config.maxTransactionHistory,
152
+ onBroadcast: (transactionMessage) => {
153
+ const currentState = serverDocument.get();
154
+ Effect.runFork(Effect.gen(function* () {
155
+ if (currentState !== void 0) {
156
+ const transformedState = yield* storage.onSave(currentState);
157
+ yield* Effect.catchAll(storage.save(documentId, transformedState), (error) => Effect.logError("Failed to save document", error));
158
+ }
159
+ }));
160
+ Effect.runSync(PubSub.publish(pubsub, {
161
+ type: "transaction",
162
+ transaction: transactionMessage.transaction,
163
+ version: transactionMessage.version
164
+ }));
165
+ },
166
+ onRejection: (transactionId, reason) => {
167
+ Effect.runSync(PubSub.publish(pubsub, {
168
+ type: "error",
169
+ transactionId,
170
+ reason
171
+ }));
172
+ }
173
+ });
174
+ const instance = {
175
+ document: serverDocument,
176
+ pubsub,
177
+ refCount: yield* Ref.make(1)
178
+ };
179
+ yield* Ref.update(documents, (map) => HashMap.set(map, documentId, instance));
180
+ return instance;
181
+ });
182
+ const submit = (documentId, transaction) => Effect.gen(function* () {
183
+ return (yield* getOrCreateDocument(documentId)).document.submit(transaction);
184
+ });
185
+ const getSnapshot = (documentId) => Effect.gen(function* () {
186
+ return (yield* getOrCreateDocument(documentId)).document.getSnapshot();
187
+ });
188
+ const subscribe = (documentId) => Effect.gen(function* () {
189
+ const instance = yield* getOrCreateDocument(documentId);
190
+ const queue = yield* PubSub.subscribe(instance.pubsub);
191
+ yield* Effect.addFinalizer(() => Effect.gen(function* () {
192
+ yield* Ref.updateAndGet(instance.refCount, (n) => n - 1);
193
+ }));
194
+ return Stream.fromQueue(queue);
195
+ });
196
+ return {
197
+ submit,
198
+ getSnapshot,
199
+ subscribe
200
+ };
201
+ });
202
+ /**
203
+ * Layer that provides DocumentManager.
204
+ * Requires MimicServerConfigTag and MimicDataStorageTag.
205
+ */
206
+ const layer$5 = Layer.effect(DocumentManagerTag, makeDocumentManager);
207
+
208
+ //#endregion
209
+ //#region src/MimicAuthService.ts
210
+ /**
211
+ * @since 0.0.1
212
+ * Authentication service interface for Mimic connections.
213
+ * Provides pluggable authentication adapters.
214
+ */
215
+ var MimicAuthService_exports = /* @__PURE__ */ __export({
216
+ MimicAuthServiceTag: () => MimicAuthServiceTag,
217
+ layer: () => layer$4,
218
+ layerEffect: () => layerEffect,
219
+ layerService: () => layerService,
220
+ make: () => make,
221
+ makeEffect: () => makeEffect
222
+ });
223
+ /**
224
+ * Context tag for MimicAuthService service.
225
+ */
226
+ var MimicAuthServiceTag = class extends Context.Tag("@voidhash/mimic-server-effect/MimicAuthService")() {};
227
+ /**
228
+ * Create a MimicAuthService layer from an auth handler function.
229
+ */
230
+ const layer$4 = (options) => Layer.succeed(MimicAuthServiceTag, { authenticate: (token) => Effect.promise(() => Promise.resolve(options.authHandler(token))) });
231
+ /**
232
+ * Create a MimicAuthService layer from an auth service implementation.
233
+ */
234
+ const layerService = (service) => Layer.succeed(MimicAuthServiceTag, service);
235
+ /**
236
+ * Create a MimicAuthService layer from an Effect that produces an auth service.
237
+ */
238
+ const layerEffect = (effect) => Layer.effect(MimicAuthServiceTag, effect);
239
+ /**
240
+ * Create an auth service from an auth handler function.
241
+ */
242
+ const make = (authHandler) => ({ authenticate: (token) => Effect.promise(() => Promise.resolve(authHandler(token))) });
243
+ /**
244
+ * Create an auth service from an Effect-based authenticate function.
245
+ */
246
+ const makeEffect = (authenticate) => ({ authenticate });
247
+
248
+ //#endregion
249
+ //#region src/PresenceManager.ts
250
+ /**
251
+ * @since 0.0.1
252
+ * Presence manager for ephemeral per-connection state.
253
+ * Handles in-memory storage and broadcasting of presence updates.
254
+ */
255
+ var PresenceManager_exports = /* @__PURE__ */ __export({
256
+ PresenceManagerTag: () => PresenceManagerTag,
257
+ layer: () => layer$3,
258
+ layerDefault: () => layerDefault$2
259
+ });
260
+ /**
261
+ * Context tag for PresenceManager.
262
+ */
263
+ var PresenceManagerTag = class extends Context.Tag("@voidhash/mimic-server-effect/PresenceManager")() {};
264
+ /**
265
+ * Create the PresenceManager service.
266
+ */
267
+ const makePresenceManager = Effect.gen(function* () {
268
+ const documents = yield* Ref.make(HashMap.empty());
269
+ const getOrCreateDocument = (documentId) => Effect.gen(function* () {
270
+ const current = yield* Ref.get(documents);
271
+ const existing = HashMap.get(current, documentId);
272
+ if (existing._tag === "Some") return existing.value;
273
+ const docPresence = {
274
+ entries: yield* Ref.make(HashMap.empty()),
275
+ pubsub: yield* PubSub.unbounded()
276
+ };
277
+ yield* Ref.update(documents, (map) => HashMap.set(map, documentId, docPresence));
278
+ return docPresence;
279
+ });
280
+ const getSnapshot = (documentId) => Effect.gen(function* () {
281
+ const docPresence = yield* getOrCreateDocument(documentId);
282
+ const entriesMap = yield* Ref.get(docPresence.entries);
283
+ const presences = {};
284
+ for (const [id, entry] of entriesMap) presences[id] = entry;
285
+ return { presences };
286
+ });
287
+ const set = (documentId, connectionId, entry) => Effect.gen(function* () {
288
+ const docPresence = yield* getOrCreateDocument(documentId);
289
+ yield* Ref.update(docPresence.entries, (map) => HashMap.set(map, connectionId, entry));
290
+ yield* PubSub.publish(docPresence.pubsub, {
291
+ type: "presence_update",
292
+ id: connectionId,
293
+ data: entry.data,
294
+ userId: entry.userId
295
+ });
296
+ });
297
+ const remove = (documentId, connectionId) => Effect.gen(function* () {
298
+ const current = yield* Ref.get(documents);
299
+ const existing = HashMap.get(current, documentId);
300
+ if (existing._tag === "None") return;
301
+ const docPresence = existing.value;
302
+ const entries = yield* Ref.get(docPresence.entries);
303
+ if (!HashMap.has(entries, connectionId)) return;
304
+ yield* Ref.update(docPresence.entries, (map) => HashMap.remove(map, connectionId));
305
+ yield* PubSub.publish(docPresence.pubsub, {
306
+ type: "presence_remove",
307
+ id: connectionId
308
+ });
309
+ });
310
+ const subscribe = (documentId) => Effect.gen(function* () {
311
+ const docPresence = yield* getOrCreateDocument(documentId);
312
+ const queue = yield* PubSub.subscribe(docPresence.pubsub);
313
+ return Stream.fromQueue(queue);
314
+ });
315
+ return {
316
+ getSnapshot,
317
+ set,
318
+ remove,
319
+ subscribe
320
+ };
321
+ });
322
+ /**
323
+ * Layer that provides PresenceManager.
324
+ */
325
+ const layer$3 = Layer.effect(PresenceManagerTag, makePresenceManager);
326
+ /**
327
+ * Default layer that provides PresenceManager.
328
+ * Uses the default priority for layer composition.
329
+ */
330
+ const layerDefault$2 = Layer.effectDiscard(Effect.succeed(void 0)).pipe(Layer.provideMerge(layer$3));
331
+
332
+ //#endregion
333
+ //#region src/errors.ts
334
+ /**
335
+ * @since 0.0.1
336
+ * Error types for the Mimic server.
337
+ */
338
+ /**
339
+ * Error when a document type is not found in the schema registry.
340
+ */
341
+ var DocumentTypeNotFoundError = class extends Data.TaggedError("DocumentTypeNotFoundError") {
342
+ get message() {
343
+ return `Document type not found: ${this.documentType}`;
344
+ }
345
+ };
346
+ /**
347
+ * Error when a document is not found.
348
+ */
349
+ var DocumentNotFoundError = class extends Data.TaggedError("DocumentNotFoundError") {
350
+ get message() {
351
+ return `Document not found: ${this.documentId}`;
352
+ }
353
+ };
354
+ /**
355
+ * Error when authentication fails.
356
+ */
357
+ var AuthenticationError = class extends Data.TaggedError("AuthenticationError") {
358
+ get message() {
359
+ return `Authentication failed: ${this.reason}`;
360
+ }
361
+ };
362
+ /**
363
+ * Error when a transaction is rejected.
364
+ */
365
+ var TransactionRejectedError = class extends Data.TaggedError("TransactionRejectedError") {
366
+ get message() {
367
+ return `Transaction ${this.transactionId} rejected: ${this.reason}`;
368
+ }
369
+ };
370
+ /**
371
+ * Error when parsing a client message fails.
372
+ */
373
+ var MessageParseError = class extends Data.TaggedError("MessageParseError") {
374
+ get message() {
375
+ return `Failed to parse message: ${String(this.cause)}`;
376
+ }
377
+ };
378
+ /**
379
+ * Error when the WebSocket connection is invalid.
380
+ */
381
+ var InvalidConnectionError = class extends Data.TaggedError("InvalidConnectionError") {
382
+ get message() {
383
+ return `Invalid connection: ${this.reason}`;
384
+ }
385
+ };
386
+ /**
387
+ * Error when the document ID is missing from the URL path.
388
+ */
389
+ var MissingDocumentIdError = class extends Data.TaggedError("MissingDocumentIdError") {
390
+ get message() {
391
+ return this.path ? `Document ID is required in the URL path: ${this.path}` : "Document ID is required in the URL path";
392
+ }
393
+ };
394
+
395
+ //#endregion
396
+ //#region \0@oxc-project+runtime@0.103.0/helpers/typeof.js
397
+ function _typeof(o) {
398
+ "@babel/helpers - typeof";
399
+ return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o$1) {
400
+ return typeof o$1;
401
+ } : function(o$1) {
402
+ return o$1 && "function" == typeof Symbol && o$1.constructor === Symbol && o$1 !== Symbol.prototype ? "symbol" : typeof o$1;
403
+ }, _typeof(o);
404
+ }
405
+
406
+ //#endregion
407
+ //#region \0@oxc-project+runtime@0.103.0/helpers/toPrimitive.js
408
+ function toPrimitive(t, r) {
409
+ if ("object" != _typeof(t) || !t) return t;
410
+ var e = t[Symbol.toPrimitive];
411
+ if (void 0 !== e) {
412
+ var i = e.call(t, r || "default");
413
+ if ("object" != _typeof(i)) return i;
414
+ throw new TypeError("@@toPrimitive must return a primitive value.");
415
+ }
416
+ return ("string" === r ? String : Number)(t);
417
+ }
418
+
419
+ //#endregion
420
+ //#region \0@oxc-project+runtime@0.103.0/helpers/toPropertyKey.js
421
+ function toPropertyKey(t) {
422
+ var i = toPrimitive(t, "string");
423
+ return "symbol" == _typeof(i) ? i : i + "";
424
+ }
425
+
426
+ //#endregion
427
+ //#region \0@oxc-project+runtime@0.103.0/helpers/defineProperty.js
428
+ function _defineProperty(e, r, t) {
429
+ return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
430
+ value: t,
431
+ enumerable: !0,
432
+ configurable: !0,
433
+ writable: !0
434
+ }) : e[r] = t, e;
435
+ }
436
+
437
+ //#endregion
438
+ //#region \0@oxc-project+runtime@0.103.0/helpers/objectSpread2.js
439
+ function ownKeys(e, r) {
440
+ var t = Object.keys(e);
441
+ if (Object.getOwnPropertySymbols) {
442
+ var o = Object.getOwnPropertySymbols(e);
443
+ r && (o = o.filter(function(r$1) {
444
+ return Object.getOwnPropertyDescriptor(e, r$1).enumerable;
445
+ })), t.push.apply(t, o);
446
+ }
447
+ return t;
448
+ }
449
+ function _objectSpread2(e) {
450
+ for (var r = 1; r < arguments.length; r++) {
451
+ var t = null != arguments[r] ? arguments[r] : {};
452
+ r % 2 ? ownKeys(Object(t), !0).forEach(function(r$1) {
453
+ _defineProperty(e, r$1, t[r$1]);
454
+ }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function(r$1) {
455
+ Object.defineProperty(e, r$1, Object.getOwnPropertyDescriptor(t, r$1));
456
+ });
457
+ }
458
+ return e;
459
+ }
460
+
461
+ //#endregion
462
+ //#region src/WebSocketHandler.ts
463
+ /**
464
+ * @since 0.0.1
465
+ * WebSocket connection handler using Effect Platform Socket API.
466
+ */
467
+ var WebSocketHandler_exports = /* @__PURE__ */ __export({
468
+ extractDocumentId: () => extractDocumentId,
469
+ handleConnection: () => handleConnection,
470
+ makeHandler: () => makeHandler
471
+ });
472
+ /**
473
+ * Extract document ID from URL path.
474
+ * Expected format: /doc/{documentId}
475
+ */
476
+ const extractDocumentId = (path) => {
477
+ const parts = path.replace(/^\/+/, "").split("/");
478
+ const docIndex = parts.lastIndexOf("doc");
479
+ const part = parts[docIndex + 1];
480
+ if (docIndex !== -1 && part) return Effect.succeed(decodeURIComponent(part));
481
+ return Effect.fail(new MissingDocumentIdError({}));
482
+ };
483
+ /**
484
+ * Decodes an encoded client message from the wire format.
485
+ */
486
+ const decodeClientMessage = (encoded) => {
487
+ if (encoded.type === "submit") return {
488
+ type: "submit",
489
+ transaction: Transaction.decode(encoded.transaction)
490
+ };
491
+ return encoded;
492
+ };
493
+ /**
494
+ * Encodes a server message for the wire format.
495
+ */
496
+ const encodeServerMessageForWire = (message) => {
497
+ if (message.type === "transaction") return {
498
+ type: "transaction",
499
+ transaction: Transaction.encode(message.transaction),
500
+ version: message.version
501
+ };
502
+ return message;
503
+ };
504
+ const parseClientMessage = (data) => Effect.try({
505
+ try: () => {
506
+ const text = typeof data === "string" ? data : new TextDecoder().decode(data);
507
+ return decodeClientMessage(JSON.parse(text));
508
+ },
509
+ catch: (cause) => new MessageParseError({ cause })
510
+ });
511
+ const encodeServerMessage = (message) => JSON.stringify(encodeServerMessageForWire(message));
512
+ /**
513
+ * Handle a WebSocket connection for a document.
514
+ *
515
+ * @param socket - The Effect Platform Socket
516
+ * @param path - The URL path (e.g., "/doc/my-document-id")
517
+ * @returns An Effect that handles the connection lifecycle
518
+ */
519
+ const handleConnection = (socket, path) => Effect.gen(function* () {
520
+ const config = yield* MimicServerConfigTag;
521
+ const authService = yield* MimicAuthServiceTag;
522
+ const documentManager = yield* DocumentManagerTag;
523
+ const presenceManager = yield* PresenceManagerTag;
524
+ const documentId = yield* extractDocumentId(path);
525
+ const connectionId = crypto.randomUUID();
526
+ let state = {
527
+ documentId,
528
+ connectionId,
529
+ authenticated: false
530
+ };
531
+ let hasPresence = false;
532
+ const write = yield* socket.writer;
533
+ const sendMessage = (message) => write(encodeServerMessage(message));
534
+ const sendPresenceSnapshot = Effect.gen(function* () {
535
+ if (!config.presence) return;
536
+ yield* sendMessage({
537
+ type: "presence_snapshot",
538
+ selfId: connectionId,
539
+ presences: (yield* presenceManager.getSnapshot(documentId)).presences
540
+ });
541
+ });
542
+ const handleAuth = (token) => Effect.gen(function* () {
543
+ const result = yield* authService.authenticate(token);
544
+ if (result.success) {
545
+ state = _objectSpread2(_objectSpread2({}, state), {}, {
546
+ authenticated: true,
547
+ userId: result.userId
548
+ });
549
+ yield* sendMessage({
550
+ type: "auth_result",
551
+ success: true
552
+ });
553
+ yield* sendPresenceSnapshot;
554
+ } else yield* sendMessage({
555
+ type: "auth_result",
556
+ success: false,
557
+ error: result.error
558
+ });
559
+ });
560
+ const handlePresenceSet = (data) => Effect.gen(function* () {
561
+ if (!state.authenticated) return;
562
+ if (!config.presence) return;
563
+ const validated = Presence.validateSafe(config.presence, data);
564
+ if (validated === void 0) {
565
+ yield* Effect.logWarning("Invalid presence data received", {
566
+ connectionId,
567
+ data
568
+ });
569
+ return;
570
+ }
571
+ yield* presenceManager.set(documentId, connectionId, {
572
+ data: validated,
573
+ userId: state.userId
574
+ });
575
+ hasPresence = true;
576
+ });
577
+ const handlePresenceClear = Effect.gen(function* () {
578
+ if (!state.authenticated) return;
579
+ if (!config.presence) return;
580
+ yield* presenceManager.remove(documentId, connectionId);
581
+ hasPresence = false;
582
+ });
583
+ const handleMessage = (message) => Effect.gen(function* () {
584
+ switch (message.type) {
585
+ case "auth":
586
+ yield* handleAuth(message.token);
587
+ break;
588
+ case "ping":
589
+ yield* sendMessage({ type: "pong" });
590
+ break;
591
+ case "submit":
592
+ if (!state.authenticated) {
593
+ yield* sendMessage({
594
+ type: "error",
595
+ transactionId: message.transaction.id,
596
+ reason: "Not authenticated"
597
+ });
598
+ return;
599
+ }
600
+ const submitResult = yield* documentManager.submit(documentId, message.transaction);
601
+ if (!submitResult.success) yield* sendMessage({
602
+ type: "error",
603
+ transactionId: message.transaction.id,
604
+ reason: submitResult.reason
605
+ });
606
+ break;
607
+ case "request_snapshot":
608
+ if (!state.authenticated) return;
609
+ yield* sendMessage(yield* Effect.catchAll(documentManager.getSnapshot(documentId), () => Effect.succeed({
610
+ type: "snapshot",
611
+ state: null,
612
+ version: 0
613
+ })));
614
+ break;
615
+ case "presence_set":
616
+ yield* handlePresenceSet(message.data);
617
+ break;
618
+ case "presence_clear":
619
+ yield* handlePresenceClear;
620
+ break;
621
+ }
622
+ });
623
+ const subscribeFiber = yield* Effect.fork(Effect.gen(function* () {
624
+ while (!state.authenticated) yield* Effect.sleep(Duration.millis(100));
625
+ const broadcastStream = yield* Effect.catchAll(documentManager.subscribe(documentId), () => Effect.succeed(Stream.empty));
626
+ yield* Stream.runForEach(broadcastStream, (broadcast) => sendMessage(broadcast));
627
+ }).pipe(Effect.scoped));
628
+ const presenceFiber = yield* Effect.fork(Effect.gen(function* () {
629
+ if (!config.presence) return;
630
+ while (!state.authenticated) yield* Effect.sleep(Duration.millis(100));
631
+ const presenceStream = yield* presenceManager.subscribe(documentId);
632
+ yield* Stream.runForEach(presenceStream, (event) => Effect.gen(function* () {
633
+ if (event.id === connectionId) return;
634
+ if (event.type === "presence_update") yield* sendMessage({
635
+ type: "presence_update",
636
+ id: event.id,
637
+ data: event.data,
638
+ userId: event.userId
639
+ });
640
+ else if (event.type === "presence_remove") yield* sendMessage({
641
+ type: "presence_remove",
642
+ id: event.id
643
+ });
644
+ }));
645
+ }).pipe(Effect.scoped));
646
+ yield* Effect.addFinalizer(() => Effect.gen(function* () {
647
+ yield* Fiber.interrupt(subscribeFiber);
648
+ yield* Fiber.interrupt(presenceFiber);
649
+ if (hasPresence && config.presence) yield* presenceManager.remove(documentId, connectionId);
650
+ }));
651
+ yield* socket.runRaw((data) => Effect.gen(function* () {
652
+ yield* handleMessage(yield* parseClientMessage(data));
653
+ }).pipe(Effect.catchAll((error) => Effect.logError("Message handling error", error))));
654
+ });
655
+ /**
656
+ * Create a handler function for the WebSocket server.
657
+ * Returns a function that takes a socket and document ID.
658
+ */
659
+ const makeHandler = Effect.gen(function* () {
660
+ const config = yield* MimicServerConfigTag;
661
+ const authService = yield* MimicAuthServiceTag;
662
+ const documentManager = yield* DocumentManagerTag;
663
+ const presenceManager = yield* PresenceManagerTag;
664
+ return (socket, documentId) => handleConnectionWithDocumentId(socket, documentId).pipe(Effect.provideService(MimicServerConfigTag, config), Effect.provideService(MimicAuthServiceTag, authService), Effect.provideService(DocumentManagerTag, documentManager), Effect.provideService(PresenceManagerTag, presenceManager), Effect.scoped);
665
+ });
666
+ /**
667
+ * Handle a WebSocket connection for a document (using document ID directly).
668
+ */
669
+ const handleConnectionWithDocumentId = (socket, documentId) => Effect.gen(function* () {
670
+ const config = yield* MimicServerConfigTag;
671
+ const authService = yield* MimicAuthServiceTag;
672
+ const documentManager = yield* DocumentManagerTag;
673
+ const presenceManager = yield* PresenceManagerTag;
674
+ const connectionId = crypto.randomUUID();
675
+ let state = {
676
+ documentId,
677
+ connectionId,
678
+ authenticated: false
679
+ };
680
+ let hasPresence = false;
681
+ const write = yield* socket.writer;
682
+ const sendMessage = (message) => write(encodeServerMessage(message));
683
+ const sendPresenceSnapshot = Effect.gen(function* () {
684
+ if (!config.presence) return;
685
+ yield* sendMessage({
686
+ type: "presence_snapshot",
687
+ selfId: connectionId,
688
+ presences: (yield* presenceManager.getSnapshot(documentId)).presences
689
+ });
690
+ });
691
+ const handleAuth = (token) => Effect.gen(function* () {
692
+ const result = yield* authService.authenticate(token);
693
+ if (result.success) {
694
+ state = _objectSpread2(_objectSpread2({}, state), {}, {
695
+ authenticated: true,
696
+ userId: result.userId
697
+ });
698
+ yield* sendMessage({
699
+ type: "auth_result",
700
+ success: true
701
+ });
702
+ yield* sendPresenceSnapshot;
703
+ } else yield* sendMessage({
704
+ type: "auth_result",
705
+ success: false,
706
+ error: result.error
707
+ });
708
+ });
709
+ const handlePresenceSet = (data) => Effect.gen(function* () {
710
+ if (!state.authenticated) return;
711
+ if (!config.presence) return;
712
+ const validated = Presence.validateSafe(config.presence, data);
713
+ if (validated === void 0) {
714
+ yield* Effect.logWarning("Invalid presence data received", {
715
+ connectionId,
716
+ data
717
+ });
718
+ return;
719
+ }
720
+ yield* presenceManager.set(documentId, connectionId, {
721
+ data: validated,
722
+ userId: state.userId
723
+ });
724
+ hasPresence = true;
725
+ });
726
+ const handlePresenceClear = Effect.gen(function* () {
727
+ if (!state.authenticated) return;
728
+ if (!config.presence) return;
729
+ yield* presenceManager.remove(documentId, connectionId);
730
+ hasPresence = false;
731
+ });
732
+ const handleMessage = (message) => Effect.gen(function* () {
733
+ switch (message.type) {
734
+ case "auth":
735
+ yield* handleAuth(message.token);
736
+ break;
737
+ case "ping":
738
+ yield* sendMessage({ type: "pong" });
739
+ break;
740
+ case "submit":
741
+ if (!state.authenticated) {
742
+ yield* sendMessage({
743
+ type: "error",
744
+ transactionId: message.transaction.id,
745
+ reason: "Not authenticated"
746
+ });
747
+ return;
748
+ }
749
+ const submitResult = yield* documentManager.submit(documentId, message.transaction);
750
+ if (!submitResult.success) yield* sendMessage({
751
+ type: "error",
752
+ transactionId: message.transaction.id,
753
+ reason: submitResult.reason
754
+ });
755
+ break;
756
+ case "request_snapshot":
757
+ if (!state.authenticated) return;
758
+ yield* sendMessage(yield* documentManager.getSnapshot(documentId));
759
+ break;
760
+ case "presence_set":
761
+ yield* handlePresenceSet(message.data);
762
+ break;
763
+ case "presence_clear":
764
+ yield* handlePresenceClear;
765
+ break;
766
+ }
767
+ });
768
+ const subscribeFiber = yield* Effect.fork(Effect.gen(function* () {
769
+ while (!state.authenticated) yield* Effect.sleep(Duration.millis(100));
770
+ const broadcastStream = yield* documentManager.subscribe(documentId);
771
+ yield* Stream.runForEach(broadcastStream, (broadcast) => sendMessage(broadcast));
772
+ }).pipe(Effect.scoped));
773
+ const presenceFiber = yield* Effect.fork(Effect.gen(function* () {
774
+ if (!config.presence) return;
775
+ while (!state.authenticated) yield* Effect.sleep(Duration.millis(100));
776
+ const presenceStream = yield* presenceManager.subscribe(documentId);
777
+ yield* Stream.runForEach(presenceStream, (event) => Effect.gen(function* () {
778
+ if (event.id === connectionId) return;
779
+ if (event.type === "presence_update") yield* sendMessage({
780
+ type: "presence_update",
781
+ id: event.id,
782
+ data: event.data,
783
+ userId: event.userId
784
+ });
785
+ else if (event.type === "presence_remove") yield* sendMessage({
786
+ type: "presence_remove",
787
+ id: event.id
788
+ });
789
+ }));
790
+ }).pipe(Effect.scoped));
791
+ yield* Effect.addFinalizer(() => Effect.gen(function* () {
792
+ yield* Fiber.interrupt(subscribeFiber);
793
+ yield* Fiber.interrupt(presenceFiber);
794
+ if (hasPresence && config.presence) yield* presenceManager.remove(documentId, connectionId);
795
+ }));
796
+ yield* socket.runRaw((data) => Effect.gen(function* () {
797
+ yield* handleMessage(yield* parseClientMessage(data));
798
+ }).pipe(Effect.catchAll((error) => Effect.logError("Message handling error", error))));
799
+ });
800
+
801
+ //#endregion
802
+ //#region src/storage/InMemoryDataStorage.ts
803
+ /**
804
+ * @since 0.0.1
805
+ * In-memory data storage implementation for Mimic documents.
806
+ * Provides ephemeral storage - data is lost when the server restarts.
807
+ */
808
+ var InMemoryDataStorage_exports = /* @__PURE__ */ __export({
809
+ layer: () => layer$2,
810
+ layerDefault: () => layerDefault$1
811
+ });
812
+ /**
813
+ * Create an in-memory storage service.
814
+ * Uses a HashMap to store documents in memory.
815
+ */
816
+ const makeInMemoryStorage = Effect.gen(function* () {
817
+ const store = yield* Ref.make(HashMap.empty());
818
+ return {
819
+ load: (documentId) => Effect.gen(function* () {
820
+ const current = yield* Ref.get(store);
821
+ const result = HashMap.get(current, documentId);
822
+ return result._tag === "Some" ? result.value : void 0;
823
+ }),
824
+ save: (documentId, state) => Ref.update(store, (map) => HashMap.set(map, documentId, state)),
825
+ delete: (documentId) => Ref.update(store, (map) => HashMap.remove(map, documentId)),
826
+ onLoad: (state) => Effect.succeed(state),
827
+ onSave: (state) => Effect.succeed(state)
828
+ };
829
+ });
830
+ /**
831
+ * Layer that provides in-memory data storage.
832
+ * This is the default storage implementation - ephemeral and non-persistent.
833
+ */
834
+ const layer$2 = Layer.effect(MimicDataStorageTag, makeInMemoryStorage);
835
+ /**
836
+ * Default layer alias for convenience.
837
+ */
838
+ const layerDefault$1 = layer$2;
839
+
840
+ //#endregion
841
+ //#region src/auth/NoAuth.ts
842
+ /**
843
+ * @since 0.0.1
844
+ * No authentication implementation for Mimic connections.
845
+ * All connections are automatically authenticated (open access).
846
+ */
847
+ var NoAuth_exports = /* @__PURE__ */ __export({
848
+ layer: () => layer$1,
849
+ layerDefault: () => layerDefault
850
+ });
851
+ /**
852
+ * Authentication service that auto-succeeds all authentication requests.
853
+ * Use this for development or when authentication is handled externally.
854
+ */
855
+ const noAuthService = { authenticate: (_token) => Effect.succeed({ success: true }) };
856
+ /**
857
+ * Layer that provides no authentication (open access).
858
+ * All connections are automatically authenticated.
859
+ *
860
+ * WARNING: Only use this for development or when authentication
861
+ * is handled at a different layer (e.g., API gateway, reverse proxy).
862
+ */
863
+ const layer$1 = Layer.succeed(MimicAuthServiceTag, noAuthService);
864
+ /**
865
+ * Default layer alias for convenience.
866
+ */
867
+ const layerDefault = layer$1;
868
+
869
+ //#endregion
870
+ //#region src/MimicServer.ts
871
+ /**
872
+ * @since 0.0.1
873
+ * Mimic server layer composition.
874
+ */
875
+ var MimicServer_exports = /* @__PURE__ */ __export({
876
+ MimicWebSocketHandler: () => MimicWebSocketHandler,
877
+ documentManagerLayer: () => documentManagerLayer,
878
+ handlerLayer: () => handlerLayer,
879
+ layer: () => layer,
880
+ layerHttpLayerRouter: () => layerHttpLayerRouter,
881
+ run: () => run
882
+ });
883
+ /**
884
+ * Tag for the WebSocket handler function.
885
+ */
886
+ var MimicWebSocketHandler = class extends Context.Tag("@voidhash/mimic-server-effect/MimicWebSocketHandler")() {};
887
+ /**
888
+ * Create a Mimic WebSocket handler layer.
889
+ *
890
+ * This layer provides a handler function that can be used with any WebSocket server
891
+ * implementation. The handler takes a socket and document ID and manages the
892
+ * document synchronization.
893
+ *
894
+ * By default, uses in-memory storage and no authentication.
895
+ * Override these by providing MimicDataStorage and MimicAuthService layers.
896
+ *
897
+ * @example
898
+ * ```typescript
899
+ * import { MimicServer, MimicAuthService } from "@voidhash/mimic-effect";
900
+ * import { Primitive } from "@voidhash/mimic";
901
+ *
902
+ * const TodoSchema = Primitive.Struct({
903
+ * title: Primitive.String(),
904
+ * completed: Primitive.Boolean(),
905
+ * });
906
+ *
907
+ * // Create the handler layer with defaults
908
+ * const HandlerLayer = MimicServer.layer({
909
+ * basePath: "/mimic/todo",
910
+ * schema: TodoSchema
911
+ * });
912
+ *
913
+ * // Or with custom auth
914
+ * const HandlerLayerWithAuth = MimicServer.layer({
915
+ * basePath: "/mimic/todo",
916
+ * schema: TodoSchema
917
+ * }).pipe(
918
+ * Layer.provideMerge(MimicAuthService.layer({
919
+ * authHandler: (token) => ({ success: true, userId: "user-123" })
920
+ * }))
921
+ * );
922
+ * ```
923
+ */
924
+ const layer = (options) => {
925
+ const configLayer = layer$7({
926
+ schema: options.schema,
927
+ maxTransactionHistory: options.maxTransactionHistory,
928
+ presence: options.presence
929
+ });
930
+ return Layer.merge(Layer.effect(MimicWebSocketHandler, makeHandler).pipe(Layer.provide(layer$5), Layer.provide(layer$3), Layer.provide(configLayer)), layer$5.pipe(Layer.provide(configLayer))).pipe(Layer.provide(layerDefault$1), Layer.provide(layerDefault));
931
+ };
932
+ /**
933
+ * Create the Mimic server handler layer.
934
+ * This layer provides the WebSocket handler that can be used with any WebSocket server.
935
+ *
936
+ * @example
937
+ * ```typescript
938
+ * import { MimicServer } from "@voidhash/mimic-server-effect";
939
+ * import { SocketServer } from "@effect/platform/SocketServer";
940
+ * import { Primitive } from "@voidhash/mimic";
941
+ *
942
+ * // Define your document schema
943
+ * const TodoSchema = Primitive.Struct({
944
+ * title: Primitive.String(),
945
+ * completed: Primitive.Boolean(),
946
+ * });
947
+ *
948
+ * // Create the server layer
949
+ * const serverLayer = MimicServer.handlerLayer({
950
+ * schema: TodoSchema,
951
+ * });
952
+ *
953
+ * // Run with your socket server
954
+ * Effect.gen(function* () {
955
+ * const handler = yield* MimicServer.MimicWebSocketHandler;
956
+ * const server = yield* SocketServer;
957
+ *
958
+ * yield* server.run((socket) =>
959
+ * // Extract document ID from request and call handler
960
+ * handler(socket, "my-document-id")
961
+ * );
962
+ * }).pipe(
963
+ * Effect.provide(serverLayer),
964
+ * Effect.provide(YourSocketServerLayer),
965
+ * );
966
+ * ```
967
+ */
968
+ const handlerLayer = (options) => Layer.effect(MimicWebSocketHandler, makeHandler).pipe(Layer.provide(layer$5), Layer.provide(layer$3), Layer.provide(layer$7(options)), Layer.provide(layerDefault$1), Layer.provide(layerDefault));
969
+ /**
970
+ * Create the document manager layer.
971
+ */
972
+ const documentManagerLayer = (options) => layer$5.pipe(Layer.provide(layer$7(options)), Layer.provide(layerDefault$1), Layer.provide(layerDefault));
973
+ /**
974
+ * Run a Mimic WebSocket server with the provided handler.
975
+ *
976
+ * This is a helper that:
977
+ * 1. Gets the WebSocket handler from context
978
+ * 2. Runs the socket server with the handler
979
+ *
980
+ * Note: The document ID extraction from socket is implementation-specific.
981
+ * You may need to customize this based on your socket server.
982
+ */
983
+ const run = (extractDocumentId$1) => Effect.gen(function* () {
984
+ const handler = yield* MimicWebSocketHandler;
985
+ yield* (yield* SocketServer).run((socket) => Effect.gen(function* () {
986
+ yield* handler(socket, yield* extractDocumentId$1(socket));
987
+ }).pipe(Effect.catchAll((error) => Effect.logError("Connection error", error))));
988
+ });
989
+ /**
990
+ * Create the HTTP handler effect for WebSocket upgrade.
991
+ * This handler:
992
+ * 1. Extracts the document ID from the URL path
993
+ * 2. Upgrades the HTTP connection to WebSocket
994
+ * 3. Delegates to the WebSocketHandler for document sync
995
+ */
996
+ const makeMimicHandler = Effect.gen(function* () {
997
+ const config = yield* MimicServerConfigTag;
998
+ const authService = yield* MimicAuthServiceTag;
999
+ const documentManager = yield* DocumentManagerTag;
1000
+ const presenceManager = yield* PresenceManagerTag;
1001
+ return Effect.gen(function* () {
1002
+ const request = yield* HttpServerRequest.HttpServerRequest;
1003
+ yield* extractDocumentId(request.url);
1004
+ const socket = yield* request.upgrade;
1005
+ yield* handleConnection(socket, request.url).pipe(Effect.provideService(MimicServerConfigTag, config), Effect.provideService(MimicAuthServiceTag, authService), Effect.provideService(DocumentManagerTag, documentManager), Effect.provideService(PresenceManagerTag, presenceManager), Effect.scoped, Effect.catchAll((error) => Effect.logError("WebSocket connection error", error)));
1006
+ return HttpServerResponse.empty();
1007
+ }).pipe(Effect.catchAll((error) => Effect.gen(function* () {
1008
+ yield* Effect.logWarning("WebSocket upgrade failed", error);
1009
+ return HttpServerResponse.text("WebSocket upgrade failed", { status: 400 });
1010
+ })));
1011
+ });
1012
+ /**
1013
+ * Create a Mimic server layer that integrates with HttpLayerRouter.
1014
+ *
1015
+ * This function creates a layer that:
1016
+ * 1. Registers a WebSocket route at the specified base path
1017
+ * 2. Handles WebSocket upgrades for document sync
1018
+ * 3. Provides all required dependencies (config, auth, storage, document manager)
1019
+ *
1020
+ * By default, uses in-memory storage and no authentication.
1021
+ * To override these defaults, provide custom layers before the defaults:
1022
+ *
1023
+ * @example
1024
+ * ```typescript
1025
+ * import { MimicServer, MimicAuthService } from "@voidhash/mimic-effect";
1026
+ * import { HttpLayerRouter } from "@effect/platform";
1027
+ * import { Primitive } from "@voidhash/mimic";
1028
+ *
1029
+ * const TodoSchema = Primitive.Struct({
1030
+ * title: Primitive.String(),
1031
+ * completed: Primitive.Boolean(),
1032
+ * });
1033
+ *
1034
+ * // Create the Mimic route layer with defaults
1035
+ * const MimicRoute = MimicServer.layerHttpLayerRouter({
1036
+ * basePath: "/mimic/todo",
1037
+ * schema: TodoSchema
1038
+ * });
1039
+ *
1040
+ * // Or with custom auth - use Layer.provide to inject before defaults
1041
+ * const MimicRouteWithAuth = MimicServer.layerHttpLayerRouter({
1042
+ * basePath: "/mimic/todo",
1043
+ * schema: TodoSchema,
1044
+ * authLayer: MimicAuthService.layer({
1045
+ * authHandler: (token) => ({ success: true, userId: token })
1046
+ * })
1047
+ * });
1048
+ *
1049
+ * // Merge with other routes and serve
1050
+ * const AllRoutes = Layer.mergeAll(MimicRoute, OtherRoutes);
1051
+ * HttpLayerRouter.serve(AllRoutes).pipe(
1052
+ * Layer.provide(BunHttpServer.layer({ port: 3000 })),
1053
+ * Layer.launch,
1054
+ * BunRuntime.runMain
1055
+ * );
1056
+ * ```
1057
+ */
1058
+ const layerHttpLayerRouter = (options) => {
1059
+ var _options$basePath, _options$authLayer, _options$storageLayer;
1060
+ const wsPath = `${(_options$basePath = options.basePath) !== null && _options$basePath !== void 0 ? _options$basePath : "/mimic"}/doc/*`;
1061
+ const configLayer = layer$7({
1062
+ schema: options.schema,
1063
+ maxTransactionHistory: options.maxTransactionHistory,
1064
+ presence: options.presence
1065
+ });
1066
+ const authLayer = (_options$authLayer = options.authLayer) !== null && _options$authLayer !== void 0 ? _options$authLayer : layerDefault;
1067
+ const storageLayer = (_options$storageLayer = options.storageLayer) !== null && _options$storageLayer !== void 0 ? _options$storageLayer : layerDefault$1;
1068
+ const registerRoute = Effect.gen(function* () {
1069
+ const router = yield* HttpLayerRouter.HttpRouter;
1070
+ const handler = yield* makeMimicHandler;
1071
+ yield* router.add("GET", wsPath, handler);
1072
+ });
1073
+ return Layer.scopedDiscard(registerRoute).pipe(Layer.provide(layer$5), Layer.provide(layer$3), Layer.provide(configLayer), Layer.provide(storageLayer), Layer.provide(authLayer));
1074
+ };
1075
+
1076
+ //#endregion
1077
+ //#region src/DocumentProtocol.ts
1078
+ /**
1079
+ * @since 0.0.1
1080
+ * Protocol and schema definitions for document communication.
1081
+ */
1082
+ var DocumentProtocol_exports = /* @__PURE__ */ __export({
1083
+ AuthResultMessageSchema: () => AuthResultMessageSchema,
1084
+ ErrorMessageSchema: () => ErrorMessageSchema,
1085
+ OperationSchema: () => OperationSchema,
1086
+ PongMessageSchema: () => PongMessageSchema,
1087
+ ServerBroadcastSchema: () => ServerBroadcastSchema,
1088
+ SnapshotMessageSchema: () => SnapshotMessageSchema,
1089
+ SubmitResultSchema: () => SubmitResultSchema,
1090
+ TransactionMessageSchema: () => TransactionMessageSchema,
1091
+ TransactionSchema: () => TransactionSchema
1092
+ });
1093
+ /**
1094
+ * Schema for a transaction operation.
1095
+ */
1096
+ const OperationSchema = Schema.Struct({
1097
+ kind: Schema.String,
1098
+ path: Schema.Unknown,
1099
+ payload: Schema.Unknown
1100
+ });
1101
+ /**
1102
+ * Schema for a transaction.
1103
+ */
1104
+ const TransactionSchema = Schema.Struct({
1105
+ id: Schema.String,
1106
+ ops: Schema.Array(OperationSchema),
1107
+ timestamp: Schema.Number
1108
+ });
1109
+ /**
1110
+ * Schema for a server message that broadcasts a committed transaction.
1111
+ */
1112
+ const TransactionMessageSchema = Schema.Struct({
1113
+ type: Schema.Literal("transaction"),
1114
+ transaction: TransactionSchema,
1115
+ version: Schema.Number
1116
+ });
1117
+ /**
1118
+ * Schema for a server message containing a snapshot.
1119
+ */
1120
+ const SnapshotMessageSchema = Schema.Struct({
1121
+ type: Schema.Literal("snapshot"),
1122
+ state: Schema.Unknown,
1123
+ version: Schema.Number
1124
+ });
1125
+ /**
1126
+ * Schema for a server error message.
1127
+ */
1128
+ const ErrorMessageSchema = Schema.Struct({
1129
+ type: Schema.Literal("error"),
1130
+ transactionId: Schema.String,
1131
+ reason: Schema.String
1132
+ });
1133
+ /**
1134
+ * Schema for a pong message.
1135
+ */
1136
+ const PongMessageSchema = Schema.Struct({ type: Schema.Literal("pong") });
1137
+ /**
1138
+ * Schema for authentication result message.
1139
+ */
1140
+ const AuthResultMessageSchema = Schema.Struct({
1141
+ type: Schema.Literal("auth_result"),
1142
+ success: Schema.Boolean,
1143
+ error: Schema.optional(Schema.String)
1144
+ });
1145
+ /**
1146
+ * Union of all server broadcast messages.
1147
+ */
1148
+ const ServerBroadcastSchema = Schema.Union(TransactionMessageSchema, ErrorMessageSchema);
1149
+ /**
1150
+ * Result of submitting a transaction.
1151
+ */
1152
+ const SubmitResultSchema = Schema.Union(Schema.Struct({
1153
+ success: Schema.Literal(true),
1154
+ version: Schema.Number
1155
+ }), Schema.Struct({
1156
+ success: Schema.Literal(false),
1157
+ reason: Schema.String
1158
+ }));
1159
+
1160
+ //#endregion
1161
+ export { AuthenticationError, DocumentManager_exports as DocumentManager, DocumentNotFoundError, DocumentProtocol_exports as DocumentProtocol, DocumentTypeNotFoundError, InvalidConnectionError, MessageParseError, MimicAuthService_exports as MimicAuthService, MimicConfig_exports as MimicConfig, MimicDataStorage_exports as MimicDataStorage, InMemoryDataStorage_exports as MimicInMemoryDataStorage, NoAuth_exports as MimicNoAuth, MimicServer_exports as MimicServer, MissingDocumentIdError, PresenceManager_exports as PresenceManager, TransactionRejectedError, WebSocketHandler_exports as WebSocketHandler };
1162
+ //# sourceMappingURL=index.mjs.map