@voidhash/mimic-effect 0.0.3 → 0.0.4
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/.turbo/turbo-build.log +24 -24
- package/dist/DocumentManager.cjs +3 -3
- package/dist/DocumentManager.d.cts +2 -2
- package/dist/DocumentManager.mjs +2 -2
- package/dist/DocumentManager.mjs.map +1 -1
- package/dist/MimicAuthService.d.cts +1 -1
- package/dist/MimicConfig.cjs +3 -1
- package/dist/MimicConfig.d.cts +17 -1
- package/dist/MimicConfig.d.cts.map +1 -1
- package/dist/MimicConfig.d.mts +16 -0
- package/dist/MimicConfig.d.mts.map +1 -1
- package/dist/MimicConfig.mjs +3 -1
- package/dist/MimicConfig.mjs.map +1 -1
- package/dist/MimicDataStorage.d.cts +1 -1
- package/dist/MimicServer.cjs +3 -111
- package/dist/MimicServer.d.cts +11 -97
- package/dist/MimicServer.d.cts.map +1 -1
- package/dist/MimicServer.d.mts +11 -97
- package/dist/MimicServer.d.mts.map +1 -1
- package/dist/MimicServer.mjs +10 -117
- package/dist/MimicServer.mjs.map +1 -1
- package/dist/PresenceManager.cjs +2 -2
- package/dist/PresenceManager.d.cts +1 -1
- package/dist/PresenceManager.mjs +1 -1
- package/dist/WebSocketHandler.cjs +2 -3
- package/dist/WebSocketHandler.d.cts +1 -1
- package/dist/WebSocketHandler.d.mts +1 -1
- package/dist/WebSocketHandler.mjs +2 -2
- package/package.json +3 -3
- package/src/DocumentManager.ts +2 -2
- package/src/MimicConfig.ts +22 -1
- package/src/MimicServer.ts +11 -161
- package/tests/DocumentManager.test.ts +61 -0
- package/tests/MimicConfig.test.ts +72 -0
- package/tests/MimicServer.test.ts +55 -162
package/dist/MimicServer.d.cts
CHANGED
|
@@ -2,25 +2,15 @@ import { MimicServerConfigOptions } from "./MimicConfig.cjs";
|
|
|
2
2
|
import { MimicDataStorageTag } from "./MimicDataStorage.cjs";
|
|
3
3
|
import { DocumentManagerTag } from "./DocumentManager.cjs";
|
|
4
4
|
import { MimicAuthServiceTag } from "./MimicAuthService.cjs";
|
|
5
|
-
import * as _effect_platform_SocketServer0 from "@effect/platform/SocketServer";
|
|
6
|
-
import { SocketServer } from "@effect/platform/SocketServer";
|
|
7
|
-
import * as Effect from "effect/Effect";
|
|
8
5
|
import * as Layer from "effect/Layer";
|
|
9
|
-
import * as Context from "effect/Context";
|
|
10
|
-
import * as Socket from "@effect/platform/Socket";
|
|
11
6
|
import { Presence, Primitive } from "@voidhash/mimic";
|
|
12
7
|
import { HttpLayerRouter } from "@effect/platform";
|
|
13
8
|
import { PathInput } from "@effect/platform/HttpRouter";
|
|
14
9
|
|
|
15
10
|
//#region src/MimicServer.d.ts
|
|
16
11
|
declare namespace MimicServer_d_exports {
|
|
17
|
-
export { MimicLayerOptions,
|
|
12
|
+
export { MimicLayerOptions, documentManagerLayer, layerHttpLayerRouter };
|
|
18
13
|
}
|
|
19
|
-
declare const MimicWebSocketHandler_base: Context.TagClass<MimicWebSocketHandler, "@voidhash/mimic-server-effect/MimicWebSocketHandler", (socket: Socket.Socket, documentId: string) => Effect.Effect<void, unknown>>;
|
|
20
|
-
/**
|
|
21
|
-
* Tag for the WebSocket handler function.
|
|
22
|
-
*/
|
|
23
|
-
declare class MimicWebSocketHandler extends MimicWebSocketHandler_base {}
|
|
24
14
|
/**
|
|
25
15
|
* Options for creating a Mimic server layer.
|
|
26
16
|
*/
|
|
@@ -44,97 +34,21 @@ interface MimicLayerOptions<TSchema extends Primitive.AnyPrimitive> {
|
|
|
44
34
|
* When provided, enables presence features on WebSocket connections.
|
|
45
35
|
*/
|
|
46
36
|
readonly presence?: Presence.AnyPresence;
|
|
37
|
+
/**
|
|
38
|
+
* Initial state for new documents.
|
|
39
|
+
* Used when a document is created and no existing state is found in storage.
|
|
40
|
+
*
|
|
41
|
+
* Type-safe: required fields (without defaults) must be provided,
|
|
42
|
+
* while optional fields and fields with defaults can be omitted.
|
|
43
|
+
*
|
|
44
|
+
* @default undefined (documents start empty or use schema defaults)
|
|
45
|
+
*/
|
|
46
|
+
readonly initial?: Primitive.InferSetInput<TSchema>;
|
|
47
47
|
}
|
|
48
|
-
/**
|
|
49
|
-
* Create a Mimic WebSocket handler layer.
|
|
50
|
-
*
|
|
51
|
-
* This layer provides a handler function that can be used with any WebSocket server
|
|
52
|
-
* implementation. The handler takes a socket and document ID and manages the
|
|
53
|
-
* document synchronization.
|
|
54
|
-
*
|
|
55
|
-
* By default, uses in-memory storage and no authentication.
|
|
56
|
-
* Override these by providing MimicDataStorage and MimicAuthService layers.
|
|
57
|
-
*
|
|
58
|
-
* @example
|
|
59
|
-
* ```typescript
|
|
60
|
-
* import { MimicServer, MimicAuthService } from "@voidhash/mimic-effect";
|
|
61
|
-
* import { Primitive } from "@voidhash/mimic";
|
|
62
|
-
*
|
|
63
|
-
* const TodoSchema = Primitive.Struct({
|
|
64
|
-
* title: Primitive.String(),
|
|
65
|
-
* completed: Primitive.Boolean(),
|
|
66
|
-
* });
|
|
67
|
-
*
|
|
68
|
-
* // Create the handler layer with defaults
|
|
69
|
-
* const HandlerLayer = MimicServer.layer({
|
|
70
|
-
* basePath: "/mimic/todo",
|
|
71
|
-
* schema: TodoSchema
|
|
72
|
-
* });
|
|
73
|
-
*
|
|
74
|
-
* // Or with custom auth
|
|
75
|
-
* const HandlerLayerWithAuth = MimicServer.layer({
|
|
76
|
-
* basePath: "/mimic/todo",
|
|
77
|
-
* schema: TodoSchema
|
|
78
|
-
* }).pipe(
|
|
79
|
-
* Layer.provideMerge(MimicAuthService.layer({
|
|
80
|
-
* authHandler: (token) => ({ success: true, userId: "user-123" })
|
|
81
|
-
* }))
|
|
82
|
-
* );
|
|
83
|
-
* ```
|
|
84
|
-
*/
|
|
85
|
-
declare const layer: <TSchema extends Primitive.AnyPrimitive>(options: MimicLayerOptions<TSchema>) => Layer.Layer<MimicWebSocketHandler | DocumentManagerTag>;
|
|
86
|
-
/**
|
|
87
|
-
* Create the Mimic server handler layer.
|
|
88
|
-
* This layer provides the WebSocket handler that can be used with any WebSocket server.
|
|
89
|
-
*
|
|
90
|
-
* @example
|
|
91
|
-
* ```typescript
|
|
92
|
-
* import { MimicServer } from "@voidhash/mimic-server-effect";
|
|
93
|
-
* import { SocketServer } from "@effect/platform/SocketServer";
|
|
94
|
-
* import { Primitive } from "@voidhash/mimic";
|
|
95
|
-
*
|
|
96
|
-
* // Define your document schema
|
|
97
|
-
* const TodoSchema = Primitive.Struct({
|
|
98
|
-
* title: Primitive.String(),
|
|
99
|
-
* completed: Primitive.Boolean(),
|
|
100
|
-
* });
|
|
101
|
-
*
|
|
102
|
-
* // Create the server layer
|
|
103
|
-
* const serverLayer = MimicServer.handlerLayer({
|
|
104
|
-
* schema: TodoSchema,
|
|
105
|
-
* });
|
|
106
|
-
*
|
|
107
|
-
* // Run with your socket server
|
|
108
|
-
* Effect.gen(function* () {
|
|
109
|
-
* const handler = yield* MimicServer.MimicWebSocketHandler;
|
|
110
|
-
* const server = yield* SocketServer;
|
|
111
|
-
*
|
|
112
|
-
* yield* server.run((socket) =>
|
|
113
|
-
* // Extract document ID from request and call handler
|
|
114
|
-
* handler(socket, "my-document-id")
|
|
115
|
-
* );
|
|
116
|
-
* }).pipe(
|
|
117
|
-
* Effect.provide(serverLayer),
|
|
118
|
-
* Effect.provide(YourSocketServerLayer),
|
|
119
|
-
* );
|
|
120
|
-
* ```
|
|
121
|
-
*/
|
|
122
|
-
declare const handlerLayer: <TSchema extends Primitive.AnyPrimitive>(options: MimicServerConfigOptions<TSchema>) => Layer.Layer<MimicWebSocketHandler>;
|
|
123
48
|
/**
|
|
124
49
|
* Create the document manager layer.
|
|
125
50
|
*/
|
|
126
51
|
declare const documentManagerLayer: <TSchema extends Primitive.AnyPrimitive>(options: MimicServerConfigOptions<TSchema>) => Layer.Layer<DocumentManagerTag>;
|
|
127
|
-
/**
|
|
128
|
-
* Run a Mimic WebSocket server with the provided handler.
|
|
129
|
-
*
|
|
130
|
-
* This is a helper that:
|
|
131
|
-
* 1. Gets the WebSocket handler from context
|
|
132
|
-
* 2. Runs the socket server with the handler
|
|
133
|
-
*
|
|
134
|
-
* Note: The document ID extraction from socket is implementation-specific.
|
|
135
|
-
* You may need to customize this based on your socket server.
|
|
136
|
-
*/
|
|
137
|
-
declare const run: (extractDocumentId: (socket: Socket.Socket) => Effect.Effect<string>) => Effect.Effect<void, _effect_platform_SocketServer0.SocketServerError, MimicWebSocketHandler | SocketServer>;
|
|
138
52
|
/**
|
|
139
53
|
* Create a Mimic server layer that integrates with HttpLayerRouter.
|
|
140
54
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MimicServer.d.cts","names":[],"sources":["../src/MimicServer.ts"],"sourcesContent":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MimicServer.d.cts","names":[],"sources":["../src/MimicServer.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;UA6BiB,kCAAkC,SAAA,CAAU;;;;;EAA5C,SAAA,QAAA,CAAA,EAKK,SALY;EAAiB;;;EAmB7B,SAAS,MAAA,EAVZ,OAUY;EAUc;;;AAO7C;EAAqD,SAAU,qBAAA,CAAA,EAAA,MAAA;EACf;;;;EAClC,SAAA,QAAA,CAAA,EAnBQ,QAAA,CAAS,WAmBjB;EAyGD;;;;;;;;;EAMV,SAAA,OAAA,CAAA,EAxHkB,SAAA,CAAU,aAwH5B,CAxH0C,OAwH1C,CAAA;;;;;cAjHU,uCAAwC,SAAA,CAAU,uBACpD,yBAAqC,aAC7C,KAAA,CAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAyGF,uCAAwC,SAAA,CAAU,uBACpD,kBAAkB;;uBAEJ,KAAA,CAAM,MAAM;;0BAET,KAAA,CAAM,MAAM;MACrC,KAAA,CAAA,oBAAA,eAAA,CAAA"}
|
package/dist/MimicServer.d.mts
CHANGED
|
@@ -2,25 +2,15 @@ import { MimicServerConfigOptions } from "./MimicConfig.mjs";
|
|
|
2
2
|
import { MimicDataStorageTag } from "./MimicDataStorage.mjs";
|
|
3
3
|
import { DocumentManagerTag } from "./DocumentManager.mjs";
|
|
4
4
|
import { MimicAuthServiceTag } from "./MimicAuthService.mjs";
|
|
5
|
-
import * as Effect from "effect/Effect";
|
|
6
5
|
import * as Layer from "effect/Layer";
|
|
7
|
-
import * as Context from "effect/Context";
|
|
8
|
-
import * as _effect_platform_SocketServer0 from "@effect/platform/SocketServer";
|
|
9
|
-
import { SocketServer } from "@effect/platform/SocketServer";
|
|
10
6
|
import { Presence, Primitive } from "@voidhash/mimic";
|
|
11
7
|
import { HttpLayerRouter } from "@effect/platform";
|
|
12
|
-
import * as Socket from "@effect/platform/Socket";
|
|
13
8
|
import { PathInput } from "@effect/platform/HttpRouter";
|
|
14
9
|
|
|
15
10
|
//#region src/MimicServer.d.ts
|
|
16
11
|
declare namespace MimicServer_d_exports {
|
|
17
|
-
export { MimicLayerOptions,
|
|
12
|
+
export { MimicLayerOptions, documentManagerLayer, layerHttpLayerRouter };
|
|
18
13
|
}
|
|
19
|
-
declare const MimicWebSocketHandler_base: Context.TagClass<MimicWebSocketHandler, "@voidhash/mimic-server-effect/MimicWebSocketHandler", (socket: Socket.Socket, documentId: string) => Effect.Effect<void, unknown>>;
|
|
20
|
-
/**
|
|
21
|
-
* Tag for the WebSocket handler function.
|
|
22
|
-
*/
|
|
23
|
-
declare class MimicWebSocketHandler extends MimicWebSocketHandler_base {}
|
|
24
14
|
/**
|
|
25
15
|
* Options for creating a Mimic server layer.
|
|
26
16
|
*/
|
|
@@ -44,97 +34,21 @@ interface MimicLayerOptions<TSchema extends Primitive.AnyPrimitive> {
|
|
|
44
34
|
* When provided, enables presence features on WebSocket connections.
|
|
45
35
|
*/
|
|
46
36
|
readonly presence?: Presence.AnyPresence;
|
|
37
|
+
/**
|
|
38
|
+
* Initial state for new documents.
|
|
39
|
+
* Used when a document is created and no existing state is found in storage.
|
|
40
|
+
*
|
|
41
|
+
* Type-safe: required fields (without defaults) must be provided,
|
|
42
|
+
* while optional fields and fields with defaults can be omitted.
|
|
43
|
+
*
|
|
44
|
+
* @default undefined (documents start empty or use schema defaults)
|
|
45
|
+
*/
|
|
46
|
+
readonly initial?: Primitive.InferSetInput<TSchema>;
|
|
47
47
|
}
|
|
48
|
-
/**
|
|
49
|
-
* Create a Mimic WebSocket handler layer.
|
|
50
|
-
*
|
|
51
|
-
* This layer provides a handler function that can be used with any WebSocket server
|
|
52
|
-
* implementation. The handler takes a socket and document ID and manages the
|
|
53
|
-
* document synchronization.
|
|
54
|
-
*
|
|
55
|
-
* By default, uses in-memory storage and no authentication.
|
|
56
|
-
* Override these by providing MimicDataStorage and MimicAuthService layers.
|
|
57
|
-
*
|
|
58
|
-
* @example
|
|
59
|
-
* ```typescript
|
|
60
|
-
* import { MimicServer, MimicAuthService } from "@voidhash/mimic-effect";
|
|
61
|
-
* import { Primitive } from "@voidhash/mimic";
|
|
62
|
-
*
|
|
63
|
-
* const TodoSchema = Primitive.Struct({
|
|
64
|
-
* title: Primitive.String(),
|
|
65
|
-
* completed: Primitive.Boolean(),
|
|
66
|
-
* });
|
|
67
|
-
*
|
|
68
|
-
* // Create the handler layer with defaults
|
|
69
|
-
* const HandlerLayer = MimicServer.layer({
|
|
70
|
-
* basePath: "/mimic/todo",
|
|
71
|
-
* schema: TodoSchema
|
|
72
|
-
* });
|
|
73
|
-
*
|
|
74
|
-
* // Or with custom auth
|
|
75
|
-
* const HandlerLayerWithAuth = MimicServer.layer({
|
|
76
|
-
* basePath: "/mimic/todo",
|
|
77
|
-
* schema: TodoSchema
|
|
78
|
-
* }).pipe(
|
|
79
|
-
* Layer.provideMerge(MimicAuthService.layer({
|
|
80
|
-
* authHandler: (token) => ({ success: true, userId: "user-123" })
|
|
81
|
-
* }))
|
|
82
|
-
* );
|
|
83
|
-
* ```
|
|
84
|
-
*/
|
|
85
|
-
declare const layer: <TSchema extends Primitive.AnyPrimitive>(options: MimicLayerOptions<TSchema>) => Layer.Layer<MimicWebSocketHandler | DocumentManagerTag>;
|
|
86
|
-
/**
|
|
87
|
-
* Create the Mimic server handler layer.
|
|
88
|
-
* This layer provides the WebSocket handler that can be used with any WebSocket server.
|
|
89
|
-
*
|
|
90
|
-
* @example
|
|
91
|
-
* ```typescript
|
|
92
|
-
* import { MimicServer } from "@voidhash/mimic-server-effect";
|
|
93
|
-
* import { SocketServer } from "@effect/platform/SocketServer";
|
|
94
|
-
* import { Primitive } from "@voidhash/mimic";
|
|
95
|
-
*
|
|
96
|
-
* // Define your document schema
|
|
97
|
-
* const TodoSchema = Primitive.Struct({
|
|
98
|
-
* title: Primitive.String(),
|
|
99
|
-
* completed: Primitive.Boolean(),
|
|
100
|
-
* });
|
|
101
|
-
*
|
|
102
|
-
* // Create the server layer
|
|
103
|
-
* const serverLayer = MimicServer.handlerLayer({
|
|
104
|
-
* schema: TodoSchema,
|
|
105
|
-
* });
|
|
106
|
-
*
|
|
107
|
-
* // Run with your socket server
|
|
108
|
-
* Effect.gen(function* () {
|
|
109
|
-
* const handler = yield* MimicServer.MimicWebSocketHandler;
|
|
110
|
-
* const server = yield* SocketServer;
|
|
111
|
-
*
|
|
112
|
-
* yield* server.run((socket) =>
|
|
113
|
-
* // Extract document ID from request and call handler
|
|
114
|
-
* handler(socket, "my-document-id")
|
|
115
|
-
* );
|
|
116
|
-
* }).pipe(
|
|
117
|
-
* Effect.provide(serverLayer),
|
|
118
|
-
* Effect.provide(YourSocketServerLayer),
|
|
119
|
-
* );
|
|
120
|
-
* ```
|
|
121
|
-
*/
|
|
122
|
-
declare const handlerLayer: <TSchema extends Primitive.AnyPrimitive>(options: MimicServerConfigOptions<TSchema>) => Layer.Layer<MimicWebSocketHandler>;
|
|
123
48
|
/**
|
|
124
49
|
* Create the document manager layer.
|
|
125
50
|
*/
|
|
126
51
|
declare const documentManagerLayer: <TSchema extends Primitive.AnyPrimitive>(options: MimicServerConfigOptions<TSchema>) => Layer.Layer<DocumentManagerTag>;
|
|
127
|
-
/**
|
|
128
|
-
* Run a Mimic WebSocket server with the provided handler.
|
|
129
|
-
*
|
|
130
|
-
* This is a helper that:
|
|
131
|
-
* 1. Gets the WebSocket handler from context
|
|
132
|
-
* 2. Runs the socket server with the handler
|
|
133
|
-
*
|
|
134
|
-
* Note: The document ID extraction from socket is implementation-specific.
|
|
135
|
-
* You may need to customize this based on your socket server.
|
|
136
|
-
*/
|
|
137
|
-
declare const run: (extractDocumentId: (socket: Socket.Socket) => Effect.Effect<string>) => Effect.Effect<void, _effect_platform_SocketServer0.SocketServerError, MimicWebSocketHandler | SocketServer>;
|
|
138
52
|
/**
|
|
139
53
|
* Create a Mimic server layer that integrates with HttpLayerRouter.
|
|
140
54
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MimicServer.d.mts","names":[],"sources":["../src/MimicServer.ts"],"sourcesContent":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MimicServer.d.mts","names":[],"sources":["../src/MimicServer.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;UA6BiB,kCAAkC,SAAA,CAAU;;;;;EAA5C,SAAA,QAAA,CAAA,EAKK,SALY;EAAiB;;;EAmB7B,SAAS,MAAA,EAVZ,OAUY;EAUc;;;AAO7C;EAAqD,SAAU,qBAAA,CAAA,EAAA,MAAA;EACf;;;;EAClC,SAAA,QAAA,CAAA,EAnBQ,QAAA,CAAS,WAmBjB;EAyGD;;;;;;;;;EAMV,SAAA,OAAA,CAAA,EAxHkB,SAAA,CAAU,aAwH5B,CAxH0C,OAwH1C,CAAA;;;;;cAjHU,uCAAwC,SAAA,CAAU,uBACpD,yBAAqC,aAC7C,KAAA,CAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAyGF,uCAAwC,SAAA,CAAU,uBACpD,kBAAkB;;uBAEJ,KAAA,CAAM,MAAM;;0BAET,KAAA,CAAM,MAAM;MACrC,KAAA,CAAA,oBAAA,eAAA,CAAA"}
|
package/dist/MimicServer.mjs
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import { __export } from "./_virtual/rolldown_runtime.mjs";
|
|
2
|
-
import { MimicServerConfigTag, layer
|
|
3
|
-
import { DocumentManagerTag, layer as layer$
|
|
2
|
+
import { MimicServerConfigTag, layer } from "./MimicConfig.mjs";
|
|
3
|
+
import { DocumentManagerTag, layer as layer$1 } from "./DocumentManager.mjs";
|
|
4
4
|
import { MimicAuthServiceTag } from "./MimicAuthService.mjs";
|
|
5
|
-
import { PresenceManagerTag, layer as layer$
|
|
6
|
-
import { extractDocumentId, handleConnection
|
|
5
|
+
import { PresenceManagerTag, layer as layer$2 } from "./PresenceManager.mjs";
|
|
6
|
+
import { extractDocumentId, handleConnection } from "./WebSocketHandler.mjs";
|
|
7
7
|
import { layerDefault } from "./storage/InMemoryDataStorage.mjs";
|
|
8
8
|
import { layerDefault as layerDefault$1 } from "./auth/NoAuth.mjs";
|
|
9
9
|
import * as Effect from "effect/Effect";
|
|
10
10
|
import * as Layer from "effect/Layer";
|
|
11
|
-
import * as Context from "effect/Context";
|
|
12
|
-
import { SocketServer } from "@effect/platform/SocketServer";
|
|
13
11
|
import { HttpLayerRouter, HttpServerRequest, HttpServerResponse } from "@effect/platform";
|
|
14
12
|
|
|
15
13
|
//#region src/MimicServer.ts
|
|
@@ -18,119 +16,13 @@ import { HttpLayerRouter, HttpServerRequest, HttpServerResponse } from "@effect/
|
|
|
18
16
|
* Mimic server layer composition.
|
|
19
17
|
*/
|
|
20
18
|
var MimicServer_exports = /* @__PURE__ */ __export({
|
|
21
|
-
MimicWebSocketHandler: () => MimicWebSocketHandler,
|
|
22
19
|
documentManagerLayer: () => documentManagerLayer,
|
|
23
|
-
|
|
24
|
-
layer: () => layer,
|
|
25
|
-
layerHttpLayerRouter: () => layerHttpLayerRouter,
|
|
26
|
-
run: () => run
|
|
20
|
+
layerHttpLayerRouter: () => layerHttpLayerRouter
|
|
27
21
|
});
|
|
28
22
|
/**
|
|
29
|
-
* Tag for the WebSocket handler function.
|
|
30
|
-
*/
|
|
31
|
-
var MimicWebSocketHandler = class extends Context.Tag("@voidhash/mimic-server-effect/MimicWebSocketHandler")() {};
|
|
32
|
-
/**
|
|
33
|
-
* Create a Mimic WebSocket handler layer.
|
|
34
|
-
*
|
|
35
|
-
* This layer provides a handler function that can be used with any WebSocket server
|
|
36
|
-
* implementation. The handler takes a socket and document ID and manages the
|
|
37
|
-
* document synchronization.
|
|
38
|
-
*
|
|
39
|
-
* By default, uses in-memory storage and no authentication.
|
|
40
|
-
* Override these by providing MimicDataStorage and MimicAuthService layers.
|
|
41
|
-
*
|
|
42
|
-
* @example
|
|
43
|
-
* ```typescript
|
|
44
|
-
* import { MimicServer, MimicAuthService } from "@voidhash/mimic-effect";
|
|
45
|
-
* import { Primitive } from "@voidhash/mimic";
|
|
46
|
-
*
|
|
47
|
-
* const TodoSchema = Primitive.Struct({
|
|
48
|
-
* title: Primitive.String(),
|
|
49
|
-
* completed: Primitive.Boolean(),
|
|
50
|
-
* });
|
|
51
|
-
*
|
|
52
|
-
* // Create the handler layer with defaults
|
|
53
|
-
* const HandlerLayer = MimicServer.layer({
|
|
54
|
-
* basePath: "/mimic/todo",
|
|
55
|
-
* schema: TodoSchema
|
|
56
|
-
* });
|
|
57
|
-
*
|
|
58
|
-
* // Or with custom auth
|
|
59
|
-
* const HandlerLayerWithAuth = MimicServer.layer({
|
|
60
|
-
* basePath: "/mimic/todo",
|
|
61
|
-
* schema: TodoSchema
|
|
62
|
-
* }).pipe(
|
|
63
|
-
* Layer.provideMerge(MimicAuthService.layer({
|
|
64
|
-
* authHandler: (token) => ({ success: true, userId: "user-123" })
|
|
65
|
-
* }))
|
|
66
|
-
* );
|
|
67
|
-
* ```
|
|
68
|
-
*/
|
|
69
|
-
const layer = (options) => {
|
|
70
|
-
const configLayer = layer$1({
|
|
71
|
-
schema: options.schema,
|
|
72
|
-
maxTransactionHistory: options.maxTransactionHistory,
|
|
73
|
-
presence: options.presence
|
|
74
|
-
});
|
|
75
|
-
return Layer.merge(Layer.effect(MimicWebSocketHandler, makeHandler).pipe(Layer.provide(layer$2), Layer.provide(layer$3), Layer.provide(configLayer)), layer$2.pipe(Layer.provide(configLayer))).pipe(Layer.provide(layerDefault), Layer.provide(layerDefault$1));
|
|
76
|
-
};
|
|
77
|
-
/**
|
|
78
|
-
* Create the Mimic server handler layer.
|
|
79
|
-
* This layer provides the WebSocket handler that can be used with any WebSocket server.
|
|
80
|
-
*
|
|
81
|
-
* @example
|
|
82
|
-
* ```typescript
|
|
83
|
-
* import { MimicServer } from "@voidhash/mimic-server-effect";
|
|
84
|
-
* import { SocketServer } from "@effect/platform/SocketServer";
|
|
85
|
-
* import { Primitive } from "@voidhash/mimic";
|
|
86
|
-
*
|
|
87
|
-
* // Define your document schema
|
|
88
|
-
* const TodoSchema = Primitive.Struct({
|
|
89
|
-
* title: Primitive.String(),
|
|
90
|
-
* completed: Primitive.Boolean(),
|
|
91
|
-
* });
|
|
92
|
-
*
|
|
93
|
-
* // Create the server layer
|
|
94
|
-
* const serverLayer = MimicServer.handlerLayer({
|
|
95
|
-
* schema: TodoSchema,
|
|
96
|
-
* });
|
|
97
|
-
*
|
|
98
|
-
* // Run with your socket server
|
|
99
|
-
* Effect.gen(function* () {
|
|
100
|
-
* const handler = yield* MimicServer.MimicWebSocketHandler;
|
|
101
|
-
* const server = yield* SocketServer;
|
|
102
|
-
*
|
|
103
|
-
* yield* server.run((socket) =>
|
|
104
|
-
* // Extract document ID from request and call handler
|
|
105
|
-
* handler(socket, "my-document-id")
|
|
106
|
-
* );
|
|
107
|
-
* }).pipe(
|
|
108
|
-
* Effect.provide(serverLayer),
|
|
109
|
-
* Effect.provide(YourSocketServerLayer),
|
|
110
|
-
* );
|
|
111
|
-
* ```
|
|
112
|
-
*/
|
|
113
|
-
const handlerLayer = (options) => Layer.effect(MimicWebSocketHandler, makeHandler).pipe(Layer.provide(layer$2), Layer.provide(layer$3), Layer.provide(layer$1(options)), Layer.provide(layerDefault), Layer.provide(layerDefault$1));
|
|
114
|
-
/**
|
|
115
23
|
* Create the document manager layer.
|
|
116
24
|
*/
|
|
117
|
-
const documentManagerLayer = (options) => layer$
|
|
118
|
-
/**
|
|
119
|
-
* Run a Mimic WebSocket server with the provided handler.
|
|
120
|
-
*
|
|
121
|
-
* This is a helper that:
|
|
122
|
-
* 1. Gets the WebSocket handler from context
|
|
123
|
-
* 2. Runs the socket server with the handler
|
|
124
|
-
*
|
|
125
|
-
* Note: The document ID extraction from socket is implementation-specific.
|
|
126
|
-
* You may need to customize this based on your socket server.
|
|
127
|
-
*/
|
|
128
|
-
const run = (extractDocumentId$1) => Effect.gen(function* () {
|
|
129
|
-
const handler = yield* MimicWebSocketHandler;
|
|
130
|
-
yield* (yield* SocketServer).run((socket) => Effect.gen(function* () {
|
|
131
|
-
yield* handler(socket, yield* extractDocumentId$1(socket));
|
|
132
|
-
}).pipe(Effect.catchAll((error) => Effect.logError("Connection error", error))));
|
|
133
|
-
});
|
|
25
|
+
const documentManagerLayer = (options) => layer$1.pipe(Layer.provide(layer(options)), Layer.provide(layerDefault), Layer.provide(layerDefault$1));
|
|
134
26
|
/**
|
|
135
27
|
* Create the HTTP handler effect for WebSocket upgrade.
|
|
136
28
|
* This handler:
|
|
@@ -203,10 +95,11 @@ const makeMimicHandler = Effect.gen(function* () {
|
|
|
203
95
|
const layerHttpLayerRouter = (options) => {
|
|
204
96
|
var _options$basePath, _options$authLayer, _options$storageLayer;
|
|
205
97
|
const wsPath = `${(_options$basePath = options.basePath) !== null && _options$basePath !== void 0 ? _options$basePath : "/mimic"}/doc/*`;
|
|
206
|
-
const configLayer = layer
|
|
98
|
+
const configLayer = layer({
|
|
207
99
|
schema: options.schema,
|
|
208
100
|
maxTransactionHistory: options.maxTransactionHistory,
|
|
209
|
-
presence: options.presence
|
|
101
|
+
presence: options.presence,
|
|
102
|
+
initial: options.initial
|
|
210
103
|
});
|
|
211
104
|
const authLayer = (_options$authLayer = options.authLayer) !== null && _options$authLayer !== void 0 ? _options$authLayer : layerDefault$1;
|
|
212
105
|
const storageLayer = (_options$storageLayer = options.storageLayer) !== null && _options$storageLayer !== void 0 ? _options$storageLayer : layerDefault;
|
|
@@ -215,7 +108,7 @@ const layerHttpLayerRouter = (options) => {
|
|
|
215
108
|
const handler = yield* makeMimicHandler;
|
|
216
109
|
yield* router.add("GET", wsPath, handler);
|
|
217
110
|
});
|
|
218
|
-
return Layer.scopedDiscard(registerRoute).pipe(Layer.provide(layer$
|
|
111
|
+
return Layer.scopedDiscard(registerRoute).pipe(Layer.provide(layer$1), Layer.provide(layer$2), Layer.provide(configLayer), Layer.provide(storageLayer), Layer.provide(authLayer));
|
|
219
112
|
};
|
|
220
113
|
|
|
221
114
|
//#endregion
|
package/dist/MimicServer.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MimicServer.mjs","names":["MimicConfig.layer","WebSocketHandler.makeHandler","DocumentManager.layer","PresenceManager.layer","InMemoryDataStorage.layerDefault","NoAuth.layerDefault","extractDocumentId","MimicConfig.MimicServerConfigTag","DocumentManager.DocumentManagerTag","PresenceManager.PresenceManagerTag","WebSocketHandler.extractDocumentId","WebSocketHandler.handleConnection","wsPath: PathInput"],"sources":["../src/MimicServer.ts"],"sourcesContent":["/**\n * @since 0.0.1\n * Mimic server layer composition.\n */\nimport * as Effect from \"effect/Effect\";\nimport * as Layer from \"effect/Layer\";\nimport * as Context from \"effect/Context\";\nimport type * as Socket from \"@effect/platform/Socket\";\nimport { SocketServer } from \"@effect/platform/SocketServer\";\nimport type { Primitive, Presence } from \"@voidhash/mimic\";\n\nimport * as DocumentManager from \"./DocumentManager.js\";\nimport * as WebSocketHandler from \"./WebSocketHandler.js\";\nimport * as MimicConfig from \"./MimicConfig.js\";\nimport { MimicDataStorageTag } from \"./MimicDataStorage.js\";\nimport { MimicAuthServiceTag } from \"./MimicAuthService.js\";\nimport * as PresenceManager from \"./PresenceManager.js\";\nimport * as InMemoryDataStorage from \"./storage/InMemoryDataStorage.js\";\nimport * as NoAuth from \"./auth/NoAuth.js\";\nimport { HttpLayerRouter, HttpServerRequest, HttpServerResponse } from \"@effect/platform\";\nimport { PathInput } from \"@effect/platform/HttpRouter\";\n\n// =============================================================================\n// Handler Tag\n// =============================================================================\n\n/**\n * Tag for the WebSocket handler function.\n */\nexport class MimicWebSocketHandler extends Context.Tag(\n \"@voidhash/mimic-server-effect/MimicWebSocketHandler\"\n)<\n MimicWebSocketHandler,\n (socket: Socket.Socket, documentId: string) => Effect.Effect<void, unknown>\n>() {}\n\n// =============================================================================\n// Layer Composition Options\n// =============================================================================\n\n/**\n * Options for creating a Mimic server layer.\n */\nexport interface MimicLayerOptions<TSchema extends Primitive.AnyPrimitive> {\n /**\n * Base path for document routes (used for path matching).\n * @example \"/mimic/todo\" - documents accessed at \"/mimic/todo/:documentId\"\n */\n readonly basePath?: PathInput;\n /**\n * The schema defining the document structure.\n */\n readonly schema: TSchema;\n /**\n * Maximum number of processed transaction IDs to track for deduplication.\n * @default 1000\n */\n readonly maxTransactionHistory?: number;\n /**\n * Optional presence schema for ephemeral per-user data.\n * When provided, enables presence features on WebSocket connections.\n */\n readonly presence?: Presence.AnyPresence;\n}\n\n// =============================================================================\n// Layer Composition\n// =============================================================================\n\n/**\n * Create a Mimic WebSocket handler layer.\n * \n * This layer provides a handler function that can be used with any WebSocket server\n * implementation. The handler takes a socket and document ID and manages the\n * document synchronization.\n * \n * By default, uses in-memory storage and no authentication.\n * Override these by providing MimicDataStorage and MimicAuthService layers.\n * \n * @example\n * ```typescript\n * import { MimicServer, MimicAuthService } from \"@voidhash/mimic-effect\";\n * import { Primitive } from \"@voidhash/mimic\";\n * \n * const TodoSchema = Primitive.Struct({\n * title: Primitive.String(),\n * completed: Primitive.Boolean(),\n * });\n * \n * // Create the handler layer with defaults\n * const HandlerLayer = MimicServer.layer({\n * basePath: \"/mimic/todo\",\n * schema: TodoSchema\n * });\n * \n * // Or with custom auth\n * const HandlerLayerWithAuth = MimicServer.layer({\n * basePath: \"/mimic/todo\",\n * schema: TodoSchema\n * }).pipe(\n * Layer.provideMerge(MimicAuthService.layer({\n * authHandler: (token) => ({ success: true, userId: \"user-123\" })\n * }))\n * );\n * ```\n */\nexport const layer = <TSchema extends Primitive.AnyPrimitive>(\n options: MimicLayerOptions<TSchema>\n): Layer.Layer<MimicWebSocketHandler | DocumentManager.DocumentManagerTag> => {\n const configLayer = MimicConfig.layer({\n schema: options.schema,\n maxTransactionHistory: options.maxTransactionHistory,\n presence: options.presence,\n });\n\n return Layer.merge(\n // Handler layer\n Layer.effect(MimicWebSocketHandler, WebSocketHandler.makeHandler).pipe(\n Layer.provide(DocumentManager.layer),\n Layer.provide(PresenceManager.layer),\n Layer.provide(configLayer)\n ),\n // Document manager layer\n DocumentManager.layer.pipe(Layer.provide(configLayer))\n ).pipe(\n // Provide defaults if not overridden\n Layer.provide(InMemoryDataStorage.layerDefault),\n Layer.provide(NoAuth.layerDefault)\n );\n};\n\n/**\n * Create the Mimic server handler layer.\n * This layer provides the WebSocket handler that can be used with any WebSocket server.\n *\n * @example\n * ```typescript\n * import { MimicServer } from \"@voidhash/mimic-server-effect\";\n * import { SocketServer } from \"@effect/platform/SocketServer\";\n * import { Primitive } from \"@voidhash/mimic\";\n *\n * // Define your document schema\n * const TodoSchema = Primitive.Struct({\n * title: Primitive.String(),\n * completed: Primitive.Boolean(),\n * });\n *\n * // Create the server layer\n * const serverLayer = MimicServer.handlerLayer({\n * schema: TodoSchema,\n * });\n *\n * // Run with your socket server\n * Effect.gen(function* () {\n * const handler = yield* MimicServer.MimicWebSocketHandler;\n * const server = yield* SocketServer;\n *\n * yield* server.run((socket) =>\n * // Extract document ID from request and call handler\n * handler(socket, \"my-document-id\")\n * );\n * }).pipe(\n * Effect.provide(serverLayer),\n * Effect.provide(YourSocketServerLayer),\n * );\n * ```\n */\nexport const handlerLayer = <TSchema extends Primitive.AnyPrimitive>(\n options: MimicConfig.MimicServerConfigOptions<TSchema>\n): Layer.Layer<MimicWebSocketHandler> =>\n Layer.effect(MimicWebSocketHandler, WebSocketHandler.makeHandler).pipe(\n Layer.provide(DocumentManager.layer),\n Layer.provide(PresenceManager.layer),\n Layer.provide(MimicConfig.layer(options)),\n // Provide defaults\n Layer.provide(InMemoryDataStorage.layerDefault),\n Layer.provide(NoAuth.layerDefault)\n );\n\n/**\n * Create the document manager layer.\n */\nexport const documentManagerLayer = <TSchema extends Primitive.AnyPrimitive>(\n options: MimicConfig.MimicServerConfigOptions<TSchema>\n): Layer.Layer<DocumentManager.DocumentManagerTag> =>\n DocumentManager.layer.pipe(\n Layer.provide(MimicConfig.layer(options)),\n // Provide defaults\n Layer.provide(InMemoryDataStorage.layerDefault),\n Layer.provide(NoAuth.layerDefault)\n );\n\n// =============================================================================\n// Convenience Functions\n// =============================================================================\n\n/**\n * Run a Mimic WebSocket server with the provided handler.\n *\n * This is a helper that:\n * 1. Gets the WebSocket handler from context\n * 2. Runs the socket server with the handler\n *\n * Note: The document ID extraction from socket is implementation-specific.\n * You may need to customize this based on your socket server.\n */\nexport const run = (\n extractDocumentId: (socket: Socket.Socket) => Effect.Effect<string>\n) =>\n Effect.gen(function* () {\n const handler = yield* MimicWebSocketHandler;\n const server = yield* SocketServer;\n\n yield* server.run((socket) =>\n Effect.gen(function* () {\n const documentId = yield* extractDocumentId(socket);\n yield* handler(socket, documentId);\n }).pipe(\n Effect.catchAll((error) =>\n Effect.logError(\"Connection error\", error)\n )\n )\n );\n });\n\n\n/**\n * Create the HTTP handler effect for WebSocket upgrade.\n * This handler:\n * 1. Extracts the document ID from the URL path\n * 2. Upgrades the HTTP connection to WebSocket\n * 3. Delegates to the WebSocketHandler for document sync\n */\nconst makeMimicHandler = Effect.gen(function* () {\n const config = yield* MimicConfig.MimicServerConfigTag;\n const authService = yield* MimicAuthServiceTag;\n const documentManager = yield* DocumentManager.DocumentManagerTag;\n const presenceManager = yield* PresenceManager.PresenceManagerTag;\n\n return Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n\n // Extract document ID from the URL path\n // Expected format: /basePath/doc/{documentId}\n const documentId = yield* WebSocketHandler.extractDocumentId(request.url);\n\n // Upgrade to WebSocket\n const socket = yield* request.upgrade;\n\n // Handle the WebSocket connection\n yield* WebSocketHandler.handleConnection(socket, request.url).pipe(\n Effect.provideService(MimicConfig.MimicServerConfigTag, config),\n Effect.provideService(MimicAuthServiceTag, authService),\n Effect.provideService(DocumentManager.DocumentManagerTag, documentManager),\n Effect.provideService(PresenceManager.PresenceManagerTag, presenceManager),\n Effect.scoped,\n Effect.catchAll((error) =>\n Effect.logError(\"WebSocket connection error\", error)\n )\n );\n\n // Return empty response - the WebSocket upgrade handles the connection\n return HttpServerResponse.empty();\n }).pipe(\n Effect.catchAll((error) =>\n Effect.gen(function* () {\n yield* Effect.logWarning(\"WebSocket upgrade failed\", error);\n return HttpServerResponse.text(\"WebSocket upgrade failed\", {\n status: 400,\n });\n })\n )\n );\n});\n\n\n\n/**\n * Create a Mimic server layer that integrates with HttpLayerRouter.\n *\n * This function creates a layer that:\n * 1. Registers a WebSocket route at the specified base path\n * 2. Handles WebSocket upgrades for document sync\n * 3. Provides all required dependencies (config, auth, storage, document manager)\n *\n * By default, uses in-memory storage and no authentication.\n * To override these defaults, provide custom layers before the defaults:\n *\n * @example\n * ```typescript\n * import { MimicServer, MimicAuthService } from \"@voidhash/mimic-effect\";\n * import { HttpLayerRouter } from \"@effect/platform\";\n * import { Primitive } from \"@voidhash/mimic\";\n *\n * const TodoSchema = Primitive.Struct({\n * title: Primitive.String(),\n * completed: Primitive.Boolean(),\n * });\n *\n * // Create the Mimic route layer with defaults\n * const MimicRoute = MimicServer.layerHttpLayerRouter({\n * basePath: \"/mimic/todo\",\n * schema: TodoSchema\n * });\n *\n * // Or with custom auth - use Layer.provide to inject before defaults\n * const MimicRouteWithAuth = MimicServer.layerHttpLayerRouter({\n * basePath: \"/mimic/todo\",\n * schema: TodoSchema,\n * authLayer: MimicAuthService.layer({\n * authHandler: (token) => ({ success: true, userId: token })\n * })\n * });\n *\n * // Merge with other routes and serve\n * const AllRoutes = Layer.mergeAll(MimicRoute, OtherRoutes);\n * HttpLayerRouter.serve(AllRoutes).pipe(\n * Layer.provide(BunHttpServer.layer({ port: 3000 })),\n * Layer.launch,\n * BunRuntime.runMain\n * );\n * ```\n */\nexport const layerHttpLayerRouter = <TSchema extends Primitive.AnyPrimitive>(\n options: MimicLayerOptions<TSchema> & {\n /** Custom auth layer. Defaults to NoAuth (all connections allowed). */\n readonly authLayer?: Layer.Layer<MimicAuthServiceTag>;\n /** Custom storage layer. Defaults to InMemoryDataStorage. */\n readonly storageLayer?: Layer.Layer<MimicDataStorageTag>;\n }\n) => {\n // Build the base path pattern for WebSocket routes\n // Append /doc/* to match /basePath/doc/{documentId}\n const basePath = options.basePath ?? \"/mimic\";\n const wsPath: PathInput = `${basePath}/doc/*` as PathInput;\n\n // Create the config layer\n const configLayer = MimicConfig.layer({\n schema: options.schema,\n maxTransactionHistory: options.maxTransactionHistory,\n presence: options.presence,\n });\n\n // Use provided layers or defaults\n const authLayer = options.authLayer ?? NoAuth.layerDefault;\n const storageLayer = options.storageLayer ?? InMemoryDataStorage.layerDefault;\n\n // Create the route registration effect\n const registerRoute = Effect.gen(function* () {\n const router = yield* HttpLayerRouter.HttpRouter;\n const handler = yield* makeMimicHandler;\n yield* router.add(\"GET\", wsPath, handler);\n });\n\n // Build the layer with all dependencies\n return Layer.scopedDiscard(registerRoute).pipe(\n Layer.provide(DocumentManager.layer),\n Layer.provide(PresenceManager.layer),\n Layer.provide(configLayer),\n Layer.provide(storageLayer),\n Layer.provide(authLayer)\n );\n};"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,IAAa,wBAAb,cAA2C,QAAQ,IACjD,sDACD,EAGE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwEJ,MAAa,SACX,YAC4E;CAC5E,MAAM,cAAcA,QAAkB;EACpC,QAAQ,QAAQ;EAChB,uBAAuB,QAAQ;EAC/B,UAAU,QAAQ;EACnB,CAAC;AAEF,QAAO,MAAM,MAEX,MAAM,OAAO,uBAAuBC,YAA6B,CAAC,KAChE,MAAM,QAAQC,QAAsB,EACpC,MAAM,QAAQC,QAAsB,EACpC,MAAM,QAAQ,YAAY,CAC3B,UAEqB,KAAK,MAAM,QAAQ,YAAY,CAAC,CACvD,CAAC,KAEA,MAAM,QAAQC,aAAiC,EAC/C,MAAM,QAAQC,eAAoB,CACnC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCH,MAAa,gBACX,YAEA,MAAM,OAAO,uBAAuBJ,YAA6B,CAAC,KAChE,MAAM,QAAQC,QAAsB,EACpC,MAAM,QAAQC,QAAsB,EACpC,MAAM,QAAQH,QAAkB,QAAQ,CAAC,EAEzC,MAAM,QAAQI,aAAiC,EAC/C,MAAM,QAAQC,eAAoB,CACnC;;;;AAKH,MAAa,wBACX,oBAEsB,KACpB,MAAM,QAAQL,QAAkB,QAAQ,CAAC,EAEzC,MAAM,QAAQI,aAAiC,EAC/C,MAAM,QAAQC,eAAoB,CACnC;;;;;;;;;;;AAgBH,MAAa,OACX,wBAEA,OAAO,IAAI,aAAa;CACtB,MAAM,UAAU,OAAO;AAGvB,SAFe,OAAO,cAER,KAAK,WACjB,OAAO,IAAI,aAAa;AAEtB,SAAO,QAAQ,QADI,OAAOC,oBAAkB,OAAO,CACjB;GAClC,CAAC,KACD,OAAO,UAAU,UACf,OAAO,SAAS,oBAAoB,MAAM,CAC3C,CACF,CACF;EACD;;;;;;;;AAUJ,MAAM,mBAAmB,OAAO,IAAI,aAAa;CAC/C,MAAM,SAAS,OAAOC;CACtB,MAAM,cAAc,OAAO;CAC3B,MAAM,kBAAkB,OAAOC;CAC/B,MAAM,kBAAkB,OAAOC;AAE/B,QAAO,OAAO,IAAI,aAAa;EAC7B,MAAM,UAAU,OAAO,kBAAkB;AAItB,SAAOC,kBAAmC,QAAQ,IAAI;EAGzE,MAAM,SAAS,OAAO,QAAQ;AAG9B,SAAOC,iBAAkC,QAAQ,QAAQ,IAAI,CAAC,KAC5D,OAAO,eAAeJ,sBAAkC,OAAO,EAC/D,OAAO,eAAe,qBAAqB,YAAY,EACvD,OAAO,eAAeC,oBAAoC,gBAAgB,EAC1E,OAAO,eAAeC,oBAAoC,gBAAgB,EAC1E,OAAO,QACP,OAAO,UAAU,UACf,OAAO,SAAS,8BAA8B,MAAM,CACrD,CACF;AAGD,SAAO,mBAAmB,OAAO;GACjC,CAAC,KACD,OAAO,UAAU,UACf,OAAO,IAAI,aAAa;AACtB,SAAO,OAAO,WAAW,4BAA4B,MAAM;AAC3D,SAAO,mBAAmB,KAAK,4BAA4B,EACzD,QAAQ,KACT,CAAC;GACF,CACH,CACF;EACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkDF,MAAa,wBACX,YAMG;;CAIH,MAAMG,SAAoB,wBADT,QAAQ,yEAAY,SACC;CAGtC,MAAM,cAAcZ,QAAkB;EACpC,QAAQ,QAAQ;EAChB,uBAAuB,QAAQ;EAC/B,UAAU,QAAQ;EACnB,CAAC;CAGF,MAAM,kCAAY,QAAQ,4EAAaK;CACvC,MAAM,wCAAe,QAAQ,qFAAgBD;CAG7C,MAAM,gBAAgB,OAAO,IAAI,aAAa;EAC5C,MAAM,SAAS,OAAO,gBAAgB;EACtC,MAAM,UAAU,OAAO;AACvB,SAAO,OAAO,IAAI,OAAO,QAAQ,QAAQ;GACzC;AAGF,QAAO,MAAM,cAAc,cAAc,CAAC,KACxC,MAAM,QAAQF,QAAsB,EACpC,MAAM,QAAQC,QAAsB,EACpC,MAAM,QAAQ,YAAY,EAC1B,MAAM,QAAQ,aAAa,EAC3B,MAAM,QAAQ,UAAU,CACzB"}
|
|
1
|
+
{"version":3,"file":"MimicServer.mjs","names":["MimicConfig.layer","InMemoryDataStorage.layerDefault","NoAuth.layerDefault","MimicConfig.MimicServerConfigTag","DocumentManager.DocumentManagerTag","PresenceManager.PresenceManagerTag","WebSocketHandler.extractDocumentId","WebSocketHandler.handleConnection","wsPath: PathInput","DocumentManager.layer","PresenceManager.layer"],"sources":["../src/MimicServer.ts"],"sourcesContent":["/**\n * @since 0.0.1\n * Mimic server layer composition.\n */\nimport * as Effect from \"effect/Effect\";\nimport * as Layer from \"effect/Layer\";\nimport * as Context from \"effect/Context\";\nimport type * as Socket from \"@effect/platform/Socket\";\nimport { SocketServer } from \"@effect/platform/SocketServer\";\nimport type { Primitive, Presence } from \"@voidhash/mimic\";\n\nimport * as DocumentManager from \"./DocumentManager.js\";\nimport * as WebSocketHandler from \"./WebSocketHandler.js\";\nimport * as MimicConfig from \"./MimicConfig.js\";\nimport { MimicDataStorageTag } from \"./MimicDataStorage.js\";\nimport { MimicAuthServiceTag } from \"./MimicAuthService.js\";\nimport * as PresenceManager from \"./PresenceManager.js\";\nimport * as InMemoryDataStorage from \"./storage/InMemoryDataStorage.js\";\nimport * as NoAuth from \"./auth/NoAuth.js\";\nimport { HttpLayerRouter, HttpServerRequest, HttpServerResponse } from \"@effect/platform\";\nimport { PathInput } from \"@effect/platform/HttpRouter\";\n\n// =============================================================================\n// Layer Composition Options\n// =============================================================================\n\n/**\n * Options for creating a Mimic server layer.\n */\nexport interface MimicLayerOptions<TSchema extends Primitive.AnyPrimitive> {\n /**\n * Base path for document routes (used for path matching).\n * @example \"/mimic/todo\" - documents accessed at \"/mimic/todo/:documentId\"\n */\n readonly basePath?: PathInput;\n /**\n * The schema defining the document structure.\n */\n readonly schema: TSchema;\n /**\n * Maximum number of processed transaction IDs to track for deduplication.\n * @default 1000\n */\n readonly maxTransactionHistory?: number;\n /**\n * Optional presence schema for ephemeral per-user data.\n * When provided, enables presence features on WebSocket connections.\n */\n readonly presence?: Presence.AnyPresence;\n /**\n * Initial state for new documents.\n * Used when a document is created and no existing state is found in storage.\n *\n * Type-safe: required fields (without defaults) must be provided,\n * while optional fields and fields with defaults can be omitted.\n *\n * @default undefined (documents start empty or use schema defaults)\n */\n readonly initial?: Primitive.InferSetInput<TSchema>;\n}\n\n\n/**\n * Create the document manager layer.\n */\nexport const documentManagerLayer = <TSchema extends Primitive.AnyPrimitive>(\n options: MimicConfig.MimicServerConfigOptions<TSchema>\n): Layer.Layer<DocumentManager.DocumentManagerTag> =>\n DocumentManager.layer.pipe(\n Layer.provide(MimicConfig.layer(options)),\n // Provide defaults\n Layer.provide(InMemoryDataStorage.layerDefault),\n Layer.provide(NoAuth.layerDefault)\n );\n\n/**\n * Create the HTTP handler effect for WebSocket upgrade.\n * This handler:\n * 1. Extracts the document ID from the URL path\n * 2. Upgrades the HTTP connection to WebSocket\n * 3. Delegates to the WebSocketHandler for document sync\n */\nconst makeMimicHandler = Effect.gen(function* () {\n const config = yield* MimicConfig.MimicServerConfigTag;\n const authService = yield* MimicAuthServiceTag;\n const documentManager = yield* DocumentManager.DocumentManagerTag;\n const presenceManager = yield* PresenceManager.PresenceManagerTag;\n\n return Effect.gen(function* () {\n const request = yield* HttpServerRequest.HttpServerRequest;\n\n // Extract document ID from the URL path\n // Expected format: /basePath/doc/{documentId}\n const documentId = yield* WebSocketHandler.extractDocumentId(request.url);\n\n // Upgrade to WebSocket\n const socket = yield* request.upgrade;\n\n // Handle the WebSocket connection\n yield* WebSocketHandler.handleConnection(socket, request.url).pipe(\n Effect.provideService(MimicConfig.MimicServerConfigTag, config),\n Effect.provideService(MimicAuthServiceTag, authService),\n Effect.provideService(DocumentManager.DocumentManagerTag, documentManager),\n Effect.provideService(PresenceManager.PresenceManagerTag, presenceManager),\n Effect.scoped,\n Effect.catchAll((error) =>\n Effect.logError(\"WebSocket connection error\", error)\n )\n );\n\n // Return empty response - the WebSocket upgrade handles the connection\n return HttpServerResponse.empty();\n }).pipe(\n Effect.catchAll((error) =>\n Effect.gen(function* () {\n yield* Effect.logWarning(\"WebSocket upgrade failed\", error);\n return HttpServerResponse.text(\"WebSocket upgrade failed\", {\n status: 400,\n });\n })\n )\n );\n});\n\n\n\n/**\n * Create a Mimic server layer that integrates with HttpLayerRouter.\n *\n * This function creates a layer that:\n * 1. Registers a WebSocket route at the specified base path\n * 2. Handles WebSocket upgrades for document sync\n * 3. Provides all required dependencies (config, auth, storage, document manager)\n *\n * By default, uses in-memory storage and no authentication.\n * To override these defaults, provide custom layers before the defaults:\n *\n * @example\n * ```typescript\n * import { MimicServer, MimicAuthService } from \"@voidhash/mimic-effect\";\n * import { HttpLayerRouter } from \"@effect/platform\";\n * import { Primitive } from \"@voidhash/mimic\";\n *\n * const TodoSchema = Primitive.Struct({\n * title: Primitive.String(),\n * completed: Primitive.Boolean(),\n * });\n *\n * // Create the Mimic route layer with defaults\n * const MimicRoute = MimicServer.layerHttpLayerRouter({\n * basePath: \"/mimic/todo\",\n * schema: TodoSchema\n * });\n *\n * // Or with custom auth - use Layer.provide to inject before defaults\n * const MimicRouteWithAuth = MimicServer.layerHttpLayerRouter({\n * basePath: \"/mimic/todo\",\n * schema: TodoSchema,\n * authLayer: MimicAuthService.layer({\n * authHandler: (token) => ({ success: true, userId: token })\n * })\n * });\n *\n * // Merge with other routes and serve\n * const AllRoutes = Layer.mergeAll(MimicRoute, OtherRoutes);\n * HttpLayerRouter.serve(AllRoutes).pipe(\n * Layer.provide(BunHttpServer.layer({ port: 3000 })),\n * Layer.launch,\n * BunRuntime.runMain\n * );\n * ```\n */\nexport const layerHttpLayerRouter = <TSchema extends Primitive.AnyPrimitive>(\n options: MimicLayerOptions<TSchema> & {\n /** Custom auth layer. Defaults to NoAuth (all connections allowed). */\n readonly authLayer?: Layer.Layer<MimicAuthServiceTag>;\n /** Custom storage layer. Defaults to InMemoryDataStorage. */\n readonly storageLayer?: Layer.Layer<MimicDataStorageTag>;\n }\n) => {\n // Build the base path pattern for WebSocket routes\n // Append /doc/* to match /basePath/doc/{documentId}\n const basePath = options.basePath ?? \"/mimic\";\n const wsPath: PathInput = `${basePath}/doc/*` as PathInput;\n\n // Create the config layer\n const configLayer = MimicConfig.layer({\n schema: options.schema,\n maxTransactionHistory: options.maxTransactionHistory,\n presence: options.presence,\n initial: options.initial,\n });\n\n // Use provided layers or defaults\n const authLayer = options.authLayer ?? NoAuth.layerDefault;\n const storageLayer = options.storageLayer ?? InMemoryDataStorage.layerDefault;\n\n // Create the route registration effect\n const registerRoute = Effect.gen(function* () {\n const router = yield* HttpLayerRouter.HttpRouter;\n const handler = yield* makeMimicHandler;\n yield* router.add(\"GET\", wsPath, handler);\n });\n\n // Build the layer with all dependencies\n return Layer.scopedDiscard(registerRoute).pipe(\n Layer.provide(DocumentManager.layer),\n Layer.provide(PresenceManager.layer),\n Layer.provide(configLayer),\n Layer.provide(storageLayer),\n Layer.provide(authLayer)\n );\n};"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAiEA,MAAa,wBACX,oBAEsB,KACpB,MAAM,QAAQA,MAAkB,QAAQ,CAAC,EAEzC,MAAM,QAAQC,aAAiC,EAC/C,MAAM,QAAQC,eAAoB,CACnC;;;;;;;;AASH,MAAM,mBAAmB,OAAO,IAAI,aAAa;CAC/C,MAAM,SAAS,OAAOC;CACtB,MAAM,cAAc,OAAO;CAC3B,MAAM,kBAAkB,OAAOC;CAC/B,MAAM,kBAAkB,OAAOC;AAE/B,QAAO,OAAO,IAAI,aAAa;EAC7B,MAAM,UAAU,OAAO,kBAAkB;AAItB,SAAOC,kBAAmC,QAAQ,IAAI;EAGzE,MAAM,SAAS,OAAO,QAAQ;AAG9B,SAAOC,iBAAkC,QAAQ,QAAQ,IAAI,CAAC,KAC5D,OAAO,eAAeJ,sBAAkC,OAAO,EAC/D,OAAO,eAAe,qBAAqB,YAAY,EACvD,OAAO,eAAeC,oBAAoC,gBAAgB,EAC1E,OAAO,eAAeC,oBAAoC,gBAAgB,EAC1E,OAAO,QACP,OAAO,UAAU,UACf,OAAO,SAAS,8BAA8B,MAAM,CACrD,CACF;AAGD,SAAO,mBAAmB,OAAO;GACjC,CAAC,KACD,OAAO,UAAU,UACf,OAAO,IAAI,aAAa;AACtB,SAAO,OAAO,WAAW,4BAA4B,MAAM;AAC3D,SAAO,mBAAmB,KAAK,4BAA4B,EACzD,QAAQ,KACT,CAAC;GACF,CACH,CACF;EACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkDF,MAAa,wBACX,YAMG;;CAIH,MAAMG,SAAoB,wBADT,QAAQ,yEAAY,SACC;CAGtC,MAAM,cAAcR,MAAkB;EACpC,QAAQ,QAAQ;EAChB,uBAAuB,QAAQ;EAC/B,UAAU,QAAQ;EAClB,SAAS,QAAQ;EAClB,CAAC;CAGF,MAAM,kCAAY,QAAQ,4EAAaE;CACvC,MAAM,wCAAe,QAAQ,qFAAgBD;CAG7C,MAAM,gBAAgB,OAAO,IAAI,aAAa;EAC5C,MAAM,SAAS,OAAO,gBAAgB;EACtC,MAAM,UAAU,OAAO;AACvB,SAAO,OAAO,IAAI,OAAO,QAAQ,QAAQ;GACzC;AAGF,QAAO,MAAM,cAAc,cAAc,CAAC,KACxC,MAAM,QAAQQ,QAAsB,EACpC,MAAM,QAAQC,QAAsB,EACpC,MAAM,QAAQ,YAAY,EAC1B,MAAM,QAAQ,aAAa,EAC3B,MAAM,QAAQ,UAAU,CACzB"}
|
package/dist/PresenceManager.cjs
CHANGED
|
@@ -3,14 +3,14 @@ let effect_Effect = require("effect/Effect");
|
|
|
3
3
|
effect_Effect = require_rolldown_runtime.__toESM(effect_Effect);
|
|
4
4
|
let effect_Layer = require("effect/Layer");
|
|
5
5
|
effect_Layer = require_rolldown_runtime.__toESM(effect_Layer);
|
|
6
|
-
let effect_Context = require("effect/Context");
|
|
7
|
-
effect_Context = require_rolldown_runtime.__toESM(effect_Context);
|
|
8
6
|
let effect_PubSub = require("effect/PubSub");
|
|
9
7
|
effect_PubSub = require_rolldown_runtime.__toESM(effect_PubSub);
|
|
10
8
|
let effect_Ref = require("effect/Ref");
|
|
11
9
|
effect_Ref = require_rolldown_runtime.__toESM(effect_Ref);
|
|
12
10
|
let effect_HashMap = require("effect/HashMap");
|
|
13
11
|
effect_HashMap = require_rolldown_runtime.__toESM(effect_HashMap);
|
|
12
|
+
let effect_Context = require("effect/Context");
|
|
13
|
+
effect_Context = require_rolldown_runtime.__toESM(effect_Context);
|
|
14
14
|
let effect_Stream = require("effect/Stream");
|
|
15
15
|
effect_Stream = require_rolldown_runtime.__toESM(effect_Stream);
|
|
16
16
|
|
package/dist/PresenceManager.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { __export } from "./_virtual/rolldown_runtime.mjs";
|
|
2
2
|
import * as Effect from "effect/Effect";
|
|
3
3
|
import * as Layer from "effect/Layer";
|
|
4
|
-
import * as Context from "effect/Context";
|
|
5
4
|
import * as PubSub from "effect/PubSub";
|
|
6
5
|
import * as Ref from "effect/Ref";
|
|
7
6
|
import * as HashMap from "effect/HashMap";
|
|
7
|
+
import * as Context from "effect/Context";
|
|
8
8
|
import * as Stream from "effect/Stream";
|
|
9
9
|
|
|
10
10
|
//#region src/PresenceManager.ts
|
|
@@ -11,9 +11,9 @@ let effect_Stream = require("effect/Stream");
|
|
|
11
11
|
effect_Stream = require_rolldown_runtime.__toESM(effect_Stream);
|
|
12
12
|
let effect_Duration = require("effect/Duration");
|
|
13
13
|
effect_Duration = require_rolldown_runtime.__toESM(effect_Duration);
|
|
14
|
+
let _voidhash_mimic = require("@voidhash/mimic");
|
|
14
15
|
let effect_Fiber = require("effect/Fiber");
|
|
15
16
|
effect_Fiber = require_rolldown_runtime.__toESM(effect_Fiber);
|
|
16
|
-
let _voidhash_mimic = require("@voidhash/mimic");
|
|
17
17
|
|
|
18
18
|
//#region src/WebSocketHandler.ts
|
|
19
19
|
/**
|
|
@@ -362,5 +362,4 @@ Object.defineProperty(exports, 'WebSocketHandler_exports', {
|
|
|
362
362
|
}
|
|
363
363
|
});
|
|
364
364
|
exports.extractDocumentId = extractDocumentId;
|
|
365
|
-
exports.handleConnection = handleConnection;
|
|
366
|
-
exports.makeHandler = makeHandler;
|
|
365
|
+
exports.handleConnection = handleConnection;
|
|
@@ -4,8 +4,8 @@ import { MimicAuthServiceTag } from "./MimicAuthService.cjs";
|
|
|
4
4
|
import { PresenceManagerTag } from "./PresenceManager.cjs";
|
|
5
5
|
import { MessageParseError, MissingDocumentIdError } from "./errors.cjs";
|
|
6
6
|
import * as Effect from "effect/Effect";
|
|
7
|
-
import * as Socket from "@effect/platform/Socket";
|
|
8
7
|
import * as Scope from "effect/Scope";
|
|
8
|
+
import * as Socket from "@effect/platform/Socket";
|
|
9
9
|
|
|
10
10
|
//#region src/WebSocketHandler.d.ts
|
|
11
11
|
declare namespace WebSocketHandler_d_exports {
|
|
@@ -4,8 +4,8 @@ import { MimicAuthServiceTag } from "./MimicAuthService.mjs";
|
|
|
4
4
|
import { PresenceManagerTag } from "./PresenceManager.mjs";
|
|
5
5
|
import { MessageParseError, MissingDocumentIdError } from "./errors.mjs";
|
|
6
6
|
import * as Effect from "effect/Effect";
|
|
7
|
-
import * as Socket from "@effect/platform/Socket";
|
|
8
7
|
import * as Scope from "effect/Scope";
|
|
8
|
+
import * as Socket from "@effect/platform/Socket";
|
|
9
9
|
|
|
10
10
|
//#region src/WebSocketHandler.d.ts
|
|
11
11
|
declare namespace WebSocketHandler_d_exports {
|
|
@@ -8,8 +8,8 @@ import { _objectSpread2 } from "./_virtual/_@oxc-project_runtime@0.103.0/helpers
|
|
|
8
8
|
import * as Effect from "effect/Effect";
|
|
9
9
|
import * as Stream from "effect/Stream";
|
|
10
10
|
import * as Duration from "effect/Duration";
|
|
11
|
-
import * as Fiber from "effect/Fiber";
|
|
12
11
|
import { Presence, Transaction } from "@voidhash/mimic";
|
|
12
|
+
import * as Fiber from "effect/Fiber";
|
|
13
13
|
|
|
14
14
|
//#region src/WebSocketHandler.ts
|
|
15
15
|
/**
|
|
@@ -351,5 +351,5 @@ const handleConnectionWithDocumentId = (socket, documentId) => Effect.gen(functi
|
|
|
351
351
|
});
|
|
352
352
|
|
|
353
353
|
//#endregion
|
|
354
|
-
export { WebSocketHandler_exports, extractDocumentId, handleConnection
|
|
354
|
+
export { WebSocketHandler_exports, extractDocumentId, handleConnection };
|
|
355
355
|
//# sourceMappingURL=WebSocketHandler.mjs.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voidhash/mimic-effect",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -24,11 +24,11 @@
|
|
|
24
24
|
"typescript": "5.8.3",
|
|
25
25
|
"vite-tsconfig-paths": "^5.1.4",
|
|
26
26
|
"vitest": "^3.2.4",
|
|
27
|
-
"@voidhash/tsconfig": "0.0.
|
|
27
|
+
"@voidhash/tsconfig": "0.0.4"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
30
|
"effect": "^3.19.12",
|
|
31
|
-
"@voidhash/mimic": "0.0.
|
|
31
|
+
"@voidhash/mimic": "0.0.4"
|
|
32
32
|
},
|
|
33
33
|
"scripts": {
|
|
34
34
|
"build": "tsdown",
|
package/src/DocumentManager.ts
CHANGED
|
@@ -113,10 +113,10 @@ const makeDocumentManager = Effect.gen(function* () {
|
|
|
113
113
|
() => Effect.succeed(undefined)
|
|
114
114
|
);
|
|
115
115
|
|
|
116
|
-
// Transform loaded state with onLoad hook
|
|
116
|
+
// Transform loaded state with onLoad hook, or use configured initial state for new docs
|
|
117
117
|
const initialState = rawState !== undefined
|
|
118
118
|
? yield* storage.onLoad(rawState)
|
|
119
|
-
:
|
|
119
|
+
: config.initial;
|
|
120
120
|
|
|
121
121
|
// Create PubSub for broadcasting
|
|
122
122
|
const pubsub = yield* PubSub.unbounded<Protocol.ServerBroadcast>();
|