@voidhash/mimic-effect 0.0.2 → 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 +99 -14
- package/dist/DocumentManager.cjs +118 -0
- package/dist/DocumentManager.d.cts +45 -0
- package/dist/DocumentManager.d.cts.map +1 -0
- package/dist/DocumentManager.d.mts +45 -0
- package/dist/DocumentManager.d.mts.map +1 -0
- package/dist/DocumentManager.mjs +105 -0
- package/dist/DocumentManager.mjs.map +1 -0
- package/dist/DocumentProtocol.cjs +94 -0
- package/dist/DocumentProtocol.d.cts +113 -0
- package/dist/DocumentProtocol.d.cts.map +1 -0
- package/dist/DocumentProtocol.d.mts +113 -0
- package/dist/DocumentProtocol.d.mts.map +1 -0
- package/dist/DocumentProtocol.mjs +89 -0
- package/dist/DocumentProtocol.mjs.map +1 -0
- package/dist/MimicAuthService.cjs +55 -0
- package/dist/MimicAuthService.d.cts +65 -0
- package/dist/MimicAuthService.d.cts.map +1 -0
- package/dist/MimicAuthService.d.mts +65 -0
- package/dist/MimicAuthService.d.mts.map +1 -0
- package/dist/MimicAuthService.mjs +47 -0
- package/dist/MimicAuthService.mjs.map +1 -0
- package/dist/MimicConfig.cjs +52 -0
- package/dist/MimicConfig.d.cts +115 -0
- package/dist/MimicConfig.d.cts.map +1 -0
- package/dist/MimicConfig.d.mts +115 -0
- package/dist/MimicConfig.d.mts.map +1 -0
- package/dist/MimicConfig.mjs +43 -0
- package/dist/MimicConfig.mjs.map +1 -0
- package/dist/MimicDataStorage.cjs +83 -0
- package/dist/MimicDataStorage.d.cts +113 -0
- package/dist/MimicDataStorage.d.cts.map +1 -0
- package/dist/MimicDataStorage.d.mts +113 -0
- package/dist/MimicDataStorage.d.mts.map +1 -0
- package/dist/MimicDataStorage.mjs +74 -0
- package/dist/MimicDataStorage.mjs.map +1 -0
- package/dist/MimicServer.cjs +122 -0
- package/dist/MimicServer.d.cts +106 -0
- package/dist/MimicServer.d.cts.map +1 -0
- package/dist/MimicServer.d.mts +106 -0
- package/dist/MimicServer.d.mts.map +1 -0
- package/dist/MimicServer.mjs +116 -0
- package/dist/MimicServer.mjs.map +1 -0
- package/dist/PresenceManager.cjs +108 -0
- package/dist/PresenceManager.d.cts +91 -0
- package/dist/PresenceManager.d.cts.map +1 -0
- package/dist/PresenceManager.d.mts +91 -0
- package/dist/PresenceManager.d.mts.map +1 -0
- package/dist/PresenceManager.mjs +95 -0
- package/dist/PresenceManager.mjs.map +1 -0
- package/dist/WebSocketHandler.cjs +365 -0
- package/dist/WebSocketHandler.d.cts +34 -0
- package/dist/WebSocketHandler.d.cts.map +1 -0
- package/dist/WebSocketHandler.d.mts +34 -0
- package/dist/WebSocketHandler.d.mts.map +1 -0
- package/dist/WebSocketHandler.mjs +355 -0
- package/dist/WebSocketHandler.mjs.map +1 -0
- package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/defineProperty.cjs +14 -0
- package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/defineProperty.mjs +14 -0
- package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/objectSpread2.cjs +27 -0
- package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/objectSpread2.mjs +27 -0
- package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/toPrimitive.cjs +16 -0
- package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/toPrimitive.mjs +16 -0
- package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/toPropertyKey.cjs +11 -0
- package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/toPropertyKey.mjs +11 -0
- package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/typeof.cjs +18 -0
- package/dist/_virtual/_@oxc-project_runtime@0.103.0/helpers/typeof.mjs +12 -0
- package/dist/_virtual/rolldown_runtime.cjs +43 -0
- package/dist/{chunk-C6wwvPpM.mjs → _virtual/rolldown_runtime.mjs} +1 -1
- package/dist/auth/NoAuth.cjs +43 -0
- package/dist/auth/NoAuth.d.cts +22 -0
- package/dist/auth/NoAuth.d.cts.map +1 -0
- package/dist/auth/NoAuth.d.mts +22 -0
- package/dist/auth/NoAuth.d.mts.map +1 -0
- package/dist/auth/NoAuth.mjs +36 -0
- package/dist/auth/NoAuth.mjs.map +1 -0
- package/dist/errors.cjs +74 -0
- package/dist/errors.d.cts +89 -0
- package/dist/errors.d.cts.map +1 -0
- package/dist/errors.d.mts +89 -0
- package/dist/errors.d.mts.map +1 -0
- package/dist/errors.mjs +67 -0
- package/dist/errors.mjs.map +1 -0
- package/dist/index.cjs +29 -1227
- package/dist/index.d.cts +12 -795
- package/dist/index.d.mts +12 -795
- package/dist/index.mjs +13 -1162
- package/dist/storage/InMemoryDataStorage.cjs +57 -0
- package/dist/storage/InMemoryDataStorage.d.cts +19 -0
- package/dist/storage/InMemoryDataStorage.d.cts.map +1 -0
- package/dist/storage/InMemoryDataStorage.d.mts +19 -0
- package/dist/storage/InMemoryDataStorage.d.mts.map +1 -0
- package/dist/storage/InMemoryDataStorage.mjs +48 -0
- package/dist/storage/InMemoryDataStorage.mjs.map +1 -0
- 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/tsdown.config.ts +1 -1
- package/dist/index.d.cts.map +0 -1
- package/dist/index.d.mts.map +0 -1
- package/dist/index.mjs.map +0 -1
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { __export } from "./_virtual/rolldown_runtime.mjs";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as Layer from "effect/Layer";
|
|
4
|
+
import * as Context from "effect/Context";
|
|
5
|
+
import * as Data from "effect/Data";
|
|
6
|
+
|
|
7
|
+
//#region src/MimicDataStorage.ts
|
|
8
|
+
/**
|
|
9
|
+
* @since 0.0.1
|
|
10
|
+
* Data storage service interface for Mimic documents.
|
|
11
|
+
* Provides pluggable storage adapters with load/save hooks for data transformation.
|
|
12
|
+
*/
|
|
13
|
+
var MimicDataStorage_exports = /* @__PURE__ */ __export({
|
|
14
|
+
MimicDataStorageTag: () => MimicDataStorageTag,
|
|
15
|
+
StorageDeleteError: () => StorageDeleteError,
|
|
16
|
+
StorageLoadError: () => StorageLoadError,
|
|
17
|
+
StorageSaveError: () => StorageSaveError,
|
|
18
|
+
layer: () => layer,
|
|
19
|
+
layerEffect: () => layerEffect,
|
|
20
|
+
make: () => make
|
|
21
|
+
});
|
|
22
|
+
/**
|
|
23
|
+
* Error when loading a document from storage fails.
|
|
24
|
+
*/
|
|
25
|
+
var StorageLoadError = class extends Data.TaggedError("StorageLoadError") {
|
|
26
|
+
get message() {
|
|
27
|
+
return `Failed to load document ${this.documentId}: ${String(this.cause)}`;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Error when saving a document to storage fails.
|
|
32
|
+
*/
|
|
33
|
+
var StorageSaveError = class extends Data.TaggedError("StorageSaveError") {
|
|
34
|
+
get message() {
|
|
35
|
+
return `Failed to save document ${this.documentId}: ${String(this.cause)}`;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Error when deleting a document from storage fails.
|
|
40
|
+
*/
|
|
41
|
+
var StorageDeleteError = class extends Data.TaggedError("StorageDeleteError") {
|
|
42
|
+
get message() {
|
|
43
|
+
return `Failed to delete document ${this.documentId}: ${String(this.cause)}`;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Context tag for MimicDataStorage service.
|
|
48
|
+
*/
|
|
49
|
+
var MimicDataStorageTag = class extends Context.Tag("@voidhash/mimic-server-effect/MimicDataStorage")() {};
|
|
50
|
+
/**
|
|
51
|
+
* Create a MimicDataStorage layer from a storage implementation.
|
|
52
|
+
*/
|
|
53
|
+
const layer = (storage) => Layer.succeed(MimicDataStorageTag, storage);
|
|
54
|
+
/**
|
|
55
|
+
* Create a MimicDataStorage layer from an Effect that produces a storage implementation.
|
|
56
|
+
*/
|
|
57
|
+
const layerEffect = (effect) => Layer.effect(MimicDataStorageTag, effect);
|
|
58
|
+
/**
|
|
59
|
+
* Create a simple storage implementation with minimal configuration.
|
|
60
|
+
*/
|
|
61
|
+
const make = (options) => {
|
|
62
|
+
var _options$delete, _options$onLoad, _options$onSave;
|
|
63
|
+
return {
|
|
64
|
+
load: options.load,
|
|
65
|
+
save: options.save,
|
|
66
|
+
delete: (_options$delete = options.delete) !== null && _options$delete !== void 0 ? _options$delete : (() => Effect.void),
|
|
67
|
+
onLoad: (_options$onLoad = options.onLoad) !== null && _options$onLoad !== void 0 ? _options$onLoad : ((state) => Effect.succeed(state)),
|
|
68
|
+
onSave: (_options$onSave = options.onSave) !== null && _options$onSave !== void 0 ? _options$onSave : ((state) => Effect.succeed(state))
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
//#endregion
|
|
73
|
+
export { MimicDataStorageTag, MimicDataStorage_exports };
|
|
74
|
+
//# sourceMappingURL=MimicDataStorage.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MimicDataStorage.mjs","names":[],"sources":["../src/MimicDataStorage.ts"],"sourcesContent":["/**\n * @since 0.0.1\n * Data storage service interface for Mimic documents.\n * Provides pluggable storage adapters with load/save hooks for data transformation.\n */\nimport * as Effect from \"effect/Effect\";\nimport * as Context from \"effect/Context\";\nimport * as Layer from \"effect/Layer\";\nimport * as Data from \"effect/Data\";\n\n// =============================================================================\n// Error Types\n// =============================================================================\n\n/**\n * Error when loading a document from storage fails.\n */\nexport class StorageLoadError extends Data.TaggedError(\"StorageLoadError\")<{\n readonly documentId: string;\n readonly cause: unknown;\n}> {\n override get message(): string {\n return `Failed to load document ${this.documentId}: ${String(this.cause)}`;\n }\n}\n\n/**\n * Error when saving a document to storage fails.\n */\nexport class StorageSaveError extends Data.TaggedError(\"StorageSaveError\")<{\n readonly documentId: string;\n readonly cause: unknown;\n}> {\n override get message(): string {\n return `Failed to save document ${this.documentId}: ${String(this.cause)}`;\n }\n}\n\n/**\n * Error when deleting a document from storage fails.\n */\nexport class StorageDeleteError extends Data.TaggedError(\"StorageDeleteError\")<{\n readonly documentId: string;\n readonly cause: unknown;\n}> {\n override get message(): string {\n return `Failed to delete document ${this.documentId}: ${String(this.cause)}`;\n }\n}\n\n/**\n * Union of all storage errors.\n */\nexport type StorageError = StorageLoadError | StorageSaveError | StorageDeleteError;\n\n// =============================================================================\n// Storage Service Interface\n// =============================================================================\n\n/**\n * Data storage service interface.\n * Implementations can persist documents to various backends (memory, S3, database, etc.)\n */\nexport interface MimicDataStorage {\n /**\n * Load a document's state from storage.\n * @param documentId - The unique identifier for the document\n * @returns The document state, or undefined if not found\n */\n readonly load: (\n documentId: string\n ) => Effect.Effect<unknown | undefined, StorageLoadError>;\n\n /**\n * Save a document's state to storage.\n * @param documentId - The unique identifier for the document\n * @param state - The document state to persist\n */\n readonly save: (\n documentId: string,\n state: unknown\n ) => Effect.Effect<void, StorageSaveError>;\n\n /**\n * Delete a document from storage.\n * @param documentId - The unique identifier for the document\n */\n readonly delete: (\n documentId: string\n ) => Effect.Effect<void, StorageDeleteError>;\n\n /**\n * Transform data after loading from storage.\n * Useful for migrations, decryption, decompression, etc.\n * @param state - The raw state loaded from storage\n * @returns The transformed state\n */\n readonly onLoad: (state: unknown) => Effect.Effect<unknown>;\n\n /**\n * Transform/validate data before saving to storage.\n * Useful for encryption, compression, validation, etc.\n * @param state - The state to be saved\n * @returns The transformed state\n */\n readonly onSave: (state: unknown) => Effect.Effect<unknown>;\n}\n\n// =============================================================================\n// Context Tag\n// =============================================================================\n\n/**\n * Context tag for MimicDataStorage service.\n */\nexport class MimicDataStorageTag extends Context.Tag(\n \"@voidhash/mimic-server-effect/MimicDataStorage\"\n)<MimicDataStorageTag, MimicDataStorage>() {}\n\n// =============================================================================\n// Layer Constructors\n// =============================================================================\n\n/**\n * Create a MimicDataStorage layer from a storage implementation.\n */\nexport const layer = (storage: MimicDataStorage): Layer.Layer<MimicDataStorageTag> =>\n Layer.succeed(MimicDataStorageTag, storage);\n\n/**\n * Create a MimicDataStorage layer from an Effect that produces a storage implementation.\n */\nexport const layerEffect = <E, R>(\n effect: Effect.Effect<MimicDataStorage, E, R>\n): Layer.Layer<MimicDataStorageTag, E, R> =>\n Layer.effect(MimicDataStorageTag, effect);\n\n// =============================================================================\n// Helper Functions\n// =============================================================================\n\n/**\n * Create a simple storage implementation with minimal configuration.\n */\nexport const make = (options: {\n readonly load: (documentId: string) => Effect.Effect<unknown | undefined, StorageLoadError>;\n readonly save: (documentId: string, state: unknown) => Effect.Effect<void, StorageSaveError>;\n readonly delete?: (documentId: string) => Effect.Effect<void, StorageDeleteError>;\n readonly onLoad?: (state: unknown) => Effect.Effect<unknown>;\n readonly onSave?: (state: unknown) => Effect.Effect<unknown>;\n}): MimicDataStorage => ({\n load: options.load,\n save: options.save,\n delete: options.delete ?? (() => Effect.void),\n onLoad: options.onLoad ?? ((state) => Effect.succeed(state)),\n onSave: options.onSave ?? ((state) => Effect.succeed(state)),\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAiBA,IAAa,mBAAb,cAAsC,KAAK,YAAY,mBAAmB,CAGvE;CACD,IAAa,UAAkB;AAC7B,SAAO,2BAA2B,KAAK,WAAW,IAAI,OAAO,KAAK,MAAM;;;;;;AAO5E,IAAa,mBAAb,cAAsC,KAAK,YAAY,mBAAmB,CAGvE;CACD,IAAa,UAAkB;AAC7B,SAAO,2BAA2B,KAAK,WAAW,IAAI,OAAO,KAAK,MAAM;;;;;;AAO5E,IAAa,qBAAb,cAAwC,KAAK,YAAY,qBAAqB,CAG3E;CACD,IAAa,UAAkB;AAC7B,SAAO,6BAA6B,KAAK,WAAW,IAAI,OAAO,KAAK,MAAM;;;;;;AAqE9E,IAAa,sBAAb,cAAyC,QAAQ,IAC/C,iDACD,EAAyC,CAAC;;;;AAS3C,MAAa,SAAS,YACpB,MAAM,QAAQ,qBAAqB,QAAQ;;;;AAK7C,MAAa,eACX,WAEA,MAAM,OAAO,qBAAqB,OAAO;;;;AAS3C,MAAa,QAAQ,YAMG;;QAAC;EACvB,MAAM,QAAQ;EACd,MAAM,QAAQ;EACd,2BAAQ,QAAQ,0EAAiB,OAAO;EACxC,2BAAQ,QAAQ,qEAAY,UAAU,OAAO,QAAQ,MAAM;EAC3D,2BAAQ,QAAQ,qEAAY,UAAU,OAAO,QAAQ,MAAM;EAC5D"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
|
|
2
|
+
const require_MimicConfig = require('./MimicConfig.cjs');
|
|
3
|
+
const require_DocumentManager = require('./DocumentManager.cjs');
|
|
4
|
+
const require_MimicAuthService = require('./MimicAuthService.cjs');
|
|
5
|
+
const require_PresenceManager = require('./PresenceManager.cjs');
|
|
6
|
+
const require_WebSocketHandler = require('./WebSocketHandler.cjs');
|
|
7
|
+
const require_InMemoryDataStorage = require('./storage/InMemoryDataStorage.cjs');
|
|
8
|
+
const require_NoAuth = require('./auth/NoAuth.cjs');
|
|
9
|
+
let effect_Effect = require("effect/Effect");
|
|
10
|
+
effect_Effect = require_rolldown_runtime.__toESM(effect_Effect);
|
|
11
|
+
let effect_Layer = require("effect/Layer");
|
|
12
|
+
effect_Layer = require_rolldown_runtime.__toESM(effect_Layer);
|
|
13
|
+
let _effect_platform = require("@effect/platform");
|
|
14
|
+
|
|
15
|
+
//#region src/MimicServer.ts
|
|
16
|
+
/**
|
|
17
|
+
* @since 0.0.1
|
|
18
|
+
* Mimic server layer composition.
|
|
19
|
+
*/
|
|
20
|
+
var MimicServer_exports = /* @__PURE__ */ require_rolldown_runtime.__export({
|
|
21
|
+
documentManagerLayer: () => documentManagerLayer,
|
|
22
|
+
layerHttpLayerRouter: () => layerHttpLayerRouter
|
|
23
|
+
});
|
|
24
|
+
/**
|
|
25
|
+
* Create the document manager layer.
|
|
26
|
+
*/
|
|
27
|
+
const documentManagerLayer = (options) => require_DocumentManager.layer.pipe(effect_Layer.provide(require_MimicConfig.layer(options)), effect_Layer.provide(require_InMemoryDataStorage.layerDefault), effect_Layer.provide(require_NoAuth.layerDefault));
|
|
28
|
+
/**
|
|
29
|
+
* Create the HTTP handler effect for WebSocket upgrade.
|
|
30
|
+
* This handler:
|
|
31
|
+
* 1. Extracts the document ID from the URL path
|
|
32
|
+
* 2. Upgrades the HTTP connection to WebSocket
|
|
33
|
+
* 3. Delegates to the WebSocketHandler for document sync
|
|
34
|
+
*/
|
|
35
|
+
const makeMimicHandler = effect_Effect.gen(function* () {
|
|
36
|
+
const config = yield* require_MimicConfig.MimicServerConfigTag;
|
|
37
|
+
const authService = yield* require_MimicAuthService.MimicAuthServiceTag;
|
|
38
|
+
const documentManager = yield* require_DocumentManager.DocumentManagerTag;
|
|
39
|
+
const presenceManager = yield* require_PresenceManager.PresenceManagerTag;
|
|
40
|
+
return effect_Effect.gen(function* () {
|
|
41
|
+
const request = yield* _effect_platform.HttpServerRequest.HttpServerRequest;
|
|
42
|
+
yield* require_WebSocketHandler.extractDocumentId(request.url);
|
|
43
|
+
const socket = yield* request.upgrade;
|
|
44
|
+
yield* require_WebSocketHandler.handleConnection(socket, request.url).pipe(effect_Effect.provideService(require_MimicConfig.MimicServerConfigTag, config), effect_Effect.provideService(require_MimicAuthService.MimicAuthServiceTag, authService), effect_Effect.provideService(require_DocumentManager.DocumentManagerTag, documentManager), effect_Effect.provideService(require_PresenceManager.PresenceManagerTag, presenceManager), effect_Effect.scoped, effect_Effect.catchAll((error) => effect_Effect.logError("WebSocket connection error", error)));
|
|
45
|
+
return _effect_platform.HttpServerResponse.empty();
|
|
46
|
+
}).pipe(effect_Effect.catchAll((error) => effect_Effect.gen(function* () {
|
|
47
|
+
yield* effect_Effect.logWarning("WebSocket upgrade failed", error);
|
|
48
|
+
return _effect_platform.HttpServerResponse.text("WebSocket upgrade failed", { status: 400 });
|
|
49
|
+
})));
|
|
50
|
+
});
|
|
51
|
+
/**
|
|
52
|
+
* Create a Mimic server layer that integrates with HttpLayerRouter.
|
|
53
|
+
*
|
|
54
|
+
* This function creates a layer that:
|
|
55
|
+
* 1. Registers a WebSocket route at the specified base path
|
|
56
|
+
* 2. Handles WebSocket upgrades for document sync
|
|
57
|
+
* 3. Provides all required dependencies (config, auth, storage, document manager)
|
|
58
|
+
*
|
|
59
|
+
* By default, uses in-memory storage and no authentication.
|
|
60
|
+
* To override these defaults, provide custom layers before the defaults:
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* import { MimicServer, MimicAuthService } from "@voidhash/mimic-effect";
|
|
65
|
+
* import { HttpLayerRouter } from "@effect/platform";
|
|
66
|
+
* import { Primitive } from "@voidhash/mimic";
|
|
67
|
+
*
|
|
68
|
+
* const TodoSchema = Primitive.Struct({
|
|
69
|
+
* title: Primitive.String(),
|
|
70
|
+
* completed: Primitive.Boolean(),
|
|
71
|
+
* });
|
|
72
|
+
*
|
|
73
|
+
* // Create the Mimic route layer with defaults
|
|
74
|
+
* const MimicRoute = MimicServer.layerHttpLayerRouter({
|
|
75
|
+
* basePath: "/mimic/todo",
|
|
76
|
+
* schema: TodoSchema
|
|
77
|
+
* });
|
|
78
|
+
*
|
|
79
|
+
* // Or with custom auth - use Layer.provide to inject before defaults
|
|
80
|
+
* const MimicRouteWithAuth = MimicServer.layerHttpLayerRouter({
|
|
81
|
+
* basePath: "/mimic/todo",
|
|
82
|
+
* schema: TodoSchema,
|
|
83
|
+
* authLayer: MimicAuthService.layer({
|
|
84
|
+
* authHandler: (token) => ({ success: true, userId: token })
|
|
85
|
+
* })
|
|
86
|
+
* });
|
|
87
|
+
*
|
|
88
|
+
* // Merge with other routes and serve
|
|
89
|
+
* const AllRoutes = Layer.mergeAll(MimicRoute, OtherRoutes);
|
|
90
|
+
* HttpLayerRouter.serve(AllRoutes).pipe(
|
|
91
|
+
* Layer.provide(BunHttpServer.layer({ port: 3000 })),
|
|
92
|
+
* Layer.launch,
|
|
93
|
+
* BunRuntime.runMain
|
|
94
|
+
* );
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
const layerHttpLayerRouter = (options) => {
|
|
98
|
+
var _options$basePath, _options$authLayer, _options$storageLayer;
|
|
99
|
+
const wsPath = `${(_options$basePath = options.basePath) !== null && _options$basePath !== void 0 ? _options$basePath : "/mimic"}/doc/*`;
|
|
100
|
+
const configLayer = require_MimicConfig.layer({
|
|
101
|
+
schema: options.schema,
|
|
102
|
+
maxTransactionHistory: options.maxTransactionHistory,
|
|
103
|
+
presence: options.presence,
|
|
104
|
+
initial: options.initial
|
|
105
|
+
});
|
|
106
|
+
const authLayer = (_options$authLayer = options.authLayer) !== null && _options$authLayer !== void 0 ? _options$authLayer : require_NoAuth.layerDefault;
|
|
107
|
+
const storageLayer = (_options$storageLayer = options.storageLayer) !== null && _options$storageLayer !== void 0 ? _options$storageLayer : require_InMemoryDataStorage.layerDefault;
|
|
108
|
+
const registerRoute = effect_Effect.gen(function* () {
|
|
109
|
+
const router = yield* _effect_platform.HttpLayerRouter.HttpRouter;
|
|
110
|
+
const handler = yield* makeMimicHandler;
|
|
111
|
+
yield* router.add("GET", wsPath, handler);
|
|
112
|
+
});
|
|
113
|
+
return effect_Layer.scopedDiscard(registerRoute).pipe(effect_Layer.provide(require_DocumentManager.layer), effect_Layer.provide(require_PresenceManager.layer), effect_Layer.provide(configLayer), effect_Layer.provide(storageLayer), effect_Layer.provide(authLayer));
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
//#endregion
|
|
117
|
+
Object.defineProperty(exports, 'MimicServer_exports', {
|
|
118
|
+
enumerable: true,
|
|
119
|
+
get: function () {
|
|
120
|
+
return MimicServer_exports;
|
|
121
|
+
}
|
|
122
|
+
});
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { MimicServerConfigOptions } from "./MimicConfig.cjs";
|
|
2
|
+
import { MimicDataStorageTag } from "./MimicDataStorage.cjs";
|
|
3
|
+
import { DocumentManagerTag } from "./DocumentManager.cjs";
|
|
4
|
+
import { MimicAuthServiceTag } from "./MimicAuthService.cjs";
|
|
5
|
+
import * as Layer from "effect/Layer";
|
|
6
|
+
import { Presence, Primitive } from "@voidhash/mimic";
|
|
7
|
+
import { HttpLayerRouter } from "@effect/platform";
|
|
8
|
+
import { PathInput } from "@effect/platform/HttpRouter";
|
|
9
|
+
|
|
10
|
+
//#region src/MimicServer.d.ts
|
|
11
|
+
declare namespace MimicServer_d_exports {
|
|
12
|
+
export { MimicLayerOptions, documentManagerLayer, layerHttpLayerRouter };
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Options for creating a Mimic server layer.
|
|
16
|
+
*/
|
|
17
|
+
interface MimicLayerOptions<TSchema extends Primitive.AnyPrimitive> {
|
|
18
|
+
/**
|
|
19
|
+
* Base path for document routes (used for path matching).
|
|
20
|
+
* @example "/mimic/todo" - documents accessed at "/mimic/todo/:documentId"
|
|
21
|
+
*/
|
|
22
|
+
readonly basePath?: PathInput;
|
|
23
|
+
/**
|
|
24
|
+
* The schema defining the document structure.
|
|
25
|
+
*/
|
|
26
|
+
readonly schema: TSchema;
|
|
27
|
+
/**
|
|
28
|
+
* Maximum number of processed transaction IDs to track for deduplication.
|
|
29
|
+
* @default 1000
|
|
30
|
+
*/
|
|
31
|
+
readonly maxTransactionHistory?: number;
|
|
32
|
+
/**
|
|
33
|
+
* Optional presence schema for ephemeral per-user data.
|
|
34
|
+
* When provided, enables presence features on WebSocket connections.
|
|
35
|
+
*/
|
|
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
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Create the document manager layer.
|
|
50
|
+
*/
|
|
51
|
+
declare const documentManagerLayer: <TSchema extends Primitive.AnyPrimitive>(options: MimicServerConfigOptions<TSchema>) => Layer.Layer<DocumentManagerTag>;
|
|
52
|
+
/**
|
|
53
|
+
* Create a Mimic server layer that integrates with HttpLayerRouter.
|
|
54
|
+
*
|
|
55
|
+
* This function creates a layer that:
|
|
56
|
+
* 1. Registers a WebSocket route at the specified base path
|
|
57
|
+
* 2. Handles WebSocket upgrades for document sync
|
|
58
|
+
* 3. Provides all required dependencies (config, auth, storage, document manager)
|
|
59
|
+
*
|
|
60
|
+
* By default, uses in-memory storage and no authentication.
|
|
61
|
+
* To override these defaults, provide custom layers before the defaults:
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```typescript
|
|
65
|
+
* import { MimicServer, MimicAuthService } from "@voidhash/mimic-effect";
|
|
66
|
+
* import { HttpLayerRouter } from "@effect/platform";
|
|
67
|
+
* import { Primitive } from "@voidhash/mimic";
|
|
68
|
+
*
|
|
69
|
+
* const TodoSchema = Primitive.Struct({
|
|
70
|
+
* title: Primitive.String(),
|
|
71
|
+
* completed: Primitive.Boolean(),
|
|
72
|
+
* });
|
|
73
|
+
*
|
|
74
|
+
* // Create the Mimic route layer with defaults
|
|
75
|
+
* const MimicRoute = MimicServer.layerHttpLayerRouter({
|
|
76
|
+
* basePath: "/mimic/todo",
|
|
77
|
+
* schema: TodoSchema
|
|
78
|
+
* });
|
|
79
|
+
*
|
|
80
|
+
* // Or with custom auth - use Layer.provide to inject before defaults
|
|
81
|
+
* const MimicRouteWithAuth = MimicServer.layerHttpLayerRouter({
|
|
82
|
+
* basePath: "/mimic/todo",
|
|
83
|
+
* schema: TodoSchema,
|
|
84
|
+
* authLayer: MimicAuthService.layer({
|
|
85
|
+
* authHandler: (token) => ({ success: true, userId: token })
|
|
86
|
+
* })
|
|
87
|
+
* });
|
|
88
|
+
*
|
|
89
|
+
* // Merge with other routes and serve
|
|
90
|
+
* const AllRoutes = Layer.mergeAll(MimicRoute, OtherRoutes);
|
|
91
|
+
* HttpLayerRouter.serve(AllRoutes).pipe(
|
|
92
|
+
* Layer.provide(BunHttpServer.layer({ port: 3000 })),
|
|
93
|
+
* Layer.launch,
|
|
94
|
+
* BunRuntime.runMain
|
|
95
|
+
* );
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
declare const layerHttpLayerRouter: <TSchema extends Primitive.AnyPrimitive>(options: MimicLayerOptions<TSchema> & {
|
|
99
|
+
/** Custom auth layer. Defaults to NoAuth (all connections allowed). */
|
|
100
|
+
readonly authLayer?: Layer.Layer<MimicAuthServiceTag>;
|
|
101
|
+
/** Custom storage layer. Defaults to InMemoryDataStorage. */
|
|
102
|
+
readonly storageLayer?: Layer.Layer<MimicDataStorageTag>;
|
|
103
|
+
}) => Layer.Layer<never, never, HttpLayerRouter.HttpRouter>;
|
|
104
|
+
//#endregion
|
|
105
|
+
export { MimicServer_d_exports };
|
|
106
|
+
//# sourceMappingURL=MimicServer.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { MimicServerConfigOptions } from "./MimicConfig.mjs";
|
|
2
|
+
import { MimicDataStorageTag } from "./MimicDataStorage.mjs";
|
|
3
|
+
import { DocumentManagerTag } from "./DocumentManager.mjs";
|
|
4
|
+
import { MimicAuthServiceTag } from "./MimicAuthService.mjs";
|
|
5
|
+
import * as Layer from "effect/Layer";
|
|
6
|
+
import { Presence, Primitive } from "@voidhash/mimic";
|
|
7
|
+
import { HttpLayerRouter } from "@effect/platform";
|
|
8
|
+
import { PathInput } from "@effect/platform/HttpRouter";
|
|
9
|
+
|
|
10
|
+
//#region src/MimicServer.d.ts
|
|
11
|
+
declare namespace MimicServer_d_exports {
|
|
12
|
+
export { MimicLayerOptions, documentManagerLayer, layerHttpLayerRouter };
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Options for creating a Mimic server layer.
|
|
16
|
+
*/
|
|
17
|
+
interface MimicLayerOptions<TSchema extends Primitive.AnyPrimitive> {
|
|
18
|
+
/**
|
|
19
|
+
* Base path for document routes (used for path matching).
|
|
20
|
+
* @example "/mimic/todo" - documents accessed at "/mimic/todo/:documentId"
|
|
21
|
+
*/
|
|
22
|
+
readonly basePath?: PathInput;
|
|
23
|
+
/**
|
|
24
|
+
* The schema defining the document structure.
|
|
25
|
+
*/
|
|
26
|
+
readonly schema: TSchema;
|
|
27
|
+
/**
|
|
28
|
+
* Maximum number of processed transaction IDs to track for deduplication.
|
|
29
|
+
* @default 1000
|
|
30
|
+
*/
|
|
31
|
+
readonly maxTransactionHistory?: number;
|
|
32
|
+
/**
|
|
33
|
+
* Optional presence schema for ephemeral per-user data.
|
|
34
|
+
* When provided, enables presence features on WebSocket connections.
|
|
35
|
+
*/
|
|
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
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Create the document manager layer.
|
|
50
|
+
*/
|
|
51
|
+
declare const documentManagerLayer: <TSchema extends Primitive.AnyPrimitive>(options: MimicServerConfigOptions<TSchema>) => Layer.Layer<DocumentManagerTag>;
|
|
52
|
+
/**
|
|
53
|
+
* Create a Mimic server layer that integrates with HttpLayerRouter.
|
|
54
|
+
*
|
|
55
|
+
* This function creates a layer that:
|
|
56
|
+
* 1. Registers a WebSocket route at the specified base path
|
|
57
|
+
* 2. Handles WebSocket upgrades for document sync
|
|
58
|
+
* 3. Provides all required dependencies (config, auth, storage, document manager)
|
|
59
|
+
*
|
|
60
|
+
* By default, uses in-memory storage and no authentication.
|
|
61
|
+
* To override these defaults, provide custom layers before the defaults:
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```typescript
|
|
65
|
+
* import { MimicServer, MimicAuthService } from "@voidhash/mimic-effect";
|
|
66
|
+
* import { HttpLayerRouter } from "@effect/platform";
|
|
67
|
+
* import { Primitive } from "@voidhash/mimic";
|
|
68
|
+
*
|
|
69
|
+
* const TodoSchema = Primitive.Struct({
|
|
70
|
+
* title: Primitive.String(),
|
|
71
|
+
* completed: Primitive.Boolean(),
|
|
72
|
+
* });
|
|
73
|
+
*
|
|
74
|
+
* // Create the Mimic route layer with defaults
|
|
75
|
+
* const MimicRoute = MimicServer.layerHttpLayerRouter({
|
|
76
|
+
* basePath: "/mimic/todo",
|
|
77
|
+
* schema: TodoSchema
|
|
78
|
+
* });
|
|
79
|
+
*
|
|
80
|
+
* // Or with custom auth - use Layer.provide to inject before defaults
|
|
81
|
+
* const MimicRouteWithAuth = MimicServer.layerHttpLayerRouter({
|
|
82
|
+
* basePath: "/mimic/todo",
|
|
83
|
+
* schema: TodoSchema,
|
|
84
|
+
* authLayer: MimicAuthService.layer({
|
|
85
|
+
* authHandler: (token) => ({ success: true, userId: token })
|
|
86
|
+
* })
|
|
87
|
+
* });
|
|
88
|
+
*
|
|
89
|
+
* // Merge with other routes and serve
|
|
90
|
+
* const AllRoutes = Layer.mergeAll(MimicRoute, OtherRoutes);
|
|
91
|
+
* HttpLayerRouter.serve(AllRoutes).pipe(
|
|
92
|
+
* Layer.provide(BunHttpServer.layer({ port: 3000 })),
|
|
93
|
+
* Layer.launch,
|
|
94
|
+
* BunRuntime.runMain
|
|
95
|
+
* );
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
declare const layerHttpLayerRouter: <TSchema extends Primitive.AnyPrimitive>(options: MimicLayerOptions<TSchema> & {
|
|
99
|
+
/** Custom auth layer. Defaults to NoAuth (all connections allowed). */
|
|
100
|
+
readonly authLayer?: Layer.Layer<MimicAuthServiceTag>;
|
|
101
|
+
/** Custom storage layer. Defaults to InMemoryDataStorage. */
|
|
102
|
+
readonly storageLayer?: Layer.Layer<MimicDataStorageTag>;
|
|
103
|
+
}) => Layer.Layer<never, never, HttpLayerRouter.HttpRouter>;
|
|
104
|
+
//#endregion
|
|
105
|
+
export { MimicServer_d_exports };
|
|
106
|
+
//# sourceMappingURL=MimicServer.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { __export } from "./_virtual/rolldown_runtime.mjs";
|
|
2
|
+
import { MimicServerConfigTag, layer } from "./MimicConfig.mjs";
|
|
3
|
+
import { DocumentManagerTag, layer as layer$1 } from "./DocumentManager.mjs";
|
|
4
|
+
import { MimicAuthServiceTag } from "./MimicAuthService.mjs";
|
|
5
|
+
import { PresenceManagerTag, layer as layer$2 } from "./PresenceManager.mjs";
|
|
6
|
+
import { extractDocumentId, handleConnection } from "./WebSocketHandler.mjs";
|
|
7
|
+
import { layerDefault } from "./storage/InMemoryDataStorage.mjs";
|
|
8
|
+
import { layerDefault as layerDefault$1 } from "./auth/NoAuth.mjs";
|
|
9
|
+
import * as Effect from "effect/Effect";
|
|
10
|
+
import * as Layer from "effect/Layer";
|
|
11
|
+
import { HttpLayerRouter, HttpServerRequest, HttpServerResponse } from "@effect/platform";
|
|
12
|
+
|
|
13
|
+
//#region src/MimicServer.ts
|
|
14
|
+
/**
|
|
15
|
+
* @since 0.0.1
|
|
16
|
+
* Mimic server layer composition.
|
|
17
|
+
*/
|
|
18
|
+
var MimicServer_exports = /* @__PURE__ */ __export({
|
|
19
|
+
documentManagerLayer: () => documentManagerLayer,
|
|
20
|
+
layerHttpLayerRouter: () => layerHttpLayerRouter
|
|
21
|
+
});
|
|
22
|
+
/**
|
|
23
|
+
* Create the document manager layer.
|
|
24
|
+
*/
|
|
25
|
+
const documentManagerLayer = (options) => layer$1.pipe(Layer.provide(layer(options)), Layer.provide(layerDefault), Layer.provide(layerDefault$1));
|
|
26
|
+
/**
|
|
27
|
+
* Create the HTTP handler effect for WebSocket upgrade.
|
|
28
|
+
* This handler:
|
|
29
|
+
* 1. Extracts the document ID from the URL path
|
|
30
|
+
* 2. Upgrades the HTTP connection to WebSocket
|
|
31
|
+
* 3. Delegates to the WebSocketHandler for document sync
|
|
32
|
+
*/
|
|
33
|
+
const makeMimicHandler = Effect.gen(function* () {
|
|
34
|
+
const config = yield* MimicServerConfigTag;
|
|
35
|
+
const authService = yield* MimicAuthServiceTag;
|
|
36
|
+
const documentManager = yield* DocumentManagerTag;
|
|
37
|
+
const presenceManager = yield* PresenceManagerTag;
|
|
38
|
+
return Effect.gen(function* () {
|
|
39
|
+
const request = yield* HttpServerRequest.HttpServerRequest;
|
|
40
|
+
yield* extractDocumentId(request.url);
|
|
41
|
+
const socket = yield* request.upgrade;
|
|
42
|
+
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)));
|
|
43
|
+
return HttpServerResponse.empty();
|
|
44
|
+
}).pipe(Effect.catchAll((error) => Effect.gen(function* () {
|
|
45
|
+
yield* Effect.logWarning("WebSocket upgrade failed", error);
|
|
46
|
+
return HttpServerResponse.text("WebSocket upgrade failed", { status: 400 });
|
|
47
|
+
})));
|
|
48
|
+
});
|
|
49
|
+
/**
|
|
50
|
+
* Create a Mimic server layer that integrates with HttpLayerRouter.
|
|
51
|
+
*
|
|
52
|
+
* This function creates a layer that:
|
|
53
|
+
* 1. Registers a WebSocket route at the specified base path
|
|
54
|
+
* 2. Handles WebSocket upgrades for document sync
|
|
55
|
+
* 3. Provides all required dependencies (config, auth, storage, document manager)
|
|
56
|
+
*
|
|
57
|
+
* By default, uses in-memory storage and no authentication.
|
|
58
|
+
* To override these defaults, provide custom layers before the defaults:
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```typescript
|
|
62
|
+
* import { MimicServer, MimicAuthService } from "@voidhash/mimic-effect";
|
|
63
|
+
* import { HttpLayerRouter } from "@effect/platform";
|
|
64
|
+
* import { Primitive } from "@voidhash/mimic";
|
|
65
|
+
*
|
|
66
|
+
* const TodoSchema = Primitive.Struct({
|
|
67
|
+
* title: Primitive.String(),
|
|
68
|
+
* completed: Primitive.Boolean(),
|
|
69
|
+
* });
|
|
70
|
+
*
|
|
71
|
+
* // Create the Mimic route layer with defaults
|
|
72
|
+
* const MimicRoute = MimicServer.layerHttpLayerRouter({
|
|
73
|
+
* basePath: "/mimic/todo",
|
|
74
|
+
* schema: TodoSchema
|
|
75
|
+
* });
|
|
76
|
+
*
|
|
77
|
+
* // Or with custom auth - use Layer.provide to inject before defaults
|
|
78
|
+
* const MimicRouteWithAuth = MimicServer.layerHttpLayerRouter({
|
|
79
|
+
* basePath: "/mimic/todo",
|
|
80
|
+
* schema: TodoSchema,
|
|
81
|
+
* authLayer: MimicAuthService.layer({
|
|
82
|
+
* authHandler: (token) => ({ success: true, userId: token })
|
|
83
|
+
* })
|
|
84
|
+
* });
|
|
85
|
+
*
|
|
86
|
+
* // Merge with other routes and serve
|
|
87
|
+
* const AllRoutes = Layer.mergeAll(MimicRoute, OtherRoutes);
|
|
88
|
+
* HttpLayerRouter.serve(AllRoutes).pipe(
|
|
89
|
+
* Layer.provide(BunHttpServer.layer({ port: 3000 })),
|
|
90
|
+
* Layer.launch,
|
|
91
|
+
* BunRuntime.runMain
|
|
92
|
+
* );
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
const layerHttpLayerRouter = (options) => {
|
|
96
|
+
var _options$basePath, _options$authLayer, _options$storageLayer;
|
|
97
|
+
const wsPath = `${(_options$basePath = options.basePath) !== null && _options$basePath !== void 0 ? _options$basePath : "/mimic"}/doc/*`;
|
|
98
|
+
const configLayer = layer({
|
|
99
|
+
schema: options.schema,
|
|
100
|
+
maxTransactionHistory: options.maxTransactionHistory,
|
|
101
|
+
presence: options.presence,
|
|
102
|
+
initial: options.initial
|
|
103
|
+
});
|
|
104
|
+
const authLayer = (_options$authLayer = options.authLayer) !== null && _options$authLayer !== void 0 ? _options$authLayer : layerDefault$1;
|
|
105
|
+
const storageLayer = (_options$storageLayer = options.storageLayer) !== null && _options$storageLayer !== void 0 ? _options$storageLayer : layerDefault;
|
|
106
|
+
const registerRoute = Effect.gen(function* () {
|
|
107
|
+
const router = yield* HttpLayerRouter.HttpRouter;
|
|
108
|
+
const handler = yield* makeMimicHandler;
|
|
109
|
+
yield* router.add("GET", wsPath, handler);
|
|
110
|
+
});
|
|
111
|
+
return Layer.scopedDiscard(registerRoute).pipe(Layer.provide(layer$1), Layer.provide(layer$2), Layer.provide(configLayer), Layer.provide(storageLayer), Layer.provide(authLayer));
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
//#endregion
|
|
115
|
+
export { MimicServer_exports };
|
|
116
|
+
//# sourceMappingURL=MimicServer.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|