@voidhash/mimic-effect 0.0.9 → 1.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +136 -90
- package/README.md +385 -0
- package/dist/ColdStorage.cjs +60 -0
- package/dist/ColdStorage.d.cts +53 -0
- package/dist/ColdStorage.d.cts.map +1 -0
- package/dist/ColdStorage.d.mts +53 -0
- package/dist/ColdStorage.d.mts.map +1 -0
- package/dist/ColdStorage.mjs +60 -0
- package/dist/ColdStorage.mjs.map +1 -0
- package/dist/DocumentManager.cjs +263 -82
- package/dist/DocumentManager.d.cts +44 -22
- package/dist/DocumentManager.d.cts.map +1 -1
- package/dist/DocumentManager.d.mts +44 -22
- package/dist/DocumentManager.d.mts.map +1 -1
- package/dist/DocumentManager.mjs +259 -67
- package/dist/DocumentManager.mjs.map +1 -1
- package/dist/Errors.cjs +54 -0
- package/dist/Errors.d.cts +96 -0
- package/dist/Errors.d.cts.map +1 -0
- package/dist/Errors.d.mts +96 -0
- package/dist/Errors.d.mts.map +1 -0
- package/dist/Errors.mjs +48 -0
- package/dist/Errors.mjs.map +1 -0
- package/dist/HotStorage.cjs +100 -0
- package/dist/HotStorage.d.cts +70 -0
- package/dist/HotStorage.d.cts.map +1 -0
- package/dist/HotStorage.d.mts +70 -0
- package/dist/HotStorage.d.mts.map +1 -0
- package/dist/HotStorage.mjs +100 -0
- package/dist/HotStorage.mjs.map +1 -0
- package/dist/Metrics.cjs +143 -0
- package/dist/Metrics.d.cts +31 -0
- package/dist/Metrics.d.cts.map +1 -0
- package/dist/Metrics.d.mts +31 -0
- package/dist/Metrics.d.mts.map +1 -0
- package/dist/Metrics.mjs +126 -0
- package/dist/Metrics.mjs.map +1 -0
- package/dist/MimicAuthService.cjs +61 -45
- package/dist/MimicAuthService.d.cts +61 -48
- package/dist/MimicAuthService.d.cts.map +1 -1
- package/dist/MimicAuthService.d.mts +61 -48
- package/dist/MimicAuthService.d.mts.map +1 -1
- package/dist/MimicAuthService.mjs +60 -36
- package/dist/MimicAuthService.mjs.map +1 -1
- package/dist/MimicClusterServerEngine.cjs +521 -0
- package/dist/MimicClusterServerEngine.d.cts +17 -0
- package/dist/MimicClusterServerEngine.d.cts.map +1 -0
- package/dist/MimicClusterServerEngine.d.mts +17 -0
- package/dist/MimicClusterServerEngine.d.mts.map +1 -0
- package/dist/MimicClusterServerEngine.mjs +523 -0
- package/dist/MimicClusterServerEngine.mjs.map +1 -0
- package/dist/MimicServer.cjs +205 -96
- package/dist/MimicServer.d.cts +9 -110
- package/dist/MimicServer.d.cts.map +1 -1
- package/dist/MimicServer.d.mts +9 -110
- package/dist/MimicServer.d.mts.map +1 -1
- package/dist/MimicServer.mjs +206 -90
- package/dist/MimicServer.mjs.map +1 -1
- package/dist/MimicServerEngine.cjs +97 -0
- package/dist/MimicServerEngine.d.cts +78 -0
- package/dist/MimicServerEngine.d.cts.map +1 -0
- package/dist/MimicServerEngine.d.mts +78 -0
- package/dist/MimicServerEngine.d.mts.map +1 -0
- package/dist/MimicServerEngine.mjs +97 -0
- package/dist/MimicServerEngine.mjs.map +1 -0
- package/dist/PresenceManager.cjs +75 -91
- package/dist/PresenceManager.d.cts +17 -66
- package/dist/PresenceManager.d.cts.map +1 -1
- package/dist/PresenceManager.d.mts +17 -66
- package/dist/PresenceManager.d.mts.map +1 -1
- package/dist/PresenceManager.mjs +74 -78
- package/dist/PresenceManager.mjs.map +1 -1
- package/dist/Protocol.cjs +146 -0
- package/dist/Protocol.d.cts +203 -0
- package/dist/Protocol.d.cts.map +1 -0
- package/dist/Protocol.d.mts +203 -0
- package/dist/Protocol.d.mts.map +1 -0
- package/dist/Protocol.mjs +132 -0
- package/dist/Protocol.mjs.map +1 -0
- package/dist/Types.d.cts +172 -0
- package/dist/Types.d.cts.map +1 -0
- package/dist/Types.d.mts +172 -0
- package/dist/Types.d.mts.map +1 -0
- package/dist/_virtual/rolldown_runtime.cjs +1 -25
- package/dist/_virtual/rolldown_runtime.mjs +4 -1
- package/dist/index.cjs +37 -75
- package/dist/index.d.cts +13 -12
- package/dist/index.d.mts +13 -12
- package/dist/index.mjs +12 -12
- package/dist/testing/ColdStorageTestSuite.cjs +508 -0
- package/dist/testing/ColdStorageTestSuite.d.cts +36 -0
- package/dist/testing/ColdStorageTestSuite.d.cts.map +1 -0
- package/dist/testing/ColdStorageTestSuite.d.mts +36 -0
- package/dist/testing/ColdStorageTestSuite.d.mts.map +1 -0
- package/dist/testing/ColdStorageTestSuite.mjs +508 -0
- package/dist/testing/ColdStorageTestSuite.mjs.map +1 -0
- package/dist/testing/FailingStorage.cjs +135 -0
- package/dist/testing/FailingStorage.d.cts +43 -0
- package/dist/testing/FailingStorage.d.cts.map +1 -0
- package/dist/testing/FailingStorage.d.mts +43 -0
- package/dist/testing/FailingStorage.d.mts.map +1 -0
- package/dist/testing/FailingStorage.mjs +136 -0
- package/dist/testing/FailingStorage.mjs.map +1 -0
- package/dist/testing/HotStorageTestSuite.cjs +585 -0
- package/dist/testing/HotStorageTestSuite.d.cts +40 -0
- package/dist/testing/HotStorageTestSuite.d.cts.map +1 -0
- package/dist/testing/HotStorageTestSuite.d.mts +40 -0
- package/dist/testing/HotStorageTestSuite.d.mts.map +1 -0
- package/dist/testing/HotStorageTestSuite.mjs +585 -0
- package/dist/testing/HotStorageTestSuite.mjs.map +1 -0
- package/dist/testing/StorageIntegrationTestSuite.cjs +349 -0
- package/dist/testing/StorageIntegrationTestSuite.d.cts +35 -0
- package/dist/testing/StorageIntegrationTestSuite.d.cts.map +1 -0
- package/dist/testing/StorageIntegrationTestSuite.d.mts +35 -0
- package/dist/testing/StorageIntegrationTestSuite.d.mts.map +1 -0
- package/dist/testing/StorageIntegrationTestSuite.mjs +349 -0
- package/dist/testing/StorageIntegrationTestSuite.mjs.map +1 -0
- package/dist/testing/assertions.cjs +114 -0
- package/dist/testing/assertions.mjs +109 -0
- package/dist/testing/assertions.mjs.map +1 -0
- package/dist/testing/index.cjs +14 -0
- package/dist/testing/index.d.cts +6 -0
- package/dist/testing/index.d.mts +6 -0
- package/dist/testing/index.mjs +7 -0
- package/dist/testing/types.cjs +15 -0
- package/dist/testing/types.d.cts +90 -0
- package/dist/testing/types.d.cts.map +1 -0
- package/dist/testing/types.d.mts +90 -0
- package/dist/testing/types.d.mts.map +1 -0
- package/dist/testing/types.mjs +16 -0
- package/dist/testing/types.mjs.map +1 -0
- package/package.json +18 -3
- package/src/ColdStorage.ts +136 -0
- package/src/DocumentManager.ts +550 -190
- package/src/Errors.ts +114 -0
- package/src/HotStorage.ts +239 -0
- package/src/Metrics.ts +187 -0
- package/src/MimicAuthService.ts +126 -64
- package/src/MimicClusterServerEngine.ts +946 -0
- package/src/MimicServer.ts +448 -195
- package/src/MimicServerEngine.ts +276 -0
- package/src/PresenceManager.ts +169 -240
- package/src/Protocol.ts +350 -0
- package/src/Types.ts +231 -0
- package/src/index.ts +57 -23
- package/src/testing/ColdStorageTestSuite.ts +589 -0
- package/src/testing/FailingStorage.ts +286 -0
- package/src/testing/HotStorageTestSuite.ts +762 -0
- package/src/testing/StorageIntegrationTestSuite.ts +504 -0
- package/src/testing/assertions.ts +181 -0
- package/src/testing/index.ts +83 -0
- package/src/testing/types.ts +100 -0
- package/tests/ColdStorage.test.ts +24 -0
- package/tests/DocumentManager.test.ts +158 -287
- package/tests/HotStorage.test.ts +24 -0
- package/tests/MimicAuthService.test.ts +102 -134
- package/tests/MimicClusterServerEngine.test.ts +587 -0
- package/tests/MimicServer.test.ts +90 -226
- package/tests/MimicServerEngine.test.ts +521 -0
- package/tests/PresenceManager.test.ts +22 -63
- package/tests/Protocol.test.ts +190 -0
- package/tests/StorageIntegration.test.ts +259 -0
- package/tsconfig.json +1 -1
- package/tsdown.config.ts +1 -1
- package/dist/DocumentProtocol.cjs +0 -94
- package/dist/DocumentProtocol.d.cts +0 -113
- package/dist/DocumentProtocol.d.cts.map +0 -1
- package/dist/DocumentProtocol.d.mts +0 -113
- package/dist/DocumentProtocol.d.mts.map +0 -1
- package/dist/DocumentProtocol.mjs +0 -89
- package/dist/DocumentProtocol.mjs.map +0 -1
- package/dist/MimicConfig.cjs +0 -60
- package/dist/MimicConfig.d.cts +0 -141
- package/dist/MimicConfig.d.cts.map +0 -1
- package/dist/MimicConfig.d.mts +0 -141
- package/dist/MimicConfig.d.mts.map +0 -1
- package/dist/MimicConfig.mjs +0 -50
- package/dist/MimicConfig.mjs.map +0 -1
- package/dist/MimicDataStorage.cjs +0 -83
- package/dist/MimicDataStorage.d.cts +0 -113
- package/dist/MimicDataStorage.d.cts.map +0 -1
- package/dist/MimicDataStorage.d.mts +0 -113
- package/dist/MimicDataStorage.d.mts.map +0 -1
- package/dist/MimicDataStorage.mjs +0 -74
- package/dist/MimicDataStorage.mjs.map +0 -1
- package/dist/WebSocketHandler.cjs +0 -365
- package/dist/WebSocketHandler.d.cts +0 -34
- package/dist/WebSocketHandler.d.cts.map +0 -1
- package/dist/WebSocketHandler.d.mts +0 -34
- package/dist/WebSocketHandler.d.mts.map +0 -1
- package/dist/WebSocketHandler.mjs +0 -355
- package/dist/WebSocketHandler.mjs.map +0 -1
- package/dist/auth/NoAuth.cjs +0 -43
- package/dist/auth/NoAuth.d.cts +0 -22
- package/dist/auth/NoAuth.d.cts.map +0 -1
- package/dist/auth/NoAuth.d.mts +0 -22
- package/dist/auth/NoAuth.d.mts.map +0 -1
- package/dist/auth/NoAuth.mjs +0 -36
- package/dist/auth/NoAuth.mjs.map +0 -1
- package/dist/errors.cjs +0 -74
- package/dist/errors.d.cts +0 -89
- package/dist/errors.d.cts.map +0 -1
- package/dist/errors.d.mts +0 -89
- package/dist/errors.d.mts.map +0 -1
- package/dist/errors.mjs +0 -67
- package/dist/errors.mjs.map +0 -1
- package/dist/storage/InMemoryDataStorage.cjs +0 -57
- package/dist/storage/InMemoryDataStorage.d.cts +0 -19
- package/dist/storage/InMemoryDataStorage.d.cts.map +0 -1
- package/dist/storage/InMemoryDataStorage.d.mts +0 -19
- package/dist/storage/InMemoryDataStorage.d.mts.map +0 -1
- package/dist/storage/InMemoryDataStorage.mjs +0 -48
- package/dist/storage/InMemoryDataStorage.mjs.map +0 -1
- package/src/DocumentProtocol.ts +0 -112
- package/src/MimicConfig.ts +0 -211
- package/src/MimicDataStorage.ts +0 -157
- package/src/WebSocketHandler.ts +0 -735
- package/src/auth/NoAuth.ts +0 -46
- package/src/errors.ts +0 -113
- package/src/storage/InMemoryDataStorage.ts +0 -66
- package/tests/DocumentProtocol.test.ts +0 -113
- package/tests/InMemoryDataStorage.test.ts +0 -190
- package/tests/MimicConfig.test.ts +0 -290
- package/tests/MimicDataStorage.test.ts +0 -190
- package/tests/NoAuth.test.ts +0 -94
- package/tests/WebSocketHandler.test.ts +0 -321
- package/tests/errors.test.ts +0 -77
package/dist/PresenceManager.mjs
CHANGED
|
@@ -1,95 +1,91 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import * as PubSub from "effect/PubSub";
|
|
5
|
-
import * as Ref from "effect/Ref";
|
|
6
|
-
import * as HashMap from "effect/HashMap";
|
|
7
|
-
import * as Context from "effect/Context";
|
|
8
|
-
import * as Stream from "effect/Stream";
|
|
1
|
+
import { presenceActive, presenceUpdates } from "./Metrics.mjs";
|
|
2
|
+
import { _objectSpread2 } from "./_virtual/_@oxc-project_runtime@0.103.0/helpers/objectSpread2.mjs";
|
|
3
|
+
import { Context, Effect, HashMap, Layer, Metric, PubSub, Ref, Stream } from "effect";
|
|
9
4
|
|
|
10
5
|
//#region src/PresenceManager.ts
|
|
11
6
|
/**
|
|
12
|
-
* @
|
|
13
|
-
*
|
|
14
|
-
*
|
|
7
|
+
* @voidhash/mimic-effect - PresenceManager
|
|
8
|
+
*
|
|
9
|
+
* Internal service for managing presence state per document.
|
|
15
10
|
*/
|
|
16
|
-
var PresenceManager_exports = /* @__PURE__ */ __export({
|
|
17
|
-
PresenceManagerTag: () => PresenceManagerTag,
|
|
18
|
-
layer: () => layer,
|
|
19
|
-
layerDefault: () => layerDefault
|
|
20
|
-
});
|
|
21
11
|
/**
|
|
22
|
-
* Context tag for PresenceManager
|
|
12
|
+
* Context tag for PresenceManager service
|
|
23
13
|
*/
|
|
24
|
-
var PresenceManagerTag = class extends Context.Tag("@voidhash/mimic-
|
|
14
|
+
var PresenceManagerTag = class extends Context.Tag("@voidhash/mimic-effect/PresenceManager")() {};
|
|
25
15
|
/**
|
|
26
|
-
* Create the PresenceManager
|
|
16
|
+
* Create the PresenceManager layer.
|
|
27
17
|
*/
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
18
|
+
const layer = Layer.effect(PresenceManagerTag, Effect.gen(function* () {
|
|
19
|
+
const store = yield* Ref.make(HashMap.empty());
|
|
20
|
+
/**
|
|
21
|
+
* Get or create presence state for a document
|
|
22
|
+
*/
|
|
23
|
+
const getOrCreateDocumentState = (documentId) => Effect.gen(function* () {
|
|
24
|
+
const current = yield* Ref.get(store);
|
|
32
25
|
const existing = HashMap.get(current, documentId);
|
|
33
26
|
if (existing._tag === "Some") return existing.value;
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
27
|
+
const pubsub = yield* PubSub.unbounded();
|
|
28
|
+
const state = {
|
|
29
|
+
presences: HashMap.empty(),
|
|
30
|
+
pubsub
|
|
37
31
|
};
|
|
38
|
-
yield* Ref.update(
|
|
39
|
-
return
|
|
40
|
-
});
|
|
41
|
-
const getSnapshot = (documentId) => Effect.gen(function* () {
|
|
42
|
-
const docPresence = yield* getOrCreateDocument(documentId);
|
|
43
|
-
const entriesMap = yield* Ref.get(docPresence.entries);
|
|
44
|
-
const presences = {};
|
|
45
|
-
for (const [id, entry] of entriesMap) presences[id] = entry;
|
|
46
|
-
return { presences };
|
|
47
|
-
});
|
|
48
|
-
const set = (documentId, connectionId, entry) => Effect.gen(function* () {
|
|
49
|
-
const docPresence = yield* getOrCreateDocument(documentId);
|
|
50
|
-
yield* Ref.update(docPresence.entries, (map) => HashMap.set(map, connectionId, entry));
|
|
51
|
-
yield* PubSub.publish(docPresence.pubsub, {
|
|
52
|
-
type: "presence_update",
|
|
53
|
-
id: connectionId,
|
|
54
|
-
data: entry.data,
|
|
55
|
-
userId: entry.userId
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
const remove = (documentId, connectionId) => Effect.gen(function* () {
|
|
59
|
-
const current = yield* Ref.get(documents);
|
|
60
|
-
const existing = HashMap.get(current, documentId);
|
|
61
|
-
if (existing._tag === "None") return;
|
|
62
|
-
const docPresence = existing.value;
|
|
63
|
-
const entries = yield* Ref.get(docPresence.entries);
|
|
64
|
-
if (!HashMap.has(entries, connectionId)) return;
|
|
65
|
-
yield* Ref.update(docPresence.entries, (map) => HashMap.remove(map, connectionId));
|
|
66
|
-
yield* PubSub.publish(docPresence.pubsub, {
|
|
67
|
-
type: "presence_remove",
|
|
68
|
-
id: connectionId
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
const subscribe = (documentId) => Effect.gen(function* () {
|
|
72
|
-
const docPresence = yield* getOrCreateDocument(documentId);
|
|
73
|
-
const queue = yield* PubSub.subscribe(docPresence.pubsub);
|
|
74
|
-
return Stream.fromQueue(queue);
|
|
32
|
+
yield* Ref.update(store, (map) => HashMap.set(map, documentId, state));
|
|
33
|
+
return state;
|
|
75
34
|
});
|
|
76
35
|
return {
|
|
77
|
-
getSnapshot
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
36
|
+
getSnapshot: (documentId) => Effect.gen(function* () {
|
|
37
|
+
const current = yield* Ref.get(store);
|
|
38
|
+
const existing = HashMap.get(current, documentId);
|
|
39
|
+
if (existing._tag === "None") return { presences: {} };
|
|
40
|
+
const presences = {};
|
|
41
|
+
for (const [id, entry] of existing.value.presences) presences[id] = entry;
|
|
42
|
+
return { presences };
|
|
43
|
+
}),
|
|
44
|
+
set: (documentId, connectionId, entry) => Effect.gen(function* () {
|
|
45
|
+
const state = yield* getOrCreateDocumentState(documentId);
|
|
46
|
+
yield* Ref.update(store, (map) => {
|
|
47
|
+
const existing = HashMap.get(map, documentId);
|
|
48
|
+
if (existing._tag === "None") return map;
|
|
49
|
+
return HashMap.set(map, documentId, _objectSpread2(_objectSpread2({}, existing.value), {}, { presences: HashMap.set(existing.value.presences, connectionId, entry) }));
|
|
50
|
+
});
|
|
51
|
+
yield* Metric.increment(presenceUpdates);
|
|
52
|
+
yield* Metric.incrementBy(presenceActive, 1);
|
|
53
|
+
const event = {
|
|
54
|
+
type: "presence_update",
|
|
55
|
+
id: connectionId,
|
|
56
|
+
data: entry.data,
|
|
57
|
+
userId: entry.userId
|
|
58
|
+
};
|
|
59
|
+
yield* PubSub.publish(state.pubsub, event);
|
|
60
|
+
}),
|
|
61
|
+
remove: (documentId, connectionId) => Effect.gen(function* () {
|
|
62
|
+
const current = yield* Ref.get(store);
|
|
63
|
+
const existing = HashMap.get(current, documentId);
|
|
64
|
+
if (existing._tag === "None") return;
|
|
65
|
+
if (!HashMap.has(existing.value.presences, connectionId)) return;
|
|
66
|
+
yield* Ref.update(store, (map) => {
|
|
67
|
+
const docState = HashMap.get(map, documentId);
|
|
68
|
+
if (docState._tag === "None") return map;
|
|
69
|
+
return HashMap.set(map, documentId, _objectSpread2(_objectSpread2({}, docState.value), {}, { presences: HashMap.remove(docState.value.presences, connectionId) }));
|
|
70
|
+
});
|
|
71
|
+
yield* Metric.incrementBy(presenceActive, -1);
|
|
72
|
+
const event = {
|
|
73
|
+
type: "presence_remove",
|
|
74
|
+
id: connectionId
|
|
75
|
+
};
|
|
76
|
+
yield* PubSub.publish(existing.value.pubsub, event);
|
|
77
|
+
}),
|
|
78
|
+
subscribe: (documentId) => Effect.gen(function* () {
|
|
79
|
+
const state = yield* getOrCreateDocumentState(documentId);
|
|
80
|
+
return Stream.fromPubSub(state.pubsub);
|
|
81
|
+
})
|
|
81
82
|
};
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Default layer that provides PresenceManager.
|
|
89
|
-
* Uses the default priority for layer composition.
|
|
90
|
-
*/
|
|
91
|
-
const layerDefault = Layer.effectDiscard(Effect.succeed(void 0)).pipe(Layer.provideMerge(layer));
|
|
83
|
+
}));
|
|
84
|
+
const PresenceManager = {
|
|
85
|
+
Tag: PresenceManagerTag,
|
|
86
|
+
layer
|
|
87
|
+
};
|
|
92
88
|
|
|
93
89
|
//#endregion
|
|
94
|
-
export {
|
|
90
|
+
export { PresenceManager, PresenceManagerTag, layer };
|
|
95
91
|
//# sourceMappingURL=PresenceManager.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PresenceManager.mjs","names":["docPresence: DocumentPresence","presences: Record<string, PresenceEntry>","layer: Layer.Layer<PresenceManagerTag>","layerDefault: Layer.Layer<PresenceManagerTag>"],"sources":["../src/PresenceManager.ts"],"sourcesContent":["/**\n * @since 0.0.1\n * Presence manager for ephemeral per-connection state.\n * Handles in-memory storage and broadcasting of presence updates.\n */\nimport * as Effect from \"effect/Effect\";\nimport * as Layer from \"effect/Layer\";\nimport * as PubSub from \"effect/PubSub\";\nimport * as Ref from \"effect/Ref\";\nimport * as HashMap from \"effect/HashMap\";\nimport * as Context from \"effect/Context\";\nimport * as Scope from \"effect/Scope\";\nimport * as Stream from \"effect/Stream\";\nimport type { Presence } from \"@voidhash/mimic\";\n\n// =============================================================================\n// Presence Entry Types\n// =============================================================================\n\n/**\n * A presence entry stored in the manager.\n */\nexport interface PresenceEntry {\n /** The presence data */\n readonly data: unknown;\n /** Optional user ID from authentication */\n readonly userId?: string;\n}\n\n// =============================================================================\n// Presence Events\n// =============================================================================\n\n/**\n * Event emitted when a presence is updated.\n */\nexport interface PresenceUpdateEvent {\n readonly type: \"presence_update\";\n /** The connection ID of the user who updated */\n readonly id: string;\n /** The presence data */\n readonly data: unknown;\n /** Optional user ID from authentication */\n readonly userId?: string;\n}\n\n/**\n * Event emitted when a presence is removed (user disconnected).\n */\nexport interface PresenceRemoveEvent {\n readonly type: \"presence_remove\";\n /** The connection ID of the user who disconnected */\n readonly id: string;\n}\n\n/**\n * Union of all presence events.\n */\nexport type PresenceEvent = PresenceUpdateEvent | PresenceRemoveEvent;\n\n// =============================================================================\n// Presence Snapshot\n// =============================================================================\n\n/**\n * A snapshot of all presence entries for a document.\n */\nexport interface PresenceSnapshot {\n /** Map of connectionId to presence entry */\n readonly presences: Record<string, PresenceEntry>;\n}\n\n// =============================================================================\n// Document Presence Instance\n// =============================================================================\n\n/**\n * Per-document presence state.\n */\ninterface DocumentPresence {\n /** Map of connectionId to presence entry */\n readonly entries: Ref.Ref<HashMap.HashMap<string, PresenceEntry>>;\n /** PubSub for broadcasting presence events */\n readonly pubsub: PubSub.PubSub<PresenceEvent>;\n}\n\n// =============================================================================\n// Presence Manager Service\n// =============================================================================\n\n/**\n * Service interface for the PresenceManager.\n */\nexport interface PresenceManager {\n /**\n * Get a snapshot of all presences for a document.\n */\n readonly getSnapshot: (\n documentId: string\n ) => Effect.Effect<PresenceSnapshot>;\n\n /**\n * Set/update presence for a connection.\n * Broadcasts the update to all subscribers.\n */\n readonly set: (\n documentId: string,\n connectionId: string,\n entry: PresenceEntry\n ) => Effect.Effect<void>;\n\n /**\n * Remove presence for a connection (e.g., on disconnect).\n * Broadcasts the removal to all subscribers.\n */\n readonly remove: (\n documentId: string,\n connectionId: string\n ) => Effect.Effect<void>;\n\n /**\n * Subscribe to presence events for a document.\n * Returns a Stream of presence events.\n */\n readonly subscribe: (\n documentId: string\n ) => Effect.Effect<\n Stream.Stream<PresenceEvent>,\n never,\n Scope.Scope\n >;\n}\n\n/**\n * Context tag for PresenceManager.\n */\nexport class PresenceManagerTag extends Context.Tag(\n \"@voidhash/mimic-server-effect/PresenceManager\"\n)<PresenceManagerTag, PresenceManager>() {}\n\n// =============================================================================\n// Presence Manager Implementation\n// =============================================================================\n\n/**\n * Create the PresenceManager service.\n */\nconst makePresenceManager = Effect.gen(function* () {\n // Map of document ID to document presence state\n const documents = yield* Ref.make(\n HashMap.empty<string, DocumentPresence>()\n );\n\n // Get or create a document presence instance\n const getOrCreateDocument = (\n documentId: string\n ): Effect.Effect<DocumentPresence> =>\n Effect.gen(function* () {\n const current = yield* Ref.get(documents);\n const existing = HashMap.get(current, documentId);\n\n if (existing._tag === \"Some\") {\n return existing.value;\n }\n\n // Create new document presence\n const entries = yield* Ref.make(\n HashMap.empty<string, PresenceEntry>()\n );\n const pubsub = yield* PubSub.unbounded<PresenceEvent>();\n\n const docPresence: DocumentPresence = {\n entries,\n pubsub,\n };\n\n // Store in map\n yield* Ref.update(documents, (map) =>\n HashMap.set(map, documentId, docPresence)\n );\n\n return docPresence;\n });\n\n // Get snapshot of all presences for a document\n const getSnapshot = (documentId: string): Effect.Effect<PresenceSnapshot> =>\n Effect.gen(function* () {\n const docPresence = yield* getOrCreateDocument(documentId);\n const entriesMap = yield* Ref.get(docPresence.entries);\n\n // Convert HashMap to Record\n const presences: Record<string, PresenceEntry> = {};\n for (const [id, entry] of entriesMap) {\n presences[id] = entry;\n }\n\n return { presences };\n });\n\n // Set/update presence for a connection\n const set = (\n documentId: string,\n connectionId: string,\n entry: PresenceEntry\n ): Effect.Effect<void> =>\n Effect.gen(function* () {\n const docPresence = yield* getOrCreateDocument(documentId);\n\n // Update the entry\n yield* Ref.update(docPresence.entries, (map) =>\n HashMap.set(map, connectionId, entry)\n );\n\n // Broadcast the update\n yield* PubSub.publish(docPresence.pubsub, {\n type: \"presence_update\",\n id: connectionId,\n data: entry.data,\n userId: entry.userId,\n });\n });\n\n // Remove presence for a connection\n const remove = (\n documentId: string,\n connectionId: string\n ): Effect.Effect<void> =>\n Effect.gen(function* () {\n const current = yield* Ref.get(documents);\n const existing = HashMap.get(current, documentId);\n\n if (existing._tag === \"None\") {\n return; // Document doesn't exist, nothing to remove\n }\n\n const docPresence = existing.value;\n\n // Check if the connection has a presence\n const entries = yield* Ref.get(docPresence.entries);\n const hasEntry = HashMap.has(entries, connectionId);\n\n if (!hasEntry) {\n return; // No presence to remove\n }\n\n // Remove the entry\n yield* Ref.update(docPresence.entries, (map) =>\n HashMap.remove(map, connectionId)\n );\n\n // Broadcast the removal\n yield* PubSub.publish(docPresence.pubsub, {\n type: \"presence_remove\",\n id: connectionId,\n });\n });\n\n // Subscribe to presence events\n const subscribe = (\n documentId: string\n ): Effect.Effect<Stream.Stream<PresenceEvent>, never, Scope.Scope> =>\n Effect.gen(function* () {\n const docPresence = yield* getOrCreateDocument(documentId);\n\n // Subscribe to the PubSub\n const queue = yield* PubSub.subscribe(docPresence.pubsub);\n\n // Convert queue to stream\n return Stream.fromQueue(queue);\n });\n\n const manager: PresenceManager = {\n getSnapshot,\n set,\n remove,\n subscribe,\n };\n\n return manager;\n});\n\n/**\n * Layer that provides PresenceManager.\n */\nexport const layer: Layer.Layer<PresenceManagerTag> = Layer.effect(\n PresenceManagerTag,\n makePresenceManager\n);\n\n/**\n * Default layer that provides PresenceManager.\n * Uses the default priority for layer composition.\n */\nexport const layerDefault: Layer.Layer<PresenceManagerTag> = Layer.effectDiscard(\n Effect.succeed(undefined)\n).pipe(Layer.provideMerge(layer));\n\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAwIA,IAAa,qBAAb,cAAwC,QAAQ,IAC9C,gDACD,EAAuC,CAAC;;;;AASzC,MAAM,sBAAsB,OAAO,IAAI,aAAa;CAElD,MAAM,YAAY,OAAO,IAAI,KAC3B,QAAQ,OAAiC,CAC1C;CAGD,MAAM,uBACJ,eAEA,OAAO,IAAI,aAAa;EACtB,MAAM,UAAU,OAAO,IAAI,IAAI,UAAU;EACzC,MAAM,WAAW,QAAQ,IAAI,SAAS,WAAW;AAEjD,MAAI,SAAS,SAAS,OACpB,QAAO,SAAS;EASlB,MAAMA,cAAgC;GACpC,SANc,OAAO,IAAI,KACzB,QAAQ,OAA8B,CACvC;GAKC,QAJa,OAAO,OAAO,WAA0B;GAKtD;AAGD,SAAO,IAAI,OAAO,YAAY,QAC5B,QAAQ,IAAI,KAAK,YAAY,YAAY,CAC1C;AAED,SAAO;GACP;CAGJ,MAAM,eAAe,eACnB,OAAO,IAAI,aAAa;EACtB,MAAM,cAAc,OAAO,oBAAoB,WAAW;EAC1D,MAAM,aAAa,OAAO,IAAI,IAAI,YAAY,QAAQ;EAGtD,MAAMC,YAA2C,EAAE;AACnD,OAAK,MAAM,CAAC,IAAI,UAAU,WACxB,WAAU,MAAM;AAGlB,SAAO,EAAE,WAAW;GACpB;CAGJ,MAAM,OACJ,YACA,cACA,UAEA,OAAO,IAAI,aAAa;EACtB,MAAM,cAAc,OAAO,oBAAoB,WAAW;AAG1D,SAAO,IAAI,OAAO,YAAY,UAAU,QACtC,QAAQ,IAAI,KAAK,cAAc,MAAM,CACtC;AAGD,SAAO,OAAO,QAAQ,YAAY,QAAQ;GACxC,MAAM;GACN,IAAI;GACJ,MAAM,MAAM;GACZ,QAAQ,MAAM;GACf,CAAC;GACF;CAGJ,MAAM,UACJ,YACA,iBAEA,OAAO,IAAI,aAAa;EACtB,MAAM,UAAU,OAAO,IAAI,IAAI,UAAU;EACzC,MAAM,WAAW,QAAQ,IAAI,SAAS,WAAW;AAEjD,MAAI,SAAS,SAAS,OACpB;EAGF,MAAM,cAAc,SAAS;EAG7B,MAAM,UAAU,OAAO,IAAI,IAAI,YAAY,QAAQ;AAGnD,MAAI,CAFa,QAAQ,IAAI,SAAS,aAAa,CAGjD;AAIF,SAAO,IAAI,OAAO,YAAY,UAAU,QACtC,QAAQ,OAAO,KAAK,aAAa,CAClC;AAGD,SAAO,OAAO,QAAQ,YAAY,QAAQ;GACxC,MAAM;GACN,IAAI;GACL,CAAC;GACF;CAGJ,MAAM,aACJ,eAEA,OAAO,IAAI,aAAa;EACtB,MAAM,cAAc,OAAO,oBAAoB,WAAW;EAG1D,MAAM,QAAQ,OAAO,OAAO,UAAU,YAAY,OAAO;AAGzD,SAAO,OAAO,UAAU,MAAM;GAC9B;AASJ,QAPiC;EAC/B;EACA;EACA;EACA;EACD;EAGD;;;;AAKF,MAAaC,QAAyC,MAAM,OAC1D,oBACA,oBACD;;;;;AAMD,MAAaC,eAAgD,MAAM,cACjE,OAAO,QAAQ,OAAU,CAC1B,CAAC,KAAK,MAAM,aAAa,MAAM,CAAC"}
|
|
1
|
+
{"version":3,"file":"PresenceManager.mjs","names":["layer: Layer.Layer<PresenceManagerTag>","state: DocumentPresenceState","presences: Record<string, PresenceEntry>","Metrics.presenceUpdates","Metrics.presenceActive","event: PresenceEvent"],"sources":["../src/PresenceManager.ts"],"sourcesContent":["/**\n * @voidhash/mimic-effect - PresenceManager\n *\n * Internal service for managing presence state per document.\n */\nimport {\n Context,\n Effect,\n HashMap,\n Layer,\n Metric,\n PubSub,\n Ref,\n Scope,\n Stream,\n} from \"effect\";\nimport type {\n PresenceEntry,\n PresenceEvent,\n PresenceSnapshot,\n} from \"./Types\";\nimport * as Metrics from \"./Metrics\";\n\n// =============================================================================\n// PresenceManager Interface\n// =============================================================================\n\n/**\n * Internal service for managing presence state per document.\n *\n * Presence is ephemeral state associated with connections, not persisted.\n * Each document has its own set of presences, keyed by connectionId.\n */\nexport interface PresenceManager {\n /**\n * Get snapshot of all presences for a document.\n */\n readonly getSnapshot: (\n documentId: string\n ) => Effect.Effect<PresenceSnapshot>;\n\n /**\n * Set/update presence for a connection.\n */\n readonly set: (\n documentId: string,\n connectionId: string,\n entry: PresenceEntry\n ) => Effect.Effect<void>;\n\n /**\n * Remove presence for a connection (on disconnect).\n */\n readonly remove: (\n documentId: string,\n connectionId: string\n ) => Effect.Effect<void>;\n\n /**\n * Subscribe to presence events for a document.\n * Returns a stream of presence update/remove events.\n */\n readonly subscribe: (\n documentId: string\n ) => Effect.Effect<Stream.Stream<PresenceEvent>, never, Scope.Scope>;\n}\n\n// =============================================================================\n// Context Tag\n// =============================================================================\n\n/**\n * Context tag for PresenceManager service\n */\nexport class PresenceManagerTag extends Context.Tag(\n \"@voidhash/mimic-effect/PresenceManager\"\n)<PresenceManagerTag, PresenceManager>() {}\n\n// =============================================================================\n// Internal Types\n// =============================================================================\n\n/**\n * Per-document presence state\n */\ninterface DocumentPresenceState {\n readonly presences: HashMap.HashMap<string, PresenceEntry>;\n readonly pubsub: PubSub.PubSub<PresenceEvent>;\n}\n\n// =============================================================================\n// Layer Implementation\n// =============================================================================\n\n/**\n * Create the PresenceManager layer.\n */\nexport const layer: Layer.Layer<PresenceManagerTag> = Layer.effect(\n PresenceManagerTag,\n Effect.gen(function* () {\n // Store: documentId -> DocumentPresenceState\n const store = yield* Ref.make(\n HashMap.empty<string, DocumentPresenceState>()\n );\n\n /**\n * Get or create presence state for a document\n */\n const getOrCreateDocumentState = (\n documentId: string\n ): Effect.Effect<DocumentPresenceState> =>\n Effect.gen(function* () {\n const current = yield* Ref.get(store);\n const existing = HashMap.get(current, documentId);\n if (existing._tag === \"Some\") {\n return existing.value;\n }\n\n // Create new state for this document\n const pubsub = yield* PubSub.unbounded<PresenceEvent>();\n const state: DocumentPresenceState = {\n presences: HashMap.empty(),\n pubsub,\n };\n\n yield* Ref.update(store, (map) => HashMap.set(map, documentId, state));\n return state;\n });\n\n return {\n getSnapshot: (documentId) =>\n Effect.gen(function* () {\n const current = yield* Ref.get(store);\n const existing = HashMap.get(current, documentId);\n if (existing._tag === \"None\") {\n return { presences: {} };\n }\n\n // Convert HashMap to Record\n const presences: Record<string, PresenceEntry> = {};\n for (const [id, entry] of existing.value.presences) {\n presences[id] = entry;\n }\n return { presences };\n }),\n\n set: (documentId, connectionId, entry) =>\n Effect.gen(function* () {\n const state = yield* getOrCreateDocumentState(documentId);\n\n // Update presence in store\n yield* Ref.update(store, (map) => {\n const existing = HashMap.get(map, documentId);\n if (existing._tag === \"None\") return map;\n return HashMap.set(map, documentId, {\n ...existing.value,\n presences: HashMap.set(\n existing.value.presences,\n connectionId,\n entry\n ),\n });\n });\n\n // Track metrics\n yield* Metric.increment(Metrics.presenceUpdates);\n yield* Metric.incrementBy(Metrics.presenceActive, 1);\n\n // Broadcast update event\n const event: PresenceEvent = {\n type: \"presence_update\",\n id: connectionId,\n data: entry.data,\n userId: entry.userId,\n };\n yield* PubSub.publish(state.pubsub, event);\n }),\n\n remove: (documentId, connectionId) =>\n Effect.gen(function* () {\n const current = yield* Ref.get(store);\n const existing = HashMap.get(current, documentId);\n if (existing._tag === \"None\") return;\n\n // Check if presence exists before removing\n const hasPresence = HashMap.has(existing.value.presences, connectionId);\n if (!hasPresence) return;\n\n // Remove presence from store\n yield* Ref.update(store, (map) => {\n const docState = HashMap.get(map, documentId);\n if (docState._tag === \"None\") return map;\n return HashMap.set(map, documentId, {\n ...docState.value,\n presences: HashMap.remove(docState.value.presences, connectionId),\n });\n });\n\n // Track metrics\n yield* Metric.incrementBy(Metrics.presenceActive, -1);\n\n // Broadcast remove event\n const event: PresenceEvent = {\n type: \"presence_remove\",\n id: connectionId,\n };\n yield* PubSub.publish(existing.value.pubsub, event);\n }),\n\n subscribe: (documentId) =>\n Effect.gen(function* () {\n const state = yield* getOrCreateDocumentState(documentId);\n return Stream.fromPubSub(state.pubsub);\n }),\n };\n })\n);\n\n// =============================================================================\n// Re-export namespace\n// =============================================================================\n\nexport const PresenceManager = {\n Tag: PresenceManagerTag,\n layer,\n};\n"],"mappings":";;;;;;;;;;;;;AA0EA,IAAa,qBAAb,cAAwC,QAAQ,IAC9C,yCACD,EAAuC,CAAC;;;;AAqBzC,MAAaA,QAAyC,MAAM,OAC1D,oBACA,OAAO,IAAI,aAAa;CAEtB,MAAM,QAAQ,OAAO,IAAI,KACvB,QAAQ,OAAsC,CAC/C;;;;CAKD,MAAM,4BACJ,eAEA,OAAO,IAAI,aAAa;EACtB,MAAM,UAAU,OAAO,IAAI,IAAI,MAAM;EACrC,MAAM,WAAW,QAAQ,IAAI,SAAS,WAAW;AACjD,MAAI,SAAS,SAAS,OACpB,QAAO,SAAS;EAIlB,MAAM,SAAS,OAAO,OAAO,WAA0B;EACvD,MAAMC,QAA+B;GACnC,WAAW,QAAQ,OAAO;GAC1B;GACD;AAED,SAAO,IAAI,OAAO,QAAQ,QAAQ,QAAQ,IAAI,KAAK,YAAY,MAAM,CAAC;AACtE,SAAO;GACP;AAEJ,QAAO;EACL,cAAc,eACZ,OAAO,IAAI,aAAa;GACtB,MAAM,UAAU,OAAO,IAAI,IAAI,MAAM;GACrC,MAAM,WAAW,QAAQ,IAAI,SAAS,WAAW;AACjD,OAAI,SAAS,SAAS,OACpB,QAAO,EAAE,WAAW,EAAE,EAAE;GAI1B,MAAMC,YAA2C,EAAE;AACnD,QAAK,MAAM,CAAC,IAAI,UAAU,SAAS,MAAM,UACvC,WAAU,MAAM;AAElB,UAAO,EAAE,WAAW;IACpB;EAEJ,MAAM,YAAY,cAAc,UAC9B,OAAO,IAAI,aAAa;GACtB,MAAM,QAAQ,OAAO,yBAAyB,WAAW;AAGzD,UAAO,IAAI,OAAO,QAAQ,QAAQ;IAChC,MAAM,WAAW,QAAQ,IAAI,KAAK,WAAW;AAC7C,QAAI,SAAS,SAAS,OAAQ,QAAO;AACrC,WAAO,QAAQ,IAAI,KAAK,8CACnB,SAAS,cACZ,WAAW,QAAQ,IACjB,SAAS,MAAM,WACf,cACA,MACD,IACD;KACF;AAGF,UAAO,OAAO,UAAUC,gBAAwB;AAChD,UAAO,OAAO,YAAYC,gBAAwB,EAAE;GAGpD,MAAMC,QAAuB;IAC3B,MAAM;IACN,IAAI;IACJ,MAAM,MAAM;IACZ,QAAQ,MAAM;IACf;AACD,UAAO,OAAO,QAAQ,MAAM,QAAQ,MAAM;IAC1C;EAEJ,SAAS,YAAY,iBACnB,OAAO,IAAI,aAAa;GACtB,MAAM,UAAU,OAAO,IAAI,IAAI,MAAM;GACrC,MAAM,WAAW,QAAQ,IAAI,SAAS,WAAW;AACjD,OAAI,SAAS,SAAS,OAAQ;AAI9B,OAAI,CADgB,QAAQ,IAAI,SAAS,MAAM,WAAW,aAAa,CACrD;AAGlB,UAAO,IAAI,OAAO,QAAQ,QAAQ;IAChC,MAAM,WAAW,QAAQ,IAAI,KAAK,WAAW;AAC7C,QAAI,SAAS,SAAS,OAAQ,QAAO;AACrC,WAAO,QAAQ,IAAI,KAAK,8CACnB,SAAS,cACZ,WAAW,QAAQ,OAAO,SAAS,MAAM,WAAW,aAAa,IACjE;KACF;AAGF,UAAO,OAAO,YAAYD,gBAAwB,GAAG;GAGrD,MAAMC,QAAuB;IAC3B,MAAM;IACN,IAAI;IACL;AACD,UAAO,OAAO,QAAQ,SAAS,MAAM,QAAQ,MAAM;IACnD;EAEJ,YAAY,eACV,OAAO,IAAI,aAAa;GACtB,MAAM,QAAQ,OAAO,yBAAyB,WAAW;AACzD,UAAO,OAAO,WAAW,MAAM,OAAO;IACtC;EACL;EACD,CACH;AAMD,MAAa,kBAAkB;CAC7B,KAAK;CACL;CACD"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
|
|
2
|
+
const require_Errors = require('./Errors.cjs');
|
|
3
|
+
let effect = require("effect");
|
|
4
|
+
let _voidhash_mimic = require("@voidhash/mimic");
|
|
5
|
+
|
|
6
|
+
//#region src/Protocol.ts
|
|
7
|
+
/**
|
|
8
|
+
* @voidhash/mimic-effect - WebSocket Protocol
|
|
9
|
+
*
|
|
10
|
+
* Message types and encoding/decoding for WebSocket communication.
|
|
11
|
+
*/
|
|
12
|
+
var Protocol_exports = /* @__PURE__ */ require_rolldown_runtime.__export({
|
|
13
|
+
authResultFailure: () => authResultFailure,
|
|
14
|
+
authResultSuccess: () => authResultSuccess,
|
|
15
|
+
decodeClientMessage: () => decodeClientMessage,
|
|
16
|
+
encodeServerMessage: () => encodeServerMessage,
|
|
17
|
+
errorMessage: () => errorMessage,
|
|
18
|
+
parseClientMessage: () => parseClientMessage,
|
|
19
|
+
pong: () => pong,
|
|
20
|
+
presenceRemoveMessage: () => presenceRemoveMessage,
|
|
21
|
+
presenceSnapshotMessage: () => presenceSnapshotMessage,
|
|
22
|
+
presenceUpdateMessage: () => presenceUpdateMessage,
|
|
23
|
+
snapshotMessage: () => snapshotMessage,
|
|
24
|
+
transactionMessage: () => transactionMessage
|
|
25
|
+
});
|
|
26
|
+
/**
|
|
27
|
+
* Decode an encoded client message to a ClientMessage
|
|
28
|
+
*/
|
|
29
|
+
const decodeClientMessage = (encoded) => {
|
|
30
|
+
if (encoded.type === "submit" && encoded.transaction) return {
|
|
31
|
+
type: "submit",
|
|
32
|
+
transaction: _voidhash_mimic.Transaction.decode(encoded.transaction)
|
|
33
|
+
};
|
|
34
|
+
return encoded;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Encode a server message for wire format
|
|
38
|
+
*/
|
|
39
|
+
const encodeServerMessage = (message) => {
|
|
40
|
+
if (message.type === "transaction") {
|
|
41
|
+
const encoded = {
|
|
42
|
+
type: "transaction",
|
|
43
|
+
transaction: _voidhash_mimic.Transaction.encode(message.transaction),
|
|
44
|
+
version: message.version
|
|
45
|
+
};
|
|
46
|
+
return JSON.stringify(encoded);
|
|
47
|
+
}
|
|
48
|
+
return JSON.stringify(message);
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Parse a raw WebSocket message to a ClientMessage
|
|
52
|
+
*/
|
|
53
|
+
const parseClientMessage = (data) => effect.Effect.try({
|
|
54
|
+
try: () => {
|
|
55
|
+
const text = typeof data === "string" ? data : new TextDecoder().decode(data instanceof ArrayBuffer ? new Uint8Array(data) : data);
|
|
56
|
+
return decodeClientMessage(JSON.parse(text));
|
|
57
|
+
},
|
|
58
|
+
catch: (cause) => new require_Errors.MessageParseError({ cause })
|
|
59
|
+
});
|
|
60
|
+
/**
|
|
61
|
+
* Create an auth result success message
|
|
62
|
+
*/
|
|
63
|
+
const authResultSuccess = (userId, permission) => ({
|
|
64
|
+
type: "auth_result",
|
|
65
|
+
success: true,
|
|
66
|
+
userId,
|
|
67
|
+
permission
|
|
68
|
+
});
|
|
69
|
+
/**
|
|
70
|
+
* Create an auth result failure message
|
|
71
|
+
*/
|
|
72
|
+
const authResultFailure = (error) => ({
|
|
73
|
+
type: "auth_result",
|
|
74
|
+
success: false,
|
|
75
|
+
error
|
|
76
|
+
});
|
|
77
|
+
/**
|
|
78
|
+
* Create a pong message
|
|
79
|
+
*/
|
|
80
|
+
const pong = () => ({ type: "pong" });
|
|
81
|
+
/**
|
|
82
|
+
* Create a transaction message
|
|
83
|
+
*/
|
|
84
|
+
const transactionMessage = (transaction, version) => ({
|
|
85
|
+
type: "transaction",
|
|
86
|
+
transaction,
|
|
87
|
+
version
|
|
88
|
+
});
|
|
89
|
+
/**
|
|
90
|
+
* Create a snapshot message
|
|
91
|
+
*/
|
|
92
|
+
const snapshotMessage = (state, version) => ({
|
|
93
|
+
type: "snapshot",
|
|
94
|
+
state,
|
|
95
|
+
version
|
|
96
|
+
});
|
|
97
|
+
/**
|
|
98
|
+
* Create an error message
|
|
99
|
+
*/
|
|
100
|
+
const errorMessage = (transactionId, reason) => ({
|
|
101
|
+
type: "error",
|
|
102
|
+
transactionId,
|
|
103
|
+
reason
|
|
104
|
+
});
|
|
105
|
+
/**
|
|
106
|
+
* Create a presence update message
|
|
107
|
+
*/
|
|
108
|
+
const presenceUpdateMessage = (id, data, userId) => ({
|
|
109
|
+
type: "presence_update",
|
|
110
|
+
id,
|
|
111
|
+
data,
|
|
112
|
+
userId
|
|
113
|
+
});
|
|
114
|
+
/**
|
|
115
|
+
* Create a presence remove message
|
|
116
|
+
*/
|
|
117
|
+
const presenceRemoveMessage = (id) => ({
|
|
118
|
+
type: "presence_remove",
|
|
119
|
+
id
|
|
120
|
+
});
|
|
121
|
+
/**
|
|
122
|
+
* Create a presence snapshot message
|
|
123
|
+
*/
|
|
124
|
+
const presenceSnapshotMessage = (selfId, presences) => ({
|
|
125
|
+
type: "presence_snapshot",
|
|
126
|
+
selfId,
|
|
127
|
+
presences
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
//#endregion
|
|
131
|
+
Object.defineProperty(exports, 'Protocol_exports', {
|
|
132
|
+
enumerable: true,
|
|
133
|
+
get: function () {
|
|
134
|
+
return Protocol_exports;
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
exports.authResultFailure = authResultFailure;
|
|
138
|
+
exports.authResultSuccess = authResultSuccess;
|
|
139
|
+
exports.encodeServerMessage = encodeServerMessage;
|
|
140
|
+
exports.errorMessage = errorMessage;
|
|
141
|
+
exports.parseClientMessage = parseClientMessage;
|
|
142
|
+
exports.pong = pong;
|
|
143
|
+
exports.presenceRemoveMessage = presenceRemoveMessage;
|
|
144
|
+
exports.presenceSnapshotMessage = presenceSnapshotMessage;
|
|
145
|
+
exports.presenceUpdateMessage = presenceUpdateMessage;
|
|
146
|
+
exports.snapshotMessage = snapshotMessage;
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { Permission, PresenceEntry } from "./Types.cjs";
|
|
2
|
+
import { MessageParseError } from "./Errors.cjs";
|
|
3
|
+
import { Effect } from "effect";
|
|
4
|
+
import { Transaction } from "@voidhash/mimic";
|
|
5
|
+
|
|
6
|
+
//#region src/Protocol.d.ts
|
|
7
|
+
declare namespace Protocol_d_exports {
|
|
8
|
+
export { AuthMessage, AuthResultMessage, ClientMessage, EncodedClientMessage, EncodedServerMessage, ErrorMessage, PingMessage, PongMessage, PresenceClearMessage, PresenceRemoveMessage, PresenceSetMessage, PresenceSnapshotMessage, PresenceUpdateMessage, RequestSnapshotMessage, ServerBroadcast, ServerMessage, SnapshotMessage, SubmitMessage, TransactionMessage, authResultFailure, authResultSuccess, decodeClientMessage, encodeServerMessage, errorMessage, parseClientMessage, pong, presenceRemoveMessage, presenceSnapshotMessage, presenceUpdateMessage, snapshotMessage, transactionMessage };
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Authentication request
|
|
12
|
+
*/
|
|
13
|
+
interface AuthMessage {
|
|
14
|
+
readonly type: "auth";
|
|
15
|
+
readonly token: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Heartbeat ping
|
|
19
|
+
*/
|
|
20
|
+
interface PingMessage {
|
|
21
|
+
readonly type: "ping";
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Submit a transaction
|
|
25
|
+
*/
|
|
26
|
+
interface SubmitMessage {
|
|
27
|
+
readonly type: "submit";
|
|
28
|
+
readonly transaction: Transaction.Transaction;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Request current document snapshot
|
|
32
|
+
*/
|
|
33
|
+
interface RequestSnapshotMessage {
|
|
34
|
+
readonly type: "request_snapshot";
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Set presence data
|
|
38
|
+
*/
|
|
39
|
+
interface PresenceSetMessage {
|
|
40
|
+
readonly type: "presence_set";
|
|
41
|
+
readonly data: unknown;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Clear presence data
|
|
45
|
+
*/
|
|
46
|
+
interface PresenceClearMessage {
|
|
47
|
+
readonly type: "presence_clear";
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Union of all client messages
|
|
51
|
+
*/
|
|
52
|
+
type ClientMessage = AuthMessage | PingMessage | SubmitMessage | RequestSnapshotMessage | PresenceSetMessage | PresenceClearMessage;
|
|
53
|
+
/**
|
|
54
|
+
* Authentication result
|
|
55
|
+
*/
|
|
56
|
+
interface AuthResultMessage {
|
|
57
|
+
readonly type: "auth_result";
|
|
58
|
+
readonly success: boolean;
|
|
59
|
+
readonly error?: string;
|
|
60
|
+
readonly userId?: string;
|
|
61
|
+
readonly permission?: Permission;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Heartbeat pong
|
|
65
|
+
*/
|
|
66
|
+
interface PongMessage {
|
|
67
|
+
readonly type: "pong";
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Transaction broadcast
|
|
71
|
+
*/
|
|
72
|
+
interface TransactionMessage {
|
|
73
|
+
readonly type: "transaction";
|
|
74
|
+
readonly transaction: Transaction.Transaction;
|
|
75
|
+
readonly version: number;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Document snapshot
|
|
79
|
+
*/
|
|
80
|
+
interface SnapshotMessage {
|
|
81
|
+
readonly type: "snapshot";
|
|
82
|
+
readonly state: unknown;
|
|
83
|
+
readonly version: number;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Transaction error
|
|
87
|
+
*/
|
|
88
|
+
interface ErrorMessage {
|
|
89
|
+
readonly type: "error";
|
|
90
|
+
readonly transactionId: string;
|
|
91
|
+
readonly reason: string;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Presence update broadcast
|
|
95
|
+
*/
|
|
96
|
+
interface PresenceUpdateMessage {
|
|
97
|
+
readonly type: "presence_update";
|
|
98
|
+
readonly id: string;
|
|
99
|
+
readonly data: unknown;
|
|
100
|
+
readonly userId?: string;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Presence removal broadcast
|
|
104
|
+
*/
|
|
105
|
+
interface PresenceRemoveMessage {
|
|
106
|
+
readonly type: "presence_remove";
|
|
107
|
+
readonly id: string;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Presence snapshot (sent after auth)
|
|
111
|
+
*/
|
|
112
|
+
interface PresenceSnapshotMessage {
|
|
113
|
+
readonly type: "presence_snapshot";
|
|
114
|
+
readonly selfId: string;
|
|
115
|
+
readonly presences: Record<string, PresenceEntry>;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Union of all server messages
|
|
119
|
+
*/
|
|
120
|
+
type ServerMessage = AuthResultMessage | PongMessage | TransactionMessage | SnapshotMessage | ErrorMessage | PresenceUpdateMessage | PresenceRemoveMessage | PresenceSnapshotMessage;
|
|
121
|
+
/**
|
|
122
|
+
* Server broadcast messages (transaction or error)
|
|
123
|
+
*/
|
|
124
|
+
type ServerBroadcast = TransactionMessage | ErrorMessage;
|
|
125
|
+
/**
|
|
126
|
+
* Encoded client message (with encoded transaction)
|
|
127
|
+
*/
|
|
128
|
+
interface EncodedClientMessage {
|
|
129
|
+
readonly type: string;
|
|
130
|
+
readonly token?: string;
|
|
131
|
+
readonly transaction?: Transaction.EncodedTransaction;
|
|
132
|
+
readonly data?: unknown;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Encoded server message (with encoded transaction)
|
|
136
|
+
*/
|
|
137
|
+
interface EncodedServerMessage {
|
|
138
|
+
readonly type: string;
|
|
139
|
+
readonly success?: boolean;
|
|
140
|
+
readonly error?: string;
|
|
141
|
+
readonly userId?: string;
|
|
142
|
+
readonly permission?: Permission;
|
|
143
|
+
readonly transaction?: Transaction.EncodedTransaction;
|
|
144
|
+
readonly version?: number;
|
|
145
|
+
readonly state?: unknown;
|
|
146
|
+
readonly transactionId?: string;
|
|
147
|
+
readonly reason?: string;
|
|
148
|
+
readonly id?: string;
|
|
149
|
+
readonly data?: unknown;
|
|
150
|
+
readonly selfId?: string;
|
|
151
|
+
readonly presences?: Record<string, PresenceEntry>;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Decode an encoded client message to a ClientMessage
|
|
155
|
+
*/
|
|
156
|
+
declare const decodeClientMessage: (encoded: EncodedClientMessage) => ClientMessage;
|
|
157
|
+
/**
|
|
158
|
+
* Encode a server message for wire format
|
|
159
|
+
*/
|
|
160
|
+
declare const encodeServerMessage: (message: ServerMessage) => string;
|
|
161
|
+
/**
|
|
162
|
+
* Parse a raw WebSocket message to a ClientMessage
|
|
163
|
+
*/
|
|
164
|
+
declare const parseClientMessage: (data: string | ArrayBuffer | Uint8Array) => Effect.Effect<ClientMessage, MessageParseError>;
|
|
165
|
+
/**
|
|
166
|
+
* Create an auth result success message
|
|
167
|
+
*/
|
|
168
|
+
declare const authResultSuccess: (userId: string, permission: Permission) => AuthResultMessage;
|
|
169
|
+
/**
|
|
170
|
+
* Create an auth result failure message
|
|
171
|
+
*/
|
|
172
|
+
declare const authResultFailure: (error: string) => AuthResultMessage;
|
|
173
|
+
/**
|
|
174
|
+
* Create a pong message
|
|
175
|
+
*/
|
|
176
|
+
declare const pong: () => PongMessage;
|
|
177
|
+
/**
|
|
178
|
+
* Create a transaction message
|
|
179
|
+
*/
|
|
180
|
+
declare const transactionMessage: (transaction: Transaction.Transaction, version: number) => TransactionMessage;
|
|
181
|
+
/**
|
|
182
|
+
* Create a snapshot message
|
|
183
|
+
*/
|
|
184
|
+
declare const snapshotMessage: (state: unknown, version: number) => SnapshotMessage;
|
|
185
|
+
/**
|
|
186
|
+
* Create an error message
|
|
187
|
+
*/
|
|
188
|
+
declare const errorMessage: (transactionId: string, reason: string) => ErrorMessage;
|
|
189
|
+
/**
|
|
190
|
+
* Create a presence update message
|
|
191
|
+
*/
|
|
192
|
+
declare const presenceUpdateMessage: (id: string, data: unknown, userId?: string) => PresenceUpdateMessage;
|
|
193
|
+
/**
|
|
194
|
+
* Create a presence remove message
|
|
195
|
+
*/
|
|
196
|
+
declare const presenceRemoveMessage: (id: string) => PresenceRemoveMessage;
|
|
197
|
+
/**
|
|
198
|
+
* Create a presence snapshot message
|
|
199
|
+
*/
|
|
200
|
+
declare const presenceSnapshotMessage: (selfId: string, presences: Record<string, PresenceEntry>) => PresenceSnapshotMessage;
|
|
201
|
+
//#endregion
|
|
202
|
+
export { Protocol_d_exports, ServerBroadcast, ServerMessage, SnapshotMessage };
|
|
203
|
+
//# sourceMappingURL=Protocol.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Protocol.d.cts","names":[],"sources":["../src/Protocol.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;UAiBiB,WAAA;;;;;;;UAQA,WAAA;;;;;;UAOA,aAAA;;wBAEO,WAAA,CAAY;;;;;UAMnB,sBAAA;;;;;;UAOA,kBAAA;;EA9BA,SAAA,IAAA,EAAW,OAAA;AAQ5B;AAOA;AAQA;AAOA;AAQiB,UAAA,oBAAA,CAAoB;EAOzB,SAAA,IAAA,EAAA,gBAAa;;;;;AAKrB,KALQ,aAAA,GACR,WAIA,GAHA,WAGA,GAFA,aAEA,GADA,sBACA,GAAA,kBAAA,GACA,oBADA;;;AAUJ;AAWiB,UAXA,iBAAA,CAWW;EAOX,SAAA,IAAA,EAAA,aAAkB;EASlB,SAAA,OAAA,EAAe,OAAA;EASf,SAAA,KAAA,CAAY,EAAA,MAAA;EASZ,SAAA,MAAA,CAAA,EAAA,MAAqB;EAUrB,SAAA,UAAA,CAAA,EAlDO,UAkDc;AAQtC;AASA;;;AAGI,UAhEa,WAAA,CAgEb;EACA,SAAA,IAAA,EAAA,MAAA;;;;;AAIuB,UA9DV,kBAAA,CA8DU;EAKf,SAAA,IAAA,EAAA,aAAe;EASV,SAAA,WAAA,EA1EO,WAAA,CAAY,WA6EX;EAOR,SAAA,OAAA,EAAA,MAAoB;;;;;AAcR,UA3FZ,eAAA,CA2FY;EAUhB,SAAA,IAAA,EAAA,UAUZ;EAKY,SAAA,KAAA,EAAA,OAUZ;EAKY,SAAA,OAAA,EAAA,MAeT;;;;;AAbD,UA5Hc,YAAA,CA4HP;EAAM,SAAA,IAAA,EAAA,OAAA;EAkBH,SAAA,aAQX,EAAA,MAAA;EAKW,SAAA,MAAA,EAAA,MAIX;AAKF;AAKA;AAYA;AAYA;AAYa,UApMI,qBAAA,CA6Mf;EAKW,SAAA,IAAA,EAAA,iBAKX;EAKW,SAAA,EAAA,EAAA,MAAA;EAEe,SAAA,IAAA,EAAA,OAAA;EAAf,SAAA,MAAA,CAAA,EAAA,MAAA;;;;;UApNI,qBAAA;;;;;;;UAQA,uBAAA;;;sBAGK,eAAe;;;;;KAMzB,aAAA,GACR,oBACA,cACA,qBACA,kBACA,eACA,wBACA,wBACA;;;;KAKQ,eAAA,GAAkB,qBAAqB;;;;UASlC,oBAAA;;;yBAGQ,WAAA,CAAY;;;;;;UAOpB,oBAAA;;;;;wBAKO;yBACC,WAAA,CAAY;;;;;;;;uBAQd,eAAe;;;;;cAUzB,+BACF,yBACR;;;;cAaU,+BAAgC;;;;cAehC,oCACI,cAAc,eAC5B,MAAA,CAAO,OAAO,eAAe;;;;cAkBnB,gDAEC,eACX;;;;cAUU,sCAAqC;;;;cASrC,YAAW;;;;cAKX,kCACE,WAAA,CAAY,iCAExB;;;;cASU,sDAGV;;;;cASU,yDAGV;;;;cASU,uEAIV;;;;cAUU,uCAEV;;;;cAQU,qDAEA,eAAe,mBACzB"}
|