stitchkit 0.54.0 → 0.55.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/README.md +4 -1
- package/dist/browser/socket-io.d.ts +11 -1
- package/dist/browser/socket-io.d.ts.map +1 -1
- package/dist/cli.js +14 -9
- package/dist/contract/file-ref.d.ts +13 -0
- package/dist/contract/file-ref.d.ts.map +1 -0
- package/dist/contract/index.d.ts +1 -0
- package/dist/contract/index.d.ts.map +1 -1
- package/dist/contract/index.js +21 -17
- package/dist/files/boundary.d.ts +52 -0
- package/dist/files/boundary.d.ts.map +1 -0
- package/dist/files.d.ts +3 -0
- package/dist/files.d.ts.map +1 -0
- package/dist/files.js +16 -0
- package/dist/{index-7vb44hy8.js → index-0gv7k0ra.js} +127 -92
- package/dist/index-1bx83sw4.js +4 -0
- package/dist/index-1k16zv57.js +269 -0
- package/dist/index-6k1937bx.js +20 -0
- package/dist/{index-bmnhqtya.js → index-7eq9va6e.js} +1 -4
- package/dist/{index-yxpe3phd.js → index-cby4ar3v.js} +1 -18
- package/dist/index-h60df7rj.js +110 -0
- package/dist/{index-yez419px.js → index-js7yexng.js} +19 -6
- package/dist/index-k4dftwf7.js +66 -0
- package/dist/{index-z7761jk2.js → index-npye5kt7.js} +16 -22
- package/dist/{index-b15rtha1.js → index-pzc32v75.js} +61 -3
- package/dist/{index-e1es808r.js → index-qh4xevf5.js} +1 -1
- package/dist/{index-gvha32jh.js → index-qx84bqzk.js} +2 -2
- package/dist/{index-pfqjb5xy.js → index-rgd18hx3.js} +23 -5
- package/dist/index-sbdmyz75.js +18 -0
- package/dist/{index-173tvqcp.js → index-v4bbq3p4.js} +9 -114
- package/dist/index.js +162 -35
- package/dist/node.js +30 -27
- package/dist/observability/index.js +28 -25
- package/dist/react.js +2 -2
- package/dist/realtime/contract.d.ts +9 -0
- 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/request.d.ts +21 -0
- package/dist/realtime/request.d.ts.map +1 -0
- package/dist/realtime/socket.d.ts +3 -0
- package/dist/realtime/socket.d.ts.map +1 -1
- package/dist/remote.d.ts +9 -0
- package/dist/remote.d.ts.map +1 -0
- package/dist/remote.js +79 -0
- package/dist/server/context-contribution.d.ts +10 -0
- package/dist/server/context-contribution.d.ts.map +1 -0
- package/dist/server/context.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 +133 -88
- package/dist/server/lifecycle.d.ts +4 -0
- package/dist/server/lifecycle.d.ts.map +1 -0
- package/dist/server/middleware/auth.d.ts +35 -7
- package/dist/server/middleware/auth.d.ts.map +1 -1
- package/dist/server/types.d.ts +1 -1
- package/dist/server/types.d.ts.map +1 -1
- package/dist/testing/surface-conformance.d.ts +47 -0
- package/dist/testing/surface-conformance.d.ts.map +1 -0
- package/dist/testing/surface-manifest.d.ts +156 -0
- package/dist/testing/surface-manifest.d.ts.map +1 -0
- package/dist/testing.d.ts +2 -0
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +366 -3
- package/dist/tools/agent.d.ts.map +1 -1
- package/dist/tools/async-operation.d.ts +168 -0
- package/dist/tools/async-operation.d.ts.map +1 -0
- package/dist/tools/cli-args.d.ts +1 -1
- package/dist/tools/cli-args.d.ts.map +1 -1
- package/dist/tools/cli-format.d.ts +2 -2
- package/dist/tools/cli-format.d.ts.map +1 -1
- package/dist/tools/cli-wait.d.ts +11 -2
- package/dist/tools/cli-wait.d.ts.map +1 -1
- package/dist/tools/cli.d.ts +2 -0
- package/dist/tools/cli.d.ts.map +1 -1
- package/dist/tools/define-download-tool.d.ts +6 -5
- package/dist/tools/define-download-tool.d.ts.map +1 -1
- package/dist/tools/define-upload-tool.d.ts +3 -1
- package/dist/tools/define-upload-tool.d.ts.map +1 -1
- package/dist/tools/define-wait-tool.d.ts +1 -1
- package/dist/tools/define-wait-tool.d.ts.map +1 -1
- package/dist/tools/download-core.d.ts +5 -10
- package/dist/tools/download-core.d.ts.map +1 -1
- package/dist/tools/lifecycle.d.ts +4 -0
- package/dist/tools/lifecycle.d.ts.map +1 -0
- package/dist/tools/mount-download.d.ts +5 -4
- package/dist/tools/mount-download.d.ts.map +1 -1
- package/dist/tools/mount-upload.d.ts +4 -2
- package/dist/tools/mount-upload.d.ts.map +1 -1
- package/dist/tools/upload-core.d.ts +3 -2
- package/dist/tools/upload-core.d.ts.map +1 -1
- package/dist/tools/view-file.d.ts +4 -3
- package/dist/tools/view-file.d.ts.map +1 -1
- package/dist/tools/wait-core.d.ts +6 -1
- package/dist/tools/wait-core.d.ts.map +1 -1
- package/dist/tools.d.ts +3 -2
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +385 -244
- package/llms-full.txt +417 -27
- package/package.json +10 -2
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
|
+
isUnsafeKey,
|
|
2
3
|
mergeMeta
|
|
3
|
-
} from "./index-
|
|
4
|
+
} from "./index-h60df7rj.js";
|
|
4
5
|
import {
|
|
5
6
|
callRuntimeHandler,
|
|
6
7
|
isRecord,
|
|
7
8
|
transportResult,
|
|
8
9
|
typedEntries
|
|
9
|
-
} from "./index-
|
|
10
|
+
} from "./index-7eq9va6e.js";
|
|
10
11
|
|
|
11
12
|
// src/server/middleware/cors.ts
|
|
12
13
|
var DEFAULT_CORS_ALLOW_HEADERS = "Content-Type, Authorization, X-Trace-Id, traceparent, tracestate";
|
|
@@ -246,6 +247,63 @@ function createImplementRegistry() {
|
|
|
246
247
|
return (contracts, handlers) => transportResult(bindRegistry(contracts, handlers));
|
|
247
248
|
}
|
|
248
249
|
|
|
250
|
+
// src/server/context-contribution.ts
|
|
251
|
+
var RUNTIME_CONTEXT_RESERVED_KEYS = new Set([
|
|
252
|
+
"params",
|
|
253
|
+
"input",
|
|
254
|
+
"files",
|
|
255
|
+
"rawBody",
|
|
256
|
+
"source",
|
|
257
|
+
"req",
|
|
258
|
+
"url",
|
|
259
|
+
"headers",
|
|
260
|
+
"traceId",
|
|
261
|
+
"spanId",
|
|
262
|
+
"ipAddress",
|
|
263
|
+
"userAgent",
|
|
264
|
+
"signal",
|
|
265
|
+
"mcp"
|
|
266
|
+
]);
|
|
267
|
+
function contributionError(scope, reason) {
|
|
268
|
+
return new TypeError(`[stitchkit] auth: invalid context contribution for scope ${JSON.stringify(scope)}: ${reason}`);
|
|
269
|
+
}
|
|
270
|
+
function mergeContextContribution(ctx, value, scope) {
|
|
271
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
272
|
+
throw contributionError(scope, "expected a plain record");
|
|
273
|
+
}
|
|
274
|
+
let prototype;
|
|
275
|
+
let descriptors;
|
|
276
|
+
let keys;
|
|
277
|
+
try {
|
|
278
|
+
prototype = Object.getPrototypeOf(value);
|
|
279
|
+
descriptors = Object.getOwnPropertyDescriptors(value);
|
|
280
|
+
keys = Reflect.ownKeys(value);
|
|
281
|
+
} catch {
|
|
282
|
+
throw contributionError(scope, "record inspection failed");
|
|
283
|
+
}
|
|
284
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
285
|
+
throw contributionError(scope, "expected Object.prototype or null prototype");
|
|
286
|
+
}
|
|
287
|
+
const prepared = Object.create(null);
|
|
288
|
+
for (const key of keys) {
|
|
289
|
+
if (typeof key !== "string") {
|
|
290
|
+
throw contributionError(scope, "symbol keys are not supported");
|
|
291
|
+
}
|
|
292
|
+
if (isUnsafeKey(key)) {
|
|
293
|
+
throw contributionError(scope, `unsafe key ${JSON.stringify(key)}`);
|
|
294
|
+
}
|
|
295
|
+
if (RUNTIME_CONTEXT_RESERVED_KEYS.has(key)) {
|
|
296
|
+
throw contributionError(scope, `reserved key ${JSON.stringify(key)}`);
|
|
297
|
+
}
|
|
298
|
+
const descriptor = descriptors[key];
|
|
299
|
+
if (!descriptor?.enumerable || !("value" in descriptor)) {
|
|
300
|
+
throw contributionError(scope, `key ${JSON.stringify(key)} must be enumerable data`);
|
|
301
|
+
}
|
|
302
|
+
prepared[key] = descriptor.value;
|
|
303
|
+
}
|
|
304
|
+
Object.assign(ctx, prepared);
|
|
305
|
+
}
|
|
306
|
+
|
|
249
307
|
// src/server/process-signal-common.ts
|
|
250
308
|
var DEFAULT_PROCESS_SIGNALS = ["SIGINT", "SIGTERM"];
|
|
251
309
|
var defaultSignalSource = {
|
|
@@ -275,4 +333,4 @@ function guardSignalCallback(run, phase, onError) {
|
|
|
275
333
|
}
|
|
276
334
|
}
|
|
277
335
|
|
|
278
|
-
export { DEFAULT_CORS_ALLOW_HEADERS, DEFAULT_CORS_EXPOSE_HEADERS, assertCorsConfig, corsHeaders, corsPreflightResponse, defineMultipartStream, createMultipartStream, contractOnlyService, implement, createImplement, createScopedImplement, createScopedImplementRegistry, implementRegistry, createImplementRegistry, DEFAULT_PROCESS_SIGNALS, defaultSignalSource, reportSignalError, guardSignalCallback };
|
|
336
|
+
export { RUNTIME_CONTEXT_RESERVED_KEYS, mergeContextContribution, DEFAULT_CORS_ALLOW_HEADERS, DEFAULT_CORS_EXPOSE_HEADERS, assertCorsConfig, corsHeaders, corsPreflightResponse, defineMultipartStream, createMultipartStream, contractOnlyService, implement, createImplement, createScopedImplement, createScopedImplementRegistry, implementRegistry, createImplementRegistry, DEFAULT_PROCESS_SIGNALS, defaultSignalSource, reportSignalError, guardSignalCallback };
|
|
@@ -350,8 +350,26 @@ function createContractFactory(config) {
|
|
|
350
350
|
};
|
|
351
351
|
return { defineContract: explicit };
|
|
352
352
|
}
|
|
353
|
-
// src/contract/
|
|
353
|
+
// src/contract/file-ref.ts
|
|
354
354
|
import { z as z3 } from "zod";
|
|
355
|
+
function isManagedFilePath(value) {
|
|
356
|
+
if (value.length === 0 || value.includes("\x00") || value.includes("\\"))
|
|
357
|
+
return false;
|
|
358
|
+
if (value.startsWith("/") || value.startsWith("//") || /^[A-Za-z]:/.test(value)) {
|
|
359
|
+
return false;
|
|
360
|
+
}
|
|
361
|
+
const parts = value.split("/");
|
|
362
|
+
return parts.every((part) => part.length > 0 && part !== "." && part !== "..");
|
|
363
|
+
}
|
|
364
|
+
var ManagedFilePathSchema = z3.string().refine(isManagedFilePath, "expected a canonical relative managed-file path");
|
|
365
|
+
var ManagedFileRefSchema = z3.object({
|
|
366
|
+
path: ManagedFilePathSchema,
|
|
367
|
+
size: z3.number().int().nonnegative(),
|
|
368
|
+
mediaType: z3.string().min(1).optional(),
|
|
369
|
+
name: z3.string().min(1).optional()
|
|
370
|
+
});
|
|
371
|
+
// src/contract/pagination.ts
|
|
372
|
+
import { z as z4 } from "zod";
|
|
355
373
|
|
|
356
374
|
// src/internal/base64url.ts
|
|
357
375
|
function bytesToBase64Url(bytes) {
|
|
@@ -371,9 +389,9 @@ function base64UrlToBytes(segment) {
|
|
|
371
389
|
|
|
372
390
|
// src/contract/pagination.ts
|
|
373
391
|
function paginatedSchema(itemSchema) {
|
|
374
|
-
return
|
|
375
|
-
items:
|
|
376
|
-
nextCursor:
|
|
392
|
+
return z4.object({
|
|
393
|
+
items: z4.array(itemSchema),
|
|
394
|
+
nextCursor: z4.string().nullable()
|
|
377
395
|
});
|
|
378
396
|
}
|
|
379
397
|
function toBase64Url(str) {
|
|
@@ -395,4 +413,4 @@ function decodeCursor(cursor, schema) {
|
|
|
395
413
|
return null;
|
|
396
414
|
}
|
|
397
415
|
}
|
|
398
|
-
export { parseTrailingWildcard, AppError, notFound, badRequest, unauthorized, forbidden, conflict, rateLimited, STITCH_ERROR_STATUS, isStitchErrorCode, appError, ALL_TRANSPORTS, defineContract, defineErrors, createContractFactory, paginatedSchema, encodeCursor, decodeCursor };
|
|
416
|
+
export { parseTrailingWildcard, AppError, notFound, badRequest, unauthorized, forbidden, conflict, rateLimited, STITCH_ERROR_STATUS, isStitchErrorCode, appError, ALL_TRANSPORTS, defineContract, defineErrors, createContractFactory, ManagedFilePathSchema, ManagedFileRefSchema, paginatedSchema, encodeCursor, decodeCursor };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// src/internal/within-dir.ts
|
|
2
|
+
import { realpath } from "node:fs/promises";
|
|
3
|
+
import { sep } from "node:path";
|
|
4
|
+
function isWithinDir(root, target) {
|
|
5
|
+
const base = root.endsWith(sep) ? root.slice(0, -sep.length) : root;
|
|
6
|
+
return target === root || target === base || target.startsWith(base + sep);
|
|
7
|
+
}
|
|
8
|
+
async function realPathWithinDir(root, target) {
|
|
9
|
+
const [realRoot, realTarget] = await Promise.all([
|
|
10
|
+
realpath(root).catch(() => null),
|
|
11
|
+
realpath(target).catch(() => null)
|
|
12
|
+
]);
|
|
13
|
+
if (realRoot === null || realTarget === null)
|
|
14
|
+
return null;
|
|
15
|
+
return isWithinDir(realRoot, realTarget) ? realTarget : null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export { isWithinDir, realPathWithinDir };
|
|
@@ -1,119 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AppError,
|
|
3
|
+
isUnsafeKey
|
|
4
|
+
} from "./index-h60df7rj.js";
|
|
1
5
|
import {
|
|
2
6
|
isRecord,
|
|
3
7
|
resolveTraceContext
|
|
4
|
-
} from "./index-
|
|
5
|
-
|
|
6
|
-
// src/contract/errors.ts
|
|
7
|
-
var APP_ERROR_BRAND = Symbol.for("stitchkit.AppError");
|
|
8
|
-
|
|
9
|
-
class AppError extends Error {
|
|
10
|
-
code;
|
|
11
|
-
status;
|
|
12
|
-
details;
|
|
13
|
-
hint;
|
|
14
|
-
traceId;
|
|
15
|
-
constructor(code, message, status = 500, details, hint, traceId) {
|
|
16
|
-
super(message ?? code);
|
|
17
|
-
this.code = code;
|
|
18
|
-
this.status = status;
|
|
19
|
-
this.details = details;
|
|
20
|
-
this.hint = hint;
|
|
21
|
-
this.traceId = traceId;
|
|
22
|
-
this.name = "AppError";
|
|
23
|
-
Object.defineProperty(this, APP_ERROR_BRAND, { value: true });
|
|
24
|
-
}
|
|
25
|
-
static is(err) {
|
|
26
|
-
return typeof err === "object" && err !== null && APP_ERROR_BRAND in err;
|
|
27
|
-
}
|
|
28
|
-
toJSON() {
|
|
29
|
-
return {
|
|
30
|
-
error: {
|
|
31
|
-
code: this.code,
|
|
32
|
-
message: this.message,
|
|
33
|
-
...this.details && { details: this.details },
|
|
34
|
-
...this.hint && { hint: this.hint }
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
function notFound(message = "Not found") {
|
|
40
|
-
throw new AppError("NOT_FOUND", message, 404);
|
|
41
|
-
}
|
|
42
|
-
function badRequest(message, details) {
|
|
43
|
-
throw new AppError("BAD_REQUEST", message, 400, details);
|
|
44
|
-
}
|
|
45
|
-
function unauthorized(message = "Unauthorized") {
|
|
46
|
-
throw new AppError("UNAUTHORIZED", message, 401);
|
|
47
|
-
}
|
|
48
|
-
function forbidden(message = "Forbidden") {
|
|
49
|
-
throw new AppError("FORBIDDEN", message, 403);
|
|
50
|
-
}
|
|
51
|
-
function conflict(message = "Conflict", details) {
|
|
52
|
-
throw new AppError("CONFLICT", message, 409, details);
|
|
53
|
-
}
|
|
54
|
-
function rateLimited(message = "Too many requests") {
|
|
55
|
-
throw new AppError("RATE_LIMITED", message, 429);
|
|
56
|
-
}
|
|
57
|
-
var STITCH_ERROR_STATUS = {
|
|
58
|
-
BAD_REQUEST: 400,
|
|
59
|
-
UNAUTHORIZED: 401,
|
|
60
|
-
FORBIDDEN: 403,
|
|
61
|
-
NOT_FOUND: 404,
|
|
62
|
-
METHOD_NOT_ALLOWED: 405,
|
|
63
|
-
CONFLICT: 409,
|
|
64
|
-
RATE_LIMITED: 429,
|
|
65
|
-
VALIDATION_ERROR: 400,
|
|
66
|
-
REALTIME_CONTRACT_VIOLATION: 500,
|
|
67
|
-
INTERNAL_SERVER_ERROR: 500
|
|
68
|
-
};
|
|
69
|
-
function isStitchErrorCode(code) {
|
|
70
|
-
return Object.hasOwn(STITCH_ERROR_STATUS, code);
|
|
71
|
-
}
|
|
72
|
-
function appError(code, message, details) {
|
|
73
|
-
throw new AppError(code, message, isStitchErrorCode(code) ? STITCH_ERROR_STATUS[code] : 500, details);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// src/contract/define.ts
|
|
77
|
-
import { z } from "zod";
|
|
78
|
-
|
|
79
|
-
// src/internal/safe-json.ts
|
|
80
|
-
function isUnsafeKey(key) {
|
|
81
|
-
return key === "__proto__";
|
|
82
|
-
}
|
|
83
|
-
function safeJsonParse(text) {
|
|
84
|
-
return JSON.parse(text, (key, value) => isUnsafeKey(key) ? undefined : value);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// src/contract/define.ts
|
|
88
|
-
function mergeMeta(contractMeta, endpointMeta) {
|
|
89
|
-
if (!contractMeta)
|
|
90
|
-
return endpointMeta;
|
|
91
|
-
if (!endpointMeta)
|
|
92
|
-
return { ...contractMeta };
|
|
93
|
-
return { ...contractMeta, ...endpointMeta };
|
|
94
|
-
}
|
|
95
|
-
// src/contract/errors-factory.ts
|
|
96
|
-
import { z as z2 } from "zod";
|
|
97
|
-
// src/contract/pagination.ts
|
|
98
|
-
import { z as z3 } from "zod";
|
|
8
|
+
} from "./index-7eq9va6e.js";
|
|
99
9
|
|
|
100
|
-
// src/internal/base64url.ts
|
|
101
|
-
function bytesToBase64Url(bytes) {
|
|
102
|
-
let binary = "";
|
|
103
|
-
for (const byte of bytes)
|
|
104
|
-
binary += String.fromCharCode(byte);
|
|
105
|
-
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
106
|
-
}
|
|
107
|
-
function base64UrlToBytes(segment) {
|
|
108
|
-
if (!/^[A-Za-z0-9_-]*$/.test(segment) || segment.length % 4 === 1) {
|
|
109
|
-
throw new Error("invalid base64url segment");
|
|
110
|
-
}
|
|
111
|
-
const b64 = segment.replace(/-/g, "+").replace(/_/g, "/");
|
|
112
|
-
const padded = b64.padEnd(Math.ceil(b64.length / 4) * 4, "=");
|
|
113
|
-
return Uint8Array.from(atob(padded), (c) => c.charCodeAt(0));
|
|
114
|
-
}
|
|
115
10
|
// src/internal/errors.ts
|
|
116
|
-
import { z
|
|
11
|
+
import { z } from "zod";
|
|
117
12
|
function issuePath(path) {
|
|
118
13
|
return path.length > 0 ? path.map(String).join(".") : "(root)";
|
|
119
14
|
}
|
|
@@ -136,7 +31,7 @@ var MAX_DETAIL_ISSUES = 20;
|
|
|
136
31
|
function errorCode(err) {
|
|
137
32
|
if (AppError.is(err))
|
|
138
33
|
return err.code;
|
|
139
|
-
if (err instanceof
|
|
34
|
+
if (err instanceof z.ZodError)
|
|
140
35
|
return "VALIDATION_ERROR";
|
|
141
36
|
return;
|
|
142
37
|
}
|
|
@@ -156,7 +51,7 @@ function normalizeError(err) {
|
|
|
156
51
|
}
|
|
157
52
|
return err;
|
|
158
53
|
}
|
|
159
|
-
if (err instanceof
|
|
54
|
+
if (err instanceof z.ZodError) {
|
|
160
55
|
return new AppError("VALIDATION_ERROR", formatZodError(err), 400, {
|
|
161
56
|
issues: zodIssues(err).slice(0, MAX_DETAIL_ISSUES)
|
|
162
57
|
});
|
|
@@ -322,4 +217,4 @@ function wrapInRequestContext(handler, options = {}) {
|
|
|
322
217
|
};
|
|
323
218
|
}
|
|
324
219
|
|
|
325
|
-
export {
|
|
220
|
+
export { formatZodError, zodIssues, errorCode, recordedErrorMessage, normalizeError, validateDeclaredOutput, generateTraceId, resolveTraceId, resolveSocketIp, extractIp, getClientInfo, parseQueryParams, runWithRequestContext, getRequestContext, getTraceId, getUserId, setRequestUser, setRequestEndpoint, setRequestDimensions, setRequestError, wrapInRequestContext };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ALL_TRANSPORTS,
|
|
3
3
|
AppError,
|
|
4
|
+
ManagedFilePathSchema,
|
|
5
|
+
ManagedFileRefSchema,
|
|
4
6
|
STITCH_ERROR_STATUS,
|
|
5
7
|
appError,
|
|
6
8
|
badRequest,
|
|
@@ -17,7 +19,7 @@ import {
|
|
|
17
19
|
parseTrailingWildcard,
|
|
18
20
|
rateLimited,
|
|
19
21
|
unauthorized
|
|
20
|
-
} from "./index-
|
|
22
|
+
} from "./index-rgd18hx3.js";
|
|
21
23
|
import {
|
|
22
24
|
isRecord,
|
|
23
25
|
mapObject,
|
|
@@ -806,6 +808,39 @@ function createUrlBuilders(contracts, source, contractConfig) {
|
|
|
806
808
|
function createScopedUrlBuilders(contracts, source, scopeConfigs) {
|
|
807
809
|
return buildScopedRegistry(contracts, scopeConfigs, "URL builder", (contract, config) => createUrlBuilder(contract, source, config));
|
|
808
810
|
}
|
|
811
|
+
// src/realtime/request.ts
|
|
812
|
+
class RealtimeRequestTimeoutError extends Error {
|
|
813
|
+
code = "REALTIME_REQUEST_TIMEOUT";
|
|
814
|
+
event;
|
|
815
|
+
timeoutMs;
|
|
816
|
+
constructor(event, timeoutMs) {
|
|
817
|
+
super(`Realtime request "${event}" timed out after ${timeoutMs}ms`);
|
|
818
|
+
this.name = "RealtimeRequestTimeoutError";
|
|
819
|
+
this.event = event;
|
|
820
|
+
this.timeoutMs = timeoutMs;
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
class RealtimeRequestDisconnectedError extends Error {
|
|
825
|
+
code = "REALTIME_REQUEST_DISCONNECTED";
|
|
826
|
+
event;
|
|
827
|
+
constructor(event) {
|
|
828
|
+
super(`Realtime request "${event}" was interrupted by disconnect`);
|
|
829
|
+
this.name = "RealtimeRequestDisconnectedError";
|
|
830
|
+
this.event = event;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
class RealtimeRequestInvalidAcknowledgementError extends Error {
|
|
835
|
+
code = "REALTIME_REQUEST_INVALID_ACKNOWLEDGEMENT";
|
|
836
|
+
event;
|
|
837
|
+
constructor(event, cause) {
|
|
838
|
+
super(`Realtime request "${event}" received an invalid acknowledgement`, { cause });
|
|
839
|
+
this.name = "RealtimeRequestInvalidAcknowledgementError";
|
|
840
|
+
this.event = event;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
|
|
809
844
|
// src/realtime/rejection.ts
|
|
810
845
|
import { z as z2 } from "zod";
|
|
811
846
|
|
|
@@ -884,6 +919,45 @@ function reportRejected(rejected, hook, logger) {
|
|
|
884
919
|
console.error("[stitchkit] realtime rejection hook failed", error);
|
|
885
920
|
});
|
|
886
921
|
}
|
|
922
|
+
function parseRealtimeRequestArguments(registry, event, direction, args) {
|
|
923
|
+
const definition = eventDefinition(registry, event, direction);
|
|
924
|
+
const parsed = definition.args.safeParse(args);
|
|
925
|
+
if (!parsed.success) {
|
|
926
|
+
throw realtimeContractViolation({
|
|
927
|
+
event,
|
|
928
|
+
direction,
|
|
929
|
+
phase: "arguments",
|
|
930
|
+
reason: "invalid-arguments",
|
|
931
|
+
fault: "local",
|
|
932
|
+
cause: parsed.error
|
|
933
|
+
}).error;
|
|
934
|
+
}
|
|
935
|
+
if (!definition.ack) {
|
|
936
|
+
throw realtimeContractViolation({
|
|
937
|
+
event,
|
|
938
|
+
direction,
|
|
939
|
+
phase: "acknowledgement",
|
|
940
|
+
reason: "missing-acknowledgement",
|
|
941
|
+
fault: "local"
|
|
942
|
+
}).error;
|
|
943
|
+
}
|
|
944
|
+
return parsed.data;
|
|
945
|
+
}
|
|
946
|
+
function parseRealtimeRequestAcknowledgement(schema, event, direction, value, hook, logger) {
|
|
947
|
+
const parsed = schema.safeParse(value);
|
|
948
|
+
if (parsed.success)
|
|
949
|
+
return parsed.data;
|
|
950
|
+
const rejected = realtimeContractViolation({
|
|
951
|
+
event,
|
|
952
|
+
direction,
|
|
953
|
+
phase: "acknowledgement",
|
|
954
|
+
reason: "invalid-acknowledgement-value",
|
|
955
|
+
fault: "peer",
|
|
956
|
+
cause: parsed.error
|
|
957
|
+
});
|
|
958
|
+
reportRejected(rejected, hook, logger);
|
|
959
|
+
throw new RealtimeRequestInvalidAcknowledgementError(event, rejected.error);
|
|
960
|
+
}
|
|
887
961
|
function createValidatedRealtimeSocket({
|
|
888
962
|
target,
|
|
889
963
|
inbound,
|
|
@@ -1070,6 +1144,25 @@ function createRealtimeClient(contract, { onRejected, logger, ...config }) {
|
|
|
1070
1144
|
};
|
|
1071
1145
|
}
|
|
1072
1146
|
});
|
|
1147
|
+
async function request(event, ...args) {
|
|
1148
|
+
const options = args.at(-1);
|
|
1149
|
+
if (!options || typeof options !== "object" || !("timeoutMs" in options)) {
|
|
1150
|
+
throw new TypeError(`Realtime request "${event}" requires { timeoutMs }`);
|
|
1151
|
+
}
|
|
1152
|
+
const timeoutMs = options.timeoutMs;
|
|
1153
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
1154
|
+
throw new RangeError(`Realtime request "${event}" timeoutMs must be finite and > 0`);
|
|
1155
|
+
}
|
|
1156
|
+
const values = args.slice(0, -1);
|
|
1157
|
+
const parsedArgs = parseRealtimeRequestArguments(contract.clientToServer, event, "client-outbound", values);
|
|
1158
|
+
const definition = contract.clientToServer[event];
|
|
1159
|
+
if (!definition?.ack) {
|
|
1160
|
+
throw new Error(`Realtime request "${event}" has no acknowledgement schema`);
|
|
1161
|
+
}
|
|
1162
|
+
const value = await transport.emitWithAck(event, parsedArgs, { timeoutMs });
|
|
1163
|
+
const acknowledgement = parseRealtimeRequestAcknowledgement(definition.ack, event, "client-inbound", value, onRejected, logger);
|
|
1164
|
+
return acknowledgement;
|
|
1165
|
+
}
|
|
1073
1166
|
return {
|
|
1074
1167
|
...events,
|
|
1075
1168
|
connect: transport.connect,
|
|
@@ -1077,6 +1170,7 @@ function createRealtimeClient(contract, { onRejected, logger, ...config }) {
|
|
|
1077
1170
|
get connected() {
|
|
1078
1171
|
return transport.connected;
|
|
1079
1172
|
},
|
|
1173
|
+
request,
|
|
1080
1174
|
onConnectionChange: transport.onConnectionChange
|
|
1081
1175
|
};
|
|
1082
1176
|
}
|
|
@@ -1084,6 +1178,7 @@ function createSocketIOClient(config) {
|
|
|
1084
1178
|
let socket = null;
|
|
1085
1179
|
let desiredConnected = false;
|
|
1086
1180
|
let reconnectTimer = null;
|
|
1181
|
+
const pendingRequestDisconnects = new Set;
|
|
1087
1182
|
const serverDisconnectDelay = config.reconnectOnServerDisconnect ?? 1000;
|
|
1088
1183
|
const connectionListeners = new Set;
|
|
1089
1184
|
const subscriptions = new Set;
|
|
@@ -1169,6 +1264,8 @@ function createSocketIOClient(config) {
|
|
|
1169
1264
|
disconnect() {
|
|
1170
1265
|
desiredConnected = false;
|
|
1171
1266
|
clearReconnectTimer();
|
|
1267
|
+
for (const reject of [...pendingRequestDisconnects])
|
|
1268
|
+
reject();
|
|
1172
1269
|
if (!socket)
|
|
1173
1270
|
return;
|
|
1174
1271
|
const wasConnected = socket.connected;
|
|
@@ -1204,6 +1301,31 @@ function createSocketIOClient(config) {
|
|
|
1204
1301
|
socket.emit(name, ...args);
|
|
1205
1302
|
return true;
|
|
1206
1303
|
},
|
|
1304
|
+
emitWithAck(event, args, options) {
|
|
1305
|
+
const active = socket;
|
|
1306
|
+
if (!active?.connected) {
|
|
1307
|
+
return Promise.reject(new RealtimeRequestDisconnectedError(event));
|
|
1308
|
+
}
|
|
1309
|
+
return new Promise((resolve, reject) => {
|
|
1310
|
+
let settled = false;
|
|
1311
|
+
const finish = (result) => {
|
|
1312
|
+
if (settled)
|
|
1313
|
+
return;
|
|
1314
|
+
settled = true;
|
|
1315
|
+
pendingRequestDisconnects.delete(onDisconnect);
|
|
1316
|
+
active.off("disconnect", onDisconnect);
|
|
1317
|
+
result();
|
|
1318
|
+
};
|
|
1319
|
+
const onDisconnect = () => {
|
|
1320
|
+
finish(() => reject(new RealtimeRequestDisconnectedError(event)));
|
|
1321
|
+
};
|
|
1322
|
+
pendingRequestDisconnects.add(onDisconnect);
|
|
1323
|
+
active.on("disconnect", onDisconnect);
|
|
1324
|
+
active.timeout(options.timeoutMs).emitWithAck(event, ...args).then((value) => finish(() => resolve(value)), () => {
|
|
1325
|
+
finish(() => reject(active.connected ? new RealtimeRequestTimeoutError(event, options.timeoutMs) : new RealtimeRequestDisconnectedError(event)));
|
|
1326
|
+
});
|
|
1327
|
+
});
|
|
1328
|
+
},
|
|
1207
1329
|
onConnectionChange(listener) {
|
|
1208
1330
|
connectionListeners.add(listener);
|
|
1209
1331
|
return () => connectionListeners.delete(listener);
|
|
@@ -1249,39 +1371,44 @@ function defineRealtimeContract(contract) {
|
|
|
1249
1371
|
return contract;
|
|
1250
1372
|
}
|
|
1251
1373
|
export {
|
|
1252
|
-
|
|
1253
|
-
rateLimited,
|
|
1254
|
-
parseTraceparent,
|
|
1255
|
-
parseSSE,
|
|
1256
|
-
paginatedSchema,
|
|
1257
|
-
notFound,
|
|
1258
|
-
isStitchErrorCode,
|
|
1259
|
-
formatTraceparent,
|
|
1260
|
-
forbidden,
|
|
1261
|
-
encodeCursor,
|
|
1262
|
-
defineRealtimeContract,
|
|
1263
|
-
defineErrors,
|
|
1264
|
-
defineContract,
|
|
1265
|
-
decodeCursor,
|
|
1266
|
-
createUrlBuilders,
|
|
1267
|
-
createUrlBuilder,
|
|
1268
|
-
createTraceContext,
|
|
1269
|
-
createSocketIOClient,
|
|
1270
|
-
createScopedUrlBuilders,
|
|
1271
|
-
createScopedClients,
|
|
1272
|
-
createRetainedTopics,
|
|
1273
|
-
createRealtimeClient,
|
|
1274
|
-
createHttpClient,
|
|
1275
|
-
createContractFactory,
|
|
1276
|
-
createClients,
|
|
1277
|
-
createClient,
|
|
1278
|
-
contractEndpointMatchers,
|
|
1279
|
-
conflict,
|
|
1280
|
-
childSpan,
|
|
1281
|
-
badRequest,
|
|
1282
|
-
appError,
|
|
1283
|
-
STITCH_ERROR_STATUS,
|
|
1284
|
-
AppError,
|
|
1374
|
+
ALL_TRANSPORTS,
|
|
1285
1375
|
ApiError,
|
|
1286
|
-
|
|
1376
|
+
AppError,
|
|
1377
|
+
ManagedFilePathSchema,
|
|
1378
|
+
ManagedFileRefSchema,
|
|
1379
|
+
RealtimeRequestDisconnectedError,
|
|
1380
|
+
RealtimeRequestInvalidAcknowledgementError,
|
|
1381
|
+
RealtimeRequestTimeoutError,
|
|
1382
|
+
STITCH_ERROR_STATUS,
|
|
1383
|
+
appError,
|
|
1384
|
+
badRequest,
|
|
1385
|
+
childSpan,
|
|
1386
|
+
conflict,
|
|
1387
|
+
contractEndpointMatchers,
|
|
1388
|
+
createClient,
|
|
1389
|
+
createClients,
|
|
1390
|
+
createContractFactory,
|
|
1391
|
+
createHttpClient,
|
|
1392
|
+
createRealtimeClient,
|
|
1393
|
+
createRetainedTopics,
|
|
1394
|
+
createScopedClients,
|
|
1395
|
+
createScopedUrlBuilders,
|
|
1396
|
+
createSocketIOClient,
|
|
1397
|
+
createTraceContext,
|
|
1398
|
+
createUrlBuilder,
|
|
1399
|
+
createUrlBuilders,
|
|
1400
|
+
decodeCursor,
|
|
1401
|
+
defineContract,
|
|
1402
|
+
defineErrors,
|
|
1403
|
+
defineRealtimeContract,
|
|
1404
|
+
encodeCursor,
|
|
1405
|
+
forbidden,
|
|
1406
|
+
formatTraceparent,
|
|
1407
|
+
isStitchErrorCode,
|
|
1408
|
+
notFound,
|
|
1409
|
+
paginatedSchema,
|
|
1410
|
+
parseSSE,
|
|
1411
|
+
parseTraceparent,
|
|
1412
|
+
rateLimited,
|
|
1413
|
+
unauthorized
|
|
1287
1414
|
};
|
package/dist/node.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
createHandler,
|
|
9
9
|
createServerLifecycle,
|
|
10
10
|
createSocketIOServer
|
|
11
|
-
} from "./index-
|
|
11
|
+
} from "./index-npye5kt7.js";
|
|
12
12
|
import {
|
|
13
13
|
createImplement,
|
|
14
14
|
createImplementRegistry,
|
|
@@ -17,7 +17,8 @@ import {
|
|
|
17
17
|
createScopedImplementRegistry,
|
|
18
18
|
implement,
|
|
19
19
|
implementRegistry
|
|
20
|
-
} from "./index-
|
|
20
|
+
} from "./index-pzc32v75.js";
|
|
21
|
+
import"./index-v4bbq3p4.js";
|
|
21
22
|
import {
|
|
22
23
|
AppError,
|
|
23
24
|
appError,
|
|
@@ -27,8 +28,10 @@ import {
|
|
|
27
28
|
notFound,
|
|
28
29
|
rateLimited,
|
|
29
30
|
unauthorized
|
|
30
|
-
} from "./index-
|
|
31
|
-
import"./index-
|
|
31
|
+
} from "./index-h60df7rj.js";
|
|
32
|
+
import"./index-6k1937bx.js";
|
|
33
|
+
import"./index-7eq9va6e.js";
|
|
34
|
+
import"./index-1bx83sw4.js";
|
|
32
35
|
// src/server/node.ts
|
|
33
36
|
import { serve } from "srvx/node";
|
|
34
37
|
async function serveNode(config) {
|
|
@@ -139,28 +142,28 @@ async function createNodeSocketIOServer(config) {
|
|
|
139
142
|
return createSocketIOServer(config);
|
|
140
143
|
}
|
|
141
144
|
export {
|
|
142
|
-
|
|
143
|
-
serveNode,
|
|
144
|
-
rateLimited,
|
|
145
|
-
notFound,
|
|
146
|
-
implementRegistry,
|
|
147
|
-
implement,
|
|
148
|
-
forbidden,
|
|
149
|
-
createNodeSocketIOServer as createSocketIOServer,
|
|
150
|
-
createScopedImplementRegistry,
|
|
151
|
-
createScopedImplement,
|
|
152
|
-
createMultipartStream,
|
|
153
|
-
createImplementRegistry,
|
|
154
|
-
createImplement,
|
|
155
|
-
createHandler,
|
|
156
|
-
conflict,
|
|
157
|
-
bindRealtimeServer,
|
|
158
|
-
bindProcessSignals,
|
|
159
|
-
badRequest,
|
|
160
|
-
appError,
|
|
161
|
-
ShutdownStatusSchema,
|
|
162
|
-
ShutdownStateSchema,
|
|
163
|
-
ShutdownResultSchema,
|
|
145
|
+
AppError,
|
|
164
146
|
ShutdownOptionsSchema,
|
|
165
|
-
|
|
147
|
+
ShutdownResultSchema,
|
|
148
|
+
ShutdownStateSchema,
|
|
149
|
+
ShutdownStatusSchema,
|
|
150
|
+
appError,
|
|
151
|
+
badRequest,
|
|
152
|
+
bindProcessSignals,
|
|
153
|
+
bindRealtimeServer,
|
|
154
|
+
conflict,
|
|
155
|
+
createHandler,
|
|
156
|
+
createImplement,
|
|
157
|
+
createImplementRegistry,
|
|
158
|
+
createMultipartStream,
|
|
159
|
+
createScopedImplement,
|
|
160
|
+
createScopedImplementRegistry,
|
|
161
|
+
createNodeSocketIOServer as createSocketIOServer,
|
|
162
|
+
forbidden,
|
|
163
|
+
implement,
|
|
164
|
+
implementRegistry,
|
|
165
|
+
notFound,
|
|
166
|
+
rateLimited,
|
|
167
|
+
serveNode,
|
|
168
|
+
unauthorized
|
|
166
169
|
};
|