@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,108 @@
|
|
|
1
|
+
const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
|
|
2
|
+
let effect_Effect = require("effect/Effect");
|
|
3
|
+
effect_Effect = require_rolldown_runtime.__toESM(effect_Effect);
|
|
4
|
+
let effect_Layer = require("effect/Layer");
|
|
5
|
+
effect_Layer = require_rolldown_runtime.__toESM(effect_Layer);
|
|
6
|
+
let effect_PubSub = require("effect/PubSub");
|
|
7
|
+
effect_PubSub = require_rolldown_runtime.__toESM(effect_PubSub);
|
|
8
|
+
let effect_Ref = require("effect/Ref");
|
|
9
|
+
effect_Ref = require_rolldown_runtime.__toESM(effect_Ref);
|
|
10
|
+
let effect_HashMap = require("effect/HashMap");
|
|
11
|
+
effect_HashMap = require_rolldown_runtime.__toESM(effect_HashMap);
|
|
12
|
+
let effect_Context = require("effect/Context");
|
|
13
|
+
effect_Context = require_rolldown_runtime.__toESM(effect_Context);
|
|
14
|
+
let effect_Stream = require("effect/Stream");
|
|
15
|
+
effect_Stream = require_rolldown_runtime.__toESM(effect_Stream);
|
|
16
|
+
|
|
17
|
+
//#region src/PresenceManager.ts
|
|
18
|
+
/**
|
|
19
|
+
* @since 0.0.1
|
|
20
|
+
* Presence manager for ephemeral per-connection state.
|
|
21
|
+
* Handles in-memory storage and broadcasting of presence updates.
|
|
22
|
+
*/
|
|
23
|
+
var PresenceManager_exports = /* @__PURE__ */ require_rolldown_runtime.__export({
|
|
24
|
+
PresenceManagerTag: () => PresenceManagerTag,
|
|
25
|
+
layer: () => layer,
|
|
26
|
+
layerDefault: () => layerDefault
|
|
27
|
+
});
|
|
28
|
+
/**
|
|
29
|
+
* Context tag for PresenceManager.
|
|
30
|
+
*/
|
|
31
|
+
var PresenceManagerTag = class extends effect_Context.Tag("@voidhash/mimic-server-effect/PresenceManager")() {};
|
|
32
|
+
/**
|
|
33
|
+
* Create the PresenceManager service.
|
|
34
|
+
*/
|
|
35
|
+
const makePresenceManager = effect_Effect.gen(function* () {
|
|
36
|
+
const documents = yield* effect_Ref.make(effect_HashMap.empty());
|
|
37
|
+
const getOrCreateDocument = (documentId) => effect_Effect.gen(function* () {
|
|
38
|
+
const current = yield* effect_Ref.get(documents);
|
|
39
|
+
const existing = effect_HashMap.get(current, documentId);
|
|
40
|
+
if (existing._tag === "Some") return existing.value;
|
|
41
|
+
const docPresence = {
|
|
42
|
+
entries: yield* effect_Ref.make(effect_HashMap.empty()),
|
|
43
|
+
pubsub: yield* effect_PubSub.unbounded()
|
|
44
|
+
};
|
|
45
|
+
yield* effect_Ref.update(documents, (map) => effect_HashMap.set(map, documentId, docPresence));
|
|
46
|
+
return docPresence;
|
|
47
|
+
});
|
|
48
|
+
const getSnapshot = (documentId) => effect_Effect.gen(function* () {
|
|
49
|
+
const docPresence = yield* getOrCreateDocument(documentId);
|
|
50
|
+
const entriesMap = yield* effect_Ref.get(docPresence.entries);
|
|
51
|
+
const presences = {};
|
|
52
|
+
for (const [id, entry] of entriesMap) presences[id] = entry;
|
|
53
|
+
return { presences };
|
|
54
|
+
});
|
|
55
|
+
const set = (documentId, connectionId, entry) => effect_Effect.gen(function* () {
|
|
56
|
+
const docPresence = yield* getOrCreateDocument(documentId);
|
|
57
|
+
yield* effect_Ref.update(docPresence.entries, (map) => effect_HashMap.set(map, connectionId, entry));
|
|
58
|
+
yield* effect_PubSub.publish(docPresence.pubsub, {
|
|
59
|
+
type: "presence_update",
|
|
60
|
+
id: connectionId,
|
|
61
|
+
data: entry.data,
|
|
62
|
+
userId: entry.userId
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
const remove = (documentId, connectionId) => effect_Effect.gen(function* () {
|
|
66
|
+
const current = yield* effect_Ref.get(documents);
|
|
67
|
+
const existing = effect_HashMap.get(current, documentId);
|
|
68
|
+
if (existing._tag === "None") return;
|
|
69
|
+
const docPresence = existing.value;
|
|
70
|
+
const entries = yield* effect_Ref.get(docPresence.entries);
|
|
71
|
+
if (!effect_HashMap.has(entries, connectionId)) return;
|
|
72
|
+
yield* effect_Ref.update(docPresence.entries, (map) => effect_HashMap.remove(map, connectionId));
|
|
73
|
+
yield* effect_PubSub.publish(docPresence.pubsub, {
|
|
74
|
+
type: "presence_remove",
|
|
75
|
+
id: connectionId
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
const subscribe = (documentId) => effect_Effect.gen(function* () {
|
|
79
|
+
const docPresence = yield* getOrCreateDocument(documentId);
|
|
80
|
+
const queue = yield* effect_PubSub.subscribe(docPresence.pubsub);
|
|
81
|
+
return effect_Stream.fromQueue(queue);
|
|
82
|
+
});
|
|
83
|
+
return {
|
|
84
|
+
getSnapshot,
|
|
85
|
+
set,
|
|
86
|
+
remove,
|
|
87
|
+
subscribe
|
|
88
|
+
};
|
|
89
|
+
});
|
|
90
|
+
/**
|
|
91
|
+
* Layer that provides PresenceManager.
|
|
92
|
+
*/
|
|
93
|
+
const layer = effect_Layer.effect(PresenceManagerTag, makePresenceManager);
|
|
94
|
+
/**
|
|
95
|
+
* Default layer that provides PresenceManager.
|
|
96
|
+
* Uses the default priority for layer composition.
|
|
97
|
+
*/
|
|
98
|
+
const layerDefault = effect_Layer.effectDiscard(effect_Effect.succeed(void 0)).pipe(effect_Layer.provideMerge(layer));
|
|
99
|
+
|
|
100
|
+
//#endregion
|
|
101
|
+
exports.PresenceManagerTag = PresenceManagerTag;
|
|
102
|
+
Object.defineProperty(exports, 'PresenceManager_exports', {
|
|
103
|
+
enumerable: true,
|
|
104
|
+
get: function () {
|
|
105
|
+
return PresenceManager_exports;
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
exports.layer = layer;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import * as Layer from "effect/Layer";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as Context from "effect/Context";
|
|
4
|
+
import * as Scope from "effect/Scope";
|
|
5
|
+
import * as Stream from "effect/Stream";
|
|
6
|
+
|
|
7
|
+
//#region src/PresenceManager.d.ts
|
|
8
|
+
declare namespace PresenceManager_d_exports {
|
|
9
|
+
export { PresenceEntry, PresenceEvent, PresenceManager, PresenceManagerTag, PresenceRemoveEvent, PresenceSnapshot, PresenceUpdateEvent, layer, layerDefault };
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* A presence entry stored in the manager.
|
|
13
|
+
*/
|
|
14
|
+
interface PresenceEntry {
|
|
15
|
+
/** The presence data */
|
|
16
|
+
readonly data: unknown;
|
|
17
|
+
/** Optional user ID from authentication */
|
|
18
|
+
readonly userId?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Event emitted when a presence is updated.
|
|
22
|
+
*/
|
|
23
|
+
interface PresenceUpdateEvent {
|
|
24
|
+
readonly type: "presence_update";
|
|
25
|
+
/** The connection ID of the user who updated */
|
|
26
|
+
readonly id: string;
|
|
27
|
+
/** The presence data */
|
|
28
|
+
readonly data: unknown;
|
|
29
|
+
/** Optional user ID from authentication */
|
|
30
|
+
readonly userId?: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Event emitted when a presence is removed (user disconnected).
|
|
34
|
+
*/
|
|
35
|
+
interface PresenceRemoveEvent {
|
|
36
|
+
readonly type: "presence_remove";
|
|
37
|
+
/** The connection ID of the user who disconnected */
|
|
38
|
+
readonly id: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Union of all presence events.
|
|
42
|
+
*/
|
|
43
|
+
type PresenceEvent = PresenceUpdateEvent | PresenceRemoveEvent;
|
|
44
|
+
/**
|
|
45
|
+
* A snapshot of all presence entries for a document.
|
|
46
|
+
*/
|
|
47
|
+
interface PresenceSnapshot {
|
|
48
|
+
/** Map of connectionId to presence entry */
|
|
49
|
+
readonly presences: Record<string, PresenceEntry>;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Service interface for the PresenceManager.
|
|
53
|
+
*/
|
|
54
|
+
interface PresenceManager {
|
|
55
|
+
/**
|
|
56
|
+
* Get a snapshot of all presences for a document.
|
|
57
|
+
*/
|
|
58
|
+
readonly getSnapshot: (documentId: string) => Effect.Effect<PresenceSnapshot>;
|
|
59
|
+
/**
|
|
60
|
+
* Set/update presence for a connection.
|
|
61
|
+
* Broadcasts the update to all subscribers.
|
|
62
|
+
*/
|
|
63
|
+
readonly set: (documentId: string, connectionId: string, entry: PresenceEntry) => Effect.Effect<void>;
|
|
64
|
+
/**
|
|
65
|
+
* Remove presence for a connection (e.g., on disconnect).
|
|
66
|
+
* Broadcasts the removal to all subscribers.
|
|
67
|
+
*/
|
|
68
|
+
readonly remove: (documentId: string, connectionId: string) => Effect.Effect<void>;
|
|
69
|
+
/**
|
|
70
|
+
* Subscribe to presence events for a document.
|
|
71
|
+
* Returns a Stream of presence events.
|
|
72
|
+
*/
|
|
73
|
+
readonly subscribe: (documentId: string) => Effect.Effect<Stream.Stream<PresenceEvent>, never, Scope.Scope>;
|
|
74
|
+
}
|
|
75
|
+
declare const PresenceManagerTag_base: Context.TagClass<PresenceManagerTag, "@voidhash/mimic-server-effect/PresenceManager", PresenceManager>;
|
|
76
|
+
/**
|
|
77
|
+
* Context tag for PresenceManager.
|
|
78
|
+
*/
|
|
79
|
+
declare class PresenceManagerTag extends PresenceManagerTag_base {}
|
|
80
|
+
/**
|
|
81
|
+
* Layer that provides PresenceManager.
|
|
82
|
+
*/
|
|
83
|
+
declare const layer: Layer.Layer<PresenceManagerTag>;
|
|
84
|
+
/**
|
|
85
|
+
* Default layer that provides PresenceManager.
|
|
86
|
+
* Uses the default priority for layer composition.
|
|
87
|
+
*/
|
|
88
|
+
declare const layerDefault: Layer.Layer<PresenceManagerTag>;
|
|
89
|
+
//#endregion
|
|
90
|
+
export { PresenceManagerTag, PresenceManager_d_exports };
|
|
91
|
+
//# sourceMappingURL=PresenceManager.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PresenceManager.d.cts","names":[],"sources":["../src/PresenceManager.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;UAsBiB,aAAA;;;;;;AAAjB;AAcA;AAaA;AASY,UAtBK,mBAAA,CAsBW;EASX,SAAA,IAAA,EAAA,iBAEoB;EAwBpB;EAMI,SAAA,EAAA,EAAA,MAAA;EAAd;EASI,SAAA,IAAA,EAAA,OAAA;EACJ;EASA,SAAO,MAAA,CAAA,EAAA,MAAA;;;;;AAQM,UA7EH,mBAAA,CA6EG;EAKnB,SAAA,IAAA,EAAA,iBAAA;;;;;AAKD;AAoJA;AASa,KA3OD,aAAA,GAAgB,mBA2OW,GA3OW,mBA2OZ;;;;UAlOrB,gBAAA;;sBAEK,eAAe;;;;;UAwBpB,eAAA;;;;gDAMV,MAAA,CAAO,OAAO;;;;;kEASV,kBACJ,MAAA,CAAO;;;;;iEASP,MAAA,CAAO;;;;;8CAQP,MAAA,CAAO,OACV,MAAA,CAAO,OAAO,uBAEd,KAAA,CAAM;;cAET;;;;cAKY,kBAAA,SAA2B,uBAAA;;;;cAoJ3B,OAAO,KAAA,CAAM,MAAM;;;;;cASnB,cAAc,KAAA,CAAM,MAAM"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Layer from "effect/Layer";
|
|
3
|
+
import * as Context from "effect/Context";
|
|
4
|
+
import * as Stream from "effect/Stream";
|
|
5
|
+
import * as Scope from "effect/Scope";
|
|
6
|
+
|
|
7
|
+
//#region src/PresenceManager.d.ts
|
|
8
|
+
declare namespace PresenceManager_d_exports {
|
|
9
|
+
export { PresenceEntry, PresenceEvent, PresenceManager, PresenceManagerTag, PresenceRemoveEvent, PresenceSnapshot, PresenceUpdateEvent, layer, layerDefault };
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* A presence entry stored in the manager.
|
|
13
|
+
*/
|
|
14
|
+
interface PresenceEntry {
|
|
15
|
+
/** The presence data */
|
|
16
|
+
readonly data: unknown;
|
|
17
|
+
/** Optional user ID from authentication */
|
|
18
|
+
readonly userId?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Event emitted when a presence is updated.
|
|
22
|
+
*/
|
|
23
|
+
interface PresenceUpdateEvent {
|
|
24
|
+
readonly type: "presence_update";
|
|
25
|
+
/** The connection ID of the user who updated */
|
|
26
|
+
readonly id: string;
|
|
27
|
+
/** The presence data */
|
|
28
|
+
readonly data: unknown;
|
|
29
|
+
/** Optional user ID from authentication */
|
|
30
|
+
readonly userId?: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Event emitted when a presence is removed (user disconnected).
|
|
34
|
+
*/
|
|
35
|
+
interface PresenceRemoveEvent {
|
|
36
|
+
readonly type: "presence_remove";
|
|
37
|
+
/** The connection ID of the user who disconnected */
|
|
38
|
+
readonly id: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Union of all presence events.
|
|
42
|
+
*/
|
|
43
|
+
type PresenceEvent = PresenceUpdateEvent | PresenceRemoveEvent;
|
|
44
|
+
/**
|
|
45
|
+
* A snapshot of all presence entries for a document.
|
|
46
|
+
*/
|
|
47
|
+
interface PresenceSnapshot {
|
|
48
|
+
/** Map of connectionId to presence entry */
|
|
49
|
+
readonly presences: Record<string, PresenceEntry>;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Service interface for the PresenceManager.
|
|
53
|
+
*/
|
|
54
|
+
interface PresenceManager {
|
|
55
|
+
/**
|
|
56
|
+
* Get a snapshot of all presences for a document.
|
|
57
|
+
*/
|
|
58
|
+
readonly getSnapshot: (documentId: string) => Effect.Effect<PresenceSnapshot>;
|
|
59
|
+
/**
|
|
60
|
+
* Set/update presence for a connection.
|
|
61
|
+
* Broadcasts the update to all subscribers.
|
|
62
|
+
*/
|
|
63
|
+
readonly set: (documentId: string, connectionId: string, entry: PresenceEntry) => Effect.Effect<void>;
|
|
64
|
+
/**
|
|
65
|
+
* Remove presence for a connection (e.g., on disconnect).
|
|
66
|
+
* Broadcasts the removal to all subscribers.
|
|
67
|
+
*/
|
|
68
|
+
readonly remove: (documentId: string, connectionId: string) => Effect.Effect<void>;
|
|
69
|
+
/**
|
|
70
|
+
* Subscribe to presence events for a document.
|
|
71
|
+
* Returns a Stream of presence events.
|
|
72
|
+
*/
|
|
73
|
+
readonly subscribe: (documentId: string) => Effect.Effect<Stream.Stream<PresenceEvent>, never, Scope.Scope>;
|
|
74
|
+
}
|
|
75
|
+
declare const PresenceManagerTag_base: Context.TagClass<PresenceManagerTag, "@voidhash/mimic-server-effect/PresenceManager", PresenceManager>;
|
|
76
|
+
/**
|
|
77
|
+
* Context tag for PresenceManager.
|
|
78
|
+
*/
|
|
79
|
+
declare class PresenceManagerTag extends PresenceManagerTag_base {}
|
|
80
|
+
/**
|
|
81
|
+
* Layer that provides PresenceManager.
|
|
82
|
+
*/
|
|
83
|
+
declare const layer: Layer.Layer<PresenceManagerTag>;
|
|
84
|
+
/**
|
|
85
|
+
* Default layer that provides PresenceManager.
|
|
86
|
+
* Uses the default priority for layer composition.
|
|
87
|
+
*/
|
|
88
|
+
declare const layerDefault: Layer.Layer<PresenceManagerTag>;
|
|
89
|
+
//#endregion
|
|
90
|
+
export { PresenceManagerTag, PresenceManager_d_exports };
|
|
91
|
+
//# sourceMappingURL=PresenceManager.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PresenceManager.d.mts","names":[],"sources":["../src/PresenceManager.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;UAsBiB,aAAA;;;;;;AAAjB;AAcA;AAaA;AASY,UAtBK,mBAAA,CAsBW;EASX,SAAA,IAAA,EAAA,iBAEoB;EAwBpB;EAMI,SAAA,EAAA,EAAA,MAAA;EAAd;EASI,SAAA,IAAA,EAAA,OAAA;EACJ;EASA,SAAO,MAAA,CAAA,EAAA,MAAA;;;;;AAQM,UA7EH,mBAAA,CA6EG;EAKnB,SAAA,IAAA,EAAA,iBAAA;;;;;AAKD;AAoJA;AASa,KA3OD,aAAA,GAAgB,mBA2OW,GA3OW,mBA2OZ;;;;UAlOrB,gBAAA;;sBAEK,eAAe;;;;;UAwBpB,eAAA;;;;gDAMV,MAAA,CAAO,OAAO;;;;;kEASV,kBACJ,MAAA,CAAO;;;;;iEASP,MAAA,CAAO;;;;;8CAQP,MAAA,CAAO,OACV,MAAA,CAAO,OAAO,uBAEd,KAAA,CAAM;;cAET;;;;cAKY,kBAAA,SAA2B,uBAAA;;;;cAoJ3B,OAAO,KAAA,CAAM,MAAM;;;;;cASnB,cAAc,KAAA,CAAM,MAAM"}
|
|
@@ -0,0 +1,95 @@
|
|
|
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 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";
|
|
9
|
+
|
|
10
|
+
//#region src/PresenceManager.ts
|
|
11
|
+
/**
|
|
12
|
+
* @since 0.0.1
|
|
13
|
+
* Presence manager for ephemeral per-connection state.
|
|
14
|
+
* Handles in-memory storage and broadcasting of presence updates.
|
|
15
|
+
*/
|
|
16
|
+
var PresenceManager_exports = /* @__PURE__ */ __export({
|
|
17
|
+
PresenceManagerTag: () => PresenceManagerTag,
|
|
18
|
+
layer: () => layer,
|
|
19
|
+
layerDefault: () => layerDefault
|
|
20
|
+
});
|
|
21
|
+
/**
|
|
22
|
+
* Context tag for PresenceManager.
|
|
23
|
+
*/
|
|
24
|
+
var PresenceManagerTag = class extends Context.Tag("@voidhash/mimic-server-effect/PresenceManager")() {};
|
|
25
|
+
/**
|
|
26
|
+
* Create the PresenceManager service.
|
|
27
|
+
*/
|
|
28
|
+
const makePresenceManager = Effect.gen(function* () {
|
|
29
|
+
const documents = yield* Ref.make(HashMap.empty());
|
|
30
|
+
const getOrCreateDocument = (documentId) => Effect.gen(function* () {
|
|
31
|
+
const current = yield* Ref.get(documents);
|
|
32
|
+
const existing = HashMap.get(current, documentId);
|
|
33
|
+
if (existing._tag === "Some") return existing.value;
|
|
34
|
+
const docPresence = {
|
|
35
|
+
entries: yield* Ref.make(HashMap.empty()),
|
|
36
|
+
pubsub: yield* PubSub.unbounded()
|
|
37
|
+
};
|
|
38
|
+
yield* Ref.update(documents, (map) => HashMap.set(map, documentId, docPresence));
|
|
39
|
+
return docPresence;
|
|
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);
|
|
75
|
+
});
|
|
76
|
+
return {
|
|
77
|
+
getSnapshot,
|
|
78
|
+
set,
|
|
79
|
+
remove,
|
|
80
|
+
subscribe
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
/**
|
|
84
|
+
* Layer that provides PresenceManager.
|
|
85
|
+
*/
|
|
86
|
+
const layer = Layer.effect(PresenceManagerTag, makePresenceManager);
|
|
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));
|
|
92
|
+
|
|
93
|
+
//#endregion
|
|
94
|
+
export { PresenceManagerTag, PresenceManager_exports, layer };
|
|
95
|
+
//# sourceMappingURL=PresenceManager.mjs.map
|
|
@@ -0,0 +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"}
|