statewire 0.0.0-0 → 0.0.1
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/LICENSE +21 -0
- package/README.md +83 -1
- package/dist/client/changes.d.ts +7 -0
- package/dist/client/changes.d.ts.map +1 -0
- package/dist/client/changes.js +32 -0
- package/dist/client/changes.js.map +1 -0
- package/dist/client/connection.d.ts +84 -0
- package/dist/client/connection.d.ts.map +1 -0
- package/dist/client/connection.js +204 -0
- package/dist/client/connection.js.map +1 -0
- package/dist/client/core.d.ts +18 -0
- package/dist/client/core.d.ts.map +1 -0
- package/dist/client/core.js +269 -0
- package/dist/client/core.js.map +1 -0
- package/dist/client/optimistic.d.ts +10 -0
- package/dist/client/optimistic.d.ts.map +1 -0
- package/dist/client/optimistic.js +128 -0
- package/dist/client/optimistic.js.map +1 -0
- package/dist/client/session.d.ts +88 -0
- package/dist/client/session.d.ts.map +1 -0
- package/dist/client/session.js +509 -0
- package/dist/client/session.js.map +1 -0
- package/dist/client/sessionStore.d.ts +16 -0
- package/dist/client/sessionStore.d.ts.map +1 -0
- package/dist/client/sessionStore.js +14 -0
- package/dist/client/sessionStore.js.map +1 -0
- package/dist/client/timing.d.ts +10 -0
- package/dist/client/timing.d.ts.map +1 -0
- package/dist/client/timing.js +11 -0
- package/dist/client/timing.js.map +1 -0
- package/dist/host/draft.d.ts +14 -0
- package/dist/host/draft.d.ts.map +1 -0
- package/dist/host/draft.js +124 -0
- package/dist/host/draft.js.map +1 -0
- package/dist/host/engine.d.ts +14 -0
- package/dist/host/engine.d.ts.map +1 -0
- package/dist/host/engine.js +17 -0
- package/dist/host/engine.js.map +1 -0
- package/dist/host/host.d.ts +79 -0
- package/dist/host/host.d.ts.map +1 -0
- package/dist/host/host.js +303 -0
- package/dist/host/host.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/server.d.ts +15 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +3 -0
- package/dist/statewire.d.ts +20 -0
- package/dist/statewire.d.ts.map +1 -0
- package/dist/statewire.js +65 -0
- package/dist/statewire.js.map +1 -0
- package/dist/transports/remote.d.ts +19 -0
- package/dist/transports/remote.d.ts.map +1 -0
- package/dist/transports/remote.js +40 -0
- package/dist/transports/remote.js.map +1 -0
- package/dist/transports/sse-parser.d.ts +5 -0
- package/dist/transports/sse-parser.d.ts.map +1 -0
- package/dist/transports/sse-parser.js +26 -0
- package/dist/transports/sse-parser.js.map +1 -0
- package/dist/transports/sse.d.ts +74 -0
- package/dist/transports/sse.d.ts.map +1 -0
- package/dist/transports/sse.js +136 -0
- package/dist/transports/sse.js.map +1 -0
- package/dist/transports/testing.d.ts +31 -0
- package/dist/transports/testing.d.ts.map +1 -0
- package/dist/transports/testing.js +121 -0
- package/dist/transports/testing.js.map +1 -0
- package/dist/transports/ws.d.ts +18 -0
- package/dist/transports/ws.d.ts.map +1 -0
- package/dist/transports/ws.js +172 -0
- package/dist/transports/ws.js.map +1 -0
- package/dist/types.d.ts +107 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +24 -0
- package/dist/types.js.map +1 -0
- package/dist/wire/meta.d.ts +22 -0
- package/dist/wire/meta.d.ts.map +1 -0
- package/dist/wire/meta.js +73 -0
- package/dist/wire/meta.js.map +1 -0
- package/dist/wire/ops.d.ts +40 -0
- package/dist/wire/ops.d.ts.map +1 -0
- package/dist/wire/ops.js +125 -0
- package/dist/wire/ops.js.map +1 -0
- package/package.json +49 -6
- package/src/client/changes.ts +48 -0
- package/src/client/connection.ts +327 -0
- package/src/client/core.ts +346 -0
- package/src/client/optimistic.ts +0 -0
- package/src/client/session.ts +679 -0
- package/src/client/sessionStore.ts +23 -0
- package/src/client/timing.ts +8 -0
- package/src/host/draft.ts +155 -0
- package/src/host/engine.ts +13 -0
- package/src/host/host.ts +481 -0
- package/src/index.ts +65 -0
- package/src/server.ts +32 -0
- package/src/statewire.ts +107 -0
- package/src/transports/remote.ts +72 -0
- package/src/transports/sse-parser.ts +24 -0
- package/src/transports/sse.ts +280 -0
- package/src/transports/testing.ts +155 -0
- package/src/transports/ws.ts +272 -0
- package/src/types.ts +169 -0
- package/src/wire/meta.ts +106 -0
- package/src/wire/ops.ts +201 -0
- package/index.js +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"draft.d.ts","names":[],"sources":["../../src/host/draft.ts"],"mappings":";;KAEY,WAAW,QAAQ;EAAe;;KAElC;EACV;EACA,SAAS,IAAI;;cAGF,SAAU;cA6BV,mBAAoB,GAC/B,MAAM,WACN,OAAM,kBACL"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
//#region src/host/draft.ts
|
|
2
|
+
const frozen = (value) => {
|
|
3
|
+
if (value instanceof Date) return new Date(value.getTime());
|
|
4
|
+
if (Array.isArray(value)) return value.map((item) => frozen(item) ?? null);
|
|
5
|
+
if (typeof value === "object" && value !== null) {
|
|
6
|
+
const out = {};
|
|
7
|
+
for (const key of Object.keys(value)) {
|
|
8
|
+
const item = frozen(value[key]);
|
|
9
|
+
if (item !== void 0) out[key] = item;
|
|
10
|
+
}
|
|
11
|
+
return out;
|
|
12
|
+
}
|
|
13
|
+
return typeof value === "function" ? void 0 : value;
|
|
14
|
+
};
|
|
15
|
+
const lookup = (root, path) => {
|
|
16
|
+
let node = root;
|
|
17
|
+
for (const key of path) node = node[key];
|
|
18
|
+
return node;
|
|
19
|
+
};
|
|
20
|
+
const isIndex = (key) => /^(0|[1-9]\d*)$/.test(key);
|
|
21
|
+
const invalid = (path, message) => /* @__PURE__ */ new Error(`statewire: ${message} at path: ${path.join(".")}`);
|
|
22
|
+
const createStateDraft = (host, path = []) => {
|
|
23
|
+
const node = () => lookup(host.root(), path);
|
|
24
|
+
const assign = (container, key, value) => {
|
|
25
|
+
const old = Object.hasOwn(container, key) ? container[key] : void 0;
|
|
26
|
+
if (old === value) return;
|
|
27
|
+
if (typeof old === "string" && typeof value === "string" && value.startsWith(old)) {
|
|
28
|
+
host.record({
|
|
29
|
+
op: "append",
|
|
30
|
+
path: [...path, key],
|
|
31
|
+
value: value.slice(old.length)
|
|
32
|
+
});
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
host.record({
|
|
36
|
+
op: "replace",
|
|
37
|
+
path: [...path, key],
|
|
38
|
+
value
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
return new Proxy(node(), {
|
|
42
|
+
get: (_target, prop) => {
|
|
43
|
+
const current = node();
|
|
44
|
+
if (typeof prop === "symbol") return current[prop];
|
|
45
|
+
const key = Array.isArray(current) && isIndex(prop) ? Number(prop) : prop;
|
|
46
|
+
const value = current[key];
|
|
47
|
+
if (typeof value === "object" && value !== null) return createStateDraft(host, [...path, key]);
|
|
48
|
+
return value;
|
|
49
|
+
},
|
|
50
|
+
set: (_target, prop, rawValue) => {
|
|
51
|
+
if (typeof prop === "symbol") return false;
|
|
52
|
+
const current = node();
|
|
53
|
+
const value = frozen(rawValue);
|
|
54
|
+
if (Array.isArray(current)) {
|
|
55
|
+
if (prop === "length") {
|
|
56
|
+
const next = Number(value);
|
|
57
|
+
if (!Number.isInteger(next) || next < 0 || next > current.length) throw invalid(path, `cannot set list length to ${String(value)}`);
|
|
58
|
+
for (let i = current.length - 1; i >= next; i--) host.record({
|
|
59
|
+
op: "remove",
|
|
60
|
+
path: [...path, i]
|
|
61
|
+
});
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
if (!isIndex(prop)) throw invalid(path, `cannot set key "${prop}" on a list`);
|
|
65
|
+
const index = Number(prop);
|
|
66
|
+
if (index > current.length) throw invalid(path, `list index ${index} out of range`);
|
|
67
|
+
if (index === current.length) {
|
|
68
|
+
host.record({
|
|
69
|
+
op: "add",
|
|
70
|
+
path: [...path, index],
|
|
71
|
+
value
|
|
72
|
+
});
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
assign(current, index, value);
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
assign(current, prop, value);
|
|
79
|
+
return true;
|
|
80
|
+
},
|
|
81
|
+
deleteProperty: (_target, prop) => {
|
|
82
|
+
if (typeof prop === "symbol") return false;
|
|
83
|
+
const current = node();
|
|
84
|
+
if (Array.isArray(current)) {
|
|
85
|
+
if (!isIndex(prop) || Number(prop) >= current.length) return true;
|
|
86
|
+
host.record({
|
|
87
|
+
op: "remove",
|
|
88
|
+
path: [...path, Number(prop)]
|
|
89
|
+
});
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
if (!Object.hasOwn(current, prop)) return true;
|
|
93
|
+
host.record({
|
|
94
|
+
op: "remove",
|
|
95
|
+
path: [...path, prop]
|
|
96
|
+
});
|
|
97
|
+
return true;
|
|
98
|
+
},
|
|
99
|
+
defineProperty: () => {
|
|
100
|
+
throw new Error("statewire: defineProperty is not supported on a state draft");
|
|
101
|
+
},
|
|
102
|
+
setPrototypeOf: () => {
|
|
103
|
+
throw new Error("statewire: setPrototypeOf is not supported on a state draft");
|
|
104
|
+
},
|
|
105
|
+
preventExtensions: () => {
|
|
106
|
+
throw new Error("statewire: preventExtensions is not supported on a state draft");
|
|
107
|
+
},
|
|
108
|
+
has: (_target, prop) => Reflect.has(node(), prop),
|
|
109
|
+
ownKeys: () => Reflect.ownKeys(node()),
|
|
110
|
+
getOwnPropertyDescriptor: (_target, prop) => {
|
|
111
|
+
const current = node();
|
|
112
|
+
const desc = Reflect.getOwnPropertyDescriptor(current, prop);
|
|
113
|
+
if (!desc) return void 0;
|
|
114
|
+
return {
|
|
115
|
+
...desc,
|
|
116
|
+
configurable: !Array.isArray(current) || prop !== "length"
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
};
|
|
121
|
+
//#endregion
|
|
122
|
+
export { createStateDraft, frozen };
|
|
123
|
+
|
|
124
|
+
//# sourceMappingURL=draft.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"draft.js","names":["StatewireOp","StatewirePath","ServerOp","Exclude","op","DraftHost","root","record","frozen","value","Date","getTime","Array","isArray","map","item","out","Record","key","Object","keys","undefined","lookup","path","node","isIndex","test","invalid","message","Error","join","createStateDraft","host","T","assign","container","old","hasOwn","startsWith","slice","length","Proxy","get","_target","prop","current","Number","set","rawValue","next","isInteger","String","i","index","deleteProperty","defineProperty","setPrototypeOf","preventExtensions","has","Reflect","ownKeys","getOwnPropertyDescriptor","desc","configurable"],"sources":["../../src/host/draft.ts"],"sourcesContent":["import type { StatewireOp, StatewirePath } from \"../wire/ops\";\n\nexport type ServerOp = Exclude<StatewireOp, { op: \"event\" }>;\n\nexport type DraftHost = {\n root: () => unknown;\n record: (op: ServerOp) => void;\n};\n\nexport const frozen = (value: unknown): unknown => {\n if (value instanceof Date) return new Date(value.getTime());\n if (Array.isArray(value)) {\n return value.map((item) => frozen(item) ?? null);\n }\n if (typeof value === \"object\" && value !== null) {\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(value)) {\n const item = frozen((value as Record<string, unknown>)[key]);\n if (item !== undefined) out[key] = item;\n }\n return out;\n }\n return typeof value === \"function\" ? undefined : value;\n};\n\nconst lookup = (root: unknown, path: StatewirePath): unknown => {\n let node = root;\n for (const key of path) {\n node = (node as Record<string | number, unknown>)[key];\n }\n return node;\n};\n\nconst isIndex = (key: string) => /^(0|[1-9]\\d*)$/.test(key);\n\nconst invalid = (path: StatewirePath, message: string) =>\n new Error(`statewire: ${message} at path: ${path.join(\".\")}`);\n\nexport const createStateDraft = <T>(\n host: DraftHost,\n path: StatewirePath = [],\n): T => {\n const node = () => lookup(host.root(), path);\n\n const assign = (\n container: Record<string | number, unknown>,\n key: string | number,\n value: unknown,\n ) => {\n const old = Object.hasOwn(container, key) ? container[key] : undefined;\n if (old === value) return;\n if (\n typeof old === \"string\" &&\n typeof value === \"string\" &&\n value.startsWith(old)\n ) {\n host.record({\n op: \"append\",\n path: [...path, key],\n value: value.slice(old.length),\n });\n return;\n }\n host.record({ op: \"replace\", path: [...path, key], value });\n };\n\n return new Proxy(node() as object, {\n get: (_target, prop) => {\n const current = node() as Record<string | number, unknown>;\n if (typeof prop === \"symbol\") {\n return (current as unknown as Record<symbol, unknown>)[prop];\n }\n const key = Array.isArray(current) && isIndex(prop) ? Number(prop) : prop;\n const value = current[key];\n if (typeof value === \"object\" && value !== null) {\n return createStateDraft(host, [...path, key]);\n }\n return value;\n },\n set: (_target, prop, rawValue) => {\n if (typeof prop === \"symbol\") return false;\n const current = node();\n const value = frozen(rawValue);\n if (Array.isArray(current)) {\n if (prop === \"length\") {\n const next = Number(value);\n if (!Number.isInteger(next) || next < 0 || next > current.length) {\n throw invalid(path, `cannot set list length to ${String(value)}`);\n }\n for (let i = current.length - 1; i >= next; i--) {\n host.record({ op: \"remove\", path: [...path, i] });\n }\n return true;\n }\n if (!isIndex(prop)) {\n throw invalid(path, `cannot set key \"${prop}\" on a list`);\n }\n const index = Number(prop);\n if (index > current.length) {\n throw invalid(path, `list index ${index} out of range`);\n }\n if (index === current.length) {\n host.record({ op: \"add\", path: [...path, index], value });\n return true;\n }\n assign(\n current as unknown as Record<string | number, unknown>,\n index,\n value,\n );\n return true;\n }\n assign(current as Record<string, unknown>, prop, value);\n return true;\n },\n deleteProperty: (_target, prop) => {\n if (typeof prop === \"symbol\") return false;\n const current = node();\n if (Array.isArray(current)) {\n if (!isIndex(prop) || Number(prop) >= current.length) return true;\n host.record({ op: \"remove\", path: [...path, Number(prop)] });\n return true;\n }\n if (!Object.hasOwn(current as object, prop)) return true;\n host.record({ op: \"remove\", path: [...path, prop] });\n return true;\n },\n defineProperty: () => {\n throw new Error(\n \"statewire: defineProperty is not supported on a state draft\",\n );\n },\n setPrototypeOf: () => {\n throw new Error(\n \"statewire: setPrototypeOf is not supported on a state draft\",\n );\n },\n preventExtensions: () => {\n throw new Error(\n \"statewire: preventExtensions is not supported on a state draft\",\n );\n },\n has: (_target, prop) => Reflect.has(node() as object, prop),\n ownKeys: () => Reflect.ownKeys(node() as object),\n getOwnPropertyDescriptor: (_target, prop) => {\n const current = node() as object;\n const desc = Reflect.getOwnPropertyDescriptor(current, prop);\n if (!desc) return undefined;\n return {\n ...desc,\n configurable: !Array.isArray(current) || prop !== \"length\",\n };\n },\n }) as T;\n};\n"],"mappings":";AASA,MAAaQ,UAAUC,UAA4B;CACjD,IAAIA,iBAAiBC,MAAM,OAAO,IAAIA,KAAKD,MAAME,QAAQ,CAAC;CAC1D,IAAIC,MAAMC,QAAQJ,KAAK,GACrB,OAAOA,MAAMK,KAAKC,SAASP,OAAOO,IAAI,KAAK,IAAI;CAEjD,IAAI,OAAON,UAAU,YAAYA,UAAU,MAAM;EAC/C,MAAMO,MAA+B,CAAC;EACtC,KAAK,MAAME,OAAOC,OAAOC,KAAKX,KAAK,GAAG;GACpC,MAAMM,OAAOP,OAAQC,MAAkCS,IAAI;GAC3D,IAAIH,SAASM,KAAAA,GAAWL,IAAIE,OAAOH;EACrC;EACA,OAAOC;CACT;CACA,OAAO,OAAOP,UAAU,aAAaY,KAAAA,IAAYZ;AACnD;AAEA,MAAMa,UAAUhB,MAAeiB,SAAiC;CAC9D,IAAIC,OAAOlB;CACX,KAAK,MAAMY,OAAOK,MAChBC,OAAQA,KAA0CN;CAEpD,OAAOM;AACT;AAEA,MAAMC,WAAWP,QAAgB,iBAAiBQ,KAAKR,GAAG;AAE1D,MAAMS,WAAWJ,MAAqBK,4BACpC,IAAIC,MAAM,cAAcD,QAAO,YAAaL,KAAKO,KAAK,GAAG,GAAG;AAE9D,MAAaC,oBACXC,MACAT,OAAsB,CAAA,MAChB;CACN,MAAMC,aAAaF,OAAOU,KAAK1B,KAAK,GAAGiB,IAAI;CAE3C,MAAMW,UACJC,WACAjB,KACAT,UACG;EACH,MAAM2B,MAAMjB,OAAOkB,OAAOF,WAAWjB,GAAG,IAAIiB,UAAUjB,OAAOG,KAAAA;EAC7D,IAAIe,QAAQ3B,OAAO;EACnB,IACE,OAAO2B,QAAQ,YACf,OAAO3B,UAAU,YACjBA,MAAM6B,WAAWF,GAAG,GACpB;GACAJ,KAAKzB,OAAO;IACVH,IAAI;IACJmB,MAAM,CAAC,GAAGA,MAAML,GAAG;IACnBT,OAAOA,MAAM8B,MAAMH,IAAII,MAAM;GAC/B,CAAC;GACD;EACF;EACAR,KAAKzB,OAAO;GAAEH,IAAI;GAAWmB,MAAM,CAAC,GAAGA,MAAML,GAAG;GAAGT;EAAM,CAAC;CAC5D;CAEA,OAAO,IAAIgC,MAAMjB,KAAK,GAAa;EACjCkB,MAAMC,SAASC,SAAS;GACtB,MAAMC,UAAUrB,KAAK;GACrB,IAAI,OAAOoB,SAAS,UAClB,OAAQC,QAA+CD;GAEzD,MAAM1B,MAAMN,MAAMC,QAAQgC,OAAO,KAAKpB,QAAQmB,IAAI,IAAIE,OAAOF,IAAI,IAAIA;GACrE,MAAMnC,QAAQoC,QAAQ3B;GACtB,IAAI,OAAOT,UAAU,YAAYA,UAAU,MACzC,OAAOsB,iBAAiBC,MAAM,CAAC,GAAGT,MAAML,GAAG,CAAC;GAE9C,OAAOT;EACT;EACAsC,MAAMJ,SAASC,MAAMI,aAAa;GAChC,IAAI,OAAOJ,SAAS,UAAU,OAAO;GACrC,MAAMC,UAAUrB,KAAK;GACrB,MAAMf,QAAQD,OAAOwC,QAAQ;GAC7B,IAAIpC,MAAMC,QAAQgC,OAAO,GAAG;IAC1B,IAAID,SAAS,UAAU;KACrB,MAAMK,OAAOH,OAAOrC,KAAK;KACzB,IAAI,CAACqC,OAAOI,UAAUD,IAAI,KAAKA,OAAO,KAAKA,OAAOJ,QAAQL,QACxD,MAAMb,QAAQJ,MAAM,6BAA6B4B,OAAO1C,KAAK,GAAG;KAElE,KAAK,IAAI2C,IAAIP,QAAQL,SAAS,GAAGY,KAAKH,MAAMG,KAC1CpB,KAAKzB,OAAO;MAAEH,IAAI;MAAUmB,MAAM,CAAC,GAAGA,MAAM6B,CAAC;KAAE,CAAC;KAElD,OAAO;IACT;IACA,IAAI,CAAC3B,QAAQmB,IAAI,GACf,MAAMjB,QAAQJ,MAAM,mBAAmBqB,KAAI,YAAa;IAE1D,MAAMS,QAAQP,OAAOF,IAAI;IACzB,IAAIS,QAAQR,QAAQL,QAClB,MAAMb,QAAQJ,MAAM,cAAc8B,MAAK,cAAe;IAExD,IAAIA,UAAUR,QAAQL,QAAQ;KAC5BR,KAAKzB,OAAO;MAAEH,IAAI;MAAOmB,MAAM,CAAC,GAAGA,MAAM8B,KAAK;MAAG5C;KAAM,CAAC;KACxD,OAAO;IACT;IACAyB,OACEW,SACAQ,OACA5C,KACF;IACA,OAAO;GACT;GACAyB,OAAOW,SAAoCD,MAAMnC,KAAK;GACtD,OAAO;EACT;EACA6C,iBAAiBX,SAASC,SAAS;GACjC,IAAI,OAAOA,SAAS,UAAU,OAAO;GACrC,MAAMC,UAAUrB,KAAK;GACrB,IAAIZ,MAAMC,QAAQgC,OAAO,GAAG;IAC1B,IAAI,CAACpB,QAAQmB,IAAI,KAAKE,OAAOF,IAAI,KAAKC,QAAQL,QAAQ,OAAO;IAC7DR,KAAKzB,OAAO;KAAEH,IAAI;KAAUmB,MAAM,CAAC,GAAGA,MAAMuB,OAAOF,IAAI,CAAC;IAAE,CAAC;IAC3D,OAAO;GACT;GACA,IAAI,CAACzB,OAAOkB,OAAOQ,SAAmBD,IAAI,GAAG,OAAO;GACpDZ,KAAKzB,OAAO;IAAEH,IAAI;IAAUmB,MAAM,CAAC,GAAGA,MAAMqB,IAAI;GAAE,CAAC;GACnD,OAAO;EACT;EACAW,sBAAsB;GACpB,MAAM,IAAI1B,MACR,6DACF;EACF;EACA2B,sBAAsB;GACpB,MAAM,IAAI3B,MACR,6DACF;EACF;EACA4B,yBAAyB;GACvB,MAAM,IAAI5B,MACR,gEACF;EACF;EACA6B,MAAMf,SAASC,SAASe,QAAQD,IAAIlC,KAAK,GAAaoB,IAAI;EAC1DgB,eAAeD,QAAQC,QAAQpC,KAAK,CAAW;EAC/CqC,2BAA2BlB,SAASC,SAAS;GAC3C,MAAMC,UAAUrB,KAAK;GACrB,MAAMsC,OAAOH,QAAQE,yBAAyBhB,SAASD,IAAI;GAC3D,IAAI,CAACkB,MAAM,OAAOzC,KAAAA;GAClB,OAAO;IACL,GAAGyC;IACHC,cAAc,CAACnD,MAAMC,QAAQgC,OAAO,KAAKD,SAAS;GACpD;EACF;CACF,CAAC;AACH"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//#region src/host/engine.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Definitive handler rejection: the command settles rejected and
|
|
4
|
+
* message/payload travel to the client's rejected promise.
|
|
5
|
+
*/
|
|
6
|
+
declare class StatewireReject extends Error {
|
|
7
|
+
readonly payload?: unknown;
|
|
8
|
+
constructor(message: string, options?: {
|
|
9
|
+
payload?: unknown;
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
//#endregion
|
|
13
|
+
export { StatewireReject };
|
|
14
|
+
//# sourceMappingURL=engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.d.ts","names":[],"sources":["../../src/host/engine.ts"],"mappings":";;;;;cAIa,wBAAwB;WAC1B;EAEG,YAAA,iBAAiB;IAAY"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//#region src/host/engine.ts
|
|
2
|
+
/**
|
|
3
|
+
* Definitive handler rejection: the command settles rejected and
|
|
4
|
+
* message/payload travel to the client's rejected promise.
|
|
5
|
+
*/
|
|
6
|
+
var StatewireReject = class extends Error {
|
|
7
|
+
payload;
|
|
8
|
+
constructor(message, options) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = "StatewireReject";
|
|
11
|
+
if (options?.payload !== void 0) this.payload = options.payload;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
//#endregion
|
|
15
|
+
export { StatewireReject };
|
|
16
|
+
|
|
17
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","names":["StatewireReject","Error","payload","constructor","message","options","name","undefined"],"sources":["../../src/host/engine.ts"],"sourcesContent":["/**\n * Definitive handler rejection: the command settles rejected and\n * message/payload travel to the client's rejected promise.\n */\nexport class StatewireReject extends Error {\n readonly payload?: unknown;\n\n constructor(message: string, options?: { payload?: unknown }) {\n super(message);\n this.name = \"StatewireReject\";\n if (options?.payload !== undefined) this.payload = options.payload;\n }\n}\n"],"mappings":";;;;;AAIA,IAAaA,kBAAb,cAAqCC,MAAM;CACzC;CAEAE,YAAYC,SAAiBC,SAAiC;EAC5D,MAAMD,OAAO;EACb,KAAKE,OAAO;EACZ,IAAID,SAASH,YAAYK,KAAAA,GAAW,KAAKL,UAAUG,QAAQH;CAC7D;AACF"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { StatewireOp } from "../wire/ops.js";
|
|
2
|
+
import { StatewireCommands, StatewireCommandsProxy, StatewireStateSource } from "../types.js";
|
|
3
|
+
import { ServerOp } from "./draft.js";
|
|
4
|
+
//#region src/host/host.d.ts
|
|
5
|
+
type StatewireStateCtl = {
|
|
6
|
+
/** Apply server-vocabulary ops (root replace = whole-state swap). */
|
|
7
|
+
apply: (ops: readonly StatewireOp[]) => void;
|
|
8
|
+
};
|
|
9
|
+
type StateHost = StatewireStateSource & {
|
|
10
|
+
/** Synchronous, per locally-recorded op; the host owns batching. */
|
|
11
|
+
setOnOp: (listener: ((op: ServerOp) => void) | null) => void;
|
|
12
|
+
};
|
|
13
|
+
declare const getStateHost: (state: object) => StateHost | undefined;
|
|
14
|
+
declare const useStatewireState: <TState>(init: () => TState) => readonly [TState, StatewireStateCtl];
|
|
15
|
+
declare const executionResult: unique symbol;
|
|
16
|
+
/**
|
|
17
|
+
* Handle a command handler returns to keep the command open past its own
|
|
18
|
+
* return: `ack()` releases the client's optimistic overlay / ack watermark,
|
|
19
|
+
* `resolve`/`reject` settle the command (and imply ack). `ack` is idempotent
|
|
20
|
+
* and a no-op after settle.
|
|
21
|
+
*/
|
|
22
|
+
type CommandExecution<T = void> = {
|
|
23
|
+
readonly [executionResult]: (value: T) => void;
|
|
24
|
+
};
|
|
25
|
+
type CommandExecutionResolvers<T = void> = {
|
|
26
|
+
ack: () => void;
|
|
27
|
+
resolve: (value: T) => void;
|
|
28
|
+
reject: (error: unknown) => void;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* `CommandExecution(executor)` runs the (possibly async) `executor({ ack })`
|
|
32
|
+
* immediately: its return value resolves the execution, a throw/rejection
|
|
33
|
+
* rejects it; `ack()` releases the client's ack watermark before the executor
|
|
34
|
+
* finishes. `CommandExecution.withResolvers()` returns `[execution,
|
|
35
|
+
* { ack, resolve, reject }]` instead — settle them from your own spawned work.
|
|
36
|
+
*/
|
|
37
|
+
declare const CommandExecution: (<T = void>(executor: (resolvers: {
|
|
38
|
+
ack: () => void;
|
|
39
|
+
}) => T | void | PromiseLike<T | void>) => CommandExecution<T>) & {
|
|
40
|
+
withResolvers: <T = void>() => [CommandExecution<T>, CommandExecutionResolvers<T>];
|
|
41
|
+
};
|
|
42
|
+
type CommandsInvocation = {
|
|
43
|
+
readonly method: string;
|
|
44
|
+
readonly params: readonly unknown[];
|
|
45
|
+
readonly ts: number;
|
|
46
|
+
readonly seq: number;
|
|
47
|
+
readonly clientId?: string;
|
|
48
|
+
};
|
|
49
|
+
type CommandsOutcome = {
|
|
50
|
+
type: "accepted";
|
|
51
|
+
payload?: unknown;
|
|
52
|
+
} | {
|
|
53
|
+
type: "rejected";
|
|
54
|
+
error: unknown;
|
|
55
|
+
};
|
|
56
|
+
type CommandsListener = {
|
|
57
|
+
onStart: (inv: CommandsInvocation) => void;
|
|
58
|
+
onPosted?: (inv: CommandsInvocation) => void;
|
|
59
|
+
onAck: (inv: CommandsInvocation) => void;
|
|
60
|
+
/** Canonical state now covers the invocation's effect — drop any overlay. */
|
|
61
|
+
onCovered?: (inv: CommandsInvocation) => void;
|
|
62
|
+
onSettle: (inv: CommandsInvocation, outcome: CommandsOutcome) => void;
|
|
63
|
+
};
|
|
64
|
+
type CommandsHost = {
|
|
65
|
+
setListener: (listener: CommandsListener | null) => void;
|
|
66
|
+
};
|
|
67
|
+
declare const getCommandsHost: (commands: object) => CommandsHost | undefined;
|
|
68
|
+
declare const bindCommandsHost: (commands: object, host: CommandsHost) => void;
|
|
69
|
+
type UnwrapExecution<V> = V extends CommandExecution<infer T> ? T : V;
|
|
70
|
+
type StatewireHandledCommands<THandlers extends StatewireCommands> = { [K in keyof THandlers]: (...args: Parameters<THandlers[K]>) => UnwrapExecution<Awaited<ReturnType<THandlers[K]>>>; };
|
|
71
|
+
declare const useStatewireCommands: <THandlers extends StatewireCommands>(commands: THandlers | undefined | null | false) => StatewireCommandsProxy<StatewireHandledCommands<THandlers>>;
|
|
72
|
+
type EmitHost = {
|
|
73
|
+
setSink: (sink: ((event: unknown) => void) | null) => void;
|
|
74
|
+
};
|
|
75
|
+
declare const getEmitHost: (emit: object) => EmitHost | undefined;
|
|
76
|
+
declare const useStatewireEmit: <TEvent>() => ((event: TEvent) => void);
|
|
77
|
+
//#endregion
|
|
78
|
+
export { CommandExecution, CommandExecutionResolvers, CommandsHost, CommandsInvocation, CommandsListener, CommandsOutcome, EmitHost, StateHost, StatewireHandledCommands, StatewireStateCtl, bindCommandsHost, getCommandsHost, getEmitHost, getStateHost, useStatewireCommands, useStatewireEmit, useStatewireState };
|
|
79
|
+
//# sourceMappingURL=host.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host.d.ts","names":[],"sources":["../../src/host/host.ts"],"mappings":";;;;KAkBY;;EAEV,QAAQ,cAAc;;KAGZ,YAAY;;EAEtB,UAAU,YAAY,IAAI;;cAKf,eAAgB,kBAAgB;cAchC,oBAAqB,QAChC,YAAY,qBACD,QAAQ;cAgFP;;;;;;;KAQF,iBAAiB;YACjB,mBAAmB,OAAO;;KAG1B,0BAA0B;EACpC;EACA,UAAU,OAAO;EACjB,SAAS;;;;;;;;;cAUE,oBACV,UAAC,WACW;EACT;MACI,WAAW,YAAY,cAC5B,iBAAiB;EAyBF,gBAAA,gBACd,iBAAiB,IACjB,0BAA0B;;KAkBpB;WACD;WACA;WACA;WACA;WACA;;KAGC;EACN;EAAkB;;EAClB;EAAkB;;KAEZ;EACV,UAAU,KAAK;EACf,YAAY,KAAK;EACjB,QAAQ,KAAK;;EAEb,aAAa,KAAK;EAClB,WAAW,KAAK,oBAAoB,SAAS;;KAGnC;EACV,cAAc,UAAU;;cAKb,kBAAmB,qBAAmB;cAGtC,mBAAoB,kBAAkB,MAAM;KAsIpD,gBAAgB,KAAK,UAAU,uBAAuB,KAAK,IAAI;KAExD,yBAAyB,kBAAkB,wBACpD,WAAW,gBACP,MAAM,WAAW,UAAU,QAC3B,gBAAgB,QAAQ,WAAW,UAAU;cAGvC,uBAAwB,kBAAkB,mBACrD,UAAU,yCACT,uBAAuB,yBAAyB;KA+EvC;EACV,UAAU,QAAQ;;cAKP,cAAe,iBAAe;cAG9B,mBAAoB,eAAa,OAAO"}
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import { applyOp, applyOps } from "../wire/ops.js";
|
|
2
|
+
import { StatewireReject } from "./engine.js";
|
|
3
|
+
import { createStateDraft, frozen } from "./draft.js";
|
|
4
|
+
import { StatewireSendError } from "../types.js";
|
|
5
|
+
import { useEffect, useState } from "@assistant-ui/tap/react-shim";
|
|
6
|
+
//#region src/host/host.ts
|
|
7
|
+
const isDev = () => typeof process === "undefined" || process.env["NODE_ENV"] !== "production";
|
|
8
|
+
const stateHosts = /* @__PURE__ */ new WeakMap();
|
|
9
|
+
const getStateHost = (state) => stateHosts.get(state);
|
|
10
|
+
const useStatewireState = (init) => {
|
|
11
|
+
const [, setVersion] = useState(0);
|
|
12
|
+
const [cell] = useState(() => {
|
|
13
|
+
const c = {
|
|
14
|
+
current: frozen(init()),
|
|
15
|
+
draft: null,
|
|
16
|
+
changed: [[]],
|
|
17
|
+
onOp: null,
|
|
18
|
+
onChange: null,
|
|
19
|
+
bump: () => setVersion((v) => v + 1),
|
|
20
|
+
ctl: { apply: (ops) => {
|
|
21
|
+
const result = applyOps(c.current, ops);
|
|
22
|
+
if (result.events.length > 0) throw new Error("statewire: event ops do not apply to state — route them to emit");
|
|
23
|
+
c.current = result.state;
|
|
24
|
+
c.changed.push(...result.changedPaths);
|
|
25
|
+
c.bump();
|
|
26
|
+
c.onChange?.();
|
|
27
|
+
} },
|
|
28
|
+
host: {
|
|
29
|
+
root: () => c.current,
|
|
30
|
+
setOnOp: (listener) => {
|
|
31
|
+
c.onOp = listener;
|
|
32
|
+
},
|
|
33
|
+
setOnChange: (listener_0) => {
|
|
34
|
+
c.onChange = listener_0;
|
|
35
|
+
},
|
|
36
|
+
takeChanged: () => {
|
|
37
|
+
const changed = c.changed;
|
|
38
|
+
c.changed = [];
|
|
39
|
+
return changed;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
return c;
|
|
44
|
+
});
|
|
45
|
+
let value = cell.current;
|
|
46
|
+
if (typeof value === "object" && value !== null) {
|
|
47
|
+
cell.draft ??= createStateDraft({
|
|
48
|
+
root: () => cell.current,
|
|
49
|
+
record: (op) => {
|
|
50
|
+
const applied = applyOp(cell.current, op);
|
|
51
|
+
cell.current = applied.state;
|
|
52
|
+
cell.changed.push(...applied.changedPaths);
|
|
53
|
+
cell.onOp?.(op);
|
|
54
|
+
cell.bump();
|
|
55
|
+
cell.onChange?.();
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
stateHosts.set(cell.draft, cell.host);
|
|
59
|
+
value = cell.draft;
|
|
60
|
+
}
|
|
61
|
+
return [value, cell.ctl];
|
|
62
|
+
};
|
|
63
|
+
const executionStates = /* @__PURE__ */ new WeakMap();
|
|
64
|
+
/**
|
|
65
|
+
* `CommandExecution(executor)` runs the (possibly async) `executor({ ack })`
|
|
66
|
+
* immediately: its return value resolves the execution, a throw/rejection
|
|
67
|
+
* rejects it; `ack()` releases the client's ack watermark before the executor
|
|
68
|
+
* finishes. `CommandExecution.withResolvers()` returns `[execution,
|
|
69
|
+
* { ack, resolve, reject }]` instead — settle them from your own spawned work.
|
|
70
|
+
*/
|
|
71
|
+
const CommandExecution = Object.assign((executor) => {
|
|
72
|
+
const state = {
|
|
73
|
+
acked: false,
|
|
74
|
+
settled: null,
|
|
75
|
+
bind: null
|
|
76
|
+
};
|
|
77
|
+
const execution = Object.freeze({});
|
|
78
|
+
executionStates.set(execution, state);
|
|
79
|
+
const settle = (settled) => {
|
|
80
|
+
if (state.settled) return;
|
|
81
|
+
state.settled = settled;
|
|
82
|
+
state.acked = true;
|
|
83
|
+
state.bind?.onSettle(settled);
|
|
84
|
+
};
|
|
85
|
+
const ack = () => {
|
|
86
|
+
if (state.acked) return;
|
|
87
|
+
state.acked = true;
|
|
88
|
+
state.bind?.onAck();
|
|
89
|
+
};
|
|
90
|
+
(async () => {
|
|
91
|
+
try {
|
|
92
|
+
settle({
|
|
93
|
+
type: "resolved",
|
|
94
|
+
value: await executor({ ack })
|
|
95
|
+
});
|
|
96
|
+
} catch (error) {
|
|
97
|
+
settle({
|
|
98
|
+
type: "rejected",
|
|
99
|
+
error
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
})();
|
|
103
|
+
return execution;
|
|
104
|
+
}, { withResolvers: () => {
|
|
105
|
+
let resolve;
|
|
106
|
+
let reject;
|
|
107
|
+
const promise = new Promise((res, rej) => {
|
|
108
|
+
resolve = res;
|
|
109
|
+
reject = rej;
|
|
110
|
+
});
|
|
111
|
+
let ack;
|
|
112
|
+
return [CommandExecution(({ ack: a }) => {
|
|
113
|
+
ack = a;
|
|
114
|
+
return promise;
|
|
115
|
+
}), {
|
|
116
|
+
ack: () => ack(),
|
|
117
|
+
resolve,
|
|
118
|
+
reject
|
|
119
|
+
}];
|
|
120
|
+
} });
|
|
121
|
+
const commandsHosts = /* @__PURE__ */ new WeakMap();
|
|
122
|
+
const getCommandsHost = (commands) => commandsHosts.get(commands);
|
|
123
|
+
const bindCommandsHost = (commands, host) => {
|
|
124
|
+
commandsHosts.set(commands, host);
|
|
125
|
+
};
|
|
126
|
+
const STALL_WARN_MS = 5e3;
|
|
127
|
+
const toSendError = (error) => error instanceof StatewireSendError ? error : new StatewireSendError({
|
|
128
|
+
fate: "rejected",
|
|
129
|
+
cause: "server",
|
|
130
|
+
message: error instanceof Error ? error.message : String(error),
|
|
131
|
+
...error instanceof StatewireReject && error.payload !== void 0 && { payload: error.payload }
|
|
132
|
+
});
|
|
133
|
+
const ackInvocation = (cell, inv) => {
|
|
134
|
+
if (inv.acked) return;
|
|
135
|
+
inv.acked = true;
|
|
136
|
+
cell.listener?.onAck(inv);
|
|
137
|
+
};
|
|
138
|
+
const settleInvocation = (cell, inv, call, settled) => {
|
|
139
|
+
ackInvocation(cell, inv);
|
|
140
|
+
if (settled.type === "resolved") {
|
|
141
|
+
const outcome = settled.value === void 0 ? { type: "accepted" } : {
|
|
142
|
+
type: "accepted",
|
|
143
|
+
payload: frozen(settled.value)
|
|
144
|
+
};
|
|
145
|
+
cell.listener?.onSettle(inv, outcome);
|
|
146
|
+
call.resolve(outcome.payload);
|
|
147
|
+
} else {
|
|
148
|
+
const error = toSendError(settled.error);
|
|
149
|
+
cell.listener?.onSettle(inv, {
|
|
150
|
+
type: "rejected",
|
|
151
|
+
error
|
|
152
|
+
});
|
|
153
|
+
call.reject(error);
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
const pump = (cell) => {
|
|
157
|
+
if (cell.running || !cell.handlers) {
|
|
158
|
+
watchStall(cell);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const call = cell.queue.shift();
|
|
162
|
+
if (!call) return;
|
|
163
|
+
const handler = cell.handlers[call.method];
|
|
164
|
+
if (handler === void 0) {
|
|
165
|
+
call.reject(new StatewireSendError({
|
|
166
|
+
fate: "unknown-command",
|
|
167
|
+
cause: "server"
|
|
168
|
+
}));
|
|
169
|
+
pump(cell);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const inv = {
|
|
173
|
+
method: call.method,
|
|
174
|
+
params: call.params,
|
|
175
|
+
ts: call.ts,
|
|
176
|
+
seq: ++cell.seq,
|
|
177
|
+
acked: false
|
|
178
|
+
};
|
|
179
|
+
cell.running = true;
|
|
180
|
+
cell.listener?.onStart(inv);
|
|
181
|
+
cell.listener?.onPosted?.(inv);
|
|
182
|
+
cell.listener?.onCovered?.(inv);
|
|
183
|
+
(async () => {
|
|
184
|
+
try {
|
|
185
|
+
const value = await handler(...call.params);
|
|
186
|
+
const execution = typeof value === "object" && value !== null ? executionStates.get(value) : void 0;
|
|
187
|
+
if (execution) {
|
|
188
|
+
if (execution.acked) ackInvocation(cell, inv);
|
|
189
|
+
if (execution.settled) settleInvocation(cell, inv, call, execution.settled);
|
|
190
|
+
else execution.bind = {
|
|
191
|
+
onAck: () => ackInvocation(cell, inv),
|
|
192
|
+
onSettle: (settled) => settleInvocation(cell, inv, call, settled)
|
|
193
|
+
};
|
|
194
|
+
} else settleInvocation(cell, inv, call, {
|
|
195
|
+
type: "resolved",
|
|
196
|
+
value
|
|
197
|
+
});
|
|
198
|
+
} catch (error) {
|
|
199
|
+
settleInvocation(cell, inv, call, {
|
|
200
|
+
type: "rejected",
|
|
201
|
+
error
|
|
202
|
+
});
|
|
203
|
+
} finally {
|
|
204
|
+
cell.running = false;
|
|
205
|
+
pump(cell);
|
|
206
|
+
}
|
|
207
|
+
})();
|
|
208
|
+
};
|
|
209
|
+
const watchStall = (cell) => {
|
|
210
|
+
if (!isDev() || cell.stallTimer !== null || cell.queue.length === 0) return;
|
|
211
|
+
if (cell.handlers) return;
|
|
212
|
+
cell.stallTimer = setTimeout(() => {
|
|
213
|
+
cell.stallTimer = null;
|
|
214
|
+
if (cell.handlers === null && cell.queue.length > 0 && !cell.unmounted) console.warn(`statewire: ${cell.queue.length} buffered command(s) waiting over ${STALL_WARN_MS}ms — did the host forget to arm useStatewireCommands?`);
|
|
215
|
+
}, STALL_WARN_MS);
|
|
216
|
+
};
|
|
217
|
+
const useStatewireCommands = (commands) => {
|
|
218
|
+
const [built] = useState(() => {
|
|
219
|
+
const cell = {
|
|
220
|
+
handlers: null,
|
|
221
|
+
queue: [],
|
|
222
|
+
running: false,
|
|
223
|
+
listener: null,
|
|
224
|
+
seq: 0,
|
|
225
|
+
unmounted: false,
|
|
226
|
+
stallTimer: null
|
|
227
|
+
};
|
|
228
|
+
const memo = /* @__PURE__ */ new Map();
|
|
229
|
+
const proxy = new Proxy({}, { get: (_target, method) => {
|
|
230
|
+
if (typeof method !== "string") return void 0;
|
|
231
|
+
let fn = memo.get(method);
|
|
232
|
+
if (fn === void 0) {
|
|
233
|
+
fn = (...params) => new Promise((resolve, reject) => {
|
|
234
|
+
if (cell.unmounted) {
|
|
235
|
+
reject(/* @__PURE__ */ new Error("statewire: commands called after unmount"));
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
cell.queue.push({
|
|
239
|
+
method,
|
|
240
|
+
params,
|
|
241
|
+
ts: Date.now(),
|
|
242
|
+
resolve,
|
|
243
|
+
reject
|
|
244
|
+
});
|
|
245
|
+
pump(cell);
|
|
246
|
+
});
|
|
247
|
+
memo.set(method, fn);
|
|
248
|
+
}
|
|
249
|
+
return fn;
|
|
250
|
+
} });
|
|
251
|
+
commandsHosts.set(proxy, { setListener: (listener) => {
|
|
252
|
+
cell.listener = listener;
|
|
253
|
+
} });
|
|
254
|
+
return {
|
|
255
|
+
cell,
|
|
256
|
+
proxy
|
|
257
|
+
};
|
|
258
|
+
});
|
|
259
|
+
useEffect(() => {
|
|
260
|
+
const { cell: cell_0 } = built;
|
|
261
|
+
cell_0.unmounted = false;
|
|
262
|
+
return () => {
|
|
263
|
+
cell_0.unmounted = true;
|
|
264
|
+
cell_0.handlers = null;
|
|
265
|
+
if (cell_0.stallTimer !== null) {
|
|
266
|
+
clearTimeout(cell_0.stallTimer);
|
|
267
|
+
cell_0.stallTimer = null;
|
|
268
|
+
}
|
|
269
|
+
queueMicrotask(() => {
|
|
270
|
+
if (!cell_0.unmounted) return;
|
|
271
|
+
const queued = cell_0.queue;
|
|
272
|
+
cell_0.queue = [];
|
|
273
|
+
for (const call of queued) call.reject(/* @__PURE__ */ new Error("statewire: unmounted"));
|
|
274
|
+
});
|
|
275
|
+
};
|
|
276
|
+
}, [built]);
|
|
277
|
+
useEffect(() => {
|
|
278
|
+
built.cell.handlers = commands || null;
|
|
279
|
+
pump(built.cell);
|
|
280
|
+
});
|
|
281
|
+
return built.proxy;
|
|
282
|
+
};
|
|
283
|
+
const emitHosts = /* @__PURE__ */ new WeakMap();
|
|
284
|
+
const getEmitHost = (emit) => emitHosts.get(emit);
|
|
285
|
+
const useStatewireEmit = () => {
|
|
286
|
+
const [built] = useState(_temp);
|
|
287
|
+
return built;
|
|
288
|
+
};
|
|
289
|
+
function _temp() {
|
|
290
|
+
const cell = { sink: null };
|
|
291
|
+
const emit = (event) => {
|
|
292
|
+
if (!cell.sink) throw new Error("statewire: emit before a host bound the event sink");
|
|
293
|
+
cell.sink(frozen(event));
|
|
294
|
+
};
|
|
295
|
+
emitHosts.set(emit, { setSink: (sink) => {
|
|
296
|
+
cell.sink = sink;
|
|
297
|
+
} });
|
|
298
|
+
return emit;
|
|
299
|
+
}
|
|
300
|
+
//#endregion
|
|
301
|
+
export { CommandExecution, bindCommandsHost, getCommandsHost, getEmitHost, getStateHost, useStatewireCommands, useStatewireEmit, useStatewireState };
|
|
302
|
+
|
|
303
|
+
//# sourceMappingURL=host.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host.js","names":["useEffect","useState","applyOp","applyOps","StatewireOp","StatewirePath","StatewireReject","createStateDraft","frozen","ServerOp","StatewireSendError","StatewireCommands","StatewireCommandsProxy","StatewireStateSource","isDev","process","env","StatewireStateCtl","apply","ops","StateHost","setOnOp","listener","op","stateHosts","WeakMap","getStateHost","state","get","StateCell","current","draft","changed","onOp","onChange","ctl","host","bump","useStatewireState","init","TState","setVersion","cell","c","v","result","events","length","Error","push","changedPaths","root","setOnChange","takeChanged","value","record","applied","set","const","ExecutionSettled","type","error","ExecutionBind","onAck","onSettle","settled","ExecutionState","acked","bind","executionStates","executionResult","CommandExecution","T","CommandExecutionResolvers","ack","resolve","reject","Object","assign","executor","resolvers","PromiseLike","execution","freeze","settle","withResolvers","promise","Promise","res","rej","a","CommandsInvocation","method","params","ts","seq","clientId","CommandsOutcome","payload","CommandsListener","onStart","inv","onPosted","onCovered","outcome","CommandsHost","setListener","commandsHosts","getCommandsHost","commands","bindCommandsHost","QueuedCall","ActiveInvocation","CommandsCell","handlers","Record","args","queue","running","unmounted","stallTimer","ReturnType","setTimeout","STALL_WARN_MS","toSendError","fate","cause","message","String","undefined","ackInvocation","settleInvocation","call","pump","watchStall","shift","handler","console","warn","UnwrapExecution","V","StatewireHandledCommands","THandlers","Parameters","K","Awaited","useStatewireCommands","built","memo","Map","proxy","Proxy","_target","fn","Date","now","clearTimeout","queueMicrotask","queued","EmitHost","setSink","sink","event","emitHosts","getEmitHost","emit","useStatewireEmit","_temp"],"sources":["../../src/host/host.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { applyOp, applyOps } from \"../wire/ops\";\nimport type { StatewireOp, StatewirePath } from \"../wire/ops\";\nimport { StatewireReject } from \"./engine\";\nimport { createStateDraft, frozen } from \"./draft\";\nimport type { ServerOp } from \"./draft\";\nimport { StatewireSendError } from \"../types\";\nimport type {\n StatewireCommands,\n StatewireCommandsProxy,\n StatewireStateSource,\n} from \"../types\";\n\nconst isDev = () =>\n typeof process === \"undefined\" || process.env[\"NODE_ENV\"] !== \"production\";\n\n// ─── state ──────────────────────────────────────────────\n\nexport type StatewireStateCtl = {\n /** Apply server-vocabulary ops (root replace = whole-state swap). */\n apply: (ops: readonly StatewireOp[]) => void;\n};\n\nexport type StateHost = StatewireStateSource & {\n /** Synchronous, per locally-recorded op; the host owns batching. */\n setOnOp: (listener: ((op: ServerOp) => void) | null) => void;\n};\n\nconst stateHosts = new WeakMap<object, StateHost>();\n\nexport const getStateHost = (state: object): StateHost | undefined =>\n stateHosts.get(state);\n\ntype StateCell = {\n current: unknown;\n draft: object | null;\n changed: StatewirePath[];\n onOp: ((op: ServerOp) => void) | null;\n onChange: (() => void) | null;\n ctl: StatewireStateCtl;\n host: StateHost;\n bump: () => void;\n};\n\nexport const useStatewireState = <TState>(\n init: () => TState,\n): readonly [TState, StatewireStateCtl] => {\n const [, setVersion] = useState(0);\n const [cell] = useState((): StateCell => {\n const c: StateCell = {\n current: frozen(init()),\n draft: null,\n changed: [[]],\n onOp: null,\n onChange: null,\n bump: () => setVersion((v) => v + 1),\n ctl: {\n apply: (ops) => {\n const result = applyOps(c.current, ops);\n if (result.events.length > 0) {\n throw new Error(\n \"statewire: event ops do not apply to state — route them to emit\",\n );\n }\n c.current = result.state;\n c.changed.push(...result.changedPaths);\n c.bump();\n c.onChange?.();\n },\n },\n host: {\n root: () => c.current,\n setOnOp: (listener) => {\n c.onOp = listener;\n },\n setOnChange: (listener) => {\n c.onChange = listener;\n },\n takeChanged: () => {\n const changed = c.changed;\n c.changed = [];\n return changed;\n },\n },\n };\n return c;\n });\n\n let value = cell.current;\n if (typeof value === \"object\" && value !== null) {\n cell.draft ??= createStateDraft<object>({\n root: () => cell.current,\n record: (op) => {\n const applied = applyOp(cell.current, op);\n cell.current = applied.state;\n cell.changed.push(...applied.changedPaths);\n cell.onOp?.(op);\n cell.bump();\n cell.onChange?.();\n },\n });\n stateHosts.set(cell.draft, cell.host);\n value = cell.draft;\n }\n return [value as TState, cell.ctl] as const;\n};\n\n// ─── commands ───────────────────────────────────────────\n\ntype ExecutionSettled =\n | { type: \"resolved\"; value: unknown }\n | { type: \"rejected\"; error: unknown };\n\ntype ExecutionBind = {\n onAck: () => void;\n onSettle: (settled: ExecutionSettled) => void;\n};\n\ntype ExecutionState = {\n acked: boolean;\n settled: ExecutionSettled | null;\n bind: ExecutionBind | null;\n};\n\nconst executionStates = new WeakMap<object, ExecutionState>();\n\ndeclare const executionResult: unique symbol;\n\n/**\n * Handle a command handler returns to keep the command open past its own\n * return: `ack()` releases the client's optimistic overlay / ack watermark,\n * `resolve`/`reject` settle the command (and imply ack). `ack` is idempotent\n * and a no-op after settle.\n */\nexport type CommandExecution<T = void> = {\n readonly [executionResult]: (value: T) => void;\n};\n\nexport type CommandExecutionResolvers<T = void> = {\n ack: () => void;\n resolve: (value: T) => void;\n reject: (error: unknown) => void;\n};\n\n/**\n * `CommandExecution(executor)` runs the (possibly async) `executor({ ack })`\n * immediately: its return value resolves the execution, a throw/rejection\n * rejects it; `ack()` releases the client's ack watermark before the executor\n * finishes. `CommandExecution.withResolvers()` returns `[execution,\n * { ack, resolve, reject }]` instead — settle them from your own spawned work.\n */\nexport const CommandExecution = Object.assign(\n <T = void>(\n executor: (resolvers: {\n ack: () => void;\n }) => T | void | PromiseLike<T | void>,\n ): CommandExecution<T> => {\n const state: ExecutionState = { acked: false, settled: null, bind: null };\n const execution = Object.freeze({}) as unknown as CommandExecution<T>;\n executionStates.set(execution, state);\n const settle = (settled: ExecutionSettled) => {\n if (state.settled) return;\n state.settled = settled;\n state.acked = true;\n state.bind?.onSettle(settled);\n };\n const ack = () => {\n if (state.acked) return;\n state.acked = true;\n state.bind?.onAck();\n };\n void (async () => {\n try {\n settle({ type: \"resolved\", value: await executor({ ack }) });\n } catch (error) {\n settle({ type: \"rejected\", error });\n }\n })();\n return execution;\n },\n {\n withResolvers: <T = void>(): [\n CommandExecution<T>,\n CommandExecutionResolvers<T>,\n ] => {\n let resolve!: (value: T) => void;\n let reject!: (error: unknown) => void;\n const promise = new Promise<T>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n let ack!: () => void;\n const execution = CommandExecution<T>(({ ack: a }) => {\n ack = a;\n return promise;\n });\n return [execution, { ack: () => ack(), resolve, reject }];\n },\n },\n);\n\nexport type CommandsInvocation = {\n readonly method: string;\n readonly params: readonly unknown[];\n readonly ts: number;\n readonly seq: number;\n readonly clientId?: string;\n};\n\nexport type CommandsOutcome =\n | { type: \"accepted\"; payload?: unknown }\n | { type: \"rejected\"; error: unknown };\n\nexport type CommandsListener = {\n onStart: (inv: CommandsInvocation) => void;\n onPosted?: (inv: CommandsInvocation) => void;\n onAck: (inv: CommandsInvocation) => void;\n /** Canonical state now covers the invocation's effect — drop any overlay. */\n onCovered?: (inv: CommandsInvocation) => void;\n onSettle: (inv: CommandsInvocation, outcome: CommandsOutcome) => void;\n};\n\nexport type CommandsHost = {\n setListener: (listener: CommandsListener | null) => void;\n};\n\nconst commandsHosts = new WeakMap<object, CommandsHost>();\n\nexport const getCommandsHost = (commands: object): CommandsHost | undefined =>\n commandsHosts.get(commands);\n\nexport const bindCommandsHost = (commands: object, host: CommandsHost) => {\n commandsHosts.set(commands, host);\n};\n\ntype QueuedCall = {\n method: string;\n params: readonly unknown[];\n ts: number;\n resolve: (value: unknown) => void;\n reject: (error: unknown) => void;\n};\n\ntype ActiveInvocation = CommandsInvocation & { acked: boolean };\n\ntype CommandsCell = {\n handlers: Record<string, (...args: never[]) => unknown> | null;\n queue: QueuedCall[];\n running: boolean;\n listener: CommandsListener | null;\n seq: number;\n unmounted: boolean;\n stallTimer: ReturnType<typeof setTimeout> | null;\n};\n\nconst STALL_WARN_MS = 5000;\n\nconst toSendError = (error: unknown): StatewireSendError =>\n error instanceof StatewireSendError\n ? error\n : new StatewireSendError({\n fate: \"rejected\",\n cause: \"server\",\n message: error instanceof Error ? error.message : String(error),\n ...(error instanceof StatewireReject &&\n error.payload !== undefined && { payload: error.payload }),\n });\n\nconst ackInvocation = (cell: CommandsCell, inv: ActiveInvocation) => {\n if (inv.acked) return;\n inv.acked = true;\n cell.listener?.onAck(inv);\n};\n\nconst settleInvocation = (\n cell: CommandsCell,\n inv: ActiveInvocation,\n call: QueuedCall,\n settled: ExecutionSettled,\n) => {\n ackInvocation(cell, inv);\n if (settled.type === \"resolved\") {\n const outcome: CommandsOutcome =\n settled.value === undefined\n ? { type: \"accepted\" }\n : { type: \"accepted\", payload: frozen(settled.value) };\n cell.listener?.onSettle(inv, outcome);\n call.resolve(outcome.payload);\n } else {\n const error = toSendError(settled.error);\n cell.listener?.onSettle(inv, { type: \"rejected\", error });\n call.reject(error);\n }\n};\n\nconst pump = (cell: CommandsCell) => {\n if (cell.running || !cell.handlers) {\n watchStall(cell);\n return;\n }\n const call = cell.queue.shift();\n if (!call) return;\n const handler = cell.handlers[call.method];\n if (handler === undefined) {\n call.reject(\n new StatewireSendError({ fate: \"unknown-command\", cause: \"server\" }),\n );\n pump(cell);\n return;\n }\n const inv: ActiveInvocation = {\n method: call.method,\n params: call.params,\n ts: call.ts,\n seq: ++cell.seq,\n acked: false,\n };\n cell.running = true;\n cell.listener?.onStart(inv);\n cell.listener?.onPosted?.(inv);\n cell.listener?.onCovered?.(inv);\n void (async () => {\n try {\n const value = await (handler as (...args: readonly unknown[]) => unknown)(\n ...call.params,\n );\n const execution =\n typeof value === \"object\" && value !== null\n ? executionStates.get(value)\n : undefined;\n if (execution) {\n if (execution.acked) ackInvocation(cell, inv);\n if (execution.settled) {\n settleInvocation(cell, inv, call, execution.settled);\n } else {\n execution.bind = {\n onAck: () => ackInvocation(cell, inv),\n onSettle: (settled) => settleInvocation(cell, inv, call, settled),\n };\n }\n } else {\n settleInvocation(cell, inv, call, { type: \"resolved\", value });\n }\n } catch (error) {\n settleInvocation(cell, inv, call, { type: \"rejected\", error });\n } finally {\n cell.running = false;\n pump(cell);\n }\n })();\n};\n\nconst watchStall = (cell: CommandsCell) => {\n if (!isDev() || cell.stallTimer !== null || cell.queue.length === 0) return;\n if (cell.handlers) return;\n cell.stallTimer = setTimeout(() => {\n cell.stallTimer = null;\n if (cell.handlers === null && cell.queue.length > 0 && !cell.unmounted) {\n console.warn(\n `statewire: ${cell.queue.length} buffered command(s) waiting over ${STALL_WARN_MS}ms — did the host forget to arm useStatewireCommands?`,\n );\n }\n }, STALL_WARN_MS);\n};\n\ntype UnwrapExecution<V> = V extends CommandExecution<infer T> ? T : V;\n\nexport type StatewireHandledCommands<THandlers extends StatewireCommands> = {\n [K in keyof THandlers]: (\n ...args: Parameters<THandlers[K]>\n ) => UnwrapExecution<Awaited<ReturnType<THandlers[K]>>>;\n};\n\nexport const useStatewireCommands = <THandlers extends StatewireCommands>(\n commands: THandlers | undefined | null | false,\n): StatewireCommandsProxy<StatewireHandledCommands<THandlers>> => {\n const [built] = useState(() => {\n const cell: CommandsCell = {\n handlers: null,\n queue: [],\n running: false,\n listener: null,\n seq: 0,\n unmounted: false,\n stallTimer: null,\n };\n const memo = new Map<string, (...params: unknown[]) => Promise<unknown>>();\n const proxy = new Proxy(\n {} as StatewireCommandsProxy<StatewireHandledCommands<THandlers>>,\n {\n get: (_target, method) => {\n if (typeof method !== \"string\") return undefined;\n let fn = memo.get(method);\n if (fn === undefined) {\n fn = (...params: unknown[]) =>\n new Promise((resolve, reject) => {\n if (cell.unmounted) {\n reject(new Error(\"statewire: commands called after unmount\"));\n return;\n }\n cell.queue.push({\n method,\n params,\n ts: Date.now(),\n resolve,\n reject,\n });\n pump(cell);\n });\n memo.set(method, fn);\n }\n return fn;\n },\n },\n );\n commandsHosts.set(proxy, {\n setListener: (listener) => {\n cell.listener = listener;\n },\n });\n return { cell, proxy };\n });\n\n useEffect(() => {\n const { cell } = built;\n cell.unmounted = false;\n return () => {\n cell.unmounted = true;\n cell.handlers = null;\n if (cell.stallTimer !== null) {\n clearTimeout(cell.stallTimer);\n cell.stallTimer = null;\n }\n queueMicrotask(() => {\n if (!cell.unmounted) return;\n const queued = cell.queue;\n cell.queue = [];\n for (const call of queued) {\n call.reject(new Error(\"statewire: unmounted\"));\n }\n });\n };\n }, [built]);\n\n useEffect(() => {\n built.cell.handlers = commands || null;\n pump(built.cell);\n });\n\n return built.proxy;\n};\n\n// ─── emit ───────────────────────────────────────────────\n\nexport type EmitHost = {\n setSink: (sink: ((event: unknown) => void) | null) => void;\n};\n\nconst emitHosts = new WeakMap<object, EmitHost>();\n\nexport const getEmitHost = (emit: object): EmitHost | undefined =>\n emitHosts.get(emit);\n\nexport const useStatewireEmit = <TEvent>(): ((event: TEvent) => void) => {\n const [built] = useState(() => {\n const cell = { sink: null as ((event: unknown) => void) | null };\n const emit = (event: TEvent) => {\n if (!cell.sink) {\n throw new Error(\"statewire: emit before a host bound the event sink\");\n }\n cell.sink(frozen(event));\n };\n emitHosts.set(emit, {\n setSink: (sink) => {\n cell.sink = sink;\n },\n });\n return emit;\n });\n return built;\n};\n"],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { StatewireOp, applyOps } from "./wire/ops.js";
|
|
2
|
+
import { StatewireConnection, StatewireSSE, StatewireSSEOptions } from "./transports/sse.js";
|
|
3
|
+
import { SendFailure, StatewireChanges, StatewireCommand, StatewireCommandUpdate, StatewireCommands, StatewireCommandsProxy, StatewireHelpers, StatewireOptimistic, StatewireOptimisticContext, StatewireOptions, StatewireSendError, StatewireStateSource, StatewireTransport } from "./types.js";
|
|
4
|
+
import { StatewireWS, StatewireWSOptions } from "./transports/ws.js";
|
|
5
|
+
import { Statewire, StatewireResource, useStatewire } from "./statewire.js";
|
|
6
|
+
//#region src/index.d.ts
|
|
7
|
+
declare namespace Statewire {
|
|
8
|
+
type Connection = StatewireConnection;
|
|
9
|
+
type ConnectionStatus = StatewireConnection["status"];
|
|
10
|
+
type Transport<TState = any, TCommands extends StatewireCommands = StatewireCommands, TEvent = any> = StatewireTransport<TState, TCommands, TEvent>;
|
|
11
|
+
type StateSource = StatewireStateSource;
|
|
12
|
+
type SSEOptions = StatewireSSEOptions;
|
|
13
|
+
type WSOptions = StatewireWSOptions;
|
|
14
|
+
type Options<TState, TCommands extends StatewireCommands = StatewireCommands, TEvent = any> = StatewireOptions<TState, TCommands, TEvent>;
|
|
15
|
+
type Helpers<TState, TCommands extends StatewireCommands = StatewireCommands> = StatewireHelpers<TState, TCommands>;
|
|
16
|
+
type Commands = StatewireCommands;
|
|
17
|
+
type Command<TCommands extends StatewireCommands = StatewireCommands> = StatewireCommand<TCommands>;
|
|
18
|
+
type CommandsProxy<TCommands extends StatewireCommands> = StatewireCommandsProxy<TCommands>;
|
|
19
|
+
type CommandUpdate<TCommands extends StatewireCommands = StatewireCommands> = StatewireCommandUpdate<TCommands>;
|
|
20
|
+
type Optimistic<TState, TCommands extends StatewireCommands = StatewireCommands> = StatewireOptimistic<TState, TCommands>;
|
|
21
|
+
type OptimisticContext = StatewireOptimisticContext;
|
|
22
|
+
type SendFailure = SendFailure;
|
|
23
|
+
type Changes = StatewireChanges;
|
|
24
|
+
type Op = StatewireOp;
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
27
|
+
export { Statewire, StatewireResource, StatewireSSE, StatewireSendError, StatewireWS, applyOps, useStatewire };
|
|
28
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;kBA4BiB;OACH,aAAa;OACb,mBAAmB;OACnB,UACV,cACA,kBAAkB,oBAAoB,mBACtC,gBACE,mBAAmB,QAAQ,WAAW;OAC9B,cAAc;OACd,aAAa;OACb,YAAY;OACZ,QACV,QACA,kBAAkB,oBAAoB,mBACtC,gBACE,iBAAiB,QAAQ,WAAW;OAC5B,QACV,QACA,kBAAkB,oBAAoB,qBACpC,iBAAiB,QAAQ;OACjB,WAAW;OACX,QAAQ,kBAAkB,oBAAoB,qBACxD,iBAAiB;OACP,cAAc,kBAAkB,qBAC1C,uBAAuB;OACb,cACV,kBAAkB,oBAAoB,qBACpC,uBAAuB;OACf,WACV,QACA,kBAAkB,oBAAoB,qBACpC,oBAAoB,QAAQ;OACpB,oBAAoB;OACpB,cAAc;OACd,UAAU;OACV,KAAK"}
|