@nyte-ai/sdk 0.0.0-stage → 0.0.19

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 (49) hide show
  1. package/LICENSE +21 -0
  2. package/THIRD-PARTY-NOTICES.md +866 -0
  3. package/dist/chunks/capability-2q3s96kg.js +57 -0
  4. package/dist/chunks/capability-9747w1b1.js +55 -0
  5. package/dist/chunks/capability-agj3gwwv.js +134 -0
  6. package/dist/chunks/capability-artgq59v.js +232 -0
  7. package/dist/chunks/capability-b52hty54.js +17 -0
  8. package/dist/chunks/capability-ctnsbfx9.js +203 -0
  9. package/dist/chunks/capability-ec663cyt.js +208 -0
  10. package/dist/chunks/capability-ke5yk1as.js +165 -0
  11. package/dist/chunks/capability-mr5m2wn2.js +6 -0
  12. package/dist/chunks/capability-nh422cp0.js +35 -0
  13. package/dist/chunks/capability-swbs68d8.js +32 -0
  14. package/dist/chunks/capability-t9k5jy3d.js +96 -0
  15. package/dist/chunks/capability-xavf3m2f.js +243 -0
  16. package/dist/src/capability.d.ts +2 -0
  17. package/dist/src/capability.js +6 -0
  18. package/dist/src/client.d.ts +13 -0
  19. package/dist/src/client.js +14 -0
  20. package/dist/src/connect.d.ts +2 -0
  21. package/dist/src/connect.js +15 -0
  22. package/dist/src/history.d.ts +14 -0
  23. package/dist/src/history.js +7 -0
  24. package/dist/src/index.client.d.ts +10 -0
  25. package/dist/src/index.client.js +57 -0
  26. package/dist/src/index.d.ts +9 -0
  27. package/dist/src/index.js +58 -0
  28. package/dist/src/index.web.d.ts +8 -0
  29. package/dist/src/index.web.js +63 -0
  30. package/dist/src/journal.d.ts +15 -0
  31. package/dist/src/journal.js +9 -0
  32. package/dist/src/login.d.ts +8 -0
  33. package/dist/src/login.js +6 -0
  34. package/dist/src/paint-spans.d.ts +11 -0
  35. package/dist/src/paint-spans.js +6 -0
  36. package/dist/src/part.d.ts +4 -0
  37. package/dist/src/part.js +6 -0
  38. package/dist/src/receipts.d.ts +9 -0
  39. package/dist/src/receipts.js +16 -0
  40. package/dist/src/rows.d.ts +37 -0
  41. package/dist/src/rows.js +12 -0
  42. package/dist/src/surface.d.ts +12 -0
  43. package/dist/src/surface.js +39 -0
  44. package/dist/src/watch.d.ts +26 -0
  45. package/dist/src/watch.js +14 -0
  46. package/dist/src/workspace.d.ts +4 -0
  47. package/dist/src/workspace.js +9 -0
  48. package/package.json +59 -3
  49. package/README.md +0 -3
