statewire 0.0.0-0 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/README.md +84 -1
  2. package/dist/client/changes.d.ts +7 -0
  3. package/dist/client/changes.d.ts.map +1 -0
  4. package/dist/client/changes.js +25 -0
  5. package/dist/client/changes.js.map +1 -0
  6. package/dist/client/connection.d.ts +63 -0
  7. package/dist/client/connection.d.ts.map +1 -0
  8. package/dist/client/connection.js +211 -0
  9. package/dist/client/connection.js.map +1 -0
  10. package/dist/client/core.d.ts +14 -0
  11. package/dist/client/core.d.ts.map +1 -0
  12. package/dist/client/core.js +272 -0
  13. package/dist/client/core.js.map +1 -0
  14. package/dist/client/session.d.ts +71 -0
  15. package/dist/client/session.d.ts.map +1 -0
  16. package/dist/client/session.js +529 -0
  17. package/dist/client/session.js.map +1 -0
  18. package/dist/client/sessionStore.d.ts +16 -0
  19. package/dist/client/sessionStore.d.ts.map +1 -0
  20. package/dist/client/sessionStore.js +14 -0
  21. package/dist/client/sessionStore.js.map +1 -0
  22. package/dist/client/timing.d.ts +11 -0
  23. package/dist/client/timing.d.ts.map +1 -0
  24. package/dist/client/timing.js +15 -0
  25. package/dist/client/timing.js.map +1 -0
  26. package/dist/host/adapter.d.ts +8 -0
  27. package/dist/host/adapter.d.ts.map +1 -0
  28. package/dist/host/adapter.js +23 -0
  29. package/dist/host/adapter.js.map +1 -0
  30. package/dist/host/draft.d.ts +28 -0
  31. package/dist/host/draft.d.ts.map +1 -0
  32. package/dist/host/draft.js +211 -0
  33. package/dist/host/draft.js.map +1 -0
  34. package/dist/host/engine.d.ts +14 -0
  35. package/dist/host/engine.d.ts.map +1 -0
  36. package/dist/host/engine.js +17 -0
  37. package/dist/host/engine.js.map +1 -0
  38. package/dist/host/host.d.ts +62 -0
  39. package/dist/host/host.d.ts.map +1 -0
  40. package/dist/host/host.js +331 -0
  41. package/dist/host/host.js.map +1 -0
  42. package/dist/host/socket.d.ts +14 -0
  43. package/dist/host/socket.d.ts.map +1 -0
  44. package/dist/host/socket.js +459 -0
  45. package/dist/host/socket.js.map +1 -0
  46. package/dist/host-internal.d.ts +27 -0
  47. package/dist/host-internal.d.ts.map +1 -0
  48. package/dist/host-internal.js +3 -0
  49. package/dist/host.d.ts +44 -0
  50. package/dist/host.d.ts.map +1 -0
  51. package/dist/host.js +4 -0
  52. package/dist/index.d.ts +5 -0
  53. package/dist/index.js +5 -0
  54. package/dist/statewire.d.ts +104 -0
  55. package/dist/statewire.d.ts.map +1 -0
  56. package/dist/statewire.js +53 -0
  57. package/dist/statewire.js.map +1 -0
  58. package/dist/transports/fake-sse.d.ts +12 -0
  59. package/dist/transports/fake-sse.d.ts.map +1 -0
  60. package/dist/transports/fake-sse.js +39 -0
  61. package/dist/transports/fake-sse.js.map +1 -0
  62. package/dist/transports/fixtures.d.ts +33 -0
  63. package/dist/transports/fixtures.d.ts.map +1 -0
  64. package/dist/transports/fixtures.js +50 -0
  65. package/dist/transports/fixtures.js.map +1 -0
  66. package/dist/transports/remote.d.ts +12 -0
  67. package/dist/transports/remote.d.ts.map +1 -0
  68. package/dist/transports/remote.js +21 -0
  69. package/dist/transports/remote.js.map +1 -0
  70. package/dist/transports/sse-parser.d.ts +5 -0
  71. package/dist/transports/sse-parser.d.ts.map +1 -0
  72. package/dist/transports/sse-parser.js +26 -0
  73. package/dist/transports/sse-parser.js.map +1 -0
  74. package/dist/transports/sse.d.ts +64 -0
  75. package/dist/transports/sse.d.ts.map +1 -0
  76. package/dist/transports/sse.js +137 -0
  77. package/dist/transports/sse.js.map +1 -0
  78. package/dist/transports/testing.d.ts +32 -0
  79. package/dist/transports/testing.d.ts.map +1 -0
  80. package/dist/transports/testing.js +94 -0
  81. package/dist/transports/testing.js.map +1 -0
  82. package/dist/transports/ws.d.ts +9 -0
  83. package/dist/transports/ws.d.ts.map +1 -0
  84. package/dist/transports/ws.js +169 -0
  85. package/dist/transports/ws.js.map +1 -0
  86. package/dist/types.d.ts +60 -0
  87. package/dist/types.d.ts.map +1 -0
  88. package/dist/types.js +24 -0
  89. package/dist/types.js.map +1 -0
  90. package/dist/util/draftTraps.d.ts +6 -0
  91. package/dist/util/draftTraps.d.ts.map +1 -0
  92. package/dist/util/draftTraps.js +25 -0
  93. package/dist/util/draftTraps.js.map +1 -0
  94. package/dist/util/macrotask.d.ts +5 -0
  95. package/dist/util/macrotask.d.ts.map +1 -0
  96. package/dist/util/macrotask.js +25 -0
  97. package/dist/util/macrotask.js.map +1 -0
  98. package/dist/util/methodProxy.d.ts +5 -0
  99. package/dist/util/methodProxy.d.ts.map +1 -0
  100. package/dist/util/methodProxy.js +17 -0
  101. package/dist/util/methodProxy.js.map +1 -0
  102. package/dist/util/randomId.d.ts +5 -0
  103. package/dist/util/randomId.d.ts.map +1 -0
  104. package/dist/util/randomId.js +6 -0
  105. package/dist/util/randomId.js.map +1 -0
  106. package/dist/util/recordMutations.d.ts +10 -0
  107. package/dist/util/recordMutations.d.ts.map +1 -0
  108. package/dist/util/recordMutations.js +109 -0
  109. package/dist/util/recordMutations.js.map +1 -0
  110. package/dist/util/statePath.d.ts +14 -0
  111. package/dist/util/statePath.d.ts.map +1 -0
  112. package/dist/util/statePath.js +31 -0
  113. package/dist/util/statePath.js.map +1 -0
  114. package/dist/wire/meta.d.ts +27 -0
  115. package/dist/wire/meta.d.ts.map +1 -0
  116. package/dist/wire/meta.js +75 -0
  117. package/dist/wire/meta.js.map +1 -0
  118. package/dist/wire/ops.d.ts +19 -0
  119. package/dist/wire/ops.d.ts.map +1 -0
  120. package/dist/wire/ops.js +112 -0
  121. package/dist/wire/ops.js.map +1 -0
  122. package/package.json +52 -5
  123. package/src/client/changes.ts +38 -0
  124. package/src/client/connection.ts +301 -0
  125. package/src/client/core.ts +312 -0
  126. package/src/client/session.ts +696 -0
  127. package/src/client/sessionStore.ts +23 -0
  128. package/src/client/timing.ts +14 -0
  129. package/src/host/adapter.ts +33 -0
  130. package/src/host/draft.ts +323 -0
  131. package/src/host/engine.ts +13 -0
  132. package/src/host/host.ts +482 -0
  133. package/src/host/socket.ts +542 -0
  134. package/src/host-internal.ts +28 -0
  135. package/src/host.ts +61 -0
  136. package/src/index.ts +5 -0
  137. package/src/statewire.ts +200 -0
  138. package/src/transports/fake-sse.ts +51 -0
  139. package/src/transports/fixtures.ts +60 -0
  140. package/src/transports/remote.ts +30 -0
  141. package/src/transports/sse-parser.ts +24 -0
  142. package/src/transports/sse.ts +267 -0
  143. package/src/transports/testing.ts +123 -0
  144. package/src/transports/ws.ts +256 -0
  145. package/src/types.ts +88 -0
  146. package/src/util/draftTraps.ts +32 -0
  147. package/src/util/macrotask.ts +25 -0
  148. package/src/util/methodProxy.ts +16 -0
  149. package/src/util/randomId.ts +4 -0
  150. package/src/util/recordMutations.ts +0 -0
  151. package/src/util/statePath.ts +61 -0
  152. package/src/wire/meta.ts +101 -0
  153. package/src/wire/ops.ts +202 -0
  154. package/index.js +0 -1
