@iadev93/zuno 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +20 -1
- package/dist/core/createZuno.d.ts +0 -77
- package/dist/core/createZuno.d.ts.map +0 -1
- package/dist/core/createZuno.js +0 -250
- package/dist/core/store.d.ts +0 -10
- package/dist/core/store.d.ts.map +0 -1
- package/dist/core/store.js +0 -27
- package/dist/core/types.d.ts +0 -107
- package/dist/core/types.d.ts.map +0 -1
- package/dist/core/types.js +0 -1
- package/dist/core/universe.d.ts +0 -12
- package/dist/core/universe.d.ts.map +0 -1
- package/dist/core/universe.js +0 -53
- package/dist/index.d.ts +0 -10
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -6
- package/dist/server/apply-state-event.d.ts +0 -26
- package/dist/server/apply-state-event.d.ts.map +0 -1
- package/dist/server/apply-state-event.js +0 -29
- package/dist/server/index.d.ts +0 -4
- package/dist/server/index.d.ts.map +0 -1
- package/dist/server/index.js +0 -3
- package/dist/server/server-transport.d.ts +0 -8
- package/dist/server/server-transport.d.ts.map +0 -1
- package/dist/server/server-transport.js +0 -25
- package/dist/server/snapshot-handler.d.ts +0 -9
- package/dist/server/snapshot-handler.d.ts.map +0 -1
- package/dist/server/snapshot-handler.js +0 -18
- package/dist/server/sse-handler.d.ts +0 -24
- package/dist/server/sse-handler.d.ts.map +0 -1
- package/dist/server/sse-handler.js +0 -127
- package/dist/server/state.bus.d.ts +0 -18
- package/dist/server/state.bus.d.ts.map +0 -1
- package/dist/server/state.bus.js +0 -26
- package/dist/server/state.log.d.ts +0 -22
- package/dist/server/state.log.d.ts.map +0 -1
- package/dist/server/state.log.js +0 -37
- package/dist/server/types.d.ts +0 -8
- package/dist/server/types.d.ts.map +0 -1
- package/dist/server/types.js +0 -1
- package/dist/server/universe-store.d.ts +0 -29
- package/dist/server/universe-store.d.ts.map +0 -1
- package/dist/server/universe-store.js +0 -26
- package/dist/shared/readable.d.ts +0 -21
- package/dist/shared/readable.d.ts.map +0 -1
- package/dist/shared/readable.js +0 -7
- package/dist/sync/apply-incoming-event.d.ts +0 -10
- package/dist/sync/apply-incoming-event.d.ts.map +0 -1
- package/dist/sync/apply-incoming-event.js +0 -28
- package/dist/sync/broadcast-channel.d.ts +0 -12
- package/dist/sync/broadcast-channel.d.ts.map +0 -1
- package/dist/sync/broadcast-channel.js +0 -73
- package/dist/sync/sse-client.d.ts +0 -21
- package/dist/sync/sse-client.d.ts.map +0 -1
- package/dist/sync/sse-client.js +0 -162
- package/dist/sync/sync-types.d.ts +0 -164
- package/dist/sync/sync-types.d.ts.map +0 -1
- package/dist/sync/sync-types.js +0 -1
- package/dist/sync/transport.d.ts +0 -10
- package/dist/sync/transport.d.ts.map +0 -1
- package/dist/sync/transport.js +0 -26
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { publishToStateEvent } from "./state.bus";
|
|
2
|
-
import { appendEvent } from "./state.log";
|
|
3
|
-
import { getUniverseRecord, updateUniverseState } from "./universe-store";
|
|
4
|
-
/**
|
|
5
|
-
* Core sync handler that applies an event to the universe
|
|
6
|
-
* and broadcasts it to all SSE subscribers.
|
|
7
|
-
* This is independent of HTTP / WebSocket / whatever transport.
|
|
8
|
-
* @property {ZunoStateEvent} incoming - The incoming event to apply.
|
|
9
|
-
* @returns {ApplyResult} The result of the application.
|
|
10
|
-
*/
|
|
11
|
-
export const applyStateEvent = (incoming) => {
|
|
12
|
-
/** Get the current state of the store */
|
|
13
|
-
const current = getUniverseRecord(incoming.storeKey) ?? { state: undefined, version: 0 };
|
|
14
|
-
/** Only enforce if client provided baseVersion */
|
|
15
|
-
if (typeof incoming.baseVersion === "number" && incoming.baseVersion !== current.version) {
|
|
16
|
-
return { ok: false, reason: "VERSION_CONFLICT", current };
|
|
17
|
-
}
|
|
18
|
-
/** Create a new event with the next version and current timestamp */
|
|
19
|
-
const event = appendEvent({
|
|
20
|
-
...incoming,
|
|
21
|
-
version: current.version + 1,
|
|
22
|
-
ts: Date.now(),
|
|
23
|
-
});
|
|
24
|
-
/** Update the universe state */
|
|
25
|
-
updateUniverseState(event);
|
|
26
|
-
/** Publish the event */
|
|
27
|
-
publishToStateEvent(event);
|
|
28
|
-
return { ok: true, event };
|
|
29
|
-
};
|
package/dist/server/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,OAAO,EACL,mBAAmB,EAAE,gBAAgB,EACtC,MAAM,eAAe,CAAC;AACvB,cAAc,qBAAqB,CAAC"}
|
package/dist/server/index.js
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { ZunoTransport } from "../sync/sync-types";
|
|
2
|
-
/**
|
|
3
|
-
* Creates a server transport for Zuno.
|
|
4
|
-
*
|
|
5
|
-
* @returns A ZunoTransport instance.
|
|
6
|
-
*/
|
|
7
|
-
export declare const createServerTransport: () => ZunoTransport;
|
|
8
|
-
//# sourceMappingURL=server-transport.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"server-transport.d.ts","sourceRoot":"","sources":["../../src/server/server-transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAOxE;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,QAAO,aAoBxC,CAAC"}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Creates a server transport for Zuno.
|
|
3
|
-
*
|
|
4
|
-
* @returns A ZunoTransport instance.
|
|
5
|
-
*/
|
|
6
|
-
export const createServerTransport = () => {
|
|
7
|
-
/**
|
|
8
|
-
* The set of subscribers to state events.
|
|
9
|
-
*/
|
|
10
|
-
const subs = new Set();
|
|
11
|
-
/**
|
|
12
|
-
* Publishes a state event to all subscribers.
|
|
13
|
-
*/
|
|
14
|
-
return {
|
|
15
|
-
async publish(event) {
|
|
16
|
-
for (const cb of subs)
|
|
17
|
-
cb(event);
|
|
18
|
-
return { ok: true, status: 200, json: null };
|
|
19
|
-
},
|
|
20
|
-
subscribe(cb) {
|
|
21
|
-
subs.add(cb);
|
|
22
|
-
return () => subs.delete(cb);
|
|
23
|
-
},
|
|
24
|
-
};
|
|
25
|
-
};
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { IncomingMessage, ServerResponse } from "http";
|
|
2
|
-
/**
|
|
3
|
-
* Sends a snapshot of the universe state to the client.
|
|
4
|
-
*
|
|
5
|
-
* @param _req The incoming HTTP request object.
|
|
6
|
-
* @param res The server response object, used to send the JSON universe state.
|
|
7
|
-
*/
|
|
8
|
-
export declare function sendSnapshot(_req: IncomingMessage, res: ServerResponse): void;
|
|
9
|
-
//# sourceMappingURL=snapshot-handler.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"snapshot-handler.d.ts","sourceRoot":"","sources":["../../src/server/snapshot-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,MAAM,CAAA;AAI3D;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,QAStE"}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { getUniverseState } from "./universe-store";
|
|
2
|
-
import { getLastEventId } from "./state.log";
|
|
3
|
-
/**
|
|
4
|
-
* Sends a snapshot of the universe state to the client.
|
|
5
|
-
*
|
|
6
|
-
* @param _req The incoming HTTP request object.
|
|
7
|
-
* @param res The server response object, used to send the JSON universe state.
|
|
8
|
-
*/
|
|
9
|
-
export function sendSnapshot(_req, res) {
|
|
10
|
-
const body = {
|
|
11
|
-
state: getUniverseState(),
|
|
12
|
-
version: getUniverseState().version ?? 0,
|
|
13
|
-
lastEventId: getLastEventId(),
|
|
14
|
-
};
|
|
15
|
-
res.statusCode = 200;
|
|
16
|
-
res.setHeader("Content-Type", "application/json");
|
|
17
|
-
res.end(JSON.stringify(body));
|
|
18
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { IncomingMessage, ServerResponse } from "http";
|
|
2
|
-
type IncomingHeaders = IncomingMessage["headers"];
|
|
3
|
-
/**
|
|
4
|
-
* Creates a Server-Sent Events (SSE) connection for Zuno state updates.
|
|
5
|
-
*/
|
|
6
|
-
export declare const createSSEConnection: (req: IncomingMessage, res: ServerResponse, headers: IncomingHeaders) => void;
|
|
7
|
-
/**
|
|
8
|
-
* Synchronizes the Zuno universe state by applying an incoming event.
|
|
9
|
-
*
|
|
10
|
-
* This endpoint accepts a POST request with a JSON body representing a `ZunoStateEvent`.
|
|
11
|
-
* It updates the universe state and then publishes the event to all SSE subscribers.
|
|
12
|
-
*
|
|
13
|
-
* @param req The incoming HTTP request object, expected to contain a JSON `ZunoStateEvent` in its body.
|
|
14
|
-
* @param res The server response object, used to acknowledge the update or report errors.
|
|
15
|
-
* @param transport The transport object used to publish the event to all SSE subscribers.
|
|
16
|
-
*/
|
|
17
|
-
export declare const syncUniverseState: (req: IncomingMessage, res: ServerResponse) => void;
|
|
18
|
-
/**
|
|
19
|
-
* Sets the universe state to a specific version.
|
|
20
|
-
* Backwards-compatible alias of syncUniverseState.
|
|
21
|
-
*/
|
|
22
|
-
export declare const setUniverseState: (req: IncomingMessage, res: ServerResponse) => void;
|
|
23
|
-
export {};
|
|
24
|
-
//# sourceMappingURL=sse-handler.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sse-handler.d.ts","sourceRoot":"","sources":["../../src/server/sse-handler.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;AAK5D,KAAK,eAAe,GAAG,eAAe,CAAC,SAAS,CAAC,CAAA;AAEjD;;GAEG;AACH,eAAO,MAAM,mBAAmB,GAAI,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,SAAS,eAAe,SA0EtG,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,GAAI,KAAK,eAAe,EAAE,KAAK,cAAc,SAwC1E,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAAI,KAAK,eAAe,EAAE,KAAK,cAAc,SAEzE,CAAC"}
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import { subscribeToStateEvents } from "./state.bus";
|
|
2
|
-
import { getUniverseState } from "./universe-store";
|
|
3
|
-
import { applyStateEvent } from "./apply-state-event";
|
|
4
|
-
import { getEventsAfter } from "./state.log";
|
|
5
|
-
/**
|
|
6
|
-
* Creates a Server-Sent Events (SSE) connection for Zuno state updates.
|
|
7
|
-
*/
|
|
8
|
-
export const createSSEConnection = (req, res, headers) => {
|
|
9
|
-
res.writeHead(200, {
|
|
10
|
-
"Cache-Control": "no-cache, no-transform",
|
|
11
|
-
"Content-Type": "text/event-stream; charset=utf-8",
|
|
12
|
-
Connection: "keep-alive",
|
|
13
|
-
"X-Accel-Buffering": "no",
|
|
14
|
-
...headers
|
|
15
|
-
});
|
|
16
|
-
/**
|
|
17
|
-
* Immediately flushes the response headers to the client.
|
|
18
|
-
* This is crucial for SSE to ensure the client receives headers and
|
|
19
|
-
* starts processing the event stream without buffering delays.
|
|
20
|
-
*/
|
|
21
|
-
res.flushHeaders?.();
|
|
22
|
-
/** Get the last event id from header `last-event-id` or query param `lastEventId` */
|
|
23
|
-
const raw = req.headers["last-event-id"] || new URL(req.url || "", "http://localhost").searchParams.get("lastEventId");
|
|
24
|
-
const lastEventId = Number.parseInt(Array.isArray(raw) ? raw[0] : (raw ?? "0"), 10) || 0;
|
|
25
|
-
/**
|
|
26
|
-
* If the client has a `last-event-id`, it means it's reconnecting after a disconnect.
|
|
27
|
-
* In this case, we need to send it any missed events since the last event it received.
|
|
28
|
-
*/
|
|
29
|
-
if (lastEventId > 0) {
|
|
30
|
-
/** Get the events that occurred after the last event the client received. */
|
|
31
|
-
const missed = getEventsAfter(lastEventId);
|
|
32
|
-
/** Send the missed events to the client. */
|
|
33
|
-
for (const event of missed) {
|
|
34
|
-
res.write(`id: ${event.eventId}\n`);
|
|
35
|
-
res.write(`event: state\n`);
|
|
36
|
-
res.write(`data: ${JSON.stringify(event)}\n\n`);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* If the client doesn't have a `last-event-id`, it means it's a fresh connection.
|
|
41
|
-
* In this case, we need to send it the current state of the universe.
|
|
42
|
-
*/
|
|
43
|
-
else {
|
|
44
|
-
/** Send the current state of the universe to the client. */
|
|
45
|
-
res.write(`event: snapshot\n`);
|
|
46
|
-
res.write(`data: ${JSON.stringify(getUniverseState())}\n\n`);
|
|
47
|
-
}
|
|
48
|
-
/** Subscribe to state events and send them to the client */
|
|
49
|
-
const unsubscribe = subscribeToStateEvents((event) => {
|
|
50
|
-
res.write(`id: ${event.eventId}\n`);
|
|
51
|
-
res.write(`event: state\n`);
|
|
52
|
-
res.write(`data: ${JSON.stringify(event)}\n\n`); // The actual event data
|
|
53
|
-
});
|
|
54
|
-
/**
|
|
55
|
-
* Set up a heartbeat mechanism to keep the SSE connection alive.
|
|
56
|
-
* A ping event is sent every 15 seconds.
|
|
57
|
-
*/
|
|
58
|
-
const heartbeat = setInterval(() => {
|
|
59
|
-
res.write(`: ping ${Date.now()}\n\n`);
|
|
60
|
-
}, 15000);
|
|
61
|
-
/**
|
|
62
|
-
* Send an initial connection message to the client.
|
|
63
|
-
* This helps the client know when the connection is established.
|
|
64
|
-
*/
|
|
65
|
-
res.write(": connected \n\n");
|
|
66
|
-
/**
|
|
67
|
-
* Clean up subscription when the client disconnects.
|
|
68
|
-
*/
|
|
69
|
-
req.on("close", () => {
|
|
70
|
-
clearInterval(heartbeat);
|
|
71
|
-
unsubscribe();
|
|
72
|
-
res.end();
|
|
73
|
-
});
|
|
74
|
-
};
|
|
75
|
-
/**
|
|
76
|
-
* Synchronizes the Zuno universe state by applying an incoming event.
|
|
77
|
-
*
|
|
78
|
-
* This endpoint accepts a POST request with a JSON body representing a `ZunoStateEvent`.
|
|
79
|
-
* It updates the universe state and then publishes the event to all SSE subscribers.
|
|
80
|
-
*
|
|
81
|
-
* @param req The incoming HTTP request object, expected to contain a JSON `ZunoStateEvent` in its body.
|
|
82
|
-
* @param res The server response object, used to acknowledge the update or report errors.
|
|
83
|
-
* @param transport The transport object used to publish the event to all SSE subscribers.
|
|
84
|
-
*/
|
|
85
|
-
export const syncUniverseState = (req, res) => {
|
|
86
|
-
const MAX_BODY_BYTES = 512 * 1024; // 512KB safety
|
|
87
|
-
let body = "";
|
|
88
|
-
// Accumulate data chunks from the request body
|
|
89
|
-
req.on("data", (chunk) => {
|
|
90
|
-
body += chunk.toString("utf8");
|
|
91
|
-
if (body.length > MAX_BODY_BYTES) {
|
|
92
|
-
res.writeHead(413, { "Content-Type": "application/json" });
|
|
93
|
-
res.end(JSON.stringify({ ok: false, reason: "PAYLOAD_TOO_LARGE" }));
|
|
94
|
-
req.destroy();
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
|
-
req.on("end", () => {
|
|
98
|
-
try {
|
|
99
|
-
const incoming = JSON.parse(body || "{}");
|
|
100
|
-
const result = applyStateEvent(incoming); // ✅ core sync
|
|
101
|
-
if (!result.ok) {
|
|
102
|
-
if (result.reason === "VERSION_CONFLICT") {
|
|
103
|
-
res.writeHead(409, { "Content-Type": "application/json" });
|
|
104
|
-
res.end(JSON.stringify({
|
|
105
|
-
ok: false,
|
|
106
|
-
reason: "VERSION_CONFLICT",
|
|
107
|
-
current: result.current,
|
|
108
|
-
}));
|
|
109
|
-
}
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
113
|
-
res.end(JSON.stringify({ ok: true, event: result.event }));
|
|
114
|
-
}
|
|
115
|
-
catch {
|
|
116
|
-
res.writeHead(400, { "Content-Type": "application/json" });
|
|
117
|
-
res.end(JSON.stringify({ ok: false, reason: "INVALID_JSON" }));
|
|
118
|
-
}
|
|
119
|
-
});
|
|
120
|
-
};
|
|
121
|
-
/**
|
|
122
|
-
* Sets the universe state to a specific version.
|
|
123
|
-
* Backwards-compatible alias of syncUniverseState.
|
|
124
|
-
*/
|
|
125
|
-
export const setUniverseState = (req, res) => {
|
|
126
|
-
return syncUniverseState(req, res);
|
|
127
|
-
};
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { ZunoStateEvent } from "../sync/sync-types";
|
|
2
|
-
import type { ZunoStateListener } from "./types";
|
|
3
|
-
/**
|
|
4
|
-
* Subscribes a listener function to state events.
|
|
5
|
-
* The listener will be called whenever a new state event is published.
|
|
6
|
-
*
|
|
7
|
-
* @param listener The function to be called when a state event occurs.
|
|
8
|
-
* @returns A cleanup function that, when called, unsubscribes the listener.
|
|
9
|
-
*/
|
|
10
|
-
export declare const subscribeToStateEvents: (listener: ZunoStateListener) => () => void;
|
|
11
|
-
/**
|
|
12
|
-
* Publishes a state event to all registered listeners.
|
|
13
|
-
* Each subscribed listener will receive the event.
|
|
14
|
-
*
|
|
15
|
-
* @param event The state event to be published.
|
|
16
|
-
*/
|
|
17
|
-
export declare const publishToStateEvent: (event: ZunoStateEvent) => void;
|
|
18
|
-
//# sourceMappingURL=state.bus.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"state.bus.d.ts","sourceRoot":"","sources":["../../src/server/state.bus.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAOjD;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,GAAI,UAAU,iBAAiB,eAKjE,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,GAAI,OAAO,cAAc,SAExD,CAAA"}
|
package/dist/server/state.bus.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The set of listeners subscribed to state events.
|
|
3
|
-
*/
|
|
4
|
-
const listeners = new Set();
|
|
5
|
-
/**
|
|
6
|
-
* Subscribes a listener function to state events.
|
|
7
|
-
* The listener will be called whenever a new state event is published.
|
|
8
|
-
*
|
|
9
|
-
* @param listener The function to be called when a state event occurs.
|
|
10
|
-
* @returns A cleanup function that, when called, unsubscribes the listener.
|
|
11
|
-
*/
|
|
12
|
-
export const subscribeToStateEvents = (listener) => {
|
|
13
|
-
listeners.add(listener);
|
|
14
|
-
return () => {
|
|
15
|
-
listeners.delete(listener);
|
|
16
|
-
};
|
|
17
|
-
};
|
|
18
|
-
/**
|
|
19
|
-
* Publishes a state event to all registered listeners.
|
|
20
|
-
* Each subscribed listener will receive the event.
|
|
21
|
-
*
|
|
22
|
-
* @param event The state event to be published.
|
|
23
|
-
*/
|
|
24
|
-
export const publishToStateEvent = (event) => {
|
|
25
|
-
listeners.forEach(listener => listener(event));
|
|
26
|
-
};
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import type { ZunoStateEvent } from "../sync/sync-types";
|
|
2
|
-
/**
|
|
3
|
-
* Appends an event to the log.
|
|
4
|
-
*
|
|
5
|
-
* @param event The event to append.
|
|
6
|
-
* @returns The appended event.
|
|
7
|
-
*/
|
|
8
|
-
export declare const appendEvent: (event: ZunoStateEvent) => ZunoStateEvent;
|
|
9
|
-
/**
|
|
10
|
-
* Returns events after the given event id.
|
|
11
|
-
*
|
|
12
|
-
* @param lastEventId The last event id to return events after.
|
|
13
|
-
* @returns The events after the given event id.
|
|
14
|
-
*/
|
|
15
|
-
export declare const getEventsAfter: (lastEventId: number) => ZunoStateEvent[];
|
|
16
|
-
/**
|
|
17
|
-
* Returns the last event id in the log.
|
|
18
|
-
*
|
|
19
|
-
* @returns The last event id in the log.
|
|
20
|
-
*/
|
|
21
|
-
export declare const getLastEventId: () => number;
|
|
22
|
-
//# sourceMappingURL=state.log.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"state.log.d.ts","sourceRoot":"","sources":["../../src/server/state.log.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAWzD;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,OAAO,cAAc,mBAShD,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,cAAc,GAAI,aAAa,MAAM,qBAEjD,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,cAAc,cAE1B,CAAA"}
|
package/dist/server/state.log.js
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/** Maximum number of events to keep in the log. */
|
|
2
|
-
const MAX_EVENTS = 1000;
|
|
3
|
-
/** The next event id to assign. */
|
|
4
|
-
let nextEventId = 1;
|
|
5
|
-
/** The log of events. */
|
|
6
|
-
const eventLog = [];
|
|
7
|
-
/**
|
|
8
|
-
* Appends an event to the log.
|
|
9
|
-
*
|
|
10
|
-
* @param event The event to append.
|
|
11
|
-
* @returns The appended event.
|
|
12
|
-
*/
|
|
13
|
-
export const appendEvent = (event) => {
|
|
14
|
-
event.eventId = nextEventId++;
|
|
15
|
-
eventLog.push(event);
|
|
16
|
-
if (eventLog.length > MAX_EVENTS) {
|
|
17
|
-
eventLog.shift();
|
|
18
|
-
}
|
|
19
|
-
return event;
|
|
20
|
-
};
|
|
21
|
-
/**
|
|
22
|
-
* Returns events after the given event id.
|
|
23
|
-
*
|
|
24
|
-
* @param lastEventId The last event id to return events after.
|
|
25
|
-
* @returns The events after the given event id.
|
|
26
|
-
*/
|
|
27
|
-
export const getEventsAfter = (lastEventId) => {
|
|
28
|
-
return eventLog.filter(event => (event?.eventId ?? 0) > lastEventId);
|
|
29
|
-
};
|
|
30
|
-
/**
|
|
31
|
-
* Returns the last event id in the log.
|
|
32
|
-
*
|
|
33
|
-
* @returns The last event id in the log.
|
|
34
|
-
*/
|
|
35
|
-
export const getLastEventId = () => {
|
|
36
|
-
return eventLog[eventLog.length - 1]?.eventId ?? 0;
|
|
37
|
-
};
|
package/dist/server/types.d.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { ZunoStateEvent } from "../sync/sync-types";
|
|
2
|
-
/**
|
|
3
|
-
* A callback function type for listening to Zuno state events.
|
|
4
|
-
*
|
|
5
|
-
* @param event The Zuno state event that occurred.
|
|
6
|
-
*/
|
|
7
|
-
export type ZunoStateListener = (event: ZunoStateEvent) => void;
|
|
8
|
-
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/server/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzD;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC"}
|
package/dist/server/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import type { ZunoStateEvent } from "../sync/sync-types";
|
|
2
|
-
/**
|
|
3
|
-
* A global map to store the state of different parts of the universe.
|
|
4
|
-
* The keys are `storeKey` strings and the values are the corresponding state objects.
|
|
5
|
-
*/
|
|
6
|
-
type UniverseRecord = {
|
|
7
|
-
state: any;
|
|
8
|
-
version: number;
|
|
9
|
-
};
|
|
10
|
-
/**
|
|
11
|
-
* Retrieves the current state of a specific store in the universe.
|
|
12
|
-
* @param storeKey The key of the store to retrieve.
|
|
13
|
-
* @returns The current state of the store, or undefined if the store does not exist.
|
|
14
|
-
*/
|
|
15
|
-
export declare const getUniverseRecord: (storeKey: string) => UniverseRecord | undefined;
|
|
16
|
-
/**
|
|
17
|
-
* Updates the state of a specific store in the universe.
|
|
18
|
-
* @param event The ZunoStateEvent containing the storeKey and the new state to set.
|
|
19
|
-
*/
|
|
20
|
-
export declare const updateUniverseState: (event: ZunoStateEvent) => void;
|
|
21
|
-
/**
|
|
22
|
-
* Retrieves the current state of the entire universe.
|
|
23
|
-
* @returns An object containing the state of all stores in the universe.
|
|
24
|
-
*/
|
|
25
|
-
export declare const getUniverseState: () => {
|
|
26
|
-
[k: string]: UniverseRecord;
|
|
27
|
-
};
|
|
28
|
-
export {};
|
|
29
|
-
//# sourceMappingURL=universe-store.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"universe-store.d.ts","sourceRoot":"","sources":["../../src/server/universe-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzD;;;GAGG;AACH,KAAK,cAAc,GAAG;IACpB,KAAK,EAAE,GAAG,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAIF;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,UAAU,MAAM,KAAG,cAAc,GAAG,SAErE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAAI,OAAO,cAAc,SAQxD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,gBAAgB;;CAE5B,CAAC"}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
const universeState = new Map();
|
|
2
|
-
/**
|
|
3
|
-
* Retrieves the current state of a specific store in the universe.
|
|
4
|
-
* @param storeKey The key of the store to retrieve.
|
|
5
|
-
* @returns The current state of the store, or undefined if the store does not exist.
|
|
6
|
-
*/
|
|
7
|
-
export const getUniverseRecord = (storeKey) => {
|
|
8
|
-
return universeState.get(storeKey);
|
|
9
|
-
};
|
|
10
|
-
/**
|
|
11
|
-
* Updates the state of a specific store in the universe.
|
|
12
|
-
* @param event The ZunoStateEvent containing the storeKey and the new state to set.
|
|
13
|
-
*/
|
|
14
|
-
export const updateUniverseState = (event) => {
|
|
15
|
-
const current = universeState.get(event.storeKey) ?? { state: undefined, version: 0 };
|
|
16
|
-
// If applyStateEvent already computed event.version, prefer it.
|
|
17
|
-
const nextVersion = typeof event.version === "number" ? event.version : current.version + 1;
|
|
18
|
-
universeState.set(event.storeKey, { state: event.state, version: nextVersion });
|
|
19
|
-
};
|
|
20
|
-
/**
|
|
21
|
-
* Retrieves the current state of the entire universe.
|
|
22
|
-
* @returns An object containing the state of all stores in the universe.
|
|
23
|
-
*/
|
|
24
|
-
export const getUniverseState = () => {
|
|
25
|
-
return Object.fromEntries(universeState);
|
|
26
|
-
};
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/** Universal UI adapter contract */
|
|
2
|
-
export type ZunoReadable<T> = {
|
|
3
|
-
/** Read current value (sync) */
|
|
4
|
-
getSnapshot(): T;
|
|
5
|
-
/**
|
|
6
|
-
* Subscribe to changes.
|
|
7
|
-
* Call `onChange()` whenever snapshot may have changed.
|
|
8
|
-
* Return unsubscribe.
|
|
9
|
-
*/
|
|
10
|
-
subscribe(onChange: () => void): () => void;
|
|
11
|
-
/** Optional: React SSR (server snapshot) */
|
|
12
|
-
getServerSnapshot?: () => T;
|
|
13
|
-
};
|
|
14
|
-
/** Minimal store shape that can be adapted into a readable */
|
|
15
|
-
export type ZunoSubscribableStore<T> = {
|
|
16
|
-
get(): T;
|
|
17
|
-
subscribe(cb: (state: T) => void): () => void;
|
|
18
|
-
};
|
|
19
|
-
/** Adapter helper: convert store => readable */
|
|
20
|
-
export declare function toReadable<T>(store: ZunoSubscribableStore<T>): ZunoReadable<T>;
|
|
21
|
-
//# sourceMappingURL=readable.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"readable.d.ts","sourceRoot":"","sources":["../../src/shared/readable.ts"],"names":[],"mappings":"AAAA,oCAAoC;AACpC,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI;IAC5B,gCAAgC;IAChC,WAAW,IAAI,CAAC,CAAC;IAEjB;;;;OAIG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;IAE5C,4CAA4C;IAC5C,iBAAiB,CAAC,EAAE,MAAM,CAAC,CAAC;CAC7B,CAAC;AAEF,8DAA8D;AAC9D,MAAM,MAAM,qBAAqB,CAAC,CAAC,IAAI;IACrC,GAAG,IAAI,CAAC,CAAC;IACT,SAAS,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CAC/C,CAAC;AAEF,gDAAgD;AAChD,wBAAgB,UAAU,CAAC,CAAC,EAAE,KAAK,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAK9E"}
|
package/dist/shared/readable.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { IncomingEventContext, Universe } from "../core/types";
|
|
2
|
-
import type { ZunoStateEvent } from "./sync-types";
|
|
3
|
-
/**
|
|
4
|
-
* Applies an incoming event to the Universe in a safe, reusable way.
|
|
5
|
-
* @property universe - The universe to apply the event to.
|
|
6
|
-
* @property event - The event to apply.
|
|
7
|
-
* @property ctx - The context for the event.
|
|
8
|
-
**/
|
|
9
|
-
export declare function applyIncomingEvent(universe: Universe, event: ZunoStateEvent, ctx: IncomingEventContext): void;
|
|
10
|
-
//# sourceMappingURL=apply-incoming-event.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"apply-incoming-event.d.ts","sourceRoot":"","sources":["../../src/sync/apply-incoming-event.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD;;;;;IAKI;AACJ,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,cAAc,EACrB,GAAG,EAAE,oBAAoB,QA4B1B"}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Applies an incoming event to the Universe in a safe, reusable way.
|
|
3
|
-
* @property universe - The universe to apply the event to.
|
|
4
|
-
* @property event - The event to apply.
|
|
5
|
-
* @property ctx - The context for the event.
|
|
6
|
-
**/
|
|
7
|
-
export function applyIncomingEvent(universe, event, ctx) {
|
|
8
|
-
/** Prevent echo loops (don’t re-apply your own broadcast) */
|
|
9
|
-
if (event.origin && event.origin === ctx.clientId)
|
|
10
|
-
return;
|
|
11
|
-
/** If versioned events exist (SSE/server/BC), ignore older ones */
|
|
12
|
-
if (ctx.versions && typeof event.version === "number") {
|
|
13
|
-
/** Check if the event has been seen */
|
|
14
|
-
const seen = ctx.versions.has(event.storeKey);
|
|
15
|
-
/** Get the current version */
|
|
16
|
-
const currentVersion = ctx.versions.get(event.storeKey) ?? -1;
|
|
17
|
-
/** If the event has been seen and the version is less than or equal to the current version, return */
|
|
18
|
-
if (seen && event.version <= currentVersion)
|
|
19
|
-
return;
|
|
20
|
-
/** Set the version */
|
|
21
|
-
ctx.versions.set(event.storeKey, event.version);
|
|
22
|
-
}
|
|
23
|
-
/** Apply to Universe store */
|
|
24
|
-
const store = universe.getStore(event.storeKey, () => event.state);
|
|
25
|
-
store.set(event.state);
|
|
26
|
-
/** Optional local state cache (for BroadcastChannel snapshot) */
|
|
27
|
-
ctx.localState?.set(event.storeKey, event.state);
|
|
28
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { ZunoBCOptions, ZunoStateEvent } from "./sync-types";
|
|
2
|
-
/**
|
|
3
|
-
* Starts a broadcast channel for real-time state updates.
|
|
4
|
-
* @param opts The options for the broadcast channel.
|
|
5
|
-
* @returns An object with a `publish` method for publishing events and a `stop` method for stopping the channel.
|
|
6
|
-
*/
|
|
7
|
-
export declare const startBroadcastChannel: (opts: ZunoBCOptions) => {
|
|
8
|
-
publish: (event: ZunoStateEvent) => void;
|
|
9
|
-
hello: () => void;
|
|
10
|
-
stop: () => void;
|
|
11
|
-
};
|
|
12
|
-
//# sourceMappingURL=broadcast-channel.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"broadcast-channel.d.ts","sourceRoot":"","sources":["../../src/sync/broadcast-channel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAS,aAAa,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAGzE;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,GAAI,MAAM,aAAa;qBAkE/B,cAAc;;;CAwBvC,CAAC"}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Starts a broadcast channel for real-time state updates.
|
|
3
|
-
* @param opts The options for the broadcast channel.
|
|
4
|
-
* @returns An object with a `publish` method for publishing events and a `stop` method for stopping the channel.
|
|
5
|
-
*/
|
|
6
|
-
export const startBroadcastChannel = (opts) => {
|
|
7
|
-
const bc = new BroadcastChannel(opts.channelName);
|
|
8
|
-
const post = (msg) => bc.postMessage(msg);
|
|
9
|
-
/**
|
|
10
|
-
* Handles incoming messages from the broadcast channel.
|
|
11
|
-
* @param e The message event.
|
|
12
|
-
*/
|
|
13
|
-
bc.onmessage = (e) => {
|
|
14
|
-
/** Message */
|
|
15
|
-
const msg = e.data;
|
|
16
|
-
/** Invalid message */
|
|
17
|
-
if (!msg || typeof msg !== "object")
|
|
18
|
-
return;
|
|
19
|
-
/** Ignore self messages */
|
|
20
|
-
if (msg.origin === opts.clientId)
|
|
21
|
-
return;
|
|
22
|
-
/** Event message */
|
|
23
|
-
if (msg.type === "zuno:event") {
|
|
24
|
-
/** Event */
|
|
25
|
-
const event = msg.event;
|
|
26
|
-
/** Apply event */
|
|
27
|
-
opts.onEvent(event);
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
/** Hello message */
|
|
31
|
-
if (msg.type === "zuno:hello") {
|
|
32
|
-
/** Someone joined: respond with snapshot (if available) */
|
|
33
|
-
if (!opts.getSnapshot)
|
|
34
|
-
return;
|
|
35
|
-
/** Snapshot */
|
|
36
|
-
const snapshot = opts.getSnapshot();
|
|
37
|
-
/** Post snapshot */
|
|
38
|
-
post({
|
|
39
|
-
type: "zuno:snapshot",
|
|
40
|
-
origin: opts.clientId,
|
|
41
|
-
target: msg.origin,
|
|
42
|
-
snapshot,
|
|
43
|
-
});
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
/** Snapshot message */
|
|
47
|
-
if (msg.type === "zuno:snapshot") {
|
|
48
|
-
/** Accept only if snapshot is meant for me */
|
|
49
|
-
if (msg.target !== opts.clientId)
|
|
50
|
-
return;
|
|
51
|
-
/** Apply snapshot */
|
|
52
|
-
opts.onSnapshot?.(msg.snapshot);
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
/** Publish event */
|
|
57
|
-
const publish = (event) => {
|
|
58
|
-
/** Post event */
|
|
59
|
-
post({
|
|
60
|
-
type: "zuno:event",
|
|
61
|
-
origin: opts.clientId,
|
|
62
|
-
event,
|
|
63
|
-
});
|
|
64
|
-
};
|
|
65
|
-
/** Hello */
|
|
66
|
-
const hello = () => {
|
|
67
|
-
/** Post hello - to notify others about my presence */
|
|
68
|
-
post({ type: "zuno:hello", origin: opts.clientId });
|
|
69
|
-
};
|
|
70
|
-
/** Stop */
|
|
71
|
-
const stop = () => bc.close();
|
|
72
|
-
return { publish, hello, stop };
|
|
73
|
-
};
|