@@ -0,0 +1,57 @@
1
+ // src/receipts.ts
2
+ import { BatchOutcomeSchema, MoveOutcomeSchema, RunEndSchema, SdkError } from "@nyte-ai/schema";
3
+ function receiptHandle2(rows, session, id, outcome) {
4
+ const input = { session, receipt: id };
5
+ return {
6
+ id,
7
+ async status() {
8
+ const status = await rows.call("receipt.status", input);
9
+ return status.kind === "settled" ? { kind: "settled", outcome: outcome(status.outcome) } : status;
10
+ },
11
+ async settled(options) {
12
+ return outcome(await rows.wait("receipt.settled", input, options));
13
+ },
14
+ async stop() {
15
+ const stopped = await rows.call("receipt.stop", input);
16
+ return stopped.kind === "settled" ? { kind: "settled", outcome: outcome(stopped.outcome) } : stopped;
17
+ }
18
+ };
19
+ }
20
+ function sendReceipt2(rows, session, id, message) {
21
+ return {
22
+ ...receiptHandle2(rows, session, id, sendOutcome),
23
+ message,
24
+ landed: () => rows.wait("receipt.landed", { session, receipt: id })
25
+ };
26
+ }
27
+ function runHandle2(rows, session, id) {
28
+ return {
29
+ id,
30
+ stop: () => rows.call("run.stop", { session, run: id }),
31
+ settled: (options) => rows.wait("run.settled", { session, run: id }, options)
32
+ };
33
+ }
34
+ function sendOutcome(value) {
35
+ if (typeof value === "object" && value !== null && "kind" in value && value.kind === "withdrawn")
36
+ return { kind: "withdrawn" };
37
+ const end = parsed2(RunEndSchema, value);
38
+ if (end !== null)
39
+ return end;
40
+ const moved = parsed2(MoveOutcomeSchema, value);
41
+ return moved !== null && moved.kind !== "moved" ? moved : unreadable("send");
42
+ }
43
+ function moveOutcome2(value) {
44
+ return parsed2(MoveOutcomeSchema, value) ?? unreadable("move");
45
+ }
46
+ function batchOutcome2(value) {
47
+ return parsed2(BatchOutcomeSchema, value) ?? unreadable("batch");
48
+ }
49
+ function parsed2(schema, value) {
50
+ const result = schema["~standard"].validate(value);
51
+ return result instanceof Promise || result.issues !== undefined ? null : result.value;
52
+ }
53
+ function unreadable(what) {
54
+ throw new SdkError("invalid", `The host answered a ${what} outcome the sdk cannot read.`);
55
+ }
56
+
57
+ export { receiptHandle2, sendReceipt2, runHandle2, moveOutcome2, batchOutcome2, parsed2 };
@@ -0,0 +1,55 @@
1
+ // src/part.ts
2
+ import { asId, isJson, SdkError } from "@nyte-ai/schema";
3
+ import { callable, fitsActionOutput, fitsAnswer, fitsState } from "@nyte-ai/core";
4
+ function partHandle2(rows, session, id, kind) {
5
+ return {
6
+ id,
7
+ async update(next, options) {
8
+ const given = callable(next) ? await derived(rows, session, id, kind, next) : { value: next };
9
+ if (given === null)
10
+ return { kind: "missing" };
11
+ if (!isJson(given.value))
12
+ return { kind: "invalid", issues: [issue("The state is not JSON.")] };
13
+ return rows.call("part.update", { session, part: id, next: given.value, options });
14
+ },
15
+ async act(action, input) {
16
+ const named = String(action);
17
+ if (!isJson(input))
18
+ throw new SdkError("invalid", `The input of ${named} is not JSON.`);
19
+ const outcome = await rows.call("part.act", { session, part: id, action: named, input });
20
+ if (outcome.kind !== "ok")
21
+ return outcome;
22
+ const absent = outcome.output === null ? undefined : outcome.output;
23
+ if (fitsActionOutput(kind, action, absent))
24
+ return { kind: "ok", output: absent };
25
+ if (fitsActionOutput(kind, action, outcome.output))
26
+ return { kind: "ok", output: outcome.output };
27
+ return { kind: "failed", message: `This handle cannot carry ${named}'s output.` };
28
+ },
29
+ async answer(options) {
30
+ try {
31
+ const answer = await rows.wait("part.answer", { session, part: id }, options);
32
+ return answer !== null && fitsAnswer(kind, answer) ? answer : null;
33
+ } catch (error) {
34
+ if (options?.signal?.aborted === true)
35
+ return null;
36
+ throw error;
37
+ }
38
+ },
39
+ async close() {
40
+ await rows.call("part.close", { session, part: id });
41
+ }
42
+ };
43
+ }
44
+ async function derived(rows, session, id, kind, next) {
45
+ const view = await rows.call("head.view", { session, head: asId("main") });
46
+ const current = view.parts.find((part) => part.id === id) ?? view.messages.find((message) => message.kind === "part" && message.part === id);
47
+ if (current === undefined)
48
+ return null;
49
+ return fitsState(kind, current.state) ? { value: next(current.state) } : null;
50
+ }
51
+ function issue(message) {
52
+ return { severity: "error", path: [], message };
53
+ }
54
+
55
+ export { partHandle2 };
@@ -0,0 +1,134 @@
1
+ import {
2
+ createSendJournal2
3
+ } from "./capability-ke5yk1as.js";
4
+ import {
5
+ parsed2
6
+ } from "./capability-2q3s96kg.js";
7
+ import {
8
+ createClient2
9
+ } from "./capability-ec663cyt.js";
10
+
11
+ // src/connect.ts
12
+ import { IdentityChallengeSchema, isJson, SdkError } from "@nyte-ai/schema";
13
+ import { dev } from "@nyte-ai/core";
14
+ import { createTransport, decodeError, decodeOutput } from "@nyte-ai/protocol";
15
+ var paired = new Map;
16
+ var encoder = new TextEncoder;
17
+ async function connect2(options) {
18
+ const { credentials } = options;
19
+ const send = options.fetch ?? fetch;
20
+ const key = credentials.kind === "pairing" ? `${options.url}
21
+ ${credentials.code}` : null;
22
+ const kept = key === null ? undefined : paired.get(key);
23
+ const transport = createTransport({
24
+ url: options.url,
25
+ fetch: send,
26
+ headers: () => readCredentialHeaders(credentials, transport.credential() ?? kept ?? null)
27
+ });
28
+ if (options.expect !== undefined)
29
+ await verifyIdentity(options.url, send, options.expect.hostId);
30
+ const { view: first, frames } = await firstView(transport);
31
+ try {
32
+ return await openClient(options, transport, first, frames, key, kept);
33
+ } catch (error) {
34
+ await frames.return?.();
35
+ throw error;
36
+ }
37
+ }
38
+ async function openClient(options, transport, first, frames, key, kept) {
39
+ const { credentials } = options;
40
+ const answered = transport.host() ?? first.id;
41
+ if (options.expect !== undefined && (answered !== options.expect.hostId || first.id !== answered))
42
+ throw new SdkError("stale", `Host ${answered} answered instead of ${options.expect.hostId}.`, {
43
+ hostId: answered
44
+ });
45
+ const token = credentials.kind === "pairing" ? deviceToken(transport, kept) : null;
46
+ if (key !== null && token !== null)
47
+ paired.set(key, token);
48
+ const credential = token === null ? credentials : { kind: "token", token };
49
+ const journal = options.journal ?? createSendJournal2();
50
+ const journaled = await journal.load();
51
+ dev("sdk", "connected", {
52
+ hostId: first.id,
53
+ credential: credentials.kind,
54
+ journaled: journaled.length
55
+ });
56
+ return createClient2(transport, {
57
+ hostId: first.id,
58
+ principal: first.principal,
59
+ credential,
60
+ journal,
61
+ journaled,
62
+ hostFrames: frames
63
+ });
64
+ }
65
+ async function readCredentialHeaders(credentials, device) {
66
+ if (credentials.kind === "custom")
67
+ return credentials.headers();
68
+ if (credentials.kind === "token")
69
+ return { Authorization: `Bearer ${credentials.token}` };
70
+ return device === null ? { Authorization: `Pairing ${credentials.code}` } : { Authorization: `Bearer ${device}` };
71
+ }
72
+ function deviceToken(transport, kept) {
73
+ const token = transport.credential() ?? kept;
74
+ if (token === undefined)
75
+ throw new SdkError("unauthenticated", "The host returned no device credential for the pairing code.");
76
+ return token;
77
+ }
78
+ async function firstView(transport) {
79
+ const frames = transport.frames("connection.watch", {})[Symbol.asyncIterator]();
80
+ try {
81
+ const next = await frames.next();
82
+ if (next.done === true)
83
+ throw new SdkError("unavailable", "The host closed the connection before it answered.");
84
+ return {
85
+ view: decodeOutput("connection.watch", next.value),
86
+ frames: replayFirst(next.value, frames)
87
+ };
88
+ } catch (error) {
89
+ await frames.return?.();
90
+ throw error;
91
+ }
92
+ }
93
+ function replayFirst(first, rest) {
94
+ let replayed = false;
95
+ return {
96
+ async next() {
97
+ if (replayed)
98
+ return rest.next();
99
+ replayed = true;
100
+ return { done: false, value: first };
101
+ },
102
+ async return() {
103
+ await rest.return?.();
104
+ return { done: true, value: undefined };
105
+ }
106
+ };
107
+ }
108
+ async function verifyIdentity(url, send, expected) {
109
+ const nonce = crypto.randomUUID();
110
+ let response;
111
+ try {
112
+ response = await send(`${url.replace(/\/+$/, "")}/.nyte/identity?nonce=${encodeURIComponent(nonce)}`);
113
+ } catch (error) {
114
+ if (!(error instanceof TypeError))
115
+ throw error;
116
+ throw new SdkError("unavailable", error.message);
117
+ }
118
+ const body = await response.json().catch(() => null);
119
+ if (response.status === 404)
120
+ return;
121
+ if (!response.ok)
122
+ throw decodeError(isJson(body) ? body : null, response.status);
123
+ const challenge = parsed2(IdentityChallengeSchema, body);
124
+ if (challenge === null || challenge.nonce !== nonce)
125
+ throw new SdkError("invalid", "The host answered the identity challenge with something else.");
126
+ const key = await crypto.subtle.importKey("raw", Uint8Array.fromBase64(challenge.publicKey, { alphabet: "base64url" }), "Ed25519", false, ["verify"]);
127
+ const signed = await crypto.subtle.verify("Ed25519", key, Uint8Array.fromBase64(challenge.signature, { alphabet: "base64url" }), encoder.encode(`nyte/identity
128
+ ${challenge.hostId}
129
+ ${nonce}`));
130
+ if (!signed || challenge.hostId !== expected)
131
+ throw new SdkError("stale", signed ? `Host ${challenge.hostId} answered instead of ${expected}.` : `The identity signature of ${challenge.hostId} does not verify.`, { hostId: challenge.hostId });
132
+ }
133
+
134
+ export { connect2 };
@@ -0,0 +1,232 @@
1
+ // src/rows.ts
2
+ import { SdkError } from "@nyte-ai/schema";
3
+ import { closable, dev } from "@nyte-ai/core";
4
+ import { decodeOutput, encodeInput } from "@nyte-ai/protocol";
5
+ var DONE = { done: true, value: undefined };
6
+ var FIRST_DELAY_MS = 50;
7
+ var BASE_DELAY_MS = 250;
8
+ var MAX_DELAY_MS = 1e4;
9
+ function createLink2() {
10
+ let state = { kind: "connected" };
11
+ const listeners = new Set;
12
+ const controller = new AbortController;
13
+ const publish = (next) => {
14
+ state = next;
15
+ for (const listener of listeners)
16
+ listener(next);
17
+ };
18
+ return {
19
+ current: () => state,
20
+ lost() {
21
+ if (state.kind === "closed")
22
+ return;
23
+ publish(state.kind === "reconnecting" ? { ...state, attempts: state.attempts + 1 } : { kind: "reconnecting", since: Date.now(), attempts: 1 });
24
+ },
25
+ restored() {
26
+ if (state.kind === "reconnecting")
27
+ publish({ kind: "connected" });
28
+ },
29
+ close(reason) {
30
+ if (state.kind === "closed")
31
+ return;
32
+ dev("sdk", "link closed", { reason });
33
+ publish({ kind: "closed", reason });
34
+ controller.abort(new SdkError("unavailable", "The connection is closed."));
35
+ },
36
+ subscribe(listener) {
37
+ listeners.add(listener);
38
+ return () => {
39
+ listeners.delete(listener);
40
+ };
41
+ },
42
+ signal: controller.signal
43
+ };
44
+ }
45
+ function createRows2(transport, link) {
46
+ const request = async (send, signal) => {
47
+ link.signal.throwIfAborted();
48
+ try {
49
+ const answer = await send(signal === undefined ? link.signal : AbortSignal.any([link.signal, signal]));
50
+ link.restored();
51
+ return answer;
52
+ } catch (error) {
53
+ if (hostGone(transport, error))
54
+ link.close("host-gone");
55
+ link.signal.throwIfAborted();
56
+ if (linkLost(error))
57
+ link.lost();
58
+ if (error instanceof SdkError && error.code === "unauthenticated")
59
+ link.close("revoked");
60
+ throw error;
61
+ }
62
+ };
63
+ const call = async (name, input, options) => decodeOutput(name, await request((signal) => transport.invoke(name, encodeInput(name, input), { signal }), options?.signal));
64
+ return {
65
+ link,
66
+ operations: () => transport.operations(),
67
+ call,
68
+ async wait(name, input, options) {
69
+ for (let attempts = 1;; attempts += 1) {
70
+ try {
71
+ return await call(name, input, options);
72
+ } catch (error) {
73
+ if (!linkLost(error) || link.signal.aborted || options?.signal?.aborted === true)
74
+ throw error;
75
+ dev("sdk", "wait retrying", { name, attempts });
76
+ await backoff2(link, retryDelay2(attempts), options?.signal === undefined ? link.signal : AbortSignal.any([link.signal, options.signal]));
77
+ options?.signal?.throwIfAborted();
78
+ }
79
+ }
80
+ },
81
+ async upload(name, input, body) {
82
+ return decodeOutput(name, await request((signal) => transport.upload(name, encodeInput(name, input), body, { signal }), undefined));
83
+ },
84
+ bytes: (name, input, options) => request((signal) => transport.bytes(name, encodeInput(name, input), { signal }), options?.signal),
85
+ frames: (name, input) => through(transport.frames(name, encodeInput(name, input)), (frame) => decoded(name, frame), link.signal),
86
+ watch: (name, input, opened) => resumable(transport, link, name, encodeInput(name, input), opened),
87
+ output: (input) => through(transport.output(encodeInput("job.output", input)), (frame) => frame.kind === "bytes" ? { value: frame } : decoded("job.output", frame), link.signal)
88
+ };
89
+ }
90
+ function resumable(transport, link, name, input, opened) {
91
+ return closable(() => {
92
+ const stop = new AbortController;
93
+ const signal = AbortSignal.any([stop.signal, link.signal]);
94
+ let frames = opened ?? null;
95
+ let attempts = 0;
96
+ let reopened = false;
97
+ const release = () => {
98
+ frames?.return?.();
99
+ };
100
+ signal.addEventListener("abort", release, { once: true });
101
+ return {
102
+ async pull() {
103
+ for (;; ) {
104
+ if (signal.aborted)
105
+ return DONE;
106
+ const current = frames ?? transport.frames(name, input)[Symbol.asyncIterator]();
107
+ frames = current;
108
+ let next = DONE;
109
+ try {
110
+ next = await current.next();
111
+ } catch (error) {
112
+ if (hostGone(transport, error)) {
113
+ link.close("host-gone");
114
+ return DONE;
115
+ }
116
+ if (error instanceof SdkError && error.code === "unauthenticated") {
117
+ link.close("revoked");
118
+ return DONE;
119
+ }
120
+ if (!linkLost(error))
121
+ throw error;
122
+ }
123
+ if (next.done !== true) {
124
+ if (attempts > 0) {
125
+ dev("sdk", "watch resumed", { name, attempts });
126
+ link.restored();
127
+ }
128
+ attempts = 0;
129
+ const value = decoded(name, next.value);
130
+ if (value !== null) {
131
+ reopened = false;
132
+ return { done: false, value: value.value };
133
+ }
134
+ if (reopened)
135
+ throw new SdkError("invalid", `The host sends ${name} frames this client cannot read.`);
136
+ reopened = true;
137
+ frames = null;
138
+ current.return?.();
139
+ dev("sdk", "watch reopened", { name });
140
+ continue;
141
+ }
142
+ frames = null;
143
+ if (signal.aborted)
144
+ return DONE;
145
+ attempts += 1;
146
+ dev("sdk", "watch reconnecting", { name, attempts });
147
+ link.lost();
148
+ await backoff2(link, retryDelay2(attempts), signal);
149
+ }
150
+ },
151
+ close() {
152
+ signal.removeEventListener("abort", release);
153
+ stop.abort();
154
+ release();
155
+ }
156
+ };
157
+ });
158
+ }
159
+ function through(source, map, signal) {
160
+ return {
161
+ [Symbol.asyncIterator]() {
162
+ const iterator = source[Symbol.asyncIterator]();
163
+ const stop = () => {
164
+ iterator.return?.();
165
+ };
166
+ signal.addEventListener("abort", stop, { once: true });
167
+ return {
168
+ async next() {
169
+ for (;; ) {
170
+ if (signal.aborted)
171
+ return DONE;
172
+ const next = await iterator.next();
173
+ if (next.done === true) {
174
+ signal.removeEventListener("abort", stop);
175
+ return DONE;
176
+ }
177
+ const mapped = map(next.value);
178
+ if (mapped !== null)
179
+ return { done: false, value: mapped.value };
180
+ }
181
+ },
182
+ async return() {
183
+ signal.removeEventListener("abort", stop);
184
+ await iterator.return?.();
185
+ return DONE;
186
+ }
187
+ };
188
+ }
189
+ };
190
+ }
191
+ function decoded(name, data) {
192
+ try {
193
+ return { value: decodeOutput(name, data) };
194
+ } catch (error) {
195
+ if (!(error instanceof SdkError) || error.code !== "invalid")
196
+ throw error;
197
+ dev("sdk", "frame skipped", { name, message: error.message });
198
+ return null;
199
+ }
200
+ }
201
+ function hostGone(transport, error) {
202
+ const pinned = transport.host();
203
+ return error instanceof SdkError && error.code === "stale" && pinned !== null && error.hostId !== null && error.hostId !== pinned;
204
+ }
205
+ function linkLost(error) {
206
+ return error instanceof SdkError && error.code === "unavailable";
207
+ }
208
+ function retryDelay2(attempts) {
209
+ return attempts <= 1 ? FIRST_DELAY_MS : Math.min(BASE_DELAY_MS * 2 ** (attempts - 2), MAX_DELAY_MS);
210
+ }
211
+ function backoff2(link, ms, signal) {
212
+ return new Promise((resolve) => {
213
+ if (signal.aborted) {
214
+ resolve();
215
+ return;
216
+ }
217
+ const done = () => {
218
+ clearTimeout(timer);
219
+ signal.removeEventListener("abort", done);
220
+ unsubscribe();
221
+ resolve();
222
+ };
223
+ const timer = setTimeout(done, ms);
224
+ signal.addEventListener("abort", done, { once: true });
225
+ const unsubscribe = link.subscribe((state) => {
226
+ if (state.kind === "connected")
227
+ done();
228
+ });
229
+ });
230
+ }
231
+
232
+ export { createLink2, createRows2, retryDelay2, backoff2 };
@@ -0,0 +1,17 @@
1
+ // src/surface.ts
2
+ import { dev as dev2, watchStore as watchStore2 } from "@nyte-ai/core";
3
+ import {
4
+ clearSpans as clearSpans2,
5
+ enableTracing as enableTracing2,
6
+ followTrace as followTrace2,
7
+ followedTrace as followedTrace2,
8
+ readSpans as readSpans2,
9
+ SPAN_NAMES as SPAN_NAMES2,
10
+ startSpan as startSpan2,
11
+ traced as traced2,
12
+ tracing as tracing2
13
+ } from "@nyte-ai/core";
14
+ import { SdkError as SdkError2 } from "@nyte-ai/schema";
15
+ import { operationsDigest as operationsDigest2 } from "@nyte-ai/protocol";
16
+
17
+ export { dev2, watchStore2, clearSpans2, enableTracing2, followTrace2, followedTrace2, readSpans2, SPAN_NAMES2, startSpan2, traced2, tracing2, SdkError2, operationsDigest2 };
@@ -0,0 +1,203 @@
1
+ // src/watch.ts
2
+ import { closable, coalesce } from "@nyte-ai/core";
3
+ import { applyViewFrame } from "@nyte-ai/protocol";
4
+ var DONE = { done: true, value: undefined };
5
+ function createHeadViews2(rows) {
6
+ const open = new Map;
7
+ const start = (key, session, head, options) => {
8
+ const sinks = new Set;
9
+ let view = null;
10
+ const frames = rows.watch("head.watch", { session, head, options })[Symbol.asyncIterator]();
11
+ const finish = (end) => {
12
+ if (open.get(key) === hold)
13
+ open.delete(key);
14
+ for (const sink of sinks)
15
+ end(sink);
16
+ };
17
+ (async () => {
18
+ try {
19
+ for (;; ) {
20
+ const next = await frames.next();
21
+ if (next.done === true)
22
+ break;
23
+ const current = applyViewFrame(view, next.value);
24
+ view = current;
25
+ for (const sink of sinks)
26
+ sink.next(current);
27
+ }
28
+ finish((sink) => sink.end());
29
+ } catch (error) {
30
+ finish((sink) => sink.fail(error instanceof Error ? error : new Error(String(error))));
31
+ }
32
+ })();
33
+ const hold = {
34
+ view: () => view,
35
+ subscribe(sink) {
36
+ sinks.add(sink);
37
+ return () => {
38
+ sinks.delete(sink);
39
+ if (sinks.size > 0)
40
+ return;
41
+ if (open.get(key) === hold)
42
+ open.delete(key);
43
+ frames.return?.();
44
+ };
45
+ }
46
+ };
47
+ return hold;
48
+ };
49
+ return {
50
+ acquire(session, head, options) {
51
+ const key = JSON.stringify([session, head, options?.tail ?? null]);
52
+ const known = open.get(key);
53
+ if (known !== undefined)
54
+ return known;
55
+ const created = start(key, session, head, options);
56
+ open.set(key, created);
57
+ return created;
58
+ }
59
+ };
60
+ }
61
+ function watchHead2(views, journal, input) {
62
+ return coalesce(pushed2((sink) => {
63
+ const { session, head, options } = input;
64
+ const hold = views.acquire(session, head, options);
65
+ let shown = null;
66
+ const emit = () => {
67
+ const view = hold.view();
68
+ if (view === null)
69
+ return;
70
+ journal.landed(session, head, view);
71
+ const next = withUnsent2(view, journal.unsent(session, head));
72
+ if (next === shown)
73
+ return;
74
+ shown = next;
75
+ sink.next(next);
76
+ };
77
+ const leave = hold.subscribe({
78
+ next: emit,
79
+ end: () => sink.end(),
80
+ fail: (error) => sink.fail(error)
81
+ });
82
+ const unsubscribe = journal.subscribe(emit);
83
+ emit();
84
+ return () => {
85
+ unsubscribe();
86
+ leave();
87
+ };
88
+ }));
89
+ }
90
+ function watchHost2(rows, opened) {
91
+ return coalesce(pushed2((sink) => {
92
+ let view = null;
93
+ const frames = rows.watch("connection.watch", {}, opened)[Symbol.asyncIterator]();
94
+ (async () => {
95
+ try {
96
+ for (;; ) {
97
+ const next = await frames.next();
98
+ if (next.done === true)
99
+ break;
100
+ view = hostView(next.value, rows.link.current(), rows.operations());
101
+ sink.next(view);
102
+ }
103
+ sink.end();
104
+ } catch (error) {
105
+ sink.fail(error instanceof Error ? error : new Error(String(error)));
106
+ }
107
+ })();
108
+ const unsubscribe = rows.link.subscribe((link) => {
109
+ if (view !== null) {
110
+ view = { ...view, link };
111
+ sink.next(view);
112
+ }
113
+ if (link.kind === "closed")
114
+ sink.end();
115
+ });
116
+ return () => {
117
+ unsubscribe();
118
+ frames.return?.();
119
+ };
120
+ }));
121
+ }
122
+ function withUnsent2(view, unsent) {
123
+ const lead = view.leadingPrompt?.id;
124
+ const held = unsent.filter((message) => message.id !== lead);
125
+ if (held.length === 0)
126
+ return view;
127
+ const over = new Map(held.map((message) => [message.id, message]));
128
+ const known = new Set(view.messages.map((message) => message.id));
129
+ const missing = held.filter((message) => !known.has(message.id));
130
+ if (missing.length === 0 && !view.messages.some((message) => over.has(message.id)))
131
+ return view;
132
+ const messages = view.messages.map((message) => over.get(message.id) ?? message);
133
+ const at = messages.findIndex((message) => message.kind === "user" && message.status.kind === "pending");
134
+ const index = at < 0 ? messages.length : at;
135
+ return {
136
+ ...view,
137
+ messages: [...messages.slice(0, index), ...missing, ...messages.slice(index)]
138
+ };
139
+ }
140
+ function hostView(wire, link, operations) {
141
+ return {
142
+ id: wire.id,
143
+ link,
144
+ operations,
145
+ name: wire.name,
146
+ platform: wire.platform,
147
+ version: wire.version,
148
+ catalog: wire.catalog,
149
+ plugins: wire.plugins,
150
+ settings: wire.settings,
151
+ defaultWorkspace: wire.defaultWorkspace,
152
+ authority: new Set(wire.authority)
153
+ };
154
+ }
155
+ function pushed2(start) {
156
+ return closable(() => {
157
+ let slot = null;
158
+ let ending = null;
159
+ let wake = null;
160
+ const signal = () => {
161
+ const resolve = wake;
162
+ wake = null;
163
+ resolve?.();
164
+ };
165
+ const stop = start({
166
+ next(value) {
167
+ if (ending !== null)
168
+ return;
169
+ slot = { value };
170
+ signal();
171
+ },
172
+ end() {
173
+ ending ??= "ended";
174
+ signal();
175
+ },
176
+ fail(error) {
177
+ ending ??= { error };
178
+ signal();
179
+ }
180
+ });
181
+ return {
182
+ async pull() {
183
+ for (;; ) {
184
+ const held = slot;
185
+ if (held !== null) {
186
+ slot = null;
187
+ return { done: false, value: held.value };
188
+ }
189
+ if (ending === "ended")
190
+ return DONE;
191
+ if (ending !== null)
192
+ throw ending.error;
193
+ await new Promise((resolve) => {
194
+ wake = resolve;
195
+ });
196
+ }
197
+ },
198
+ close: stop
199
+ };
200
+ });
201
+ }
202
+
203
+ export { createHeadViews2, watchHead2, watchHost2, withUnsent2, pushed2 };