iterate 0.2.7 → 0.3.0

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 (218) hide show
  1. package/README.md +86 -76
  2. package/THIRD_PARTY_NOTICES.md +55 -0
  3. package/bin/iterate.js +11 -3
  4. package/dist/api-url-B6404M82.mjs +17 -0
  5. package/dist/api-url-B6404M82.mjs.map +1 -0
  6. package/dist/app-ref-BipL0feU.mjs +35 -0
  7. package/dist/app-ref-BipL0feU.mjs.map +1 -0
  8. package/dist/app-ref-C1CrgXqX.mjs +7 -0
  9. package/dist/app-ref-C1CrgXqX.mjs.map +1 -0
  10. package/dist/app-ref-DYai_om1.mjs +7 -0
  11. package/dist/app-ref-DYai_om1.mjs.map +1 -0
  12. package/dist/cli-D0c-pDL_.mjs +1010 -0
  13. package/dist/cli-D0c-pDL_.mjs.map +1 -0
  14. package/dist/client.d.ts +3 -0
  15. package/dist/client.mjs +4 -0
  16. package/dist/cloudflare-BTm90gQ4.mjs +951 -0
  17. package/dist/cloudflare-BTm90gQ4.mjs.map +1 -0
  18. package/dist/contract-s4FW4eES.mjs +309 -0
  19. package/dist/contract-s4FW4eES.mjs.map +1 -0
  20. package/dist/document-review/index.d.ts +5 -0
  21. package/dist/document-review/types.d.ts +107 -0
  22. package/dist/document-review.mjs +7015 -0
  23. package/dist/document-review.mjs.map +1 -0
  24. package/dist/durable-object-processor-durability-CNsTjAJS.mjs +205 -0
  25. package/dist/durable-object-processor-durability-CNsTjAJS.mjs.map +1 -0
  26. package/dist/idempotency-DleloJNt.mjs +28 -0
  27. package/dist/idempotency-DleloJNt.mjs.map +1 -0
  28. package/dist/index.mjs +1 -1
  29. package/dist/itx/api-url.d.ts +6 -0
  30. package/dist/itx/itx-node-client.d.ts +65 -0
  31. package/dist/itx/itx-session.d.ts +215 -0
  32. package/dist/itx/owned-rpc-session.d.ts +14 -0
  33. package/dist/itx/query-client.d.ts +10 -0
  34. package/dist/itx-api.generated.d.ts +6195 -0
  35. package/dist/itx-session-sjud8GiT.mjs +534 -0
  36. package/dist/itx-session-sjud8GiT.mjs.map +1 -0
  37. package/dist/live-state-BJNqOwFw.mjs +299 -0
  38. package/dist/live-state-BJNqOwFw.mjs.map +1 -0
  39. package/dist/next/api.d.ts +479 -0
  40. package/dist/next/api.mjs +0 -0
  41. package/dist/next/app-server.d.ts +44 -0
  42. package/dist/next/app-server.mjs +479 -0
  43. package/dist/next/app-server.mjs.map +1 -0
  44. package/dist/next/app-session.d.ts +49 -0
  45. package/dist/next/app-session.mjs +238 -0
  46. package/dist/next/app-session.mjs.map +1 -0
  47. package/dist/next/app.d.ts +29 -0
  48. package/dist/next/app.mjs +141 -0
  49. package/dist/next/app.mjs.map +1 -0
  50. package/dist/next/client/live-state.d.ts +63 -0
  51. package/dist/next/client/oauth.d.ts +12 -0
  52. package/dist/next/client/react.d.ts +109 -0
  53. package/dist/next/client/socket.d.ts +6 -0
  54. package/dist/next/client.mjs +156 -0
  55. package/dist/next/client.mjs.map +1 -0
  56. package/dist/next/expression.d.ts +146 -0
  57. package/dist/next/expression.mjs +399 -0
  58. package/dist/next/expression.mjs.map +1 -0
  59. package/dist/next/lib.d.ts +56 -0
  60. package/dist/next/lib.mjs +199 -0
  61. package/dist/next/lib.mjs.map +1 -0
  62. package/dist/next/oauth-scopes.d.ts +32 -0
  63. package/dist/next/oauth-scopes.mjs +40 -0
  64. package/dist/next/oauth-scopes.mjs.map +1 -0
  65. package/dist/next/oauth.mjs +29 -0
  66. package/dist/next/oauth.mjs.map +1 -0
  67. package/dist/next/principal.d.ts +64 -0
  68. package/dist/next/principal.mjs +98 -0
  69. package/dist/next/principal.mjs.map +1 -0
  70. package/dist/next/project-ingress.d.ts +37 -0
  71. package/dist/next/project-ingress.mjs +75 -0
  72. package/dist/next/project-ingress.mjs.map +1 -0
  73. package/dist/next/react.mjs +285 -0
  74. package/dist/next/react.mjs.map +1 -0
  75. package/dist/next/sdk/auth.d.ts +5 -0
  76. package/dist/next/sdk/index.d.ts +112 -0
  77. package/dist/next/sdk.mjs +139 -0
  78. package/dist/next/sdk.mjs.map +1 -0
  79. package/dist/next/stream/processor.d.ts +378 -0
  80. package/dist/next/stream/processor.mjs +582 -0
  81. package/dist/next/stream/processor.mjs.map +1 -0
  82. package/dist/next/stream/run.d.ts +58 -0
  83. package/dist/next/stream/run.mjs +40 -0
  84. package/dist/next/stream/run.mjs.map +1 -0
  85. package/dist/next-node.d.ts +15 -0
  86. package/dist/next-node.mjs +51 -0
  87. package/dist/next-node.mjs.map +1 -0
  88. package/dist/node.d.ts +3 -0
  89. package/dist/node.mjs +185 -0
  90. package/dist/node.mjs.map +1 -0
  91. package/dist/processor-host-capabilities-BMFH3KTM.mjs +56 -0
  92. package/dist/processor-host-capabilities-BMFH3KTM.mjs.map +1 -0
  93. package/dist/processors/cloudflare.d.ts +3 -0
  94. package/dist/processors/durable-object-processor-durability.d.ts +79 -0
  95. package/dist/processors/event-consumption-metrics.d.ts +82 -0
  96. package/dist/processors/idempotency.d.ts +13 -0
  97. package/dist/processors/index.d.ts +12 -0
  98. package/dist/processors/processor-contracts.d.ts +342 -0
  99. package/dist/processors/processor-facet.d.ts +186 -0
  100. package/dist/processors/processor-host-capabilities.d.ts +60 -0
  101. package/dist/processors/prompt-sections.d.ts +17 -0
  102. package/dist/processors/rpc-types.d.ts +515 -0
  103. package/dist/processors/schemas.d.ts +102 -0
  104. package/dist/processors/stream-handle.d.ts +45 -0
  105. package/dist/processors/stream-processor-keepalive.d.ts +95 -0
  106. package/dist/processors/stream-processor-registry.d.ts +233 -0
  107. package/dist/processors/stream-processor-runner.d.ts +289 -0
  108. package/dist/processors/stream-processor.d.ts +339 -0
  109. package/dist/processors/stream-runtime-metrics.d.ts +107 -0
  110. package/dist/processors/testing.d.ts +302 -0
  111. package/dist/processors-BoNyeBfQ.mjs +10 -0
  112. package/dist/processors-BoNyeBfQ.mjs.map +1 -0
  113. package/dist/processors-cloudflare.mjs +3 -0
  114. package/dist/processors-testing.mjs +435 -0
  115. package/dist/processors-testing.mjs.map +1 -0
  116. package/dist/processors.mjs +52 -0
  117. package/dist/processors.mjs.map +1 -0
  118. package/dist/protocol-DnK_f2m6.mjs +251 -0
  119. package/dist/protocol-DnK_f2m6.mjs.map +1 -0
  120. package/dist/sdk/capnweb/index.d.ts +2 -0
  121. package/dist/sdk/capnweb/live-state/compact.d.ts +5 -0
  122. package/dist/sdk/capnweb/live-state/diff.d.ts +41 -0
  123. package/dist/sdk/capnweb/live-state/engine.d.ts +44 -0
  124. package/dist/sdk/capnweb/live-state/index.d.ts +41 -0
  125. package/dist/sdk/capnweb/live-state/protocol.d.ts +87 -0
  126. package/dist/sdk/capnweb/live-state/retain.d.ts +23 -0
  127. package/dist/sdk/capnweb/live-state/store.d.ts +20 -0
  128. package/dist/sdk/capnweb/live-state/types.d.ts +11 -0
  129. package/dist/sdk/capnweb/react.d.ts +45 -0
  130. package/dist/sdk/capnweb/react.mjs +316 -0
  131. package/dist/sdk/capnweb/react.mjs.map +1 -0
  132. package/dist/sdk/capnweb.mjs +4 -0
  133. package/dist/sdk/itx/react.d.ts +191 -0
  134. package/dist/sdk/itx/react.mjs +383 -0
  135. package/dist/sdk/itx/react.mjs.map +1 -0
  136. package/dist/sdk-DMB-IM11.mjs +933 -0
  137. package/dist/sdk-DMB-IM11.mjs.map +1 -0
  138. package/dist/sdk.d.ts +339 -0
  139. package/dist/sdk.mjs +2 -0
  140. package/dist/serve-itx.d.ts +46 -0
  141. package/dist/starter-apps/flake-dashboard/app-ref.d.ts +31 -0
  142. package/dist/starter-apps/flake-dashboard/configured-worker.mjs +1055 -0
  143. package/dist/starter-apps/flake-dashboard/configured-worker.mjs.map +1 -0
  144. package/dist/starter-apps/flake-dashboard/contract.d.ts +4839 -0
  145. package/dist/starter-apps/flake-dashboard/contract.mjs +2 -0
  146. package/dist/starter-apps/flake-dashboard/index.d.ts +17 -0
  147. package/dist/starter-apps/flake-dashboard/index.mjs +56 -0
  148. package/dist/starter-apps/flake-dashboard/index.mjs.map +1 -0
  149. package/dist/starter-apps/flake-dashboard/worker.d.ts +4607 -0
  150. package/dist/starter-apps/github-ai-linter/ai-linter.d.ts +8914 -0
  151. package/dist/starter-apps/github-ai-linter/configured-worker.mjs +17987 -0
  152. package/dist/starter-apps/github-ai-linter/configured-worker.mjs.map +1 -0
  153. package/dist/starter-apps/github-ai-linter/contract.d.ts +9193 -0
  154. package/dist/starter-apps/github-ai-linter/index.d.ts +10 -0
  155. package/dist/starter-apps/github-ai-linter/index.mjs +36 -0
  156. package/dist/starter-apps/github-ai-linter/index.mjs.map +1 -0
  157. package/dist/starter-apps/github-ai-linter/prompt.d.ts +13 -0
  158. package/dist/starter-apps/github-ai-linter/review-bot.d.ts +808 -0
  159. package/dist/starter-apps/github-ai-linter/rules.d.ts +34 -0
  160. package/dist/starter-apps/github-ai-linter/worker-ref.d.ts +19 -0
  161. package/dist/starter-apps/github-ai-linter/worker.d.ts +19 -0
  162. package/dist/starter-apps/github-ai-linter/worker.mjs +947 -0
  163. package/dist/starter-apps/github-ai-linter/worker.mjs.map +1 -0
  164. package/dist/starter-apps/guestbook/app-ref.d.ts +27 -0
  165. package/dist/starter-apps/guestbook/client.d.ts +7 -0
  166. package/dist/starter-apps/guestbook/client.mjs +59 -0
  167. package/dist/starter-apps/guestbook/configured-worker.mjs +205 -0
  168. package/dist/starter-apps/guestbook/configured-worker.mjs.map +1 -0
  169. package/dist/starter-apps/guestbook/index.d.ts +9 -0
  170. package/dist/starter-apps/guestbook/index.mjs +31 -0
  171. package/dist/starter-apps/guestbook/index.mjs.map +1 -0
  172. package/dist/starter-apps/guestbook/processor.d.ts +2267 -0
  173. package/dist/starter-apps/guestbook/worker.d.ts +26 -0
  174. package/dist/starter-apps/guestbook/worker.mjs +191 -0
  175. package/dist/starter-apps/guestbook/worker.mjs.map +1 -0
  176. package/dist/starter-apps/media/configured-worker.mjs +577 -0
  177. package/dist/starter-apps/media/configured-worker.mjs.map +1 -0
  178. package/dist/starter-apps/media/index.mjs +36 -0
  179. package/dist/starter-apps/media/index.mjs.map +1 -0
  180. package/dist/starter-apps/media/ref.mjs +20 -0
  181. package/dist/starter-apps/media/ref.mjs.map +1 -0
  182. package/dist/starter-apps/media/worker.mjs +579 -0
  183. package/dist/starter-apps/media/worker.mjs.map +1 -0
  184. package/dist/starter-apps/notes/configured-worker.mjs +6134 -0
  185. package/dist/starter-apps/notes/configured-worker.mjs.map +1 -0
  186. package/dist/starter-apps/notes/index.mjs +23 -0
  187. package/dist/starter-apps/notes/index.mjs.map +1 -0
  188. package/dist/starter-apps/notes/ref.mjs +21 -0
  189. package/dist/starter-apps/notes/ref.mjs.map +1 -0
  190. package/dist/starter-apps/notes/worker.mjs +427 -0
  191. package/dist/starter-apps/notes/worker.mjs.map +1 -0
  192. package/dist/starter-apps/todo/client.mjs +59 -0
  193. package/dist/starter-apps/todo/configured-worker.mjs +2864 -0
  194. package/dist/starter-apps/todo/configured-worker.mjs.map +1 -0
  195. package/dist/starter-apps/todo/index.d.ts +8 -0
  196. package/dist/starter-apps/todo/index.mjs +29 -0
  197. package/dist/starter-apps/todo/index.mjs.map +1 -0
  198. package/dist/stream-processor-keepalive-DAQTP6m3.mjs +2082 -0
  199. package/dist/stream-processor-keepalive-DAQTP6m3.mjs.map +1 -0
  200. package/dist/usingCtx-inzbY1Qz.mjs +57 -0
  201. package/dist/usingCtx-mZx5nsAW.mjs +11800 -0
  202. package/dist/usingCtx-mZx5nsAW.mjs.map +1 -0
  203. package/dist/worker-ref-DZxPDmb_.mjs +390 -0
  204. package/dist/worker-ref-DZxPDmb_.mjs.map +1 -0
  205. package/menubar/Iterate.entitlements +12 -0
  206. package/menubar/Iterate.swift +914 -0
  207. package/menubar/IterateIcon.swift +145 -0
  208. package/menubar/README.md +28 -0
  209. package/menubar/build-menubar-app.sh +59 -0
  210. package/package.json +235 -18
  211. package/dist/cli-DMS4kJph.mjs +0 -868
  212. package/dist/cli-DMS4kJph.mjs.map +0 -1
  213. package/dist/config-DtnR7Lv7.mjs +0 -170
  214. package/dist/config-DtnR7Lv7.mjs.map +0 -1
  215. package/dist/index.d.mts.map +0 -1
  216. package/dist/stream-tui/agent-chat-terminal.d.mts +0 -1
  217. package/dist/stream-tui/agent-chat-terminal.mjs +0 -933
  218. package/dist/stream-tui/agent-chat-terminal.mjs.map +0 -1