@@ -0,0 +1,272 @@
1
+ import { recordMutations } from "../util/recordMutations.js";
2
+ import { createMethodProxy } from "../util/methodProxy.js";
3
+ import { StatewireSendError } from "../types.js";
4
+ import { getCommandsHost, getEmitHost, getStateHost } from "../host/host.js";
5
+ import { createMacrotaskPoster } from "../util/macrotask.js";
6
+ import { createChanges } from "./changes.js";
7
+ import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
8
+ import { useEffect, useState } from "@assistant-ui/tap/react-shim";
9
+ //#region src/client/core.ts
10
+ const LIVE_CONNECTION = {
11
+ status: "live",
12
+ reconnect: () => {}
13
+ };
14
+ const failureOf = (error) => error instanceof StatewireSendError ? error.failure : {
15
+ fate: "rejected",
16
+ cause: "server",
17
+ message: error instanceof Error ? error.message : String(error)
18
+ };
19
+ const createCore = (getOptions) => {
20
+ let value = null;
21
+ let source = null;
22
+ let overlays = [];
23
+ let updates = [];
24
+ let events = [];
25
+ let pendingChanged = [];
26
+ let canonical;
27
+ let published;
28
+ let overlayPaths = [];
29
+ let notifiedPublished;
30
+ let notifiedOverlayPaths = [];
31
+ let notifiedConnection = null;
32
+ let stale = false;
33
+ let overlaysDirty = false;
34
+ let flushing = false;
35
+ const listeners = /* @__PURE__ */ new Set();
36
+ const post = createMacrotaskPoster(() => {
37
+ scheduled = false;
38
+ flush();
39
+ });
40
+ let scheduled = false;
41
+ const schedule = () => {
42
+ if (scheduled) return;
43
+ scheduled = true;
44
+ post();
45
+ };
46
+ const markStale = () => {
47
+ stale = true;
48
+ schedule();
49
+ };
50
+ const drainCanonical = () => {
51
+ if (source) {
52
+ pendingChanged.push(...source.takeChanged());
53
+ canonical = source.snapshot();
54
+ } else if (value && value.state !== canonical) {
55
+ pendingChanged.push([]);
56
+ canonical = value.state;
57
+ }
58
+ };
59
+ const recompute = () => {
60
+ if (!stale || !value) return;
61
+ stale = false;
62
+ const prevCanonical = canonical;
63
+ drainCanonical();
64
+ if (canonical === prevCanonical && !overlaysDirty) return;
65
+ overlaysDirty = false;
66
+ const { optimistic } = getOptions();
67
+ let next = canonical;
68
+ let paths = [];
69
+ if (optimistic && canonical !== void 0) for (const overlay of overlays) {
70
+ if (overlay.dropped) continue;
71
+ const { inv } = overlay;
72
+ const ctx = {
73
+ ts: inv.ts,
74
+ seq: inv.seq,
75
+ ...inv.clientId !== void 0 && { clientId: inv.clientId }
76
+ };
77
+ try {
78
+ const recorded = recordMutations(next, (draft) => optimistic(draft, {
79
+ method: inv.method,
80
+ params: inv.params
81
+ }, ctx));
82
+ next = recorded.state;
83
+ paths = [...paths, ...recorded.changedPaths];
84
+ } catch (error) {
85
+ overlay.dropped = true;
86
+ console.error(`statewire: optimistic reducer threw for "${inv.method}"; dropping its optimistic update`, error);
87
+ }
88
+ }
89
+ published = next;
90
+ overlayPaths = paths;
91
+ };
92
+ const getConnection = () => value?.connection ?? LIVE_CONNECTION;
93
+ const flush = () => {
94
+ if (!value) return;
95
+ if (flushing) {
96
+ schedule();
97
+ return;
98
+ }
99
+ flushing = true;
100
+ try {
101
+ recompute();
102
+ const options = getOptions();
103
+ const stateChanged = published !== notifiedPublished;
104
+ if (stateChanged) {
105
+ const prev = notifiedPublished;
106
+ const changed = [
107
+ ...pendingChanged,
108
+ ...notifiedOverlayPaths,
109
+ ...overlayPaths
110
+ ];
111
+ pendingChanged = [];
112
+ notifiedPublished = published;
113
+ notifiedOverlayPaths = overlayPaths;
114
+ options.onStateChange?.(published, { changes: createChanges(prev, published, changed) });
115
+ }
116
+ if (updates.length > 0 && published !== void 0) {
117
+ const batch = updates;
118
+ updates = [];
119
+ for (const update of batch) options.onCommandChange?.(update, { state: published });
120
+ }
121
+ if (events.length > 0 && published !== void 0) {
122
+ const batch = events;
123
+ events = [];
124
+ for (const event of batch) options.onEvent?.(event, { state: published });
125
+ }
126
+ const connection = getConnection();
127
+ const connectionChanged = connection !== notifiedConnection;
128
+ notifiedConnection = connection;
129
+ if (stateChanged || connectionChanged) for (const listener of [...listeners]) listener();
130
+ } finally {
131
+ flushing = false;
132
+ }
133
+ };
134
+ const note = (inv, status, extra) => {
135
+ if (!getOptions().onCommandChange) return;
136
+ updates.push({
137
+ method: inv.method,
138
+ params: inv.params,
139
+ seq: inv.seq,
140
+ status,
141
+ ...extra
142
+ });
143
+ schedule();
144
+ };
145
+ const commandsListener = {
146
+ onStart: (inv) => {
147
+ overlays.push({
148
+ inv,
149
+ dropped: false
150
+ });
151
+ overlaysDirty = true;
152
+ markStale();
153
+ note(inv, "queued");
154
+ },
155
+ onPosted: (inv) => note(inv, "posted"),
156
+ onAck: (inv) => note(inv, "acked"),
157
+ onCovered: (inv) => {
158
+ if (!overlays.some((o) => o.inv === inv)) return;
159
+ overlays = overlays.filter((o) => o.inv !== inv);
160
+ overlaysDirty = true;
161
+ markStale();
162
+ },
163
+ onSettle: (inv, outcome) => {
164
+ note(inv, "settled", outcome.type === "accepted" ? { result: outcome.payload } : { failure: failureOf(outcome.error) });
165
+ flush();
166
+ }
167
+ };
168
+ const eventSink = (event) => {
169
+ if (!getOptions().onEvent) return;
170
+ events.push(event);
171
+ schedule();
172
+ };
173
+ const attach = (next) => {
174
+ if (next === value) return;
175
+ const prev = value;
176
+ value = next;
177
+ const nextSource = next.source ?? (typeof next.state === "object" && next.state !== null ? getStateHost(next.state) : void 0) ?? null;
178
+ if (nextSource !== source) {
179
+ source?.setOnChange(null);
180
+ source = nextSource;
181
+ source?.setOnChange(markStale);
182
+ markStale();
183
+ }
184
+ if (next.commands !== prev?.commands) {
185
+ if (prev) getCommandsHost(prev.commands)?.setListener(null);
186
+ getCommandsHost(next.commands)?.setListener(commandsListener);
187
+ if (overlays.length > 0) {
188
+ overlays = [];
189
+ overlaysDirty = true;
190
+ }
191
+ markStale();
192
+ }
193
+ if (next.emit !== prev?.emit) {
194
+ if (prev) getEmitHost(prev.emit)?.setSink(null);
195
+ getEmitHost(next.emit)?.setSink(eventSink);
196
+ }
197
+ if (!source) markStale();
198
+ schedule();
199
+ };
200
+ const detach = () => {
201
+ source?.setOnChange(null);
202
+ source = null;
203
+ if (value) {
204
+ getCommandsHost(value.commands)?.setListener(null);
205
+ getEmitHost(value.emit)?.setSink(null);
206
+ value = null;
207
+ }
208
+ };
209
+ return {
210
+ attach,
211
+ detach,
212
+ getState: () => {
213
+ recompute();
214
+ return published;
215
+ },
216
+ commands: createMethodProxy((method, params) => {
217
+ if (!value) return Promise.reject(new StatewireSendError({
218
+ fate: "aborted",
219
+ cause: "unmount"
220
+ }));
221
+ const send = value.commands[method];
222
+ const promise = send(...params);
223
+ flush();
224
+ return promise;
225
+ }),
226
+ subscribe: (listener) => {
227
+ listeners.add(listener);
228
+ return () => {
229
+ listeners.delete(listener);
230
+ };
231
+ }
232
+ };
233
+ };
234
+ const useStatewireCore = (getOptions, value) => {
235
+ const $ = c(8);
236
+ let t0;
237
+ if ($[0] !== getOptions) {
238
+ t0 = () => createCore(getOptions);
239
+ $[0] = getOptions;
240
+ $[1] = t0;
241
+ } else t0 = $[1];
242
+ const [core] = useState(t0);
243
+ core.attach(value);
244
+ let t1;
245
+ if ($[2] !== core || $[3] !== value) {
246
+ t1 = () => {
247
+ core.attach(value);
248
+ };
249
+ $[2] = core;
250
+ $[3] = value;
251
+ $[4] = t1;
252
+ } else t1 = $[4];
253
+ useEffect(t1);
254
+ let t2;
255
+ let t3;
256
+ if ($[5] !== core) {
257
+ t2 = () => () => core.detach();
258
+ t3 = [core];
259
+ $[5] = core;
260
+ $[6] = t2;
261
+ $[7] = t3;
262
+ } else {
263
+ t2 = $[6];
264
+ t3 = $[7];
265
+ }
266
+ useEffect(t2, t3);
267
+ return core;
268
+ };
269
+ //#endregion
270
+ export { LIVE_CONNECTION, useStatewireCore };
271
+
272
+ //# sourceMappingURL=core.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core.js","names":["useEffect","useState","createChanges","getCommandsHost","getEmitHost","getStateHost","CommandsInvocation","CommandsListener","recordMutations","createMacrotaskPoster","createMethodProxy","StatewirePath","StatewireSendError","Statewire","StatewireClient","LIVE_CONNECTION","Connection","status","reconnect","failureOf","error","SendFailure","failure","fate","cause","message","Error","String","Overlay","inv","dropped","createCore","getOptions","Options","value","source","StateSource","overlays","updates","CommandUpdate","events","pendingChanged","canonical","published","overlayPaths","notifiedPublished","notifiedOverlayPaths","notifiedConnection","stale","overlaysDirty","flushing","listeners","Set","post","scheduled","flush","schedule","markStale","drainCanonical","push","takeChanged","snapshot","state","recompute","prevCanonical","optimistic","next","paths","undefined","overlay","ctx","OptimisticContext","ts","seq","clientId","recorded","draft","method","params","Command","changedPaths","console","getConnection","connection","options","stateChanged","prev","changed","onStateChange","changes","length","batch","update","onCommandChange","event","onEvent","connectionChanged","listener","note","extra","result","commandsListener","onStart","onPosted","onAck","onCovered","some","o","filter","onSettle","outcome","type","payload","eventSink","attach","nextSource","setOnChange","commands","setListener","emit","setSink","detach","CommandsProxy","Promise","reject","send","Record","args","promise","getState","subscribe","add","delete","useStatewireCore","$","_c","t0","core","t1","t2","t3"],"sources":["../../src/client/core.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { createChanges } from \"./changes\";\nimport { getCommandsHost, getEmitHost, getStateHost } from \"../host/host\";\nimport type { CommandsInvocation, CommandsListener } from \"../host/host\";\nimport { recordMutations } from \"../util/recordMutations\";\nimport { createMacrotaskPoster } from \"../util/macrotask\";\nimport { createMethodProxy } from \"../util/methodProxy\";\nimport type { StatewirePath } from \"../wire/ops\";\nimport { StatewireSendError } from \"../types\";\nimport type { Statewire } from \"../types\";\nimport type { StatewireClient } from \"../statewire\";\n\nexport const LIVE_CONNECTION: StatewireClient.Connection = {\n status: \"live\",\n reconnect: () => {},\n};\n\nconst failureOf = (error: unknown): StatewireClient.SendFailure =>\n error instanceof StatewireSendError\n ? error.failure\n : {\n fate: \"rejected\",\n cause: \"server\",\n message: error instanceof Error ? error.message : String(error),\n };\n\ntype Overlay = { inv: CommandsInvocation; dropped: boolean };\n\nconst createCore = (\n getOptions: () => StatewireClient.Options<any, any, any>,\n) => {\n let value: Statewire<unknown> | null = null;\n let source: Statewire.StateSource | null = null;\n let overlays: Overlay[] = [];\n let updates: Statewire.CommandUpdate[] = [];\n let events: unknown[] = [];\n let pendingChanged: StatewirePath[] = [];\n let canonical: unknown;\n let published: unknown;\n let overlayPaths: readonly StatewirePath[] = [];\n let notifiedPublished: unknown;\n let notifiedOverlayPaths: readonly StatewirePath[] = [];\n let notifiedConnection: StatewireClient.Connection | null = null;\n let stale = false;\n let overlaysDirty = false;\n let flushing = false;\n const listeners = new Set<() => void>();\n\n const post = createMacrotaskPoster(() => {\n scheduled = false;\n flush();\n });\n let scheduled = false;\n const schedule = () => {\n if (scheduled) return;\n scheduled = true;\n post();\n };\n\n const markStale = () => {\n stale = true;\n schedule();\n };\n\n const drainCanonical = () => {\n if (source) {\n pendingChanged.push(...source.takeChanged());\n canonical = source.snapshot();\n } else if (value && value.state !== canonical) {\n pendingChanged.push([]);\n canonical = value.state;\n }\n };\n\n const recompute = () => {\n if (!stale || !value) return;\n stale = false;\n const prevCanonical = canonical;\n drainCanonical();\n if (canonical === prevCanonical && !overlaysDirty) return;\n overlaysDirty = false;\n const { optimistic } = getOptions();\n let next = canonical;\n let paths: readonly StatewirePath[] = [];\n if (optimistic && canonical !== undefined) {\n for (const overlay of overlays) {\n if (overlay.dropped) continue;\n const { inv } = overlay;\n const ctx: StatewireClient.OptimisticContext = {\n ts: inv.ts,\n seq: inv.seq,\n ...(inv.clientId !== undefined && { clientId: inv.clientId }),\n };\n try {\n const recorded = recordMutations(next, (draft) =>\n optimistic(\n draft as never,\n { method: inv.method, params: inv.params } as Statewire.Command,\n ctx,\n ),\n );\n next = recorded.state;\n paths = [...paths, ...recorded.changedPaths];\n } catch (error) {\n overlay.dropped = true;\n console.error(\n `statewire: optimistic reducer threw for \"${inv.method}\"; dropping its optimistic update`,\n error,\n );\n }\n }\n }\n published = next;\n overlayPaths = paths;\n };\n\n const getConnection = (): StatewireClient.Connection =>\n value?.connection ?? LIVE_CONNECTION;\n\n const flush = () => {\n if (!value) return;\n if (flushing) {\n schedule();\n return;\n }\n flushing = true;\n try {\n recompute();\n const options = getOptions();\n const stateChanged = published !== notifiedPublished;\n if (stateChanged) {\n const prev = notifiedPublished;\n const changed = [\n ...pendingChanged,\n ...notifiedOverlayPaths,\n ...overlayPaths,\n ];\n pendingChanged = [];\n notifiedPublished = published;\n notifiedOverlayPaths = overlayPaths;\n options.onStateChange?.(published as never, {\n changes: createChanges(prev, published, changed),\n });\n }\n if (updates.length > 0 && published !== undefined) {\n const batch = updates;\n updates = [];\n for (const update of batch) {\n options.onCommandChange?.(update, { state: published as never });\n }\n }\n if (events.length > 0 && published !== undefined) {\n const batch = events;\n events = [];\n for (const event of batch) {\n options.onEvent?.(event, { state: published as never });\n }\n }\n const connection = getConnection();\n const connectionChanged = connection !== notifiedConnection;\n notifiedConnection = connection;\n if (stateChanged || connectionChanged) {\n for (const listener of [...listeners]) listener();\n }\n } finally {\n flushing = false;\n }\n };\n\n const note = (\n inv: CommandsInvocation,\n status: Statewire.CommandUpdate[\"status\"],\n extra?: { result?: unknown; failure?: StatewireClient.SendFailure },\n ) => {\n if (!getOptions().onCommandChange) return;\n updates.push({\n method: inv.method,\n params: inv.params as never[],\n seq: inv.seq,\n status,\n ...extra,\n });\n schedule();\n };\n\n const commandsListener: CommandsListener = {\n onStart: (inv) => {\n overlays.push({ inv, dropped: false });\n overlaysDirty = true;\n markStale();\n note(inv, \"queued\");\n },\n onPosted: (inv) => note(inv, \"posted\"),\n onAck: (inv) => note(inv, \"acked\"),\n onCovered: (inv) => {\n if (!overlays.some((o) => o.inv === inv)) return;\n overlays = overlays.filter((o) => o.inv !== inv);\n overlaysDirty = true;\n markStale();\n },\n onSettle: (inv, outcome) => {\n note(\n inv,\n \"settled\",\n outcome.type === \"accepted\"\n ? { result: outcome.payload }\n : { failure: failureOf(outcome.error) },\n );\n flush();\n },\n };\n\n const eventSink = (event: unknown) => {\n if (!getOptions().onEvent) return;\n events.push(event);\n schedule();\n };\n\n const attach = (next: Statewire<unknown>) => {\n if (next === value) return;\n const prev = value;\n value = next;\n const nextSource =\n next.source ??\n (typeof next.state === \"object\" && next.state !== null\n ? getStateHost(next.state)\n : undefined) ??\n null;\n if (nextSource !== source) {\n source?.setOnChange(null);\n source = nextSource;\n source?.setOnChange(markStale);\n markStale();\n }\n if (next.commands !== prev?.commands) {\n if (prev) getCommandsHost(prev.commands)?.setListener(null);\n getCommandsHost(next.commands)?.setListener(commandsListener);\n if (overlays.length > 0) {\n overlays = [];\n overlaysDirty = true;\n }\n markStale();\n }\n if (next.emit !== prev?.emit) {\n if (prev) getEmitHost(prev.emit)?.setSink(null);\n getEmitHost(next.emit)?.setSink(eventSink);\n }\n if (!source) markStale();\n schedule();\n };\n\n const detach = () => {\n source?.setOnChange(null);\n source = null;\n if (value) {\n getCommandsHost(value.commands)?.setListener(null);\n getEmitHost(value.emit)?.setSink(null);\n value = null;\n }\n };\n\n const commands = createMethodProxy<Statewire.CommandsProxy<any>>(\n (method, params) => {\n if (!value) {\n return Promise.reject(\n new StatewireSendError({ fate: \"aborted\", cause: \"unmount\" }),\n );\n }\n const send = (\n value.commands as Record<\n string,\n (...args: unknown[]) => Promise<unknown>\n >\n )[method]!;\n const promise = send(...params);\n flush();\n return promise;\n },\n );\n\n return {\n attach,\n detach,\n getState: () => {\n recompute();\n return published;\n },\n commands,\n subscribe: (listener: () => void) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n };\n};\n\nexport const useStatewireCore = (\n getOptions: () => StatewireClient.Options<any, any, any>,\n value: Statewire<unknown>,\n) => {\n const [core] = useState(() => createCore(getOptions));\n // Attach during render so the first snapshot reads the transport's state;\n // re-attaching the same transport is a no-op and the single-slot host\n // listeners make a swap land on the last writer.\n core.attach(value);\n useEffect(() => {\n core.attach(value);\n });\n useEffect(() => () => core.detach(), [core]);\n return core;\n};\n"],"mappings":"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"}
@@ -0,0 +1,71 @@
1
+ import { CommandsInvocation, CommandsListener } from "../host/host.js";
2
+ import { RemoteLink, StatewireRemoteProps, StatewireWireCommand } from "../transports/sse.js";
3
+ import { StatewireSessionRecord, StatewireSessionStore, StatewireSessionStoreOptions } from "./sessionStore.js";
4
+ import { Statewire, StatewireSendError } from "../types.js";
5
+ import { StatewirePath } from "../wire/ops.js";
6
+ //#region src/client/session.d.ts
7
+ /**
8
+ * queued/trying/posted: overlay live, result pending. awaiting-result: acked
9
+ * (overlay gone) but the result rides a late res; `posted: false` re-sends.
10
+ * settled-live: result settled but the overlay stays until the covering ack.
11
+ * done: removed from the queue.
12
+ */
13
+ type PendingState = {
14
+ at: "queued";
15
+ pendingMarked: boolean;
16
+ } | {
17
+ at: "trying";
18
+ pendingMarked: boolean;
19
+ failedAttempts: number;
20
+ } | {
21
+ at: "posted";
22
+ pendingMarked: boolean;
23
+ failedAttempts: number;
24
+ } | {
25
+ at: "awaiting-result";
26
+ posted: boolean;
27
+ failedAttempts: number;
28
+ } | {
29
+ at: "settled-live";
30
+ posted: boolean;
31
+ failedAttempts: number;
32
+ } | {
33
+ at: "done";
34
+ };
35
+ type Pending = {
36
+ readonly seq: number;
37
+ readonly command: StatewireWireCommand;
38
+ readonly inv: CommandsInvocation;
39
+ state: PendingState;
40
+ resolve: (payload: unknown) => void;
41
+ reject: (error: StatewireSendError) => void;
42
+ };
43
+ type AttachState = "detached" | "attached" | "finished";
44
+ type Session = {
45
+ record: StatewireSessionRecord;
46
+ ctx: StatewireRemoteProps;
47
+ recordPersisted: boolean;
48
+ store: StatewireSessionStore;
49
+ lastAck: number;
50
+ pending: Pending[];
51
+ canonical: unknown;
52
+ lastPublished: unknown;
53
+ changed: StatewirePath[];
54
+ source: Statewire.StateSource;
55
+ attach: AttachState;
56
+ attachEpoch: number;
57
+ running: boolean;
58
+ pumping: boolean;
59
+ transport: RemoteLink | null;
60
+ waiters: (() => void)[];
61
+ lifecycle: CommandsListener | null;
62
+ notify: () => void;
63
+ emitEvent: (event: unknown) => void;
64
+ send: (command: StatewireWireCommand) => Promise<unknown>;
65
+ commands: Record<string, (...params: unknown[]) => Promise<unknown>>;
66
+ };
67
+ declare const setTransport: (s: Session, transport: RemoteLink) => void;
68
+ declare const useStatewireSession: (options: StatewireSessionStoreOptions, emit: (event: unknown) => void) => Session;
69
+ //#endregion
70
+ export { setTransport, useStatewireSession };
71
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","names":[],"sources":["../../src/client/session.ts"],"mappings":";;;;;;;;;;;;KA+BK;EACC;EAAc;;EACd;EAAc;EAAwB;;EACtC;EAAc;EAAwB;;EACtC;EAAuB;EAAiB;;EACxC;EAAoB;EAAiB;;EACrC;;KAED;WACM;WACA,SAAS;WACT,KAAK;EACd,OAAO;EACP,UAAU;EACV,SAAS,OAAO;;KAmGb;KAEA;EACH,QAAQ;EACR,KAAK;EACL;EACA,OAAO;EACP;EACA,SAAS;EACT;EACA;EACA,SAAS;EACT,QAAQ,UAAU;EAClB,QAAQ;EACR;EACA;EACA;EACA,WAAW;EACX;EACA,WAAW;EACX;EACA,YAAY;EACZ,OAAO,SAAS,yBAAyB;EACzC,UAAU,mBAAmB,sBAAsB;;cAkd/C,eAAgB,GAAG,SAAS,WAAW;cA4ChC,sBACX,SAAS,8BACT,OAAO,4BACN"}