stitchkit 0.74.1 → 0.75.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.
- package/dist/agent-runtime/sqlite-purge.d.ts +3 -3
- package/dist/agent-runtime/sqlite-purge.d.ts.map +1 -1
- package/dist/agent-runtime/sqlite.d.ts +5 -17
- package/dist/agent-runtime/sqlite.d.ts.map +1 -1
- package/dist/agent-runtime-coding-tools.js +2 -1
- package/dist/agent-runtime-harness.js +5 -4
- package/dist/agent-runtime-sqlite-bun.d.ts +1 -1
- package/dist/agent-runtime-sqlite-bun.d.ts.map +1 -1
- package/dist/agent-runtime-sqlite-node.d.ts +1 -1
- package/dist/agent-runtime-sqlite-node.d.ts.map +1 -1
- package/dist/agent-runtime.js +8 -7
- package/dist/application/keyspace-sqlite.d.ts +31 -0
- package/dist/application/keyspace-sqlite.d.ts.map +1 -0
- package/dist/application/keyspace.d.ts +142 -0
- package/dist/application/keyspace.d.ts.map +1 -0
- package/dist/application/watch-hub.d.ts +113 -0
- package/dist/application/watch-hub.d.ts.map +1 -0
- package/dist/application.d.ts +4 -0
- package/dist/application.d.ts.map +1 -1
- package/dist/application.js +455 -0
- package/dist/cli.js +7 -6
- package/dist/contract/index.js +2 -1
- package/dist/{index-aczggrty.js → index-28jsdwt1.js} +1 -1
- package/dist/index-35z5h2ty.js +594 -0
- package/dist/{index-f6n5n7nz.js → index-4kp4fxvp.js} +4 -18
- package/dist/{index-118v0z41.js → index-52400rdd.js} +8 -3
- package/dist/{index-da1aqnhb.js → index-65kathdm.js} +6 -4
- package/dist/{index-jqtsc9mj.js → index-6wr9f6qm.js} +2 -2
- package/dist/{index-7qy2ex0m.js → index-885chjxq.js} +1 -1
- package/dist/{index-k2zczx1g.js → index-9c5dbr5q.js} +1 -1
- package/dist/{index-s2rchahr.js → index-9my2n66s.js} +4 -4
- package/dist/{index-nemjkxjp.js → index-e0a57ymn.js} +1 -1
- package/dist/{index-vbf2p6me.js → index-endzd8sj.js} +1 -1
- package/dist/{index-vj3vvpaa.js → index-hsz2mmk0.js} +1 -17
- package/dist/{index-s4c8wy8m.js → index-k68e5jz0.js} +2 -2
- package/dist/index-rxfy4cq7.js +17 -0
- package/dist/{index-58jzmnn4.js → index-v5dm4bwq.js} +1 -1
- package/dist/{index-m668wzyc.js → index-v7xjfy10.js} +1 -1
- package/dist/index-vdvmcy7r.js +22 -0
- package/dist/{index-ezmn6ac6.js → index-xgahrwdf.js} +3 -3
- package/dist/index.js +26 -611
- package/dist/internal/sqlite.d.ts +27 -0
- package/dist/internal/sqlite.d.ts.map +1 -0
- package/dist/internal/stable-digest.d.ts +24 -0
- package/dist/internal/stable-digest.d.ts.map +1 -0
- package/dist/live/events.d.ts +155 -0
- package/dist/live/events.d.ts.map +1 -0
- package/dist/live/watch-client.d.ts +66 -0
- package/dist/live/watch-client.d.ts.map +1 -0
- package/dist/live/watch-contract.d.ts +157 -0
- package/dist/live/watch-contract.d.ts.map +1 -0
- package/dist/live.d.ts +18 -0
- package/dist/live.d.ts.map +1 -0
- package/dist/live.js +284 -0
- package/dist/node.js +5 -4
- package/dist/observability/index.js +4 -3
- package/dist/primitives.js +2 -1
- package/dist/remote.js +3 -2
- package/dist/server/create.d.ts.map +1 -1
- package/dist/server/event-bus.d.ts +49 -1
- package/dist/server/event-bus.d.ts.map +1 -1
- package/dist/server/index.d.ts +2 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +224 -12
- package/dist/server/middleware/trust-fence.d.ts +116 -0
- package/dist/server/middleware/trust-fence.d.ts.map +1 -0
- package/dist/server/types.d.ts +16 -1
- package/dist/server/types.d.ts.map +1 -1
- package/dist/testing.js +3 -2
- package/dist/tool-invoker.js +6 -5
- package/dist/tools/mcp-round.d.ts.map +1 -1
- package/dist/tools.js +15 -30
- package/llms-full.txt +412 -4
- package/llms.txt +1 -0
- package/package.json +6 -2
|
@@ -0,0 +1,594 @@
|
|
|
1
|
+
// src/browser/live-state.ts
|
|
2
|
+
import { z as z2 } from "zod";
|
|
3
|
+
|
|
4
|
+
// src/application/channel.ts
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
var PositiveSafeIntegerSchema = z.number().int().positive().safe();
|
|
7
|
+
var BoundedChannelPolicySchema = z.enum(["ordered", "latest"]);
|
|
8
|
+
var BoundedChannelStateSchema = z.enum(["open", "draining", "closed", "failed"]);
|
|
9
|
+
var BoundedChannelSnapshotSchema = z.object({
|
|
10
|
+
state: BoundedChannelStateSchema,
|
|
11
|
+
policy: BoundedChannelPolicySchema,
|
|
12
|
+
queuedItems: z.number().int().nonnegative(),
|
|
13
|
+
queuedBytes: z.number().int().nonnegative(),
|
|
14
|
+
waitingReader: z.boolean(),
|
|
15
|
+
offered: z.number().int().nonnegative(),
|
|
16
|
+
delivered: z.number().int().nonnegative(),
|
|
17
|
+
queued: z.number().int().nonnegative(),
|
|
18
|
+
coalesced: z.number().int().nonnegative(),
|
|
19
|
+
refused: z.number().int().nonnegative(),
|
|
20
|
+
discarded: z.number().int().nonnegative()
|
|
21
|
+
}).strict().readonly();
|
|
22
|
+
|
|
23
|
+
class BoundedChannelReaderError extends Error {
|
|
24
|
+
constructor() {
|
|
25
|
+
super("A bounded channel permits only one pending next() call");
|
|
26
|
+
this.name = "BoundedChannelReaderError";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function createBoundedChannel(config) {
|
|
30
|
+
const policy = BoundedChannelPolicySchema.parse(config.policy);
|
|
31
|
+
const maxItems = PositiveSafeIntegerSchema.parse(config.maxItems);
|
|
32
|
+
const maxBytes = PositiveSafeIntegerSchema.parse(config.maxBytes);
|
|
33
|
+
const queue = [];
|
|
34
|
+
let state = "open";
|
|
35
|
+
let queuedBytes = 0;
|
|
36
|
+
let waiting;
|
|
37
|
+
let failure;
|
|
38
|
+
let hasFailure = false;
|
|
39
|
+
let offered = 0;
|
|
40
|
+
let delivered = 0;
|
|
41
|
+
let queued = 0;
|
|
42
|
+
let coalesced = 0;
|
|
43
|
+
let refused = 0;
|
|
44
|
+
let discarded = 0;
|
|
45
|
+
const removeAbortListener = () => {
|
|
46
|
+
config.signal?.removeEventListener("abort", abortChannel);
|
|
47
|
+
};
|
|
48
|
+
const snapshot = () => BoundedChannelSnapshotSchema.parse({
|
|
49
|
+
state,
|
|
50
|
+
policy,
|
|
51
|
+
queuedItems: queue.length,
|
|
52
|
+
queuedBytes,
|
|
53
|
+
waitingReader: waiting !== undefined,
|
|
54
|
+
offered,
|
|
55
|
+
delivered,
|
|
56
|
+
queued,
|
|
57
|
+
coalesced,
|
|
58
|
+
refused,
|
|
59
|
+
discarded
|
|
60
|
+
});
|
|
61
|
+
const clearQueue = () => {
|
|
62
|
+
discarded += queue.length;
|
|
63
|
+
queue.length = 0;
|
|
64
|
+
queuedBytes = 0;
|
|
65
|
+
};
|
|
66
|
+
const settleClosed = () => {
|
|
67
|
+
if (state !== "draining" || queue.length > 0)
|
|
68
|
+
return;
|
|
69
|
+
state = "closed";
|
|
70
|
+
removeAbortListener();
|
|
71
|
+
const reader = waiting;
|
|
72
|
+
waiting = undefined;
|
|
73
|
+
reader?.resolve({ done: true, value: undefined });
|
|
74
|
+
};
|
|
75
|
+
const close = (options = {}) => {
|
|
76
|
+
if (state === "closed" || state === "failed")
|
|
77
|
+
return snapshot();
|
|
78
|
+
const mode = options.mode ?? "drain";
|
|
79
|
+
if (mode !== "drain" && mode !== "discard") {
|
|
80
|
+
throw new TypeError("Bounded channel close mode must be drain or discard");
|
|
81
|
+
}
|
|
82
|
+
if (mode === "discard") {
|
|
83
|
+
clearQueue();
|
|
84
|
+
state = "closed";
|
|
85
|
+
removeAbortListener();
|
|
86
|
+
const reader = waiting;
|
|
87
|
+
waiting = undefined;
|
|
88
|
+
reader?.resolve({ done: true, value: undefined });
|
|
89
|
+
return snapshot();
|
|
90
|
+
}
|
|
91
|
+
state = "draining";
|
|
92
|
+
settleClosed();
|
|
93
|
+
return snapshot();
|
|
94
|
+
};
|
|
95
|
+
function abortChannel() {
|
|
96
|
+
close({ mode: "discard" });
|
|
97
|
+
}
|
|
98
|
+
const channel = {
|
|
99
|
+
[Symbol.asyncIterator]() {
|
|
100
|
+
return channel;
|
|
101
|
+
},
|
|
102
|
+
offer(value) {
|
|
103
|
+
offered += 1;
|
|
104
|
+
if (state !== "open") {
|
|
105
|
+
refused += 1;
|
|
106
|
+
return { outcome: "refused", reason: "not-open" };
|
|
107
|
+
}
|
|
108
|
+
const bytes = config.sizeOf(value);
|
|
109
|
+
if (!Number.isSafeInteger(bytes) || bytes < 0) {
|
|
110
|
+
throw new TypeError("Bounded channel sizeOf() must return a non-negative safe integer");
|
|
111
|
+
}
|
|
112
|
+
if (bytes > maxBytes) {
|
|
113
|
+
refused += 1;
|
|
114
|
+
return { outcome: "refused", reason: "item-too-large" };
|
|
115
|
+
}
|
|
116
|
+
const reader = waiting;
|
|
117
|
+
if (reader) {
|
|
118
|
+
waiting = undefined;
|
|
119
|
+
delivered += 1;
|
|
120
|
+
reader.resolve({ done: false, value });
|
|
121
|
+
return { outcome: "delivered" };
|
|
122
|
+
}
|
|
123
|
+
if (policy === "latest" && queue.length > 0) {
|
|
124
|
+
const previous = queue[0];
|
|
125
|
+
if (!previous)
|
|
126
|
+
throw new Error("Bounded latest channel lost its pending value");
|
|
127
|
+
queue[0] = { value, bytes };
|
|
128
|
+
queuedBytes += bytes - previous.bytes;
|
|
129
|
+
coalesced += 1;
|
|
130
|
+
return { outcome: "coalesced", replaced: 1 };
|
|
131
|
+
}
|
|
132
|
+
if (queue.length >= maxItems) {
|
|
133
|
+
refused += 1;
|
|
134
|
+
return { outcome: "refused", reason: "item-capacity" };
|
|
135
|
+
}
|
|
136
|
+
if (queuedBytes + bytes > maxBytes) {
|
|
137
|
+
refused += 1;
|
|
138
|
+
return { outcome: "refused", reason: "byte-capacity" };
|
|
139
|
+
}
|
|
140
|
+
queue.push({ value, bytes });
|
|
141
|
+
queuedBytes += bytes;
|
|
142
|
+
queued += 1;
|
|
143
|
+
return { outcome: "queued" };
|
|
144
|
+
},
|
|
145
|
+
next() {
|
|
146
|
+
if (hasFailure)
|
|
147
|
+
return Promise.reject(failure);
|
|
148
|
+
const next = queue.shift();
|
|
149
|
+
if (next) {
|
|
150
|
+
queuedBytes -= next.bytes;
|
|
151
|
+
delivered += 1;
|
|
152
|
+
settleClosed();
|
|
153
|
+
return Promise.resolve({ done: false, value: next.value });
|
|
154
|
+
}
|
|
155
|
+
if (state === "closed" || state === "draining") {
|
|
156
|
+
settleClosed();
|
|
157
|
+
return Promise.resolve({ done: true, value: undefined });
|
|
158
|
+
}
|
|
159
|
+
if (waiting)
|
|
160
|
+
return Promise.reject(new BoundedChannelReaderError);
|
|
161
|
+
return new Promise((resolve, reject) => {
|
|
162
|
+
waiting = { resolve, reject };
|
|
163
|
+
});
|
|
164
|
+
},
|
|
165
|
+
return() {
|
|
166
|
+
close({ mode: "discard" });
|
|
167
|
+
return Promise.resolve({ done: true, value: undefined });
|
|
168
|
+
},
|
|
169
|
+
throw(error) {
|
|
170
|
+
channel.fail(error);
|
|
171
|
+
return Promise.reject(error);
|
|
172
|
+
},
|
|
173
|
+
close,
|
|
174
|
+
fail(error) {
|
|
175
|
+
if (state === "closed" || state === "failed")
|
|
176
|
+
return snapshot();
|
|
177
|
+
failure = error;
|
|
178
|
+
hasFailure = true;
|
|
179
|
+
state = "failed";
|
|
180
|
+
clearQueue();
|
|
181
|
+
removeAbortListener();
|
|
182
|
+
const reader = waiting;
|
|
183
|
+
waiting = undefined;
|
|
184
|
+
reader?.reject(error);
|
|
185
|
+
return snapshot();
|
|
186
|
+
},
|
|
187
|
+
getSnapshot: snapshot
|
|
188
|
+
};
|
|
189
|
+
if (config.signal?.aborted)
|
|
190
|
+
abortChannel();
|
|
191
|
+
else
|
|
192
|
+
config.signal?.addEventListener("abort", abortChannel, { once: true });
|
|
193
|
+
return channel;
|
|
194
|
+
}
|
|
195
|
+
var CreditWindowSnapshotSchema = z.object({
|
|
196
|
+
state: z.enum(["open", "closed"]),
|
|
197
|
+
capacityBytes: PositiveSafeIntegerSchema,
|
|
198
|
+
availableBytes: z.number().int().nonnegative(),
|
|
199
|
+
leasedBytes: z.number().int().nonnegative(),
|
|
200
|
+
acquired: z.number().int().nonnegative(),
|
|
201
|
+
refused: z.number().int().nonnegative(),
|
|
202
|
+
replenishedBytes: z.number().int().nonnegative(),
|
|
203
|
+
waiting: z.number().int().nonnegative()
|
|
204
|
+
}).strict().readonly();
|
|
205
|
+
|
|
206
|
+
// src/browser/live-state.ts
|
|
207
|
+
var PositiveSafeIntegerSchema2 = z2.number().int().positive().safe();
|
|
208
|
+
var LiveStatePhaseSchema = z2.enum([
|
|
209
|
+
"idle",
|
|
210
|
+
"opening",
|
|
211
|
+
"live",
|
|
212
|
+
"resync-required",
|
|
213
|
+
"unavailable",
|
|
214
|
+
"closed"
|
|
215
|
+
]);
|
|
216
|
+
var LiveStateStopReasonSchema = z2.enum([
|
|
217
|
+
"gap",
|
|
218
|
+
"buffer-overflow",
|
|
219
|
+
"source-unavailable",
|
|
220
|
+
"source-error",
|
|
221
|
+
"controller-error",
|
|
222
|
+
"controller-capacity"
|
|
223
|
+
]);
|
|
224
|
+
var LiveStateControllerStatusSchema = z2.object({
|
|
225
|
+
phase: LiveStatePhaseSchema,
|
|
226
|
+
generation: z2.number().int().nonnegative(),
|
|
227
|
+
hasValue: z2.boolean(),
|
|
228
|
+
bufferedEvents: z2.number().int().nonnegative(),
|
|
229
|
+
bufferedBytes: z2.number().int().nonnegative(),
|
|
230
|
+
receivedEvents: z2.number().int().nonnegative(),
|
|
231
|
+
appliedEvents: z2.number().int().nonnegative(),
|
|
232
|
+
duplicateEvents: z2.number().int().nonnegative(),
|
|
233
|
+
gapEvents: z2.number().int().nonnegative(),
|
|
234
|
+
refusedEvents: z2.number().int().nonnegative(),
|
|
235
|
+
reason: LiveStateStopReasonSchema.optional()
|
|
236
|
+
}).strict().readonly();
|
|
237
|
+
function createLiveStateController(config) {
|
|
238
|
+
const maxBufferedEvents = PositiveSafeIntegerSchema2.parse(config.maxBufferedEvents);
|
|
239
|
+
const maxBufferedBytes = PositiveSafeIntegerSchema2.parse(config.maxBufferedBytes);
|
|
240
|
+
const subscribers = new Set;
|
|
241
|
+
let phase = "idle";
|
|
242
|
+
let reason;
|
|
243
|
+
let generation = 0;
|
|
244
|
+
let stored = { hasValue: false };
|
|
245
|
+
let receivedEvents = 0;
|
|
246
|
+
let appliedEvents = 0;
|
|
247
|
+
let duplicateEvents = 0;
|
|
248
|
+
let gapEvents = 0;
|
|
249
|
+
let refusedEvents = 0;
|
|
250
|
+
let current;
|
|
251
|
+
let opening;
|
|
252
|
+
let unsettledSourceOpens = 0;
|
|
253
|
+
let unsettledSourceCloses = 0;
|
|
254
|
+
let publishDepth = 0;
|
|
255
|
+
let controllerErrorObserverEnabled = true;
|
|
256
|
+
let subscriberErrorObserverEnabled = true;
|
|
257
|
+
const isPromiseLike = (value) => (typeof value === "object" || typeof value === "function") && value !== null && ("then" in value) && typeof value.then === "function";
|
|
258
|
+
const status = () => {
|
|
259
|
+
const buffered = current?.buffer.getSnapshot();
|
|
260
|
+
return LiveStateControllerStatusSchema.parse({
|
|
261
|
+
phase,
|
|
262
|
+
generation,
|
|
263
|
+
hasValue: stored.hasValue,
|
|
264
|
+
bufferedEvents: buffered?.queuedItems ?? 0,
|
|
265
|
+
bufferedBytes: buffered?.queuedBytes ?? 0,
|
|
266
|
+
receivedEvents,
|
|
267
|
+
appliedEvents,
|
|
268
|
+
duplicateEvents,
|
|
269
|
+
gapEvents,
|
|
270
|
+
refusedEvents,
|
|
271
|
+
...reason && { reason }
|
|
272
|
+
});
|
|
273
|
+
};
|
|
274
|
+
const buildSnapshot = () => {
|
|
275
|
+
const metadata = status();
|
|
276
|
+
return stored.hasValue ? Object.freeze({ ...metadata, value: stored.value }) : metadata;
|
|
277
|
+
};
|
|
278
|
+
let published = buildSnapshot();
|
|
279
|
+
const reportControllerError = (error, at, atGeneration) => {
|
|
280
|
+
if (!config.onControllerError || !controllerErrorObserverEnabled)
|
|
281
|
+
return;
|
|
282
|
+
try {
|
|
283
|
+
const returned = config.onControllerError({
|
|
284
|
+
error,
|
|
285
|
+
generation: atGeneration,
|
|
286
|
+
phase: at
|
|
287
|
+
});
|
|
288
|
+
if (isPromiseLike(returned)) {
|
|
289
|
+
controllerErrorObserverEnabled = false;
|
|
290
|
+
Promise.resolve(returned).catch(() => {
|
|
291
|
+
return;
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
} catch {}
|
|
295
|
+
};
|
|
296
|
+
const reportSubscriberError = (error, snapshot) => {
|
|
297
|
+
if (!config.onSubscriberError || !subscriberErrorObserverEnabled)
|
|
298
|
+
return;
|
|
299
|
+
try {
|
|
300
|
+
const returned = config.onSubscriberError({ error, snapshot });
|
|
301
|
+
if (isPromiseLike(returned)) {
|
|
302
|
+
subscriberErrorObserverEnabled = false;
|
|
303
|
+
Promise.resolve(returned).catch(() => {
|
|
304
|
+
return;
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
} catch {}
|
|
308
|
+
};
|
|
309
|
+
const publish = () => {
|
|
310
|
+
const snapshot = buildSnapshot();
|
|
311
|
+
published = snapshot;
|
|
312
|
+
publishDepth += 1;
|
|
313
|
+
try {
|
|
314
|
+
for (const listener of subscribers) {
|
|
315
|
+
try {
|
|
316
|
+
const returned = listener(snapshot);
|
|
317
|
+
if (isPromiseLike(returned)) {
|
|
318
|
+
subscribers.delete(listener);
|
|
319
|
+
reportSubscriberError(new TypeError("Live-state subscribers must settle synchronously"), snapshot);
|
|
320
|
+
Promise.resolve(returned).catch(() => {
|
|
321
|
+
return;
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
} catch (error) {
|
|
325
|
+
reportSubscriberError(error, snapshot);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
} finally {
|
|
329
|
+
publishDepth -= 1;
|
|
330
|
+
}
|
|
331
|
+
return published;
|
|
332
|
+
};
|
|
333
|
+
const publishAvailableCapacity = () => {
|
|
334
|
+
if (phase !== "unavailable" || reason !== "controller-capacity" || unsettledSourceOpens + unsettledSourceCloses >= 2)
|
|
335
|
+
return;
|
|
336
|
+
phase = "resync-required";
|
|
337
|
+
publish();
|
|
338
|
+
};
|
|
339
|
+
const closeSource = (active) => {
|
|
340
|
+
if (active.closePromise)
|
|
341
|
+
return active.closePromise;
|
|
342
|
+
if (!active.closeSource)
|
|
343
|
+
return Promise.resolve();
|
|
344
|
+
const at = phase;
|
|
345
|
+
unsettledSourceCloses += 1;
|
|
346
|
+
active.closePromise = Promise.resolve().then(() => active.closeSource?.()).then(() => {
|
|
347
|
+
return;
|
|
348
|
+
}).catch((error) => {
|
|
349
|
+
reportControllerError(error, at, active.id);
|
|
350
|
+
}).finally(() => {
|
|
351
|
+
unsettledSourceCloses -= 1;
|
|
352
|
+
publishAvailableCapacity();
|
|
353
|
+
});
|
|
354
|
+
return active.closePromise;
|
|
355
|
+
};
|
|
356
|
+
const retire = (active) => {
|
|
357
|
+
if (!active.retired) {
|
|
358
|
+
active.retired = true;
|
|
359
|
+
active.settleRetired();
|
|
360
|
+
active.abort.abort();
|
|
361
|
+
active.buffer.close({ mode: "discard" });
|
|
362
|
+
}
|
|
363
|
+
return closeSource(active);
|
|
364
|
+
};
|
|
365
|
+
const stopGeneration = (active, nextPhase, nextReason) => {
|
|
366
|
+
if (current !== active || active.retired || phase === "closed")
|
|
367
|
+
return;
|
|
368
|
+
phase = nextPhase;
|
|
369
|
+
reason = nextReason;
|
|
370
|
+
retire(active);
|
|
371
|
+
publish();
|
|
372
|
+
};
|
|
373
|
+
const apply = (active, event) => {
|
|
374
|
+
if (current !== active || active.retired || !stored.hasValue || phase !== "opening" && phase !== "live")
|
|
375
|
+
return;
|
|
376
|
+
let decision;
|
|
377
|
+
try {
|
|
378
|
+
decision = config.applyEvent(stored.value, event);
|
|
379
|
+
} catch (error) {
|
|
380
|
+
reportControllerError(error, phase, active.id);
|
|
381
|
+
stopGeneration(active, "unavailable", "controller-error");
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
if (current !== active || active.retired || phase !== "opening" && phase !== "live")
|
|
385
|
+
return;
|
|
386
|
+
if (!decision || !["applied", "duplicate", "gap"].includes(decision.outcome) || decision.outcome === "applied" && !("state" in decision)) {
|
|
387
|
+
reportControllerError(new Error("applyEvent returned an invalid decision"), phase, active.id);
|
|
388
|
+
stopGeneration(active, "unavailable", "controller-error");
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
if (decision.outcome === "duplicate") {
|
|
392
|
+
duplicateEvents += 1;
|
|
393
|
+
publish();
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
if (decision.outcome === "gap") {
|
|
397
|
+
gapEvents += 1;
|
|
398
|
+
stopGeneration(active, "resync-required", "gap");
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
stored = { hasValue: true, value: decision.state };
|
|
402
|
+
appliedEvents += 1;
|
|
403
|
+
publish();
|
|
404
|
+
};
|
|
405
|
+
const onEvent = (active, event) => {
|
|
406
|
+
if (current !== active || active.retired || phase === "closed")
|
|
407
|
+
return;
|
|
408
|
+
receivedEvents += 1;
|
|
409
|
+
if (phase === "opening") {
|
|
410
|
+
try {
|
|
411
|
+
const offered = active.buffer.offer(event);
|
|
412
|
+
if (offered.outcome === "refused") {
|
|
413
|
+
refusedEvents += 1;
|
|
414
|
+
stopGeneration(active, "resync-required", "buffer-overflow");
|
|
415
|
+
} else {
|
|
416
|
+
publish();
|
|
417
|
+
}
|
|
418
|
+
} catch (error) {
|
|
419
|
+
refusedEvents += 1;
|
|
420
|
+
reportControllerError(error, phase, active.id);
|
|
421
|
+
stopGeneration(active, "unavailable", "controller-error");
|
|
422
|
+
}
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
if (phase === "live")
|
|
426
|
+
apply(active, event);
|
|
427
|
+
};
|
|
428
|
+
const openGeneration = async (active) => {
|
|
429
|
+
try {
|
|
430
|
+
if (active.retired || current !== active || phase === "closed")
|
|
431
|
+
return published;
|
|
432
|
+
unsettledSourceOpens += 1;
|
|
433
|
+
let sourceOpening;
|
|
434
|
+
try {
|
|
435
|
+
sourceOpening = config.source.open({
|
|
436
|
+
signal: active.abort.signal,
|
|
437
|
+
onEvent: (event) => onEvent(active, event),
|
|
438
|
+
onUnavailable: () => stopGeneration(active, "unavailable", "source-unavailable")
|
|
439
|
+
});
|
|
440
|
+
} catch (error) {
|
|
441
|
+
unsettledSourceOpens -= 1;
|
|
442
|
+
publishAvailableCapacity();
|
|
443
|
+
throw error;
|
|
444
|
+
}
|
|
445
|
+
sourceOpening = sourceOpening.then((opened2) => {
|
|
446
|
+
active.closeSource = () => opened2.close();
|
|
447
|
+
unsettledSourceOpens -= 1;
|
|
448
|
+
if (active.retired || current !== active || phase === "closed") {
|
|
449
|
+
closeSource(active);
|
|
450
|
+
}
|
|
451
|
+
publishAvailableCapacity();
|
|
452
|
+
return opened2;
|
|
453
|
+
}, (error) => {
|
|
454
|
+
unsettledSourceOpens -= 1;
|
|
455
|
+
publishAvailableCapacity();
|
|
456
|
+
throw error;
|
|
457
|
+
});
|
|
458
|
+
const outcome = await Promise.race([
|
|
459
|
+
sourceOpening.then((opened2) => ({ outcome: "opened", opened: opened2 })),
|
|
460
|
+
active.retiredPromise.then(() => ({ outcome: "retired" }))
|
|
461
|
+
]);
|
|
462
|
+
if (outcome.outcome === "retired") {
|
|
463
|
+
return published;
|
|
464
|
+
}
|
|
465
|
+
const { opened } = outcome;
|
|
466
|
+
if (current !== active || active.retired || phase !== "opening") {
|
|
467
|
+
closeSource(active);
|
|
468
|
+
return published;
|
|
469
|
+
}
|
|
470
|
+
stored = { hasValue: true, value: opened.snapshot };
|
|
471
|
+
publish();
|
|
472
|
+
while (current === active && !active.retired && phase === "opening") {
|
|
473
|
+
if (active.buffer.getSnapshot().queuedItems === 0) {
|
|
474
|
+
active.buffer.close({ mode: "discard" });
|
|
475
|
+
phase = "live";
|
|
476
|
+
reason = undefined;
|
|
477
|
+
return publish();
|
|
478
|
+
}
|
|
479
|
+
const next = await active.buffer.next();
|
|
480
|
+
if (next.done)
|
|
481
|
+
break;
|
|
482
|
+
apply(active, next.value);
|
|
483
|
+
}
|
|
484
|
+
if (current && current !== active && opening)
|
|
485
|
+
return opening;
|
|
486
|
+
return published;
|
|
487
|
+
} catch (error) {
|
|
488
|
+
if (current === active && !active.retired && phase !== "closed") {
|
|
489
|
+
reportControllerError(error, phase, active.id);
|
|
490
|
+
stopGeneration(active, "unavailable", "source-error");
|
|
491
|
+
}
|
|
492
|
+
publishAvailableCapacity();
|
|
493
|
+
return published;
|
|
494
|
+
}
|
|
495
|
+
};
|
|
496
|
+
const begin = (startedByResync) => {
|
|
497
|
+
generation += 1;
|
|
498
|
+
phase = "opening";
|
|
499
|
+
reason = undefined;
|
|
500
|
+
let settleRetired = () => {
|
|
501
|
+
return;
|
|
502
|
+
};
|
|
503
|
+
const retiredPromise = new Promise((resolve) => {
|
|
504
|
+
settleRetired = resolve;
|
|
505
|
+
});
|
|
506
|
+
const active = {
|
|
507
|
+
id: generation,
|
|
508
|
+
startedByResync,
|
|
509
|
+
abort: new AbortController,
|
|
510
|
+
buffer: createBoundedChannel({
|
|
511
|
+
policy: "ordered",
|
|
512
|
+
maxItems: maxBufferedEvents,
|
|
513
|
+
maxBytes: maxBufferedBytes,
|
|
514
|
+
sizeOf: config.sizeOfEvent
|
|
515
|
+
}),
|
|
516
|
+
retiredPromise,
|
|
517
|
+
settleRetired,
|
|
518
|
+
retired: false,
|
|
519
|
+
sourceOpenStarted: false
|
|
520
|
+
};
|
|
521
|
+
current = active;
|
|
522
|
+
publish();
|
|
523
|
+
if (current !== active || active.retired || phase !== "opening") {
|
|
524
|
+
return opening ?? Promise.resolve(published);
|
|
525
|
+
}
|
|
526
|
+
active.sourceOpenStarted = true;
|
|
527
|
+
const operation = openGeneration(active);
|
|
528
|
+
opening = operation;
|
|
529
|
+
operation.finally(() => {
|
|
530
|
+
if (opening === operation)
|
|
531
|
+
opening = undefined;
|
|
532
|
+
});
|
|
533
|
+
return operation;
|
|
534
|
+
};
|
|
535
|
+
const close = () => {
|
|
536
|
+
if (phase === "closed")
|
|
537
|
+
return Promise.resolve(published);
|
|
538
|
+
const active = current;
|
|
539
|
+
current = undefined;
|
|
540
|
+
phase = "closed";
|
|
541
|
+
reason = undefined;
|
|
542
|
+
const snapshot = publish();
|
|
543
|
+
if (active)
|
|
544
|
+
retire(active);
|
|
545
|
+
config.signal?.removeEventListener("abort", abortFromConfig);
|
|
546
|
+
subscribers.clear();
|
|
547
|
+
return Promise.resolve(snapshot);
|
|
548
|
+
};
|
|
549
|
+
function abortFromConfig() {
|
|
550
|
+
close();
|
|
551
|
+
}
|
|
552
|
+
if (config.signal?.aborted) {
|
|
553
|
+
phase = "closed";
|
|
554
|
+
published = buildSnapshot();
|
|
555
|
+
} else {
|
|
556
|
+
config.signal?.addEventListener("abort", abortFromConfig, { once: true });
|
|
557
|
+
}
|
|
558
|
+
return {
|
|
559
|
+
start() {
|
|
560
|
+
if (phase === "idle")
|
|
561
|
+
return begin(false);
|
|
562
|
+
return opening ?? Promise.resolve(published);
|
|
563
|
+
},
|
|
564
|
+
resync() {
|
|
565
|
+
if (phase === "closed")
|
|
566
|
+
return Promise.resolve(published);
|
|
567
|
+
const active = current;
|
|
568
|
+
if (active?.startedByResync && !active.sourceOpenStarted && phase === "opening" && publishDepth > 0) {
|
|
569
|
+
return opening ?? Promise.resolve(published);
|
|
570
|
+
}
|
|
571
|
+
if (active) {
|
|
572
|
+
retire(active);
|
|
573
|
+
if (unsettledSourceOpens + unsettledSourceCloses >= 2) {
|
|
574
|
+
phase = "unavailable";
|
|
575
|
+
reason = "controller-capacity";
|
|
576
|
+
return Promise.resolve(publish());
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
return begin(true);
|
|
580
|
+
},
|
|
581
|
+
getSnapshot: () => published,
|
|
582
|
+
subscribe(listener) {
|
|
583
|
+
if (phase === "closed")
|
|
584
|
+
return () => {
|
|
585
|
+
return;
|
|
586
|
+
};
|
|
587
|
+
subscribers.add(listener);
|
|
588
|
+
return () => subscribers.delete(listener);
|
|
589
|
+
},
|
|
590
|
+
close
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
export { LiveStatePhaseSchema, LiveStateStopReasonSchema, LiveStateControllerStatusSchema, createLiveStateController };
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
base64UrlToBytes,
|
|
3
|
+
bytesToBase64Url
|
|
4
|
+
} from "./index-rxfy4cq7.js";
|
|
1
5
|
import {
|
|
2
6
|
mapObjectTypeBoundary
|
|
3
7
|
} from "./index-ksp6e2ye.js";
|
|
@@ -431,24 +435,6 @@ var ManagedFileRefSchema = z3.object({
|
|
|
431
435
|
});
|
|
432
436
|
// src/contract/pagination.ts
|
|
433
437
|
import { z as z4 } from "zod";
|
|
434
|
-
|
|
435
|
-
// src/internal/base64url.ts
|
|
436
|
-
function bytesToBase64Url(bytes) {
|
|
437
|
-
let binary = "";
|
|
438
|
-
for (const byte of bytes)
|
|
439
|
-
binary += String.fromCharCode(byte);
|
|
440
|
-
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
441
|
-
}
|
|
442
|
-
function base64UrlToBytes(segment) {
|
|
443
|
-
if (!/^[A-Za-z0-9_-]*$/.test(segment) || segment.length % 4 === 1) {
|
|
444
|
-
throw new Error("invalid base64url segment");
|
|
445
|
-
}
|
|
446
|
-
const b64 = segment.replace(/-/g, "+").replace(/_/g, "/");
|
|
447
|
-
const padded = b64.padEnd(Math.ceil(b64.length / 4) * 4, "=");
|
|
448
|
-
return Uint8Array.from(atob(padded), (c) => c.charCodeAt(0));
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
// src/contract/pagination.ts
|
|
452
438
|
function paginatedSchema(itemSchema) {
|
|
453
439
|
return z4.object({
|
|
454
440
|
items: z4.array(itemSchema),
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
defaultSignalSource,
|
|
8
8
|
guardSignalCallback,
|
|
9
9
|
reportSignalError
|
|
10
|
-
} from "./index-
|
|
10
|
+
} from "./index-endzd8sj.js";
|
|
11
11
|
import {
|
|
12
12
|
extractIp,
|
|
13
13
|
getClientInfo,
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
runWithRequestContext,
|
|
19
19
|
setRequestEndpoint,
|
|
20
20
|
setRequestError
|
|
21
|
-
} from "./index-
|
|
21
|
+
} from "./index-e0a57ymn.js";
|
|
22
22
|
import {
|
|
23
23
|
ShutdownOptionsSchema,
|
|
24
24
|
isStreamCancellation,
|
|
@@ -39,7 +39,7 @@ import {
|
|
|
39
39
|
joinRoutePath,
|
|
40
40
|
parseTrailingWildcard,
|
|
41
41
|
safeJsonParse
|
|
42
|
-
} from "./index-
|
|
42
|
+
} from "./index-hsz2mmk0.js";
|
|
43
43
|
import {
|
|
44
44
|
AppError,
|
|
45
45
|
badRequest
|
|
@@ -1563,6 +1563,11 @@ function createHandler(config) {
|
|
|
1563
1563
|
return fallback ?? resolveTraceId(req);
|
|
1564
1564
|
}
|
|
1565
1565
|
};
|
|
1566
|
+
for (const group of config.groups ?? []) {
|
|
1567
|
+
if (group.hooks && "onRequest" in group.hooks) {
|
|
1568
|
+
throw new Error("[stitchkit] a route group cannot declare `onRequest` — it runs before routing, so the group it belongs to is not known yet and the hook would never be dispatched. Refuse before dispatch with the server-level `hooks.onRequest`, or gate the group with its `authorize`, which runs once the endpoint is known.");
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1566
1571
|
const routeMap = buildRouteMap(normalizeGroups(config));
|
|
1567
1572
|
validateRoutes(routeMap);
|
|
1568
1573
|
validateRawRoutes(config.rawRoutes);
|
|
@@ -2,13 +2,15 @@ import {
|
|
|
2
2
|
contextContributionDelta,
|
|
3
3
|
mergeContextContribution,
|
|
4
4
|
prepareContextContribution
|
|
5
|
-
} from "./index-
|
|
5
|
+
} from "./index-endzd8sj.js";
|
|
6
6
|
import {
|
|
7
|
-
base64UrlToBytes,
|
|
8
|
-
bytesToBase64Url,
|
|
9
7
|
isUnsafeKey,
|
|
10
8
|
safeJsonParse
|
|
11
|
-
} from "./index-
|
|
9
|
+
} from "./index-hsz2mmk0.js";
|
|
10
|
+
import {
|
|
11
|
+
base64UrlToBytes,
|
|
12
|
+
bytesToBase64Url
|
|
13
|
+
} from "./index-rxfy4cq7.js";
|
|
12
14
|
import {
|
|
13
15
|
forbidden,
|
|
14
16
|
unauthorized
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
collectTools,
|
|
3
3
|
createToolRunner
|
|
4
|
-
} from "./index-
|
|
4
|
+
} from "./index-885chjxq.js";
|
|
5
5
|
import {
|
|
6
6
|
toolErrorFromResult
|
|
7
|
-
} from "./index-
|
|
7
|
+
} from "./index-k68e5jz0.js";
|
|
8
8
|
import {
|
|
9
9
|
assertUniqueToolName
|
|
10
10
|
} from "./index-22by16v6.js";
|