stitchkit 0.45.0 → 0.46.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 +1 -1
- package/dist/browser/http.d.ts.map +1 -1
- package/dist/browser/socket-io.d.ts +3 -1
- package/dist/browser/socket-io.d.ts.map +1 -1
- package/dist/cli.js +3 -4
- package/dist/contract/errors.d.ts +1 -0
- package/dist/contract/errors.d.ts.map +1 -1
- package/dist/contract/index.js +2 -2
- package/dist/{index-yh0p5ddn.js → index-5s8b7z6q.js} +31 -11
- package/dist/{index-mvjyq9h9.js → index-62pqb23z.js} +309 -144
- package/dist/index-c40tkxcd.js +181 -0
- package/dist/{index-r6czv7yt.js → index-pwyedf7b.js} +4 -3
- package/dist/{index-6jypn22c.js → index-r1qp4rve.js} +9 -1
- package/dist/{index-s4qsmgwe.js → index-tss6bk5c.js} +4 -1
- package/dist/{index-36f1xkdb.js → index-w1s873ng.js} +2 -2
- package/dist/index-yxpe3phd.js +63 -0
- package/dist/{index-w0mec79h.js → index-zwqty9zf.js} +261 -281
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +129 -51
- package/dist/internal/errors.d.ts.map +1 -1
- package/dist/internal/secure-fetch.d.ts +23 -3
- package/dist/internal/secure-fetch.d.ts.map +1 -1
- package/dist/internal/typed.d.ts +7 -0
- package/dist/internal/typed.d.ts.map +1 -1
- package/dist/internal/within-dir.d.ts +6 -0
- package/dist/internal/within-dir.d.ts.map +1 -1
- package/dist/logger.d.ts +8 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/node.js +3 -4
- package/dist/observability/audit.d.ts.map +1 -1
- package/dist/observability/event.d.ts +7 -0
- package/dist/observability/event.d.ts.map +1 -1
- package/dist/observability/index.js +54 -125
- package/dist/observability/sanitize.d.ts +8 -1
- package/dist/observability/sanitize.d.ts.map +1 -1
- package/dist/observability/trace.d.ts +3 -1
- package/dist/observability/trace.d.ts.map +1 -1
- package/dist/react/cache-bridge.d.ts +4 -0
- package/dist/react/cache-bridge.d.ts.map +1 -1
- package/dist/react.js +46 -5
- package/dist/realtime/contract.d.ts +4 -1
- package/dist/realtime/contract.d.ts.map +1 -1
- package/dist/realtime/rejection.d.ts +10 -0
- package/dist/realtime/rejection.d.ts.map +1 -0
- package/dist/realtime/socket.d.ts +14 -1
- package/dist/realtime/socket.d.ts.map +1 -1
- package/dist/server/cache.d.ts +5 -1
- package/dist/server/cache.d.ts.map +1 -1
- package/dist/server/error-hook.d.ts +1 -0
- package/dist/server/error-hook.d.ts.map +1 -1
- package/dist/server/file.d.ts +6 -0
- package/dist/server/file.d.ts.map +1 -1
- package/dist/server/implement.d.ts.map +1 -1
- package/dist/server/index.d.ts +2 -3
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +108 -11
- package/dist/server/middleware/cors.d.ts.map +1 -1
- package/dist/server/openapi.d.ts.map +1 -1
- package/dist/server/realtime.d.ts +2 -0
- package/dist/server/realtime.d.ts.map +1 -1
- package/dist/server/router.d.ts +0 -8
- package/dist/server/router.d.ts.map +1 -1
- package/dist/server/stream.d.ts.map +1 -1
- package/dist/server/types.d.ts +2 -7
- package/dist/server/types.d.ts.map +1 -1
- package/dist/tools/cli-args.d.ts +11 -3
- package/dist/tools/cli-args.d.ts.map +1 -1
- package/dist/tools/cli.d.ts +5 -0
- package/dist/tools/cli.d.ts.map +1 -1
- package/dist/tools/coerce.d.ts.map +1 -1
- package/dist/tools/json-schema.d.ts.map +1 -1
- package/dist/tools/mcp-handler.d.ts +4 -3
- package/dist/tools/mcp-handler.d.ts.map +1 -1
- package/dist/tools/mcp-prepare.d.ts +95 -0
- package/dist/tools/mcp-prepare.d.ts.map +1 -0
- package/dist/tools/mcp-round.d.ts +4 -0
- package/dist/tools/mcp-round.d.ts.map +1 -1
- package/dist/tools/mcp-stdio.d.ts +2 -1
- package/dist/tools/mcp-stdio.d.ts.map +1 -1
- package/dist/tools/mcp.d.ts +2 -78
- package/dist/tools/mcp.d.ts.map +1 -1
- package/dist/tools/mount-download.d.ts +5 -0
- package/dist/tools/mount-download.d.ts.map +1 -1
- package/dist/tools/native-mcp.d.ts +1 -1
- package/dist/tools/native-mcp.d.ts.map +1 -1
- package/dist/tools/oauth-provider.d.ts +20 -1
- package/dist/tools/oauth-provider.d.ts.map +1 -1
- package/dist/tools/tool-logger.d.ts +1 -1
- package/dist/tools/tool-logger.d.ts.map +1 -1
- package/dist/tools/view-file.d.ts +9 -0
- package/dist/tools/view-file.d.ts.map +1 -1
- package/dist/tools.d.ts +2 -1
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +342 -214
- package/llms-full.txt +107 -19
- package/llms.txt +1 -1
- package/package.json +2 -2
- package/dist/index-frfyw9fa.js +0 -33
- package/dist/index-x3fcszf8.js +0 -8
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isRecord,
|
|
3
|
+
isUnsafeKey
|
|
4
|
+
} from "./index-5s8b7z6q.js";
|
|
5
|
+
|
|
6
|
+
// src/observability/sanitize.ts
|
|
7
|
+
var SENSITIVE_WORDS = new Set([
|
|
8
|
+
"password",
|
|
9
|
+
"passwords",
|
|
10
|
+
"passwd",
|
|
11
|
+
"pwd",
|
|
12
|
+
"secret",
|
|
13
|
+
"secrets",
|
|
14
|
+
"token",
|
|
15
|
+
"tokens",
|
|
16
|
+
"apikey",
|
|
17
|
+
"auth",
|
|
18
|
+
"authorization",
|
|
19
|
+
"bearer",
|
|
20
|
+
"cookie",
|
|
21
|
+
"cookies",
|
|
22
|
+
"credential",
|
|
23
|
+
"credentials"
|
|
24
|
+
]);
|
|
25
|
+
var SENSITIVE_WORD_PAIRS = new Set([
|
|
26
|
+
"apikey",
|
|
27
|
+
"privatekey",
|
|
28
|
+
"initdata",
|
|
29
|
+
"sessionid",
|
|
30
|
+
"sessionkey"
|
|
31
|
+
]);
|
|
32
|
+
var SENSITIVE_EXACT = new Set(["session"]);
|
|
33
|
+
function splitKeyWords(key) {
|
|
34
|
+
return key.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").toLowerCase().split(/[^a-z0-9]+/).filter((word) => word.length > 0);
|
|
35
|
+
}
|
|
36
|
+
function isSensitiveKeyDefault(key) {
|
|
37
|
+
const words = splitKeyWords(key);
|
|
38
|
+
if (words.length === 1 && SENSITIVE_EXACT.has(words[0] ?? ""))
|
|
39
|
+
return true;
|
|
40
|
+
for (let index = 0;index < words.length; index += 1) {
|
|
41
|
+
const word = words[index] ?? "";
|
|
42
|
+
if (SENSITIVE_WORDS.has(word))
|
|
43
|
+
return true;
|
|
44
|
+
const next = words[index + 1];
|
|
45
|
+
if (next !== undefined && SENSITIVE_WORD_PAIRS.has(word + next))
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
var DEFAULT_MAX_BYTES = 16000;
|
|
51
|
+
var DEFAULT_MAX_DEPTH = 20;
|
|
52
|
+
var MASK = "[redacted]";
|
|
53
|
+
function redact(value, options = {}) {
|
|
54
|
+
const custom = options.sensitiveKeys;
|
|
55
|
+
const isSensitive = custom === undefined ? isSensitiveKeyDefault : (key) => {
|
|
56
|
+
custom.lastIndex = 0;
|
|
57
|
+
return custom.test(key);
|
|
58
|
+
};
|
|
59
|
+
const maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH;
|
|
60
|
+
const ancestors = new Set;
|
|
61
|
+
const walk = (input, depth) => {
|
|
62
|
+
if (depth > maxDepth)
|
|
63
|
+
return "[max depth]";
|
|
64
|
+
if (input === null || input === undefined)
|
|
65
|
+
return null;
|
|
66
|
+
if (typeof input === "string" || typeof input === "number" || typeof input === "boolean") {
|
|
67
|
+
return input;
|
|
68
|
+
}
|
|
69
|
+
if (input instanceof Uint8Array || input instanceof ArrayBuffer) {
|
|
70
|
+
return { _type: "binary", size: input.byteLength };
|
|
71
|
+
}
|
|
72
|
+
if (input instanceof Blob) {
|
|
73
|
+
return { _type: "blob", size: input.size, mime: input.type };
|
|
74
|
+
}
|
|
75
|
+
if (typeof FormData !== "undefined" && input instanceof FormData) {
|
|
76
|
+
return { _type: "formdata", keys: [...input.keys()] };
|
|
77
|
+
}
|
|
78
|
+
if (input instanceof Map) {
|
|
79
|
+
if (ancestors.has(input))
|
|
80
|
+
return "[circular]";
|
|
81
|
+
ancestors.add(input);
|
|
82
|
+
const result = [...input.entries()].map(([key, value2]) => [
|
|
83
|
+
walk(key, depth + 1),
|
|
84
|
+
walk(value2, depth + 1)
|
|
85
|
+
]);
|
|
86
|
+
ancestors.delete(input);
|
|
87
|
+
return { _type: "map", entries: result };
|
|
88
|
+
}
|
|
89
|
+
if (input instanceof Set) {
|
|
90
|
+
if (ancestors.has(input))
|
|
91
|
+
return "[circular]";
|
|
92
|
+
ancestors.add(input);
|
|
93
|
+
const values = [...input].map((item) => walk(item, depth + 1));
|
|
94
|
+
ancestors.delete(input);
|
|
95
|
+
return { _type: "set", values };
|
|
96
|
+
}
|
|
97
|
+
if (input instanceof Error) {
|
|
98
|
+
return {
|
|
99
|
+
_type: "error",
|
|
100
|
+
name: input.name,
|
|
101
|
+
message: input.message,
|
|
102
|
+
...input.stack !== undefined && { stack: input.stack }
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
if (Array.isArray(input)) {
|
|
106
|
+
if (ancestors.has(input))
|
|
107
|
+
return "[circular]";
|
|
108
|
+
ancestors.add(input);
|
|
109
|
+
const result = input.map((item) => walk(item, depth + 1));
|
|
110
|
+
ancestors.delete(input);
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
113
|
+
if (isRecord(input)) {
|
|
114
|
+
if (ancestors.has(input))
|
|
115
|
+
return "[circular]";
|
|
116
|
+
ancestors.add(input);
|
|
117
|
+
const out = {};
|
|
118
|
+
for (const key of Object.keys(input)) {
|
|
119
|
+
if (isUnsafeKey(key))
|
|
120
|
+
continue;
|
|
121
|
+
if (isSensitive(key)) {
|
|
122
|
+
out[key] = MASK;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
out[key] = walk(Reflect.get(input, key), depth + 1);
|
|
127
|
+
} catch {
|
|
128
|
+
out[key] = "[unreadable]";
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
ancestors.delete(input);
|
|
132
|
+
return out;
|
|
133
|
+
}
|
|
134
|
+
return String(input);
|
|
135
|
+
};
|
|
136
|
+
return walk(value, 0);
|
|
137
|
+
}
|
|
138
|
+
function truncatePreview(value, maxBytes = DEFAULT_MAX_BYTES) {
|
|
139
|
+
const json = JSON.stringify(value);
|
|
140
|
+
const bytes = new TextEncoder().encode(json);
|
|
141
|
+
if (bytes.byteLength <= maxBytes)
|
|
142
|
+
return value;
|
|
143
|
+
let end = Math.max(0, maxBytes);
|
|
144
|
+
while (end > 0 && ((bytes[end] ?? 0) & 192) === 128)
|
|
145
|
+
end -= 1;
|
|
146
|
+
const preview = new TextDecoder().decode(bytes.subarray(0, end));
|
|
147
|
+
return {
|
|
148
|
+
_truncated: true,
|
|
149
|
+
_originalBytes: bytes.byteLength,
|
|
150
|
+
preview: `${preview}…`
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
function sanitizePayload(value, options = {}) {
|
|
154
|
+
if (value === undefined || value === null)
|
|
155
|
+
return null;
|
|
156
|
+
try {
|
|
157
|
+
return truncatePreview(redact(value, options), options.maxBytes ?? DEFAULT_MAX_BYTES);
|
|
158
|
+
} catch {
|
|
159
|
+
return "[unserializable]";
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
function measureSize(value) {
|
|
163
|
+
if (value === undefined || value === null) {
|
|
164
|
+
return { resultSize: null, responseBytes: 0 };
|
|
165
|
+
}
|
|
166
|
+
let responseBytes = 0;
|
|
167
|
+
try {
|
|
168
|
+
responseBytes = new TextEncoder().encode(JSON.stringify(value)).byteLength;
|
|
169
|
+
} catch {
|
|
170
|
+
return { resultSize: null, responseBytes: 0, unserializable: true };
|
|
171
|
+
}
|
|
172
|
+
if (Array.isArray(value)) {
|
|
173
|
+
return { resultSize: value.length, responseBytes };
|
|
174
|
+
}
|
|
175
|
+
if (isRecord(value) && Array.isArray(value.items) && "nextCursor" in value) {
|
|
176
|
+
return { resultSize: value.items.length, responseBytes };
|
|
177
|
+
}
|
|
178
|
+
return { resultSize: null, responseBytes };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export { redact, truncatePreview, sanitizePayload, measureSize };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
mapObjectTypeBoundary
|
|
3
|
-
} from "./index-
|
|
3
|
+
} from "./index-tss6bk5c.js";
|
|
4
4
|
|
|
5
5
|
// src/contract/define.ts
|
|
6
6
|
import { z } from "zod";
|
|
@@ -228,10 +228,11 @@ var STITCH_ERROR_STATUS = {
|
|
|
228
228
|
CONFLICT: 409,
|
|
229
229
|
RATE_LIMITED: 429,
|
|
230
230
|
VALIDATION_ERROR: 400,
|
|
231
|
+
REALTIME_CONTRACT_VIOLATION: 500,
|
|
231
232
|
INTERNAL_SERVER_ERROR: 500
|
|
232
233
|
};
|
|
233
234
|
function isStitchErrorCode(code) {
|
|
234
|
-
return code
|
|
235
|
+
return Object.hasOwn(STITCH_ERROR_STATUS, code);
|
|
235
236
|
}
|
|
236
237
|
function appError(code, message, details) {
|
|
237
238
|
throw new AppError(code, message, isStitchErrorCode(code) ? STITCH_ERROR_STATUS[code] : 500, details);
|
|
@@ -342,4 +343,4 @@ function decodeCursor(cursor, schema) {
|
|
|
342
343
|
return null;
|
|
343
344
|
}
|
|
344
345
|
}
|
|
345
|
-
export { parseTrailingWildcard,
|
|
346
|
+
export { parseTrailingWildcard, AppError, notFound, badRequest, unauthorized, forbidden, conflict, rateLimited, STITCH_ERROR_STATUS, isStitchErrorCode, appError, ALL_TRANSPORTS, defineContract, defineErrors, createContractFactory, paginatedSchema, encodeCursor, decodeCursor };
|
|
@@ -5,9 +5,17 @@ function assertCorsConfig(config) {
|
|
|
5
5
|
if (config.credentials && (config.origin === undefined || config.origin === "*")) {
|
|
6
6
|
throw new Error("[stitchkit] cors: `credentials: true` cannot be combined with a wildcard origin. " + "Set `origin` to an explicit string or list.");
|
|
7
7
|
}
|
|
8
|
+
if (config.origin === undefined) {
|
|
9
|
+
throw new Error("[stitchkit] cors: `origin` is required. Pass an explicit origin (or list), or '*' to deliberately allow every origin — omit `cors` entirely to emit no CORS headers.");
|
|
10
|
+
}
|
|
11
|
+
if (Array.isArray(config.origin) && config.origin.length === 0) {
|
|
12
|
+
throw new Error("[stitchkit] cors: `origin` cannot be an empty list. Pass explicit origins, or '*' to deliberately allow every origin.");
|
|
13
|
+
}
|
|
8
14
|
}
|
|
9
15
|
function resolveOrigin(config, requestOrigin) {
|
|
10
|
-
if (config.origin === undefined
|
|
16
|
+
if (config.origin === undefined)
|
|
17
|
+
return;
|
|
18
|
+
if (config.origin === "*") {
|
|
11
19
|
return "*";
|
|
12
20
|
}
|
|
13
21
|
if (Array.isArray(config.origin)) {
|
|
@@ -5,6 +5,9 @@ function typedEntries(value) {
|
|
|
5
5
|
function isRecord(value) {
|
|
6
6
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7
7
|
}
|
|
8
|
+
function transportResult(value) {
|
|
9
|
+
return value;
|
|
10
|
+
}
|
|
8
11
|
function mapObject(source, mapper) {
|
|
9
12
|
const result = {};
|
|
10
13
|
for (const [key, value] of typedEntries(source)) {
|
|
@@ -18,4 +21,4 @@ function mapObjectTypeBoundary(source, mapper) {
|
|
|
18
21
|
return mapObject(source, (key, value) => mapper(key, value));
|
|
19
22
|
}
|
|
20
23
|
|
|
21
|
-
export { typedEntries, isRecord, mapObject, mapObjectTypeBoundary };
|
|
24
|
+
export { typedEntries, isRecord, transportResult, mapObject, mapObjectTypeBoundary };
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
isUnsafeKey,
|
|
7
7
|
safeJsonParse,
|
|
8
8
|
unauthorized
|
|
9
|
-
} from "./index-
|
|
9
|
+
} from "./index-5s8b7z6q.js";
|
|
10
10
|
|
|
11
11
|
// src/server/middleware/cookies.ts
|
|
12
12
|
function parseCookies(header) {
|
|
@@ -157,7 +157,7 @@ function createAuthHook(config) {
|
|
|
157
157
|
const scope = endpoint.scope ?? config.defaultScope;
|
|
158
158
|
if (!scope)
|
|
159
159
|
return;
|
|
160
|
-
const rule = config.rules[scope];
|
|
160
|
+
const rule = Object.hasOwn(config.rules, scope) ? config.rules[scope] : undefined;
|
|
161
161
|
if (!rule) {
|
|
162
162
|
throw new Error(`[stitchkit] auth: no rule for scope "${scope}"`);
|
|
163
163
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
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
|
+
// src/tools/json-schema.ts
|
|
19
|
+
import { z } from "zod";
|
|
20
|
+
function toJsonSchema(schema, io, unrepresentable = "throw", target = "draft-2020-12") {
|
|
21
|
+
return z.toJSONSchema(schema, {
|
|
22
|
+
io,
|
|
23
|
+
target,
|
|
24
|
+
unrepresentable,
|
|
25
|
+
cycles: "ref"
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function isObjectNode(value) {
|
|
29
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
30
|
+
}
|
|
31
|
+
function jsonSchemaFields(jsonSchema) {
|
|
32
|
+
const properties = jsonSchema.properties;
|
|
33
|
+
if (!isObjectNode(properties)) {
|
|
34
|
+
const conjunctive = Array.isArray(jsonSchema.allOf);
|
|
35
|
+
const members = conjunctive ? jsonSchema.allOf : Array.isArray(jsonSchema.oneOf) ? jsonSchema.oneOf : Array.isArray(jsonSchema.anyOf) ? jsonSchema.anyOf : [];
|
|
36
|
+
const byName = new Map;
|
|
37
|
+
for (const member of Array.isArray(members) ? members : []) {
|
|
38
|
+
if (!isObjectNode(member))
|
|
39
|
+
continue;
|
|
40
|
+
for (const field of jsonSchemaFields(member)) {
|
|
41
|
+
const existing = byName.get(field.name);
|
|
42
|
+
const required2 = existing ? conjunctive ? existing.required || field.required : existing.required && field.required : field.required;
|
|
43
|
+
byName.set(field.name, existing ? { ...field, required: required2 } : field);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return [...byName.values()];
|
|
47
|
+
}
|
|
48
|
+
const required = new Set(Array.isArray(jsonSchema.required) ? jsonSchema.required.filter((k) => typeof k === "string") : []);
|
|
49
|
+
const fields = [];
|
|
50
|
+
for (const [name, raw] of Object.entries(properties)) {
|
|
51
|
+
const schema = isObjectNode(raw) ? raw : {};
|
|
52
|
+
const description = typeof schema.description === "string" ? schema.description : undefined;
|
|
53
|
+
fields.push({
|
|
54
|
+
name,
|
|
55
|
+
schema,
|
|
56
|
+
required: required.has(name),
|
|
57
|
+
...description && { description }
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
return fields;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export { isWithinDir, realPathWithinDir, toJsonSchema, jsonSchemaFields };
|