@@ -0,0 +1,316 @@
1
+ import { r as createLiveStateStore, t as isLiveStateSnapshot } from "../../protocol-DnK_f2m6.mjs";
2
+ import { createContext, createElement, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
3
+ //#region src/sdk/capnweb/react.tsx
4
+ const CapnWebContext = createContext(void 0);
5
+ const CONNECTION_STABLE_MS = 3e4;
6
+ const CONNECTION_RETRY_MAX_MS = 1e4;
7
+ const SUBSCRIBE_RETRY_MS = 1e4;
8
+ const MAX_SUBSCRIBE_RETRIES = 2;
9
+ const SUBSCRIBE_TIMEOUT_MS = 15e3;
10
+ const PING_INTERVAL_MS = 45e3;
11
+ const PING_TIMEOUT_MS = 1e4;
12
+ function releaseLiveStateSubscription(handle) {
13
+ Promise.resolve().then(() => handle.unsubscribe()).catch(() => {}).finally(() => handle[Symbol.dispose]?.());
14
+ }
15
+ /**
16
+ * Own one reconnectable Cap'n Web root for a React subtree. The factory is the
17
+ * only transport policy: it can dial a WebSocket directly or duplicate a root
18
+ * from a lower-level shared connection keeper. Broken roots are disposed and
19
+ * replaced with bounded exponential backoff.
20
+ */
21
+ function CapnWebProvider({ children, makeConnection }) {
22
+ return createElement(CapnWebContext, { value: useCapnWebConnection(makeConnection) }, children);
23
+ }
24
+ function useCapnWebConnection(makeConnection, enabled = true) {
25
+ const [epoch, setEpoch] = useState(0);
26
+ const failures = useRef(0);
27
+ const failureSource = useRef(makeConnection);
28
+ const reconnect = useCallback(() => setEpoch((current) => current + 1), []);
29
+ const [snapshot, setSnapshot] = useState({
30
+ enabled,
31
+ generation: 0,
32
+ reconnect,
33
+ source: makeConnection,
34
+ status: "connecting"
35
+ });
36
+ useEffect(() => {
37
+ let disposed = false;
38
+ let root;
39
+ let retry;
40
+ let stable;
41
+ if (failureSource.current !== makeConnection) {
42
+ failureSource.current = makeConnection;
43
+ failures.current = 0;
44
+ }
45
+ if (!enabled || makeConnection === void 0) {
46
+ failures.current = 0;
47
+ setSnapshot((current) => ({
48
+ enabled,
49
+ generation: current.generation,
50
+ reconnect,
51
+ source: makeConnection,
52
+ status: "connecting"
53
+ }));
54
+ } else {
55
+ const scheduleReconnect = () => {
56
+ const nextFailures = failures.current += 1;
57
+ retry = setTimeout(reconnect, Math.min(250 * 2 ** (nextFailures - 1), CONNECTION_RETRY_MAX_MS));
58
+ };
59
+ setSnapshot((current) => ({
60
+ enabled,
61
+ generation: current.generation,
62
+ reconnect,
63
+ source: makeConnection,
64
+ status: "connecting"
65
+ }));
66
+ Promise.resolve(makeConnection()).then((connected) => {
67
+ if (disposed) {
68
+ connected[Symbol.dispose]?.();
69
+ return;
70
+ }
71
+ root = connected;
72
+ setSnapshot((current) => ({
73
+ enabled,
74
+ generation: current.generation + 1,
75
+ reconnect,
76
+ root: connected,
77
+ source: makeConnection,
78
+ status: "connected"
79
+ }));
80
+ stable = setTimeout(() => {
81
+ if (!disposed && root === connected) failures.current = 0;
82
+ }, CONNECTION_STABLE_MS);
83
+ connected.onRpcBroken?.(() => {
84
+ if (disposed || root !== connected) return;
85
+ clearTimeout(stable);
86
+ root = void 0;
87
+ connected[Symbol.dispose]?.();
88
+ setSnapshot((current) => ({
89
+ enabled,
90
+ generation: current.generation,
91
+ reconnect,
92
+ source: makeConnection,
93
+ status: "connecting"
94
+ }));
95
+ scheduleReconnect();
96
+ });
97
+ }, (cause) => {
98
+ if (disposed) return;
99
+ setSnapshot((current) => ({
100
+ enabled,
101
+ error: cause instanceof Error ? cause.message : String(cause),
102
+ generation: current.generation,
103
+ reconnect,
104
+ source: makeConnection,
105
+ status: "error"
106
+ }));
107
+ scheduleReconnect();
108
+ });
109
+ }
110
+ return () => {
111
+ disposed = true;
112
+ clearTimeout(retry);
113
+ clearTimeout(stable);
114
+ root?.[Symbol.dispose]?.();
115
+ };
116
+ }, [
117
+ enabled,
118
+ epoch,
119
+ makeConnection,
120
+ reconnect
121
+ ]);
122
+ if (snapshot.enabled !== enabled || snapshot.source !== makeConnection) return {
123
+ generation: snapshot.generation,
124
+ reconnect,
125
+ scope: makeConnection,
126
+ status: "connecting"
127
+ };
128
+ const { enabled: _enabled, source: _source, ...connection } = snapshot;
129
+ return {
130
+ ...connection,
131
+ scope: makeConnection
132
+ };
133
+ }
134
+ /** The current provider root, undefined while its connection is being replaced. */
135
+ function useCapnWebRoot() {
136
+ const connection = useContext(CapnWebContext);
137
+ if (connection === void 0) throw new Error("useCapnWebRoot must be rendered under <CapnWebProvider>.");
138
+ return connection.root;
139
+ }
140
+ /** Resolve provider, owned connection, or borrowed root with one logical scope. */
141
+ function useLiveStateConnection(options) {
142
+ const provider = useContext(CapnWebContext);
143
+ const hasRootOverride = options !== void 0 && Object.hasOwn(options, "root");
144
+ const connectionFactory = hasRootOverride ? void 0 : options?.makeConnection;
145
+ const owned = useCapnWebConnection(connectionFactory, !hasRootOverride && (options?.enabled ?? true));
146
+ if (!hasRootOverride && !connectionFactory && !provider) throw new Error("useLiveState needs <CapnWebProvider>, a makeConnection option, or an explicit { root } override.");
147
+ const connection = hasRootOverride ? void 0 : connectionFactory ? owned : provider;
148
+ const root = (hasRootOverride ? options.root : connection?.root) ?? void 0;
149
+ return {
150
+ connection,
151
+ root,
152
+ scope: hasRootOverride ? root : connectionFactory ?? connection?.scope
153
+ };
154
+ }
155
+ /**
156
+ * Render a selected slice of any LiveStateRpc reachable from the provider root.
157
+ * Pass `{ root }` to borrow an explicit root instead; borrowed roots are never
158
+ * disposed or reconnected by this hook.
159
+ */
160
+ function useLiveState(live, selector = (state) => state, deps = [], options) {
161
+ const { connection, root, scope } = useLiveStateConnection(options);
162
+ const enabled = options?.enabled ?? true;
163
+ const [epoch, setEpoch] = useState(0);
164
+ const liveRef = useRef(live);
165
+ const selectorRef = useRef(selector);
166
+ useEffect(() => {
167
+ liveRef.current = live;
168
+ selectorRef.current = selector;
169
+ });
170
+ const store = useMemo(() => createLiveStateStore(), [scope, ...deps]);
171
+ const subscribeRetries = useMemo(() => ({
172
+ count: 0,
173
+ resyncs: 0
174
+ }), [store]);
175
+ const refresh = useCallback(() => {
176
+ subscribeRetries.count = 0;
177
+ subscribeRetries.resyncs = 0;
178
+ setEpoch((current) => current + 1);
179
+ }, [subscribeRetries]);
180
+ const [subscriptionState, setSubscriptionState] = useState({
181
+ epoch,
182
+ generation: connection?.generation,
183
+ status: "connecting",
184
+ store
185
+ });
186
+ useEffect(() => {
187
+ let disposed = false;
188
+ let stale = false;
189
+ let handle;
190
+ let pingTimeout;
191
+ let retry;
192
+ let timeout;
193
+ let watchdog;
194
+ const release = () => {
195
+ stale = true;
196
+ if (handle === void 0) return;
197
+ releaseLiveStateSubscription(handle);
198
+ handle = void 0;
199
+ };
200
+ setSubscriptionState({
201
+ epoch,
202
+ generation: connection?.generation,
203
+ status: "connecting",
204
+ store
205
+ });
206
+ if (enabled && root !== void 0) {
207
+ const report = (cause, shouldRetry = false) => {
208
+ if (disposed || stale) return;
209
+ release();
210
+ setSubscriptionState({
211
+ error: cause instanceof Error ? cause.message : String(cause),
212
+ epoch,
213
+ generation: connection?.generation,
214
+ status: "error",
215
+ store
216
+ });
217
+ if (shouldRetry && subscribeRetries.count < MAX_SUBSCRIBE_RETRIES) {
218
+ subscribeRetries.count += 1;
219
+ retry = setTimeout(() => setEpoch((current) => current + 1), SUBSCRIBE_RETRY_MS);
220
+ }
221
+ };
222
+ try {
223
+ const pending = liveRef.current(root).subscribe((update) => {
224
+ if (disposed || stale) return;
225
+ try {
226
+ store.apply(update, () => {
227
+ throw new Error("Live-state revision gap");
228
+ });
229
+ if (!isLiveStateSnapshot(update)) subscribeRetries.resyncs = 0;
230
+ } catch (error) {
231
+ const shouldResync = subscribeRetries.resyncs < MAX_SUBSCRIBE_RETRIES;
232
+ if (shouldResync) subscribeRetries.resyncs += 1;
233
+ report(error, shouldResync);
234
+ }
235
+ }, { patchVersion: 3 });
236
+ timeout = setTimeout(() => report(/* @__PURE__ */ new Error(`live-state subscription timed out after ${SUBSCRIBE_TIMEOUT_MS}ms`), true), SUBSCRIBE_TIMEOUT_MS);
237
+ Promise.resolve(pending).then((subscription) => {
238
+ clearTimeout(timeout);
239
+ if (disposed || stale) {
240
+ releaseLiveStateSubscription(subscription);
241
+ return;
242
+ }
243
+ handle = subscription;
244
+ subscribeRetries.count = 0;
245
+ setSubscriptionState({
246
+ epoch,
247
+ generation: connection?.generation,
248
+ status: "live",
249
+ store
250
+ });
251
+ watchdog = setInterval(() => {
252
+ if (handle === void 0) return;
253
+ Promise.race([Promise.resolve(handle.ping()), new Promise((resolve) => {
254
+ pingTimeout = setTimeout(() => resolve(false), PING_TIMEOUT_MS);
255
+ })]).then((alive) => {
256
+ if (!disposed && !alive) refresh();
257
+ }, (cause) => report(cause, true)).finally(() => clearTimeout(pingTimeout));
258
+ }, PING_INTERVAL_MS);
259
+ }, (cause) => {
260
+ clearTimeout(timeout);
261
+ report(cause, true);
262
+ });
263
+ } catch (cause) {
264
+ report(cause, true);
265
+ }
266
+ }
267
+ return () => {
268
+ disposed = true;
269
+ clearTimeout(pingTimeout);
270
+ clearTimeout(timeout);
271
+ clearTimeout(retry);
272
+ clearInterval(watchdog);
273
+ release();
274
+ };
275
+ }, [
276
+ connection?.generation,
277
+ enabled,
278
+ epoch,
279
+ root,
280
+ store,
281
+ ...deps
282
+ ]);
283
+ const selected = useRef({
284
+ state: void 0,
285
+ value: void 0
286
+ });
287
+ const getSelected = () => {
288
+ const state = store.getState();
289
+ if (Object.is(selected.current.state, state)) return selected.current.value;
290
+ const value = state === void 0 ? void 0 : selectorRef.current(state);
291
+ selected.current = {
292
+ state,
293
+ value
294
+ };
295
+ return value;
296
+ };
297
+ const value = useSyncExternalStore(store.subscribe, getSelected, () => void 0);
298
+ const providerError = connection?.status === "error" ? connection.error : void 0;
299
+ const activeSubscriptionState = subscriptionState.store === store && subscriptionState.epoch === epoch && subscriptionState.generation === connection?.generation ? subscriptionState : {
300
+ epoch,
301
+ generation: connection?.generation,
302
+ status: "connecting",
303
+ store
304
+ };
305
+ const connectionIsConnecting = connection?.status === "connecting";
306
+ return {
307
+ error: providerError ?? (connectionIsConnecting || !enabled ? void 0 : activeSubscriptionState.error),
308
+ refresh: connection?.status === "error" ? connection.reconnect : refresh,
309
+ status: providerError !== void 0 ? "error" : connectionIsConnecting || !enabled ? "connecting" : activeSubscriptionState.status,
310
+ value
311
+ };
312
+ }
313
+ //#endregion
314
+ export { CapnWebProvider, useCapnWebRoot, useLiveState };
315
+
316
+ //# sourceMappingURL=react.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.mjs","names":[],"sources":["../../../src/sdk/capnweb/react.tsx"],"sourcesContent":["// oxlint-disable react/only-export-components -- this public entry deliberately colocates its provider with the hooks that consume it.\nimport {\n createContext,\n createElement,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n type ReactNode,\n} from \"react\";\nimport { createLiveStateStore } from \"./live-state/store.ts\";\nimport { isLiveStateSnapshot } from \"./live-state/protocol.ts\";\nimport type { LiveStateRpc, LiveStateSubscriptionHandle } from \"./live-state/types.ts\";\n\nexport type CapnWebRoot = object &\n Partial<Disposable> & {\n onRpcBroken?(callback: (error?: unknown) => void): void;\n };\n\nexport type MakeCapnWebConnection<Root extends CapnWebRoot> = () => Root | PromiseLike<Root>;\nexport type LiveStateStatus = \"connecting\" | \"live\" | \"error\";\n\ntype ConnectionSnapshot = {\n error?: string;\n generation: number;\n reconnect: () => void;\n root?: CapnWebRoot;\n /** Stable for one connection factory, across all of that factory's reconnects. */\n scope: object | undefined;\n status: \"connecting\" | \"connected\" | \"error\";\n};\n\nconst CapnWebContext = createContext<ConnectionSnapshot | undefined>(undefined);\nconst CONNECTION_STABLE_MS = 30_000;\nconst CONNECTION_RETRY_MAX_MS = 10_000;\nconst SUBSCRIBE_RETRY_MS = 10_000;\nconst MAX_SUBSCRIBE_RETRIES = 2;\nconst SUBSCRIBE_TIMEOUT_MS = 15_000;\nconst PING_INTERVAL_MS = 45_000;\nconst PING_TIMEOUT_MS = 10_000;\n\nfunction releaseLiveStateSubscription(handle: LiveStateSubscriptionHandle): void {\n // Teardown commonly races a dead transport: the remote unsubscribe may\n // reject, but the local capability must still leave Cap'n Web's import table.\n void Promise.resolve()\n .then(() => handle.unsubscribe())\n .catch(() => {})\n .finally(() => handle[Symbol.dispose]?.());\n}\n\n/**\n * Own one reconnectable Cap'n Web root for a React subtree. The factory is the\n * only transport policy: it can dial a WebSocket directly or duplicate a root\n * from a lower-level shared connection keeper. Broken roots are disposed and\n * replaced with bounded exponential backoff.\n */\nexport function CapnWebProvider<Root extends CapnWebRoot>({\n children,\n makeConnection,\n}: {\n children?: ReactNode;\n makeConnection: MakeCapnWebConnection<Root>;\n}) {\n const snapshot = useCapnWebConnection(makeConnection);\n\n return createElement(CapnWebContext, { value: snapshot }, children);\n}\n\nfunction useCapnWebConnection<Root extends CapnWebRoot>(\n makeConnection: MakeCapnWebConnection<Root> | undefined,\n enabled = true,\n): ConnectionSnapshot {\n const [epoch, setEpoch] = useState(0);\n const failures = useRef(0);\n const failureSource = useRef(makeConnection);\n const reconnect = useCallback(() => setEpoch((current) => current + 1), []);\n const [snapshot, setSnapshot] = useState<InternalConnectionSnapshot<Root>>({\n enabled,\n generation: 0,\n reconnect,\n source: makeConnection,\n status: \"connecting\",\n });\n\n // react-doctor-disable-next-line effect-needs-cleanup -- the unconditional cleanup below owns both timers assigned by async connection callbacks and the root\n useEffect(() => {\n let disposed = false;\n let root: Root | undefined;\n let retry: ReturnType<typeof setTimeout> | undefined;\n let stable: ReturnType<typeof setTimeout> | undefined;\n if (failureSource.current !== makeConnection) {\n failureSource.current = makeConnection;\n failures.current = 0;\n }\n if (!enabled || makeConnection === undefined) {\n failures.current = 0;\n setSnapshot((current) => ({\n enabled,\n generation: current.generation,\n reconnect,\n source: makeConnection,\n status: \"connecting\",\n }));\n } else {\n const scheduleReconnect = () => {\n const nextFailures = (failures.current += 1);\n retry = setTimeout(\n reconnect,\n Math.min(250 * 2 ** (nextFailures - 1), CONNECTION_RETRY_MAX_MS),\n );\n };\n setSnapshot((current) => ({\n enabled,\n generation: current.generation,\n reconnect,\n source: makeConnection,\n status: \"connecting\",\n }));\n\n void Promise.resolve(makeConnection()).then(\n (connected) => {\n if (disposed) {\n connected[Symbol.dispose]?.();\n return;\n }\n root = connected;\n setSnapshot((current) => ({\n enabled,\n generation: current.generation + 1,\n reconnect,\n root: connected,\n source: makeConnection,\n status: \"connected\",\n }));\n stable = setTimeout(() => {\n if (!disposed && root === connected) failures.current = 0;\n }, CONNECTION_STABLE_MS);\n connected.onRpcBroken?.(() => {\n if (disposed || root !== connected) return;\n clearTimeout(stable);\n root = undefined;\n connected[Symbol.dispose]?.();\n setSnapshot((current) => ({\n enabled,\n generation: current.generation,\n reconnect,\n source: makeConnection,\n status: \"connecting\",\n }));\n scheduleReconnect();\n });\n },\n (cause: unknown) => {\n if (disposed) return;\n setSnapshot((current) => ({\n enabled,\n error: cause instanceof Error ? cause.message : String(cause),\n generation: current.generation,\n reconnect,\n source: makeConnection,\n status: \"error\",\n }));\n scheduleReconnect();\n },\n );\n }\n\n return () => {\n disposed = true;\n clearTimeout(retry);\n clearTimeout(stable);\n root?.[Symbol.dispose]?.();\n };\n }, [enabled, epoch, makeConnection, reconnect]);\n\n if (snapshot.enabled !== enabled || snapshot.source !== makeConnection) {\n return {\n generation: snapshot.generation,\n reconnect,\n scope: makeConnection,\n status: \"connecting\",\n };\n }\n const { enabled: _enabled, source: _source, ...connection } = snapshot;\n return { ...connection, scope: makeConnection };\n}\n\ntype InternalConnectionSnapshot<Root extends CapnWebRoot> = Omit<ConnectionSnapshot, \"scope\"> & {\n enabled: boolean;\n source: MakeCapnWebConnection<Root> | undefined;\n};\n\n/** The current provider root, undefined while its connection is being replaced. */\nexport function useCapnWebRoot<Root extends CapnWebRoot>(): Root | undefined {\n const connection = useContext(CapnWebContext);\n if (connection === undefined) {\n throw new Error(\"useCapnWebRoot must be rendered under <CapnWebProvider>.\");\n }\n return connection.root as Root | undefined;\n}\n\ntype LiveStateOptions<Root extends CapnWebRoot> = {\n enabled?: boolean;\n makeConnection?: MakeCapnWebConnection<Root>;\n root?: Root | null;\n};\n\n/** Resolve provider, owned connection, or borrowed root with one logical scope. */\nfunction useLiveStateConnection<Root extends CapnWebRoot>(options?: LiveStateOptions<Root>) {\n const provider = useContext(CapnWebContext);\n const hasRootOverride = options !== undefined && Object.hasOwn(options, \"root\");\n const connectionFactory = hasRootOverride ? undefined : options?.makeConnection;\n const owned = useCapnWebConnection(\n connectionFactory,\n !hasRootOverride && (options?.enabled ?? true),\n );\n if (!hasRootOverride && !connectionFactory && !provider) {\n throw new Error(\n \"useLiveState needs <CapnWebProvider>, a makeConnection option, or an explicit { root } override.\",\n );\n }\n const connection = hasRootOverride ? undefined : connectionFactory ? owned : provider;\n const root = (hasRootOverride ? options.root : connection?.root) ?? undefined;\n const scope = hasRootOverride ? root : (connectionFactory ?? connection?.scope);\n return { connection, root, scope };\n}\n\n/**\n * Render a selected slice of any LiveStateRpc reachable from the provider root.\n * Pass `{ root }` to borrow an explicit root instead; borrowed roots are never\n * disposed or reconnected by this hook.\n */\nexport function useLiveState<Root extends CapnWebRoot, State, Selected = State>(\n live: (root: Root) => LiveStateRpc<State>,\n selector: (state: State) => Selected = (state) => state as unknown as Selected,\n deps: unknown[] = [],\n options?: LiveStateOptions<Root>,\n): {\n error?: string;\n refresh: () => void;\n status: LiveStateStatus;\n value: Selected | undefined;\n} {\n const { connection, root, scope } = useLiveStateConnection(options);\n const enabled = options?.enabled ?? true;\n const [epoch, setEpoch] = useState(0);\n const liveRef = useRef(live);\n const selectorRef = useRef(selector);\n useEffect(() => {\n liveRef.current = live;\n selectorRef.current = selector;\n });\n\n // A different provider/factory, explicit borrowed root, or logical node gets\n // an empty store synchronously. A new transport generation within the same\n // connection scope keeps the last value visible while it re-subscribes.\n // oxlint-disable-next-line react-hooks/exhaustive-deps -- this memo is intentionally keyed; deps complete the caller's logical-node identity\n const store = useMemo(() => createLiveStateStore<State>(), [scope, ...deps]);\n // oxlint-disable-next-line react-hooks/exhaustive-deps -- store is the logical subscription identity; its replacement must reset the retry budget\n const subscribeRetries = useMemo(() => ({ count: 0, resyncs: 0 }), [store]);\n const refresh = useCallback(() => {\n subscribeRetries.count = 0;\n subscribeRetries.resyncs = 0;\n setEpoch((current) => current + 1);\n }, [subscribeRetries]);\n const [subscriptionState, setSubscriptionState] = useState<{\n error?: string;\n epoch: number;\n generation: number | undefined;\n status: LiveStateStatus;\n store: typeof store;\n }>({ epoch, generation: connection?.generation, status: \"connecting\", store });\n\n // react-doctor-disable-next-line effect-needs-cleanup -- the unconditional cleanup below owns subscribe/retry/ping timers and the subscription handle\n useEffect(() => {\n let disposed = false;\n let stale = false;\n let handle: LiveStateSubscriptionHandle | undefined;\n let pingTimeout: ReturnType<typeof setTimeout> | undefined;\n let retry: ReturnType<typeof setTimeout> | undefined;\n let timeout: ReturnType<typeof setTimeout> | undefined;\n let watchdog: ReturnType<typeof setInterval> | undefined;\n const release = () => {\n stale = true;\n if (handle === undefined) return;\n releaseLiveStateSubscription(handle);\n handle = undefined;\n };\n setSubscriptionState({\n epoch,\n generation: connection?.generation,\n status: \"connecting\",\n store,\n });\n if (enabled && root !== undefined) {\n const report = (cause: unknown, shouldRetry = false) => {\n if (disposed || stale) return;\n release();\n setSubscriptionState({\n error: cause instanceof Error ? cause.message : String(cause),\n epoch,\n generation: connection?.generation,\n status: \"error\",\n store,\n });\n if (shouldRetry && subscribeRetries.count < MAX_SUBSCRIBE_RETRIES) {\n subscribeRetries.count += 1;\n retry = setTimeout(() => setEpoch((current) => current + 1), SUBSCRIBE_RETRY_MS);\n }\n };\n\n // The context erases its root parameter; this hook's provider/factory/root\n // inputs are the only sources, so restoring the caller's Root is safe.\n try {\n const pending = liveRef.current(root as Root).subscribe(\n (update) => {\n if (disposed || stale) return;\n try {\n store.apply(update, () => {\n throw new Error(\"Live-state revision gap\");\n });\n if (!isLiveStateSnapshot(update)) subscribeRetries.resyncs = 0;\n } catch (error) {\n // A successful subscribe alone cannot reset this budget: a host\n // could repeatedly seed a valid snapshot followed by a bad patch.\n const shouldResync = subscribeRetries.resyncs < MAX_SUBSCRIBE_RETRIES;\n if (shouldResync) subscribeRetries.resyncs += 1;\n report(error, shouldResync);\n }\n },\n { patchVersion: 3 },\n );\n timeout = setTimeout(\n () =>\n report(\n new Error(`live-state subscription timed out after ${SUBSCRIBE_TIMEOUT_MS}ms`),\n true,\n ),\n SUBSCRIBE_TIMEOUT_MS,\n );\n void Promise.resolve(pending).then(\n (subscription) => {\n clearTimeout(timeout);\n if (disposed || stale) {\n releaseLiveStateSubscription(subscription);\n return;\n }\n handle = subscription;\n subscribeRetries.count = 0;\n setSubscriptionState({\n epoch,\n generation: connection?.generation,\n status: \"live\",\n store,\n });\n watchdog = setInterval(() => {\n if (handle === undefined) return;\n void Promise.race([\n Promise.resolve(handle.ping()),\n new Promise<boolean>((resolve) => {\n pingTimeout = setTimeout(() => resolve(false), PING_TIMEOUT_MS);\n }),\n ])\n .then(\n (alive) => {\n if (!disposed && !alive) refresh();\n },\n (cause: unknown) => report(cause, true),\n )\n .finally(() => clearTimeout(pingTimeout));\n }, PING_INTERVAL_MS);\n },\n (cause: unknown) => {\n clearTimeout(timeout);\n report(cause, true);\n },\n );\n } catch (cause) {\n report(cause, true);\n }\n }\n\n return () => {\n disposed = true;\n clearTimeout(pingTimeout);\n clearTimeout(timeout);\n clearTimeout(retry);\n clearInterval(watchdog);\n release();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- deps define the caller's logical node\n }, [connection?.generation, enabled, epoch, root, store, ...deps]);\n\n const selected = useRef<{ state: State | undefined; value: Selected | undefined }>({\n state: undefined,\n value: undefined,\n });\n const getSelected = () => {\n const state = store.getState();\n if (Object.is(selected.current.state, state)) return selected.current.value;\n const value = state === undefined ? undefined : selectorRef.current(state);\n selected.current = { state, value };\n return value;\n };\n const value = useSyncExternalStore(store.subscribe, getSelected, () => undefined);\n const providerError = connection?.status === \"error\" ? connection.error : undefined;\n const activeSubscriptionState =\n subscriptionState.store === store &&\n subscriptionState.epoch === epoch &&\n subscriptionState.generation === connection?.generation\n ? subscriptionState\n : {\n epoch,\n generation: connection?.generation,\n status: \"connecting\" as const,\n store,\n };\n const connectionIsConnecting = connection?.status === \"connecting\";\n return {\n error:\n providerError ??\n (connectionIsConnecting || !enabled ? undefined : activeSubscriptionState.error),\n refresh: connection?.status === \"error\" ? connection.reconnect : refresh,\n status:\n providerError !== undefined\n ? \"error\"\n : connectionIsConnecting || !enabled\n ? \"connecting\"\n : activeSubscriptionState.status,\n value,\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { a as isThenable, i as disposeIgnoredRpcResult, n as LiveStateSubscriptionRpcTarget, o as retainCallback, r as LiveState, t as LiveStateRpcTarget } from "../live-state-BJNqOwFw.mjs";
2
+ import { a as compactPatch, i as applyCompactPatch, n as liveStateRevision, o as applyPatch, r as createLiveStateStore, s as diff, t as isLiveStateSnapshot } from "../protocol-DnK_f2m6.mjs";
3
+ export * from "@iterate-com/capnweb";
4
+ export { LiveState, LiveStateRpcTarget, LiveStateSubscriptionRpcTarget, applyCompactPatch, applyPatch, compactPatch, createLiveStateStore, diff, disposeIgnoredRpcResult, isLiveStateSnapshot, isThenable, liveStateRevision, retainCallback };
@@ -0,0 +1,191 @@
1
+ /**
2
+ * `iterate/sdk/itx/react` — hooks over the framework-free session keeper in
3
+ * ../../itx/itx-session.ts (one WebSocket per tab, generations, invisible
4
+ * reconnect — the session model lives there) plus TanStack Query for reads.
5
+ *
6
+ * Renderer-agnostic on purpose: nothing here touches the DOM, so the same
7
+ * hooks run under react-dom (the OS dashboard), @opentui/react (the chat TUI),
8
+ * and React Native — TanStack Query itself only uses React primitives. The
9
+ * browser-specific pieces (cookie auth, window-derived URL, visibility/online
10
+ * wake probes) live in the keeper behind runtime guards; non-browser apps
11
+ * point the keeper at a deployment with `configureIterateSession`.
12
+ *
13
+ * ───────────────────────────────────────────────────────────────────────────
14
+ * THE PRIMITIVES
15
+ * ───────────────────────────────────────────────────────────────────────────
16
+ *
17
+ * SESSION (the catalog authenticate() returned)
18
+ * useIterateSession() in render; suspends once on first connect
19
+ * connectIterateSession() imperative (handlers/closures); a Promise
20
+ *
21
+ * ITX (a project capability handle — session.projects.get(slug))
22
+ * useItx(slug?) in render; slug (or prj_ id) from the arg or <ProjectScope>
23
+ * connectItx(slug) imperative; a Promise
24
+ *
25
+ * READ ONCE useItxQuery({ key, query }) project read; SUSPENDS
26
+ * useIterateSessionQuery({ key, query }) session read; NON-suspending (shell)
27
+ * LIVE STATE useLiveState((itx) => itx.liveState, selector) project snapshot + diffs
28
+ * useIterateSessionLiveState(...) session snapshot + diffs
29
+ * CONNECT useStreamConnection((itx) => handle, deps) raw event batches (escape hatch)
30
+ * MOUNT <ProjectScope slug> ambient project + reconnectable Cap'n Web provider
31
+ *
32
+ * ACTIONS (mutations) — imperative on the handle, no extra primitive:
33
+ * const itx = useItx();
34
+ * <button onClick={() => itx.chat.sendMessage(text)} />
35
+ *
36
+ * RECONNECT, AS REACT SEES IT: components read an immutable Snapshot via
37
+ * `useSyncExternalStore`; `snapshot.session` holds the LAST live session and is
38
+ * kept across a transport gap. So `useIterateSession()` suspends exactly once —
39
+ * first load, on the stable first-connect promise — and never again: when the
40
+ * socket dies we keep showing the last session while a fresh generation connects
41
+ * in the background, then swap when it establishes. TanStack keeps cached read
42
+ * data through a reconnect (no re-suspend, no spinner); only an in-flight read
43
+ * retries, on the transport-only policy — see useItxQuery. A stray action fired
44
+ * during the sub-second gap rides the dead stub and rejects — the one accepted
45
+ * edge.
46
+ *
47
+ * CONNECTING THROWS ON THE SERVER (never SSRs): a forever-pending `use()`
48
+ * during streaming SSR would hang the response. Render itx consumers under an
49
+ * `ssr: false` route or `<ClientOnly>` + `<Suspense>`.
50
+ */
51
+ import { type ReactNode } from "react";
52
+ import { type QueryKey, type UseQueryResult } from "@tanstack/react-query";
53
+ import type { LiveStateRpc } from "../../itx-api.generated.ts";
54
+ import { type LiveStateStatus } from "../capnweb/react.tsx";
55
+ import { type ItxRecoverableConnectionHandle, type ProjectStub, type SessionStub } from "../../itx/itx-session.ts";
56
+ export { configureIterateSession, connectIterateSession, connectItx, disconnectIterateSession, isItxTransportError, reconnectIterateSession, retryFailedIterateSession, reportTransportSuspicion, type Itx, type ItxRecoverableConnectionHandle, type IterateSessionConfig, type ProjectStub, type SessionStub, } from "../../itx/itx-session.ts";
57
+ export { createIterateQueryClient } from "../../itx/query-client.ts";
58
+ export type * from "../../itx-api.generated.ts";
59
+ /**
60
+ * The Session — the catalog `authenticate()` returned (`projects.list/create`,
61
+ * admin `streams`). Suspends exactly ONCE, on first connect; a later reconnect
62
+ * keeps returning the last session (no re-suspend — see module header).
63
+ */
64
+ export declare function useIterateSession(): SessionStub;
65
+ /**
66
+ * The project itx for `slug` — `session.projects.get(slug)`, the real capnweb
67
+ * stub. `slug` comes from the argument or the nearest <ProjectScope>; an
68
+ * explicit argument wins. The returned stub's identity is stable within a socket
69
+ * and changes once per reconnect (which re-runs effects/memos keyed on it).
70
+ *
71
+ * const itx = useItx(); // ambient project (under <ProjectScope>)
72
+ * const itx = useItx("other-slug"); // a specific project
73
+ * const onSend = () => itx.chat.sendMessage(text);
74
+ */
75
+ export declare function useItx(explicitSlug?: string): ProjectStub;
76
+ /**
77
+ * Set the ambient project for a subtree and provide its reconnectable Cap'n Web
78
+ * root. The project-specific layer is deliberately only a connection factory;
79
+ * live-state ownership and recovery live in `iterate/sdk/capnweb/react`.
80
+ *
81
+ * The provider dials in an effect, never during render, so this context remains
82
+ * safe to render on the server. The duplicate belongs to the provider; the
83
+ * lower-level session keeper keeps owning and reconnecting the shared socket
84
+ * beneath it.
85
+ */
86
+ export declare function ProjectScope({ slug, children }: {
87
+ slug: string;
88
+ children?: ReactNode;
89
+ }): import("react").FunctionComponentElement<import("react").ProviderProps<string | undefined>>;
90
+ /**
91
+ * Read once through a project itx, suspending until it resolves. A thin adapter
92
+ * over TanStack Query's `useSuspenseQuery`. `key` (prefixed with "itx"
93
+ * internally) is the cache key — it must encode what the result is scoped to,
94
+ * INCLUDING the project, so two projects' data can't collide: a per-project read
95
+ * keys by the project, e.g. `["repo-files", projectId, repoPath]`. (The ambient
96
+ * slug drives the CONNECTION, not the key — so a mutation invalidates with the
97
+ * same `["itx", ...key]` it was written under.)
98
+ *
99
+ * const files = useItxQuery({
100
+ * key: ["repo-files", projectId, repoPath],
101
+ * query: (itx) => itx.repos.get(repoPath).listFiles(),
102
+ * });
103
+ *
104
+ * The connection is resolved PER FETCH (never a render-captured stub — that
105
+ * would pin a dead socket after a reconnect), and the per-fetch project stub is
106
+ * disposed once the read resolves. A resolved query keeps its cached data across
107
+ * a reconnect (no re-suspend, no spinner); only an in-flight read retries, on a
108
+ * finite transport-only policy. Errors with no cached data throw to the nearest
109
+ * error boundary; refetch after a mutation with
110
+ * `queryClient.invalidateQueries({ queryKey: ["itx", ...key] })`.
111
+ */
112
+ export declare function useItxQuery<T>({ key, query, }: {
113
+ key: QueryKey;
114
+ query: (itx: ProjectStub) => Promise<T>;
115
+ }): T;
116
+ /**
117
+ * Read once through the SESSION (the catalog — `projects.list`, admin `streams`),
118
+ * the session-scoped sibling of {@link useItxQuery}. NON-suspending: session
119
+ * reads live in the always-mounted shell (sidebar, ⌘K, admin) which must not
120
+ * suspend on the socket, so this returns the full TanStack result (`data`,
121
+ * `isPending`, …) and accepts the usual options (`enabled`, `staleTime`). Same
122
+ * `["itx", ...key]` namespace and the same transport-only retry as useItxQuery,
123
+ * resolved per fetch (never a render-captured session stub).
124
+ *
125
+ * const { data } = useIterateSessionQuery({ key: ["projects"], query: (s) => s.projects.list() });
126
+ */
127
+ export declare function useIterateSessionQuery<T>({ key, query, enabled, staleTime, }: {
128
+ key: QueryKey;
129
+ query: (session: SessionStub) => Promise<T>;
130
+ enabled?: boolean;
131
+ staleTime?: number;
132
+ }): UseQueryResult<T>;
133
+ export type ItxConnectionStatus = LiveStateStatus;
134
+ /**
135
+ * Hold one raw stream callback connection for the component's lifetime.
136
+ * Reconnects, silent connection death, and transport-failed open attempts recover
137
+ * through the shared watchdog; permanent failures remain in `"error"` until
138
+ * `refresh()` or a reconnect. Reopening replays the first push, so callbacks
139
+ * must be replay-tolerant. Teardown closes and disposes the handle.
140
+ *
141
+ * `enabled: false` is inert; `deps` reopen on change. `opts.slug` targets
142
+ * a project outside the ambient {@link ProjectScope} without suspending.
143
+ */
144
+ export declare function useStreamConnection(open: (itx: ProjectStub) => Promise<ItxRecoverableConnectionHandle>, deps: unknown[], opts?: {
145
+ enabled?: boolean;
146
+ slug?: string;
147
+ }): {
148
+ status: ItxConnectionStatus;
149
+ error?: string;
150
+ refresh: () => void;
151
+ };
152
+ /**
153
+ * THE live-state primitive: subscribe to any `.liveState` node, render the slice
154
+ * you pick. The server pushes a snapshot then minimal diffs; this hook
155
+ * reassembles them and hands back `selector(state)`.
156
+ *
157
+ * const streams = useLiveState((itx) => itx.liveState, (s) => s.streamsIndex);
158
+ *
159
+ * THE SELECTOR CONTRACT — a pure function of the state, nothing else:
160
+ * - Return a STABLE slice (`s => s.rows`), not a fresh object; map downstream.
161
+ * - Do NOT close over props/state (`s => s.rows[props.id]`): selection is cached
162
+ * by STATE identity, so a closure-captured value going stale is invisible until
163
+ * the next push. Select the broader slice and index in render, or route the
164
+ * changing input through `deps`.
165
+ *
166
+ * `value` is `undefined` between mount and the first snapshot (one round trip);
167
+ * render a loading row for that window. It never suspends — a reconnect keeps the
168
+ * last value while the subscription silently re-establishes. `deps` (or a changed
169
+ * `opts.slug`) re-point the hook at a different node, and the held value drops to
170
+ * `undefined` immediately — the previous node's state is never shown against the
171
+ * new one. To read a DIFFERENT project's live state from OUTSIDE its scope (⌘K in
172
+ * the app shell), pass `opts.slug`.
173
+ */
174
+ export declare function useLiveState<State, Selected = State>(live: (itx: ProjectStub) => LiveStateRpc<State>, selector: (state: State) => Selected, deps?: unknown[], opts?: {
175
+ slug?: string;
176
+ enabled?: boolean;
177
+ }): {
178
+ value: Selected | undefined;
179
+ status: ItxConnectionStatus;
180
+ error?: string;
181
+ refresh: () => void;
182
+ };
183
+ /** Session-scoped sibling of {@link useLiveState}, for deployment-wide live nodes. */
184
+ export declare function useIterateSessionLiveState<State, Selected = State>(live: (session: SessionStub) => LiveStateRpc<State>, selector: (state: State) => Selected, deps?: unknown[], opts?: {
185
+ enabled?: boolean;
186
+ }): {
187
+ value: Selected | undefined;
188
+ status: ItxConnectionStatus;
189
+ error?: string;
190
+ refresh: () => void;
191
+ };