stitchkit 0.60.0 → 0.61.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/coordinator.d.ts +10 -0
- package/dist/agent-runtime/coordinator.d.ts.map +1 -1
- package/dist/agent-runtime/run-execution.d.ts.map +1 -1
- package/dist/agent-runtime/runtime.d.ts.map +1 -1
- package/dist/agent-runtime/schemas.d.ts.map +1 -1
- package/dist/agent-runtime/store-driver.d.ts.map +1 -1
- package/dist/agent-runtime.js +97 -12
- package/dist/application/grammy.d.ts +12 -6
- package/dist/application/grammy.d.ts.map +1 -1
- package/dist/application/kernel.d.ts +35 -2
- package/dist/application/kernel.d.ts.map +1 -1
- package/dist/application/server-resource.d.ts.map +1 -1
- package/dist/application-grammy.js +5 -3
- package/dist/application.d.ts +1 -1
- package/dist/application.d.ts.map +1 -1
- package/dist/application.js +12 -6
- package/dist/browser/socket-io.d.ts +47 -0
- package/dist/browser/socket-io.d.ts.map +1 -1
- package/dist/browser/stream.d.ts +23 -0
- package/dist/browser/stream.d.ts.map +1 -1
- package/dist/cli.js +5 -4
- package/dist/contract/errors.d.ts +1 -0
- package/dist/contract/errors.d.ts.map +1 -1
- package/dist/contract/index.js +1 -1
- package/dist/{index-8pc5s3pz.js → index-1pgeyyee.js} +3 -3
- package/dist/{index-9h1vba8n.js → index-413xk7ga.js} +2 -1
- package/dist/{index-8es1hrv4.js → index-6taryy00.js} +1 -1
- package/dist/{index-1zencgcb.js → index-eabpd4tb.js} +61 -10
- package/dist/{index-3w74r08v.js → index-mfw1pec7.js} +7 -5
- package/dist/{index-f7521k0x.js → index-s1tywej8.js} +95 -24
- package/dist/{index-6fbp58g8.js → index-tvwcrx2d.js} +4 -2
- package/dist/{index-v6e8hnkx.js → index-wd8g8z6e.js} +1 -84
- package/dist/{index-2akar0rq.js → index-wrhf06ak.js} +7 -5
- package/dist/index-xxye8j3k.js +85 -0
- package/dist/{index-3hn10n4c.js → index-y91zd0ch.js} +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +213 -17
- package/dist/internal/optional-peer.d.ts +14 -0
- package/dist/internal/optional-peer.d.ts.map +1 -0
- package/dist/node.js +8 -7
- package/dist/observability/index.js +4 -3
- package/dist/realtime/contract.d.ts +7 -1
- package/dist/realtime/contract.d.ts.map +1 -1
- package/dist/realtime/index.d.ts +2 -1
- package/dist/realtime/index.d.ts.map +1 -1
- package/dist/realtime/rejected-frame.d.ts +86 -0
- package/dist/realtime/rejected-frame.d.ts.map +1 -0
- package/dist/realtime/request.d.ts +24 -0
- package/dist/realtime/request.d.ts.map +1 -1
- package/dist/realtime/socket.d.ts.map +1 -1
- package/dist/remote.js +4 -2
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +209 -15
- package/dist/server/socket-io.d.ts.map +1 -1
- package/dist/server/stream.d.ts.map +1 -1
- package/dist/server/streaming-route.d.ts +130 -0
- package/dist/server/streaming-route.d.ts.map +1 -0
- package/dist/testing.js +2 -1
- package/dist/tools.js +11 -9
- package/llms-full.txt +422 -79
- package/package.json +5 -5
package/dist/browser/stream.d.ts
CHANGED
|
@@ -1,2 +1,25 @@
|
|
|
1
1
|
export { type ParseSSEOptions, parseSSE } from '../server/stream';
|
|
2
|
+
/** Options for `parseNDJSON`. */
|
|
3
|
+
export interface ParseNDJSONOptions {
|
|
4
|
+
/** Called for a line that is not valid JSON — the alternative to throwing. */
|
|
5
|
+
onParseError?: (raw: string, error: Error) => void;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Parse a newline-delimited JSON response body — the client half of
|
|
9
|
+
* `ndjsonRoute`.
|
|
10
|
+
*
|
|
11
|
+
* **Blank lines are skipped**, and that is the contract rather than a
|
|
12
|
+
* convenience. A long-lived NDJSON stream has to send something while it is
|
|
13
|
+
* idle or intermediaries drop it, and the natural pulse for this framing is an
|
|
14
|
+
* empty line. Writing the skip down here is what stops it being a verbal
|
|
15
|
+
* agreement between the two halves of one project: the server's keep-alive and
|
|
16
|
+
* the reader's rule are one decision with two implementations.
|
|
17
|
+
*
|
|
18
|
+
* **To end a subscription, abort the request.** Leaving the loop cancels the
|
|
19
|
+
* body — which is right, and this does it — but a client-side cancel is not a
|
|
20
|
+
* reliable way to reach the server: measured against Bun today, the source on
|
|
21
|
+
* the other end stayed alive for seconds afterwards. An aborted request reaches
|
|
22
|
+
* the route's `context.signal` at once.
|
|
23
|
+
*/
|
|
24
|
+
export declare function parseNDJSON<T>(response: Response, options?: ParseNDJSONOptions): AsyncGenerator<T>;
|
|
2
25
|
//# sourceMappingURL=stream.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../src/browser/stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,eAAe,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC"}
|
|
1
|
+
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../src/browser/stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,eAAe,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAElE,iCAAiC;AACjC,MAAM,WAAW,kBAAkB;IACjC,8EAA8E;IAC9E,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CACpD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAuB,WAAW,CAAC,CAAC,EAClC,QAAQ,EAAE,QAAQ,EAClB,OAAO,CAAC,EAAE,kBAAkB,GAC3B,cAAc,CAAC,CAAC,CAAC,CAmDnB"}
|
package/dist/cli.js
CHANGED
|
@@ -5,17 +5,18 @@ import {
|
|
|
5
5
|
emitResult,
|
|
6
6
|
parseCliArgs,
|
|
7
7
|
pollUntilDone
|
|
8
|
-
} from "./index-
|
|
9
|
-
import"./index-
|
|
8
|
+
} from "./index-1pgeyyee.js";
|
|
9
|
+
import"./index-wrhf06ak.js";
|
|
10
10
|
import"./index-sbdmyz75.js";
|
|
11
11
|
import"./index-vkk06pv1.js";
|
|
12
12
|
import"./index-6djpbnda.js";
|
|
13
13
|
import"./index-cby4ar3v.js";
|
|
14
|
-
import"./index-
|
|
15
|
-
import"./index-
|
|
14
|
+
import"./index-tvwcrx2d.js";
|
|
15
|
+
import"./index-wd8g8z6e.js";
|
|
16
16
|
import"./index-6k1937bx.js";
|
|
17
17
|
import"./index-pzyt11ch.js";
|
|
18
18
|
import"./index-smpbdg6k.js";
|
|
19
|
+
import"./index-xxye8j3k.js";
|
|
19
20
|
export {
|
|
20
21
|
DEFAULT_EXIT_CODES,
|
|
21
22
|
createCli,
|
|
@@ -82,6 +82,7 @@ export declare const STITCH_ERROR_STATUS: {
|
|
|
82
82
|
FILE_EXISTS: number;
|
|
83
83
|
REALTIME_CONTRACT_VIOLATION: number;
|
|
84
84
|
APPLICATION_NOT_ACCEPTING: number;
|
|
85
|
+
GRAMMY_WEBHOOK_NOT_ACCEPTING: number;
|
|
85
86
|
WAIT_TIMEOUT: number;
|
|
86
87
|
WAIT_FAILED: number;
|
|
87
88
|
DOWNLOAD_NOT_FOUND: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/contract/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE;QACL,2CAA2C;QAC3C,IAAI,EAAE,MAAM,CAAC;QACb,8BAA8B;QAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,gCAAgC;QAChC,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,0BAA0B;QAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;CACH;AAcD;;;;GAIG;AACH,qBAAa,QAAQ,CACnB,KAAK,SAAS,MAAM,GAAG,MAAM,EAC7B,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAC1F,SAAQ,KAAK;aAEK,IAAI,EAAE,KAAK;aAEX,MAAM,EAAE,MAAM;aACd,OAAO,CAAC,EAAE,QAAQ;aAClB,IAAI,CAAC,EAAE,MAAM;IAC7B;;;;OAIG;aACa,OAAO,CAAC,EAAE,MAAM;IAXlC,YACkB,IAAI,EAAE,KAAK,EAC3B,OAAO,CAAC,EAAE,MAAM,EACA,MAAM,GAAE,MAAY,EACpB,OAAO,CAAC,EAAE,QAAQ,YAAA,EAClB,IAAI,CAAC,EAAE,MAAM,YAAA;IAC7B;;;;OAIG;IACa,OAAO,CAAC,EAAE,MAAM,YAAA,EAQjC;IAED,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,QAAQ,CAEvC;IAED,MAAM,IAAI,aAAa,CAStB;CACF;AAED,qCAAqC;AACrC,wBAAgB,QAAQ,CAAC,OAAO,SAAc,GAAG,KAAK,CAErD;AAED,6EAA6E;AAC7E,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,KAAK,CAEpF;AAED,wCAAwC;AACxC,wBAAgB,YAAY,CAAC,OAAO,SAAiB,GAAG,KAAK,CAE5D;AAED,qCAAqC;AACrC,wBAAgB,SAAS,CAAC,OAAO,SAAc,GAAG,KAAK,CAEtD;AAED,0EAA0E;AAC1E,wBAAgB,QAAQ,CAAC,OAAO,SAAa,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,KAAK,CAEvF;AAED,wCAAwC;AACxC,wBAAgB,WAAW,CAAC,OAAO,SAAsB,GAAG,KAAK,CAEhE;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/contract/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE;QACL,2CAA2C;QAC3C,IAAI,EAAE,MAAM,CAAC;QACb,8BAA8B;QAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,gCAAgC;QAChC,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,0BAA0B;QAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;CACH;AAcD;;;;GAIG;AACH,qBAAa,QAAQ,CACnB,KAAK,SAAS,MAAM,GAAG,MAAM,EAC7B,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAC1F,SAAQ,KAAK;aAEK,IAAI,EAAE,KAAK;aAEX,MAAM,EAAE,MAAM;aACd,OAAO,CAAC,EAAE,QAAQ;aAClB,IAAI,CAAC,EAAE,MAAM;IAC7B;;;;OAIG;aACa,OAAO,CAAC,EAAE,MAAM;IAXlC,YACkB,IAAI,EAAE,KAAK,EAC3B,OAAO,CAAC,EAAE,MAAM,EACA,MAAM,GAAE,MAAY,EACpB,OAAO,CAAC,EAAE,QAAQ,YAAA,EAClB,IAAI,CAAC,EAAE,MAAM,YAAA;IAC7B;;;;OAIG;IACa,OAAO,CAAC,EAAE,MAAM,YAAA,EAQjC;IAED,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,QAAQ,CAEvC;IAED,MAAM,IAAI,aAAa,CAStB;CACF;AAED,qCAAqC;AACrC,wBAAgB,QAAQ,CAAC,OAAO,SAAc,GAAG,KAAK,CAErD;AAED,6EAA6E;AAC7E,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,KAAK,CAEpF;AAED,wCAAwC;AACxC,wBAAgB,YAAY,CAAC,OAAO,SAAiB,GAAG,KAAK,CAE5D;AAED,qCAAqC;AACrC,wBAAgB,SAAS,CAAC,OAAO,SAAc,GAAG,KAAK,CAEtD;AAED,0EAA0E;AAC1E,wBAAgB,QAAQ,CAAC,OAAO,SAAa,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,KAAK,CAEvF;AAED,wCAAwC;AACxC,wBAAgB,WAAW,CAAC,OAAO,SAAsB,GAAG,KAAK,CAEhE;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;CA+BE,CAAC;AAEnC,mFAAmF;AACnF,MAAM,MAAM,eAAe,GAAG,MAAM,OAAO,mBAAmB,CAAC;AAE/D,iEAAiE;AACjE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,IAAI,eAAe,CAEvE;AAED,+FAA+F;AAC/F,wBAAgB,QAAQ,CACtB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,KAAK,CAOP"}
|
package/dist/contract/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
coerceJsonArgs,
|
|
3
3
|
executeToolMethod,
|
|
4
4
|
toolResultFromError
|
|
5
|
-
} from "./index-
|
|
5
|
+
} from "./index-wrhf06ak.js";
|
|
6
6
|
import {
|
|
7
7
|
isWithinDir
|
|
8
8
|
} from "./index-sbdmyz75.js";
|
|
@@ -24,11 +24,11 @@ import {
|
|
|
24
24
|
import {
|
|
25
25
|
formatZodError,
|
|
26
26
|
validateDeclaredOutput
|
|
27
|
-
} from "./index-
|
|
27
|
+
} from "./index-tvwcrx2d.js";
|
|
28
28
|
import {
|
|
29
29
|
isUnsafeKey,
|
|
30
30
|
safeJsonParse
|
|
31
|
-
} from "./index-
|
|
31
|
+
} from "./index-wd8g8z6e.js";
|
|
32
32
|
import {
|
|
33
33
|
isRecord
|
|
34
34
|
} from "./index-smpbdg6k.js";
|
|
@@ -285,6 +285,7 @@ var STITCH_ERROR_STATUS = {
|
|
|
285
285
|
FILE_EXISTS: 409,
|
|
286
286
|
REALTIME_CONTRACT_VIOLATION: 500,
|
|
287
287
|
APPLICATION_NOT_ACCEPTING: 503,
|
|
288
|
+
GRAMMY_WEBHOOK_NOT_ACCEPTING: 503,
|
|
288
289
|
WAIT_TIMEOUT: 408,
|
|
289
290
|
WAIT_FAILED: 409,
|
|
290
291
|
DOWNLOAD_NOT_FOUND: 404,
|
|
@@ -426,4 +427,4 @@ function decodeCursor(cursor, schema) {
|
|
|
426
427
|
return null;
|
|
427
428
|
}
|
|
428
429
|
}
|
|
429
|
-
export { parseTrailingWildcard, AppError, notFound, badRequest, unauthorized, forbidden, conflict, rateLimited, STITCH_ERROR_STATUS, isStitchErrorCode, appError,
|
|
430
|
+
export { parseTrailingWildcard, ALL_TRANSPORTS, defineContract, AppError, notFound, badRequest, unauthorized, forbidden, conflict, rateLimited, STITCH_ERROR_STATUS, isStitchErrorCode, appError, defineErrors, createContractFactory, ManagedFilePathSchema, ManagedFileRefSchema, paginatedSchema, encodeCursor, decodeCursor };
|
|
@@ -6,6 +6,9 @@ import {
|
|
|
6
6
|
import {
|
|
7
7
|
ShutdownOptionsSchema
|
|
8
8
|
} from "./index-dk6e56g0.js";
|
|
9
|
+
import {
|
|
10
|
+
AppError
|
|
11
|
+
} from "./index-xxye8j3k.js";
|
|
9
12
|
|
|
10
13
|
// src/application/graph.ts
|
|
11
14
|
function resolveResourceGraph(resources) {
|
|
@@ -72,11 +75,14 @@ var ApplicationShutdownOptionsSchema = ShutdownOptionsSchema.pick({
|
|
|
72
75
|
forceTimeoutMs: true,
|
|
73
76
|
signal: true
|
|
74
77
|
});
|
|
78
|
+
var ApplicationShutdownBudgetSchema = ShutdownOptionsSchema.pick({
|
|
79
|
+
gracePeriodMs: true,
|
|
80
|
+
forceTimeoutMs: true
|
|
81
|
+
});
|
|
75
82
|
|
|
76
|
-
class ApplicationAdmissionError extends
|
|
77
|
-
code = "APPLICATION_NOT_ACCEPTING";
|
|
83
|
+
class ApplicationAdmissionError extends AppError {
|
|
78
84
|
constructor() {
|
|
79
|
-
super("Application is not accepting new operations");
|
|
85
|
+
super("APPLICATION_NOT_ACCEPTING", "Application is not accepting new operations", 503);
|
|
80
86
|
this.name = "ApplicationAdmissionError";
|
|
81
87
|
}
|
|
82
88
|
}
|
|
@@ -98,6 +104,7 @@ async function untilDeadline(work, signal) {
|
|
|
98
104
|
function createApplication(config) {
|
|
99
105
|
const id = ApplicationIdSchema.parse(config.id);
|
|
100
106
|
const ordered = resolveResourceGraph(config.resources ?? []);
|
|
107
|
+
const shutdownBudget = ApplicationShutdownBudgetSchema.parse(config.shutdown ?? {});
|
|
101
108
|
const reverse = [...ordered].reverse();
|
|
102
109
|
const reportFailure = (resourceId, phase, error) => {
|
|
103
110
|
if (!config.onResourceFailure)
|
|
@@ -114,6 +121,8 @@ function createApplication(config) {
|
|
|
114
121
|
entry,
|
|
115
122
|
state: "registered",
|
|
116
123
|
health: "unknown",
|
|
124
|
+
healthReported: false,
|
|
125
|
+
everHealthy: false,
|
|
117
126
|
attempted: false,
|
|
118
127
|
activated: false,
|
|
119
128
|
closeInvoked: false,
|
|
@@ -201,6 +210,9 @@ function createApplication(config) {
|
|
|
201
210
|
if (record.state === "stopped")
|
|
202
211
|
return;
|
|
203
212
|
record.health = health;
|
|
213
|
+
record.healthReported = true;
|
|
214
|
+
if (health === "healthy")
|
|
215
|
+
record.everHealthy = true;
|
|
204
216
|
accepting = activationComplete && !shutdownRequested && isReady();
|
|
205
217
|
publish();
|
|
206
218
|
}
|
|
@@ -219,6 +231,22 @@ function createApplication(config) {
|
|
|
219
231
|
};
|
|
220
232
|
const closeAttempted = async () => {
|
|
221
233
|
const errors = [];
|
|
234
|
+
const rollbackStartedAt = performance.now();
|
|
235
|
+
const rollbackDeadlineAt = rollbackStartedAt + shutdownBudget.gracePeriodMs;
|
|
236
|
+
const rollbackForceDeadlineAt = rollbackDeadlineAt + shutdownBudget.forceTimeoutMs;
|
|
237
|
+
const rollbackAbort = new AbortController;
|
|
238
|
+
const rollbackTimer = setTimeout(() => rollbackAbort.abort(), Math.max(0, rollbackForceDeadlineAt - performance.now()));
|
|
239
|
+
try {
|
|
240
|
+
await closeEachAttempted(rollbackAbort.signal, errors, {
|
|
241
|
+
deadlineAt: rollbackDeadlineAt,
|
|
242
|
+
forceDeadlineAt: rollbackForceDeadlineAt
|
|
243
|
+
});
|
|
244
|
+
} finally {
|
|
245
|
+
clearTimeout(rollbackTimer);
|
|
246
|
+
}
|
|
247
|
+
return errors;
|
|
248
|
+
};
|
|
249
|
+
const closeEachAttempted = async (bound, errors, deadlines) => {
|
|
222
250
|
for (const entry of reverse) {
|
|
223
251
|
const record = records.get(entry.id);
|
|
224
252
|
if (!record?.attempted || record.closed)
|
|
@@ -226,7 +254,18 @@ function createApplication(config) {
|
|
|
226
254
|
record.state = "stopping";
|
|
227
255
|
try {
|
|
228
256
|
record.closeInvoked = true;
|
|
229
|
-
await entry.resource.close?.(contextFor(record, { signal: startupAbort.signal }));
|
|
257
|
+
const settled = await untilDeadline(Promise.resolve(entry.resource.close?.(contextFor(record, { signal: startupAbort.signal, ...deadlines }))), bound);
|
|
258
|
+
if (!settled.settled) {
|
|
259
|
+
record.failures.push("close");
|
|
260
|
+
record.state = "failed";
|
|
261
|
+
const timedOut = new Error(`[stitchkit] resource "${entry.id}" did not finish closing during rollback`);
|
|
262
|
+
reportFailure(entry.id, "close", timedOut);
|
|
263
|
+
errors.push(timedOut);
|
|
264
|
+
publish();
|
|
265
|
+
break;
|
|
266
|
+
}
|
|
267
|
+
if (settled.error !== undefined)
|
|
268
|
+
throw settled.error;
|
|
230
269
|
record.closed = true;
|
|
231
270
|
record.state = "stopped";
|
|
232
271
|
} catch (error) {
|
|
@@ -237,7 +276,6 @@ function createApplication(config) {
|
|
|
237
276
|
}
|
|
238
277
|
publish();
|
|
239
278
|
}
|
|
240
|
-
return errors;
|
|
241
279
|
};
|
|
242
280
|
const runStart = async () => {
|
|
243
281
|
lifecycle = "starting";
|
|
@@ -305,7 +343,10 @@ function createApplication(config) {
|
|
|
305
343
|
throw new ApplicationStartupInterruptedError;
|
|
306
344
|
}
|
|
307
345
|
record.state = "ready";
|
|
308
|
-
record.
|
|
346
|
+
if (!record.healthReported) {
|
|
347
|
+
record.health = "healthy";
|
|
348
|
+
record.everHealthy = true;
|
|
349
|
+
}
|
|
309
350
|
publish();
|
|
310
351
|
} catch (error) {
|
|
311
352
|
const interrupted = shutdownRequested || startupAbort.signal.aborted;
|
|
@@ -350,7 +391,8 @@ function createApplication(config) {
|
|
|
350
391
|
}
|
|
351
392
|
record.activated = true;
|
|
352
393
|
if (entry.required && (record.state !== "ready" || record.health !== "healthy")) {
|
|
353
|
-
|
|
394
|
+
const observed = `${record.state}/${record.health}`;
|
|
395
|
+
throw new Error(record.everHealthy ? `[stitchkit] required resource "${entry.id}" lost readiness during activation (${observed})` : `[stitchkit] required resource "${entry.id}" is not healthy (${observed}). A required resource must be healthy for the application to be ready; a resource that is expected to start degraded belongs behind \`required: false\`.`);
|
|
354
396
|
}
|
|
355
397
|
} catch (error) {
|
|
356
398
|
const interrupted = shutdownRequested || startupAbort.signal.aborted;
|
|
@@ -369,7 +411,11 @@ function createApplication(config) {
|
|
|
369
411
|
throw new ApplicationStartupInterruptedError;
|
|
370
412
|
}
|
|
371
413
|
if (!isReady()) {
|
|
372
|
-
|
|
414
|
+
const blocking = [...records.values()].filter((record) => record.entry.required && (record.state !== "ready" || record.health !== "healthy")).map((record) => `${record.entry.id} (${record.state}/${record.health})`);
|
|
415
|
+
if (blocking.length === 0)
|
|
416
|
+
blocking.push("(none identified)");
|
|
417
|
+
const chosen = [...records.values()].some((record) => record.entry.required && record.health !== "healthy" && !record.everHealthy);
|
|
418
|
+
throw new Error(`[stitchkit] the application is not ready after startup — required ${blocking.length === 1 ? "resource" : "resources"} ${blocking.join(", ")}. A required resource must be healthy for the application to be ready; ${chosen ? "a resource that is expected to start degraded belongs behind `required: false`." : "this one was healthy and stopped being so — read `onResourceFailure` for the cause."}`);
|
|
373
419
|
}
|
|
374
420
|
activationComplete = true;
|
|
375
421
|
accepting = isReady();
|
|
@@ -445,7 +491,12 @@ function createApplication(config) {
|
|
|
445
491
|
const shutdown = (options) => {
|
|
446
492
|
if (shutdownPromise)
|
|
447
493
|
return shutdownPromise;
|
|
448
|
-
const
|
|
494
|
+
const requested = options ?? {};
|
|
495
|
+
const parsed = ApplicationShutdownOptionsSchema.parse({
|
|
496
|
+
gracePeriodMs: requested.gracePeriodMs ?? shutdownBudget.gracePeriodMs,
|
|
497
|
+
forceTimeoutMs: requested.forceTimeoutMs ?? shutdownBudget.forceTimeoutMs,
|
|
498
|
+
...requested.signal !== undefined && { signal: requested.signal }
|
|
499
|
+
});
|
|
449
500
|
const startedAt = performance.now();
|
|
450
501
|
const graceDeadlineAt = startedAt + parsed.gracePeriodMs;
|
|
451
502
|
const forceDeadlineAt = graceDeadlineAt + parsed.forceTimeoutMs;
|
|
@@ -648,4 +699,4 @@ function createApplication(config) {
|
|
|
648
699
|
};
|
|
649
700
|
}
|
|
650
701
|
|
|
651
|
-
export { ApplicationShutdownOptionsSchema, ApplicationAdmissionError, createApplication };
|
|
702
|
+
export { ApplicationShutdownOptionsSchema, ApplicationShutdownBudgetSchema, ApplicationAdmissionError, createApplication };
|
|
@@ -2,18 +2,20 @@ import {
|
|
|
2
2
|
contextContributionDelta,
|
|
3
3
|
mergeContextContribution,
|
|
4
4
|
prepareContextContribution
|
|
5
|
-
} from "./index-
|
|
5
|
+
} from "./index-y91zd0ch.js";
|
|
6
6
|
import {
|
|
7
7
|
base64UrlToBytes,
|
|
8
8
|
bytesToBase64Url,
|
|
9
|
-
forbidden,
|
|
10
9
|
isUnsafeKey,
|
|
11
|
-
safeJsonParse
|
|
12
|
-
|
|
13
|
-
} from "./index-v6e8hnkx.js";
|
|
10
|
+
safeJsonParse
|
|
11
|
+
} from "./index-wd8g8z6e.js";
|
|
14
12
|
import {
|
|
15
13
|
isRecord
|
|
16
14
|
} from "./index-smpbdg6k.js";
|
|
15
|
+
import {
|
|
16
|
+
forbidden,
|
|
17
|
+
unauthorized
|
|
18
|
+
} from "./index-xxye8j3k.js";
|
|
17
19
|
|
|
18
20
|
// src/server/middleware/cookies.ts
|
|
19
21
|
function parseCookies(header) {
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
defaultSignalSource,
|
|
8
8
|
guardSignalCallback,
|
|
9
9
|
reportSignalError
|
|
10
|
-
} from "./index-
|
|
10
|
+
} from "./index-y91zd0ch.js";
|
|
11
11
|
import {
|
|
12
12
|
ShutdownOptionsSchema
|
|
13
13
|
} from "./index-dk6e56g0.js";
|
|
@@ -26,13 +26,11 @@ import {
|
|
|
26
26
|
setRequestError,
|
|
27
27
|
validateDeclaredOutput,
|
|
28
28
|
zodIssues
|
|
29
|
-
} from "./index-
|
|
29
|
+
} from "./index-tvwcrx2d.js";
|
|
30
30
|
import {
|
|
31
|
-
AppError,
|
|
32
|
-
badRequest,
|
|
33
31
|
isUnsafeKey,
|
|
34
32
|
safeJsonParse
|
|
35
|
-
} from "./index-
|
|
33
|
+
} from "./index-wd8g8z6e.js";
|
|
36
34
|
import {
|
|
37
35
|
joinRoutePath,
|
|
38
36
|
parseTrailingWildcard,
|
|
@@ -42,6 +40,10 @@ import {
|
|
|
42
40
|
isRecord,
|
|
43
41
|
transportResult
|
|
44
42
|
} from "./index-smpbdg6k.js";
|
|
43
|
+
import {
|
|
44
|
+
AppError,
|
|
45
|
+
badRequest
|
|
46
|
+
} from "./index-xxye8j3k.js";
|
|
45
47
|
|
|
46
48
|
// src/server/multipart.ts
|
|
47
49
|
var DEFAULT_MAX_REQUEST_BYTES = 25 * 1024 * 1024;
|
|
@@ -1561,6 +1563,46 @@ function bindProcessSignals(handle, options = {}) {
|
|
|
1561
1563
|
return { promise, close };
|
|
1562
1564
|
}
|
|
1563
1565
|
|
|
1566
|
+
// src/realtime/rejected-frame.ts
|
|
1567
|
+
var REALTIME_REJECTION_KEY = "@stitchkit/realtime-rejected";
|
|
1568
|
+
var MAX_REJECTION_ISSUES = 20;
|
|
1569
|
+
function realtimeRejectionEnvelope(report) {
|
|
1570
|
+
return { [REALTIME_REJECTION_KEY]: report };
|
|
1571
|
+
}
|
|
1572
|
+
function asRealtimeRejection(value) {
|
|
1573
|
+
if (typeof value !== "object" || value === null)
|
|
1574
|
+
return null;
|
|
1575
|
+
const report = Reflect.get(value, REALTIME_REJECTION_KEY);
|
|
1576
|
+
if (typeof report !== "object" || report === null)
|
|
1577
|
+
return null;
|
|
1578
|
+
const event = Reflect.get(report, "event");
|
|
1579
|
+
const reason = Reflect.get(report, "reason");
|
|
1580
|
+
const message = Reflect.get(report, "message");
|
|
1581
|
+
if (typeof event !== "string" || typeof reason !== "string" || typeof message !== "string") {
|
|
1582
|
+
return null;
|
|
1583
|
+
}
|
|
1584
|
+
if (reason.length === 0)
|
|
1585
|
+
return null;
|
|
1586
|
+
const issues = parseIssues(Reflect.get(report, "issues"));
|
|
1587
|
+
return { event, reason, message, ...issues !== undefined && { issues } };
|
|
1588
|
+
}
|
|
1589
|
+
function parseIssues(value) {
|
|
1590
|
+
if (!Array.isArray(value))
|
|
1591
|
+
return;
|
|
1592
|
+
const issues = [];
|
|
1593
|
+
for (const entry of value.slice(0, MAX_REJECTION_ISSUES)) {
|
|
1594
|
+
if (typeof entry !== "object" || entry === null)
|
|
1595
|
+
continue;
|
|
1596
|
+
const path = Reflect.get(entry, "path");
|
|
1597
|
+
const code = Reflect.get(entry, "code");
|
|
1598
|
+
const message = Reflect.get(entry, "message");
|
|
1599
|
+
if (typeof path === "string" && typeof code === "string" && typeof message === "string") {
|
|
1600
|
+
issues.push({ path, code, message });
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
return issues.length > 0 ? issues : undefined;
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1564
1606
|
// src/realtime/rejection.ts
|
|
1565
1607
|
import { z } from "zod";
|
|
1566
1608
|
function realtimeContractViolation(options) {
|
|
@@ -1588,15 +1630,11 @@ function method(target, name) {
|
|
|
1588
1630
|
return (...args) => Reflect.apply(value, target, args);
|
|
1589
1631
|
}
|
|
1590
1632
|
function splitWireArguments(definition, args) {
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
return {
|
|
1597
|
-
values: args.slice(0, -1),
|
|
1598
|
-
acknowledgement: (...callbackArgs) => Reflect.apply(acknowledgement, undefined, callbackArgs)
|
|
1599
|
-
};
|
|
1633
|
+
const trailing = args.at(-1);
|
|
1634
|
+
const replyTo = typeof trailing === "function" ? (...callbackArgs) => Reflect.apply(trailing, undefined, callbackArgs) : undefined;
|
|
1635
|
+
if (!definition.ack || !replyTo)
|
|
1636
|
+
return { values: args, ...replyTo && { replyTo } };
|
|
1637
|
+
return { values: args.slice(0, -1), acknowledgement: replyTo, replyTo };
|
|
1600
1638
|
}
|
|
1601
1639
|
function eventDefinition(registry, event, direction) {
|
|
1602
1640
|
const definition = registry[event];
|
|
@@ -1624,6 +1662,19 @@ function reportRejected(rejected, hook, logger) {
|
|
|
1624
1662
|
console.error("[stitchkit] realtime rejection hook failed", error);
|
|
1625
1663
|
});
|
|
1626
1664
|
}
|
|
1665
|
+
function answerWithRejection(acknowledgement, report, logger) {
|
|
1666
|
+
if (!acknowledgement)
|
|
1667
|
+
return;
|
|
1668
|
+
try {
|
|
1669
|
+
acknowledgement(realtimeRejectionEnvelope(report));
|
|
1670
|
+
} catch (error) {
|
|
1671
|
+
const message = "[stitchkit] could not answer a rejected realtime frame";
|
|
1672
|
+
if (logger)
|
|
1673
|
+
logger.warn(message, { error });
|
|
1674
|
+
else
|
|
1675
|
+
console.warn(message, error);
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1627
1678
|
function createValidatedRealtimeSocket({
|
|
1628
1679
|
target,
|
|
1629
1680
|
inbound,
|
|
@@ -1639,17 +1690,24 @@ function createValidatedRealtimeSocket({
|
|
|
1639
1690
|
on: (event, handler) => {
|
|
1640
1691
|
const definition = eventDefinition(inbound, event, inboundDirection);
|
|
1641
1692
|
const wrapped = (...wireArgs) => {
|
|
1642
|
-
const { values, acknowledgement } = splitWireArguments(definition, wireArgs);
|
|
1693
|
+
const { values, acknowledgement, replyTo } = splitWireArguments(definition, wireArgs);
|
|
1643
1694
|
const parsed = definition.args.safeParse(values);
|
|
1644
1695
|
if (!parsed.success) {
|
|
1645
|
-
|
|
1696
|
+
const rejected = realtimeContractViolation({
|
|
1646
1697
|
event,
|
|
1647
1698
|
direction: inboundDirection,
|
|
1648
1699
|
phase: "arguments",
|
|
1649
1700
|
reason: "invalid-arguments",
|
|
1650
1701
|
fault: "peer",
|
|
1651
1702
|
cause: parsed.error
|
|
1652
|
-
})
|
|
1703
|
+
});
|
|
1704
|
+
reportRejected(rejected, onRejected, logger);
|
|
1705
|
+
answerWithRejection(replyTo, {
|
|
1706
|
+
event,
|
|
1707
|
+
reason: "invalid-arguments",
|
|
1708
|
+
message: rejected.error.message,
|
|
1709
|
+
issues: zodIssues(parsed.error).slice(0, MAX_REJECTION_ISSUES)
|
|
1710
|
+
}, logger);
|
|
1653
1711
|
return;
|
|
1654
1712
|
}
|
|
1655
1713
|
if (definition.ack && !acknowledgement) {
|
|
@@ -1719,6 +1777,17 @@ function createValidatedRealtimeSocket({
|
|
|
1719
1777
|
const wireArgs = [...parsed.data];
|
|
1720
1778
|
if (definition.ack && acknowledgement) {
|
|
1721
1779
|
wireArgs.push((value) => {
|
|
1780
|
+
const refusal = asRealtimeRejection(value);
|
|
1781
|
+
if (refusal) {
|
|
1782
|
+
reportRejected(realtimeContractViolation({
|
|
1783
|
+
event,
|
|
1784
|
+
direction: outboundDirection,
|
|
1785
|
+
phase: "arguments",
|
|
1786
|
+
reason: "rejected-by-peer",
|
|
1787
|
+
fault: "local"
|
|
1788
|
+
}), onRejected, logger);
|
|
1789
|
+
return;
|
|
1790
|
+
}
|
|
1722
1791
|
const ack = definition.ack?.safeParse(value);
|
|
1723
1792
|
if (!ack?.success) {
|
|
1724
1793
|
if (ack) {
|
|
@@ -1839,17 +1908,19 @@ function composeWebSocketHandlers(lanes, config) {
|
|
|
1839
1908
|
};
|
|
1840
1909
|
}
|
|
1841
1910
|
|
|
1911
|
+
// src/internal/optional-peer.ts
|
|
1912
|
+
function isModuleNotFound(error) {
|
|
1913
|
+
if (typeof error !== "object" || error === null)
|
|
1914
|
+
return false;
|
|
1915
|
+
const code = "code" in error ? error.code : undefined;
|
|
1916
|
+
const message = "message" in error && typeof error.message === "string" ? error.message : "";
|
|
1917
|
+
return code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND" || message.includes("Cannot find module") || message.includes("Cannot find package");
|
|
1918
|
+
}
|
|
1919
|
+
|
|
1842
1920
|
// src/server/socket-io.ts
|
|
1843
1921
|
var SOCKET_IO_SERVER = "socket.io";
|
|
1844
1922
|
var SOCKET_IO_BUN_ENGINE = "@socket.io/bun-engine";
|
|
1845
1923
|
var noop = () => {};
|
|
1846
|
-
function isModuleNotFound(err) {
|
|
1847
|
-
if (typeof err !== "object" || err === null)
|
|
1848
|
-
return false;
|
|
1849
|
-
const code = "code" in err ? err.code : undefined;
|
|
1850
|
-
const message = "message" in err && typeof err.message === "string" ? err.message : "";
|
|
1851
|
-
return code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND" || message.includes("Cannot find module") || message.includes("Cannot find package");
|
|
1852
|
-
}
|
|
1853
1924
|
async function importPeer(load, pkg, injected) {
|
|
1854
1925
|
try {
|
|
1855
1926
|
return await load();
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
|
-
AppError,
|
|
3
2
|
isUnsafeKey
|
|
4
|
-
} from "./index-
|
|
3
|
+
} from "./index-wd8g8z6e.js";
|
|
5
4
|
import {
|
|
6
5
|
resolveTraceContext
|
|
7
6
|
} from "./index-pzyt11ch.js";
|
|
8
7
|
import {
|
|
9
8
|
isRecord
|
|
10
9
|
} from "./index-smpbdg6k.js";
|
|
10
|
+
import {
|
|
11
|
+
AppError
|
|
12
|
+
} from "./index-xxye8j3k.js";
|
|
11
13
|
|
|
12
14
|
// src/internal/errors.ts
|
|
13
15
|
import { z } from "zod";
|
|
@@ -2,89 +2,6 @@ import {
|
|
|
2
2
|
parseTrailingWildcard
|
|
3
3
|
} from "./index-pzyt11ch.js";
|
|
4
4
|
|
|
5
|
-
// src/contract/errors.ts
|
|
6
|
-
var APP_ERROR_BRAND = Symbol.for("stitchkit.AppError");
|
|
7
|
-
|
|
8
|
-
class AppError extends Error {
|
|
9
|
-
code;
|
|
10
|
-
status;
|
|
11
|
-
details;
|
|
12
|
-
hint;
|
|
13
|
-
traceId;
|
|
14
|
-
constructor(code, message, status = 500, details, hint, traceId) {
|
|
15
|
-
super(message ?? code);
|
|
16
|
-
this.code = code;
|
|
17
|
-
this.status = status;
|
|
18
|
-
this.details = details;
|
|
19
|
-
this.hint = hint;
|
|
20
|
-
this.traceId = traceId;
|
|
21
|
-
this.name = "AppError";
|
|
22
|
-
Object.defineProperty(this, APP_ERROR_BRAND, { value: true });
|
|
23
|
-
}
|
|
24
|
-
static is(err) {
|
|
25
|
-
return typeof err === "object" && err !== null && APP_ERROR_BRAND in err;
|
|
26
|
-
}
|
|
27
|
-
toJSON() {
|
|
28
|
-
return {
|
|
29
|
-
error: {
|
|
30
|
-
code: this.code,
|
|
31
|
-
message: this.message,
|
|
32
|
-
...this.details && { details: this.details },
|
|
33
|
-
...this.hint && { hint: this.hint }
|
|
34
|
-
}
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
function notFound(message = "Not found") {
|
|
39
|
-
throw new AppError("NOT_FOUND", message, 404);
|
|
40
|
-
}
|
|
41
|
-
function badRequest(message, details) {
|
|
42
|
-
throw new AppError("BAD_REQUEST", message, 400, details);
|
|
43
|
-
}
|
|
44
|
-
function unauthorized(message = "Unauthorized") {
|
|
45
|
-
throw new AppError("UNAUTHORIZED", message, 401);
|
|
46
|
-
}
|
|
47
|
-
function forbidden(message = "Forbidden") {
|
|
48
|
-
throw new AppError("FORBIDDEN", message, 403);
|
|
49
|
-
}
|
|
50
|
-
function conflict(message = "Conflict", details) {
|
|
51
|
-
throw new AppError("CONFLICT", message, 409, details);
|
|
52
|
-
}
|
|
53
|
-
function rateLimited(message = "Too many requests") {
|
|
54
|
-
throw new AppError("RATE_LIMITED", message, 429);
|
|
55
|
-
}
|
|
56
|
-
var STITCH_ERROR_STATUS = {
|
|
57
|
-
BAD_REQUEST: 400,
|
|
58
|
-
UNAUTHORIZED: 401,
|
|
59
|
-
FORBIDDEN: 403,
|
|
60
|
-
NOT_FOUND: 404,
|
|
61
|
-
METHOD_NOT_ALLOWED: 405,
|
|
62
|
-
CONFLICT: 409,
|
|
63
|
-
RATE_LIMITED: 429,
|
|
64
|
-
VALIDATION_ERROR: 400,
|
|
65
|
-
FILE_INVALID_PATH: 400,
|
|
66
|
-
FILE_OUTSIDE_ROOT: 400,
|
|
67
|
-
FILE_NOT_FOUND: 404,
|
|
68
|
-
FILE_NOT_REGULAR: 422,
|
|
69
|
-
FILE_INSPECTION_REJECTED: 422,
|
|
70
|
-
FILE_TOO_LARGE: 413,
|
|
71
|
-
FILE_EXISTS: 409,
|
|
72
|
-
REALTIME_CONTRACT_VIOLATION: 500,
|
|
73
|
-
APPLICATION_NOT_ACCEPTING: 503,
|
|
74
|
-
WAIT_TIMEOUT: 408,
|
|
75
|
-
WAIT_FAILED: 409,
|
|
76
|
-
DOWNLOAD_NOT_FOUND: 404,
|
|
77
|
-
VIEW_HTTP_ERROR: 502,
|
|
78
|
-
OPERATION_NOT_SUCCEEDED: 409,
|
|
79
|
-
INTERNAL_SERVER_ERROR: 500
|
|
80
|
-
};
|
|
81
|
-
function isStitchErrorCode(code) {
|
|
82
|
-
return Object.hasOwn(STITCH_ERROR_STATUS, code);
|
|
83
|
-
}
|
|
84
|
-
function appError(code, message, details) {
|
|
85
|
-
throw new AppError(code, message, isStitchErrorCode(code) ? STITCH_ERROR_STATUS[code] : 500, details);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
5
|
// src/contract/define.ts
|
|
89
6
|
import { z } from "zod";
|
|
90
7
|
|
|
@@ -286,4 +203,4 @@ function base64UrlToBytes(segment) {
|
|
|
286
203
|
const padded = b64.padEnd(Math.ceil(b64.length / 4) * 4, "=");
|
|
287
204
|
return Uint8Array.from(atob(padded), (c) => c.charCodeAt(0));
|
|
288
205
|
}
|
|
289
|
-
export { isUnsafeKey, safeJsonParse, defineContract, mergeMeta,
|
|
206
|
+
export { isUnsafeKey, safeJsonParse, defineContract, mergeMeta, bytesToBase64Url, base64UrlToBytes };
|
|
@@ -7,17 +7,19 @@ import {
|
|
|
7
7
|
normalizeError,
|
|
8
8
|
runWithRequestContext,
|
|
9
9
|
validateDeclaredOutput
|
|
10
|
-
} from "./index-
|
|
10
|
+
} from "./index-tvwcrx2d.js";
|
|
11
11
|
import {
|
|
12
|
-
AppError,
|
|
13
|
-
STITCH_ERROR_STATUS,
|
|
14
|
-
isStitchErrorCode,
|
|
15
12
|
isUnsafeKey,
|
|
16
13
|
safeJsonParse
|
|
17
|
-
} from "./index-
|
|
14
|
+
} from "./index-wd8g8z6e.js";
|
|
18
15
|
import {
|
|
19
16
|
isRecord
|
|
20
17
|
} from "./index-smpbdg6k.js";
|
|
18
|
+
import {
|
|
19
|
+
AppError,
|
|
20
|
+
STITCH_ERROR_STATUS,
|
|
21
|
+
isStitchErrorCode
|
|
22
|
+
} from "./index-xxye8j3k.js";
|
|
21
23
|
|
|
22
24
|
// src/tools/coerce.ts
|
|
23
25
|
import { z } from "zod";
|