stitchkit 0.41.0 → 0.43.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 +22 -5
- package/dist/cli.js +2 -2
- package/dist/contract/errors-factory.d.ts +88 -15
- package/dist/contract/errors-factory.d.ts.map +1 -1
- package/dist/contract/errors.d.ts +4 -4
- package/dist/contract/errors.d.ts.map +1 -1
- package/dist/contract/factory.d.ts +29 -2
- package/dist/contract/factory.d.ts.map +1 -1
- package/dist/contract/index.d.ts +2 -2
- package/dist/contract/index.d.ts.map +1 -1
- package/dist/contract/index.js +2 -2
- package/dist/{index-dnkefke9.js → index-310bfer5.js} +1 -1
- package/dist/{index-4e1c0hsw.js → index-esqmem78.js} +101 -67
- package/dist/{index-sc67e454.js → index-gex6gxhe.js} +7 -4
- package/dist/{index-ncqqn1bc.js → index-h2wdcsby.js} +95 -17
- package/dist/{index-xax049k6.js → index-r6czv7yt.js} +58 -22
- package/dist/{index-809wc1tt.js → index-s4qsmgwe.js} +4 -1
- package/dist/index.js +2 -2
- package/dist/internal/typed.d.ts +8 -0
- package/dist/internal/typed.d.ts.map +1 -1
- package/dist/node.js +2 -2
- package/dist/observability/audit.d.ts +27 -15
- package/dist/observability/audit.d.ts.map +1 -1
- package/dist/observability/event.d.ts +2 -2
- package/dist/observability/index.d.ts +4 -4
- package/dist/observability/index.d.ts.map +1 -1
- package/dist/observability/index.js +70 -59
- package/dist/react.js +1 -1
- package/dist/server/create.d.ts.map +1 -1
- package/dist/server/error-hook.d.ts +8 -4
- package/dist/server/error-hook.d.ts.map +1 -1
- package/dist/server/index.js +7 -6
- package/dist/server/logger.d.ts +0 -1
- package/dist/server/logger.d.ts.map +1 -1
- package/dist/server/types.d.ts +8 -13
- package/dist/server/types.d.ts.map +1 -1
- package/dist/tools/agent.d.ts +1 -1
- package/dist/tools/agent.d.ts.map +1 -1
- package/dist/tools/flatten-join.d.ts.map +1 -1
- package/dist/tools/list-names.d.ts +8 -6
- package/dist/tools/list-names.d.ts.map +1 -1
- package/dist/tools/manifest.d.ts +13 -5
- package/dist/tools/manifest.d.ts.map +1 -1
- package/dist/tools/mcp-handler.d.ts +5 -4
- package/dist/tools/mcp-handler.d.ts.map +1 -1
- package/dist/tools/mcp-stdio.d.ts +3 -2
- package/dist/tools/mcp-stdio.d.ts.map +1 -1
- package/dist/tools/mcp.d.ts +49 -9
- package/dist/tools/mcp.d.ts.map +1 -1
- package/dist/tools/native-mcp.d.ts +4 -18
- package/dist/tools/native-mcp.d.ts.map +1 -1
- package/dist/tools/runtime-tool.d.ts +32 -1
- package/dist/tools/runtime-tool.d.ts.map +1 -1
- package/dist/tools/surface.d.ts +37 -0
- package/dist/tools/surface.d.ts.map +1 -0
- package/dist/tools/toolkit.d.ts +4 -4
- package/dist/tools/toolkit.d.ts.map +1 -1
- package/dist/tools/transports.d.ts +11 -9
- package/dist/tools/transports.d.ts.map +1 -1
- package/dist/tools/view-file.d.ts +3 -3
- package/dist/tools.d.ts +6 -6
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +330 -155
- package/llms-full.txt +524 -131
- package/llms.txt +1 -0
- package/package.json +2 -2
|
@@ -121,6 +121,9 @@ function mergeMeta(contractMeta, endpointMeta) {
|
|
|
121
121
|
return { ...contractMeta };
|
|
122
122
|
return { ...contractMeta, ...endpointMeta };
|
|
123
123
|
}
|
|
124
|
+
// src/contract/errors-factory.ts
|
|
125
|
+
import { z as z2 } from "zod";
|
|
126
|
+
|
|
124
127
|
// src/internal/typed.ts
|
|
125
128
|
function typedEntries(value) {
|
|
126
129
|
return Object.entries(value);
|
|
@@ -129,7 +132,7 @@ function isRecord(value) {
|
|
|
129
132
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
130
133
|
}
|
|
131
134
|
// src/contract/pagination.ts
|
|
132
|
-
import { z as
|
|
135
|
+
import { z as z3 } from "zod";
|
|
133
136
|
|
|
134
137
|
// src/internal/base64url.ts
|
|
135
138
|
function bytesToBase64Url(bytes) {
|
|
@@ -147,7 +150,7 @@ function base64UrlToBytes(segment) {
|
|
|
147
150
|
return Uint8Array.from(atob(padded), (c) => c.charCodeAt(0));
|
|
148
151
|
}
|
|
149
152
|
// src/internal/errors.ts
|
|
150
|
-
import { z as
|
|
153
|
+
import { z as z4 } from "zod";
|
|
151
154
|
function issuePath(path) {
|
|
152
155
|
return path.length > 0 ? path.map(String).join(".") : "(root)";
|
|
153
156
|
}
|
|
@@ -170,7 +173,7 @@ var MAX_DETAIL_ISSUES = 20;
|
|
|
170
173
|
function errorCode(err) {
|
|
171
174
|
if (AppError.is(err))
|
|
172
175
|
return err.code;
|
|
173
|
-
if (err instanceof
|
|
176
|
+
if (err instanceof z4.ZodError)
|
|
174
177
|
return "VALIDATION_ERROR";
|
|
175
178
|
return;
|
|
176
179
|
}
|
|
@@ -186,7 +189,7 @@ function recordedErrorMessage(code, envelopeMessage, thrown) {
|
|
|
186
189
|
function normalizeError(err) {
|
|
187
190
|
if (AppError.is(err))
|
|
188
191
|
return err;
|
|
189
|
-
if (err instanceof
|
|
192
|
+
if (err instanceof z4.ZodError) {
|
|
190
193
|
return new AppError("VALIDATION_ERROR", formatZodError(err), 400, {
|
|
191
194
|
issues: zodIssues(err).slice(0, MAX_DETAIL_ISSUES)
|
|
192
195
|
});
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
runWithRequestContext,
|
|
18
18
|
safeJsonParse,
|
|
19
19
|
validateDeclaredOutput
|
|
20
|
-
} from "./index-
|
|
20
|
+
} from "./index-gex6gxhe.js";
|
|
21
21
|
|
|
22
22
|
// src/tools/coerce.ts
|
|
23
23
|
import { z } from "zod";
|
|
@@ -165,22 +165,97 @@ function stringValues(schema) {
|
|
|
165
165
|
const values = schema.enum.filter((value) => typeof value === "string");
|
|
166
166
|
return values.length === schema.enum.length ? values : null;
|
|
167
167
|
}
|
|
168
|
+
var JSON_SCHEMA_TYPE_ORDER = [
|
|
169
|
+
"string",
|
|
170
|
+
"number",
|
|
171
|
+
"integer",
|
|
172
|
+
"boolean",
|
|
173
|
+
"object",
|
|
174
|
+
"array",
|
|
175
|
+
"null"
|
|
176
|
+
];
|
|
177
|
+
function jsonSchemaType(value) {
|
|
178
|
+
switch (value) {
|
|
179
|
+
case "array":
|
|
180
|
+
case "boolean":
|
|
181
|
+
case "integer":
|
|
182
|
+
case "null":
|
|
183
|
+
case "number":
|
|
184
|
+
case "object":
|
|
185
|
+
case "string":
|
|
186
|
+
return value;
|
|
187
|
+
default:
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
function valueType(value) {
|
|
192
|
+
if (value === null)
|
|
193
|
+
return "null";
|
|
194
|
+
if (Array.isArray(value))
|
|
195
|
+
return "array";
|
|
196
|
+
if (isRecord(value))
|
|
197
|
+
return "object";
|
|
198
|
+
if (typeof value === "string")
|
|
199
|
+
return "string";
|
|
200
|
+
if (typeof value === "number")
|
|
201
|
+
return "number";
|
|
202
|
+
if (typeof value === "boolean")
|
|
203
|
+
return "boolean";
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
function normalizeTypes(types) {
|
|
207
|
+
const normalized = new Set(types);
|
|
208
|
+
if (normalized.has("number"))
|
|
209
|
+
normalized.delete("integer");
|
|
210
|
+
return JSON_SCHEMA_TYPE_ORDER.filter((type) => normalized.has(type));
|
|
211
|
+
}
|
|
212
|
+
function combinedBranchTypes(schema) {
|
|
213
|
+
for (const keyword of ["oneOf", "anyOf"]) {
|
|
214
|
+
const value = schema[keyword];
|
|
215
|
+
if (!Array.isArray(value) || value.length === 0)
|
|
216
|
+
continue;
|
|
217
|
+
const collected = [];
|
|
218
|
+
for (const branch of value) {
|
|
219
|
+
if (!isRecord(branch))
|
|
220
|
+
return null;
|
|
221
|
+
const types = typeNames(branch);
|
|
222
|
+
if (!types)
|
|
223
|
+
return null;
|
|
224
|
+
collected.push(...types);
|
|
225
|
+
}
|
|
226
|
+
return normalizeTypes(collected);
|
|
227
|
+
}
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
168
230
|
function typeNames(schema) {
|
|
169
|
-
|
|
170
|
-
|
|
231
|
+
const direct = jsonSchemaType(schema.type);
|
|
232
|
+
if (direct)
|
|
233
|
+
return [direct];
|
|
171
234
|
if (Array.isArray(schema.type)) {
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
if (
|
|
182
|
-
|
|
183
|
-
|
|
235
|
+
const values = [];
|
|
236
|
+
for (const value of schema.type) {
|
|
237
|
+
const type = jsonSchemaType(value);
|
|
238
|
+
if (!type)
|
|
239
|
+
return null;
|
|
240
|
+
values.push(type);
|
|
241
|
+
}
|
|
242
|
+
return values.length > 0 ? normalizeTypes(values) : null;
|
|
243
|
+
}
|
|
244
|
+
if (schema.const !== undefined) {
|
|
245
|
+
const type = valueType(schema.const);
|
|
246
|
+
return type ? [type] : null;
|
|
247
|
+
}
|
|
248
|
+
if (Array.isArray(schema.enum) && schema.enum.length > 0) {
|
|
249
|
+
const values = [];
|
|
250
|
+
for (const value of schema.enum) {
|
|
251
|
+
const type = valueType(value);
|
|
252
|
+
if (!type)
|
|
253
|
+
return null;
|
|
254
|
+
values.push(type);
|
|
255
|
+
}
|
|
256
|
+
return normalizeTypes(values);
|
|
257
|
+
}
|
|
258
|
+
return combinedBranchTypes(schema);
|
|
184
259
|
}
|
|
185
260
|
function typeSchema(type, nullable) {
|
|
186
261
|
const schema = { type: nullable ? [type, "null"] : type };
|
|
@@ -206,6 +281,8 @@ function commonBaseType(schemas) {
|
|
|
206
281
|
bases.add(name);
|
|
207
282
|
}
|
|
208
283
|
}
|
|
284
|
+
if (bases.size === 0)
|
|
285
|
+
return nullable ? { type: "null" } : {};
|
|
209
286
|
if (bases.size === 1) {
|
|
210
287
|
const [only] = bases;
|
|
211
288
|
return only ? typeSchema(only, nullable) : {};
|
|
@@ -213,7 +290,8 @@ function commonBaseType(schemas) {
|
|
|
213
290
|
if ([...bases].every((name) => name === "integer" || name === "number")) {
|
|
214
291
|
return typeSchema("number", nullable);
|
|
215
292
|
}
|
|
216
|
-
|
|
293
|
+
const types = normalizeTypes(nullable ? [...bases, "null"] : bases);
|
|
294
|
+
return types.length > 0 ? { type: types } : {};
|
|
217
295
|
}
|
|
218
296
|
function mergePropertySchemas(properties) {
|
|
219
297
|
const schemas = properties.map((property) => nullableProjection(property.schema));
|
|
@@ -717,7 +795,7 @@ function toToolName(serviceName, methodName) {
|
|
|
717
795
|
}
|
|
718
796
|
function assertUniqueToolName(name, taken, surface) {
|
|
719
797
|
if (taken) {
|
|
720
|
-
throw new Error(`Duplicate ${surface} "${name}" across mounted
|
|
798
|
+
throw new Error(`Duplicate ${surface} "${name}" across mounted operations`);
|
|
721
799
|
}
|
|
722
800
|
}
|
|
723
801
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
} from "./index-
|
|
2
|
+
mapObjectTypeBoundary
|
|
3
|
+
} from "./index-s4qsmgwe.js";
|
|
4
4
|
|
|
5
5
|
// src/contract/define.ts
|
|
6
6
|
import { z } from "zod";
|
|
@@ -237,32 +237,68 @@ function appError(code, message, details) {
|
|
|
237
237
|
throw new AppError(code, message, isStitchErrorCode(code) ? STITCH_ERROR_STATUS[code] : 500, details);
|
|
238
238
|
}
|
|
239
239
|
// src/contract/errors-factory.ts
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
240
|
+
import { z as z2 } from "zod";
|
|
241
|
+
function isErrorDetailsSchema(value) {
|
|
242
|
+
return value instanceof z2.ZodObject || value instanceof z2.ZodOptional && value.unwrap() instanceof z2.ZodObject;
|
|
243
|
+
}
|
|
244
|
+
function validateDefinition(code, definition) {
|
|
245
|
+
if (!Number.isInteger(definition.status) || definition.status < 400 || definition.status > 599) {
|
|
246
|
+
throw new Error(`[stitchkit] Error "${code}" must declare an integer HTTP status from 400 to 599`);
|
|
247
|
+
}
|
|
248
|
+
if (definition.details !== undefined && !isErrorDetailsSchema(definition.details)) {
|
|
249
|
+
throw new Error(`[stitchkit] Error "${code}" details must be a Zod object or optional Zod object`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
function defineErrors(source) {
|
|
253
|
+
const definitions = mapObjectTypeBoundary(source, (code, definition) => {
|
|
254
|
+
const name = String(code);
|
|
255
|
+
validateDefinition(name, definition);
|
|
256
|
+
return Object.freeze({ ...definition });
|
|
257
|
+
});
|
|
258
|
+
Object.freeze(definitions);
|
|
259
|
+
const errors = mapObjectTypeBoundary(definitions, (code, definition) => {
|
|
260
|
+
const name = String(code);
|
|
261
|
+
return (options = {}) => {
|
|
262
|
+
if (definition.details === undefined) {
|
|
263
|
+
if ("details" in options) {
|
|
264
|
+
throw new Error(`[stitchkit] Error "${name}" does not declare details`);
|
|
265
|
+
}
|
|
266
|
+
return new AppError(name, options.message, definition.status, undefined, options.hint);
|
|
267
|
+
}
|
|
268
|
+
const details = definition.details.parse(options.details);
|
|
269
|
+
return new AppError(name, options.message, definition.status, details, options.hint);
|
|
270
|
+
};
|
|
243
271
|
});
|
|
244
|
-
|
|
245
|
-
const
|
|
246
|
-
|
|
272
|
+
Object.freeze(errors);
|
|
273
|
+
const codes = mapObjectTypeBoundary(definitions, (code) => String(code));
|
|
274
|
+
Object.freeze(codes);
|
|
275
|
+
const known = new Set(Object.keys(definitions));
|
|
276
|
+
return Object.freeze({
|
|
247
277
|
errors,
|
|
248
278
|
codes,
|
|
279
|
+
definitions,
|
|
249
280
|
isCode: (code) => known.has(code)
|
|
250
|
-
};
|
|
281
|
+
});
|
|
251
282
|
}
|
|
252
283
|
// src/contract/factory.ts
|
|
253
|
-
function createContractFactory() {
|
|
254
|
-
|
|
255
|
-
defineContract:
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
};
|
|
261
|
-
}
|
|
284
|
+
function createContractFactory(config) {
|
|
285
|
+
const scoped = (meta, endpoints) => {
|
|
286
|
+
const validated = defineContract({ prefix: meta.prefix }, endpoints);
|
|
287
|
+
return {
|
|
288
|
+
endpoints: validated.endpoints,
|
|
289
|
+
meta: { ...meta, prefix: meta.prefix, scope: meta.scope }
|
|
290
|
+
};
|
|
262
291
|
};
|
|
292
|
+
if (config?.toolExposure !== "explicit")
|
|
293
|
+
return { defineContract: scoped };
|
|
294
|
+
const explicit = (meta, endpoints) => {
|
|
295
|
+
const materialized = mapObjectTypeBoundary(endpoints, (_key, endpoint) => endpoint.expose ? endpoint : { ...endpoint, expose: ["HTTP"] });
|
|
296
|
+
return scoped(meta, materialized);
|
|
297
|
+
};
|
|
298
|
+
return { defineContract: explicit };
|
|
263
299
|
}
|
|
264
300
|
// src/contract/pagination.ts
|
|
265
|
-
import { z as
|
|
301
|
+
import { z as z3 } from "zod";
|
|
266
302
|
|
|
267
303
|
// src/internal/base64url.ts
|
|
268
304
|
function bytesToBase64Url(bytes) {
|
|
@@ -282,9 +318,9 @@ function base64UrlToBytes(segment) {
|
|
|
282
318
|
|
|
283
319
|
// src/contract/pagination.ts
|
|
284
320
|
function paginatedSchema(itemSchema) {
|
|
285
|
-
return
|
|
286
|
-
items:
|
|
287
|
-
nextCursor:
|
|
321
|
+
return z3.object({
|
|
322
|
+
items: z3.array(itemSchema),
|
|
323
|
+
nextCursor: z3.string().nullable()
|
|
288
324
|
});
|
|
289
325
|
}
|
|
290
326
|
function toBase64Url(str) {
|
|
@@ -14,5 +14,8 @@ function mapObject(source, mapper) {
|
|
|
14
14
|
}
|
|
15
15
|
return result;
|
|
16
16
|
}
|
|
17
|
+
function mapObjectTypeBoundary(source, mapper) {
|
|
18
|
+
return mapObject(source, (key, value) => mapper(key, value));
|
|
19
|
+
}
|
|
17
20
|
|
|
18
|
-
export { typedEntries, isRecord, mapObject };
|
|
21
|
+
export { typedEntries, isRecord, mapObject, mapObjectTypeBoundary };
|
package/dist/index.js
CHANGED
|
@@ -17,12 +17,12 @@ import {
|
|
|
17
17
|
parseTrailingWildcard,
|
|
18
18
|
rateLimited,
|
|
19
19
|
unauthorized
|
|
20
|
-
} from "./index-
|
|
20
|
+
} from "./index-r6czv7yt.js";
|
|
21
21
|
import {
|
|
22
22
|
isRecord,
|
|
23
23
|
mapObject,
|
|
24
24
|
typedEntries
|
|
25
|
-
} from "./index-
|
|
25
|
+
} from "./index-s4qsmgwe.js";
|
|
26
26
|
|
|
27
27
|
// src/browser/client-multipart.ts
|
|
28
28
|
function isFileDescriptor(value) {
|
package/dist/internal/typed.d.ts
CHANGED
|
@@ -6,4 +6,12 @@ export declare function isRecord(value: unknown): value is Record<string, unknow
|
|
|
6
6
|
export declare function mapObject<TSource extends object, TResult extends {
|
|
7
7
|
[K in keyof TSource]?: unknown;
|
|
8
8
|
}>(source: TSource, mapper: <K extends keyof TSource>(key: K, value: TSource[K]) => TResult[K] | undefined): TResult;
|
|
9
|
+
/**
|
|
10
|
+
* Dynamic key-wise factory bridge. Use when runtime construction preserves a
|
|
11
|
+
* mapped type's key/value relation but TypeScript cannot express the dependent
|
|
12
|
+
* callback return. The assertion is intentionally isolated at this boundary.
|
|
13
|
+
*/
|
|
14
|
+
export declare function mapObjectTypeBoundary<TSource extends object, TResult extends {
|
|
15
|
+
[K in keyof TSource]?: unknown;
|
|
16
|
+
}>(source: TSource, mapper: (key: keyof TSource, value: TSource[keyof TSource]) => unknown): TResult;
|
|
9
17
|
//# sourceMappingURL=typed.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typed.d.ts","sourceRoot":"","sources":["../../src/internal/typed.ts"],"names":[],"mappings":"AAAA,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,EAC3C,KAAK,EAAE,CAAC,GACP,KAAK,CAAC;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAE/C;AAED,4EAA4E;AAC5E,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE;AAED,wBAAgB,SAAS,CACvB,OAAO,SAAS,MAAM,EACtB,OAAO,SAAS;KAAG,CAAC,IAAI,MAAM,OAAO,CAAC,CAAC,EAAE,OAAO;CAAE,EAElD,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,CAAC,CAAC,SAAS,MAAM,OAAO,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,SAAS,GACrF,OAAO,CAOT"}
|
|
1
|
+
{"version":3,"file":"typed.d.ts","sourceRoot":"","sources":["../../src/internal/typed.ts"],"names":[],"mappings":"AAAA,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,EAC3C,KAAK,EAAE,CAAC,GACP,KAAK,CAAC;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAE/C;AAED,4EAA4E;AAC5E,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE;AAED,wBAAgB,SAAS,CACvB,OAAO,SAAS,MAAM,EACtB,OAAO,SAAS;KAAG,CAAC,IAAI,MAAM,OAAO,CAAC,CAAC,EAAE,OAAO;CAAE,EAElD,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,CAAC,CAAC,SAAS,MAAM,OAAO,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,SAAS,GACrF,OAAO,CAOT;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,SAAS,MAAM,EACtB,OAAO,SAAS;KAAG,CAAC,IAAI,MAAM,OAAO,CAAC,CAAC,EAAE,OAAO;CAAE,EAElD,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,MAAM,OAAO,CAAC,KAAK,OAAO,GACrE,OAAO,CAKT"}
|
package/dist/node.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
createImplement,
|
|
4
4
|
createSocketIOServer,
|
|
5
5
|
implement
|
|
6
|
-
} from "./index-
|
|
6
|
+
} from "./index-esqmem78.js";
|
|
7
7
|
import"./index-6jypn22c.js";
|
|
8
8
|
import"./index-x3fcszf8.js";
|
|
9
9
|
import {
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
notFound,
|
|
16
16
|
rateLimited,
|
|
17
17
|
unauthorized
|
|
18
|
-
} from "./index-
|
|
18
|
+
} from "./index-gex6gxhe.js";
|
|
19
19
|
// src/server/node.ts
|
|
20
20
|
import { serve } from "srvx";
|
|
21
21
|
async function serveNode(config) {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { ToolCallHooks } from '../tools/execute';
|
|
2
|
+
import { type RequestContext } from './context';
|
|
2
3
|
import type { RequestEvent } from './event';
|
|
3
4
|
import { type SanitizeOptions } from './sanitize';
|
|
4
|
-
/**
|
|
5
|
-
export interface
|
|
5
|
+
/** One isolated RequestEvent sink and its sanitisation policy. */
|
|
6
|
+
export interface RequestEventSinkConfig {
|
|
6
7
|
/**
|
|
7
8
|
* The sink — persists one audit event. It runs fire-and-forget and its own
|
|
8
9
|
* errors are swallowed, so a slow or failing write never blocks or breaks the
|
|
@@ -14,19 +15,30 @@ export interface AuditConfig {
|
|
|
14
15
|
/** Payload sanitisation tuning — passed through to `sanitizePayload`. */
|
|
15
16
|
sanitize?: SanitizeOptions;
|
|
16
17
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
18
|
+
export interface RequestObservabilityConfig extends RequestEventSinkConfig {
|
|
19
|
+
/** Clone and record JSON request bodies. Default false. */
|
|
20
|
+
includePayload?: boolean;
|
|
21
|
+
}
|
|
22
|
+
export interface ObservabilityConfig {
|
|
23
|
+
request?: RequestObservabilityConfig;
|
|
24
|
+
tools?: RequestEventSinkConfig;
|
|
25
|
+
}
|
|
26
|
+
/** The one HTTP completion snapshot produced by the handler. */
|
|
27
|
+
export interface HttpRequestCompletion {
|
|
28
|
+
context: RequestContext;
|
|
29
|
+
statusCode: number;
|
|
30
|
+
durationMs: number;
|
|
31
|
+
payload?: Promise<unknown>;
|
|
32
|
+
}
|
|
33
|
+
/** Server-facing request projection returned by `createObservability`. */
|
|
34
|
+
export interface HttpRequestObserver {
|
|
35
|
+
includePayload: boolean;
|
|
36
|
+
complete(completion: HttpRequestCompletion): void;
|
|
37
|
+
}
|
|
38
|
+
/** What `createObservability` returns — one wiring point per enabled surface. */
|
|
39
|
+
export interface Observability {
|
|
40
|
+
request?: HttpRequestObserver;
|
|
29
41
|
toolCall: ToolCallHooks;
|
|
30
42
|
}
|
|
31
|
-
export declare function
|
|
43
|
+
export declare function createObservability(config: ObservabilityConfig): Observability;
|
|
32
44
|
//# sourceMappingURL=audit.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/observability/audit.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/observability/audit.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,aAAa,EAAc,MAAM,kBAAkB,CAAC;AAClE,OAAO,EAAqB,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAe,KAAK,eAAe,EAAmB,MAAM,YAAY,CAAC;AAGhF,kEAAkE;AAClE,MAAM,WAAW,sBAAsB;IACrC;;;;OAIG;IACH,KAAK,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,iFAAiF;IACjF,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC;IAC1C,yEAAyE;IACzE,QAAQ,CAAC,EAAE,eAAe,CAAC;CAC5B;AAED,MAAM,WAAW,0BAA2B,SAAQ,sBAAsB;IACxE,2DAA2D;IAC3D,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,0BAA0B,CAAC;IACrC,KAAK,CAAC,EAAE,sBAAsB,CAAC;CAChC;AAED,gEAAgE;AAChE,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,cAAc,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;CAC5B;AAED,0EAA0E;AAC1E,MAAM,WAAW,mBAAmB;IAClC,cAAc,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,qBAAqB,GAAG,IAAI,CAAC;CACnD;AAED,iFAAiF;AACjF,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,QAAQ,EAAE,aAAa,CAAC;CACzB;AAsCD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,mBAAmB,GAAG,aAAa,CA0G9E"}
|
|
@@ -2,8 +2,8 @@ import type { HttpMethod, TransportSource } from '../contract';
|
|
|
2
2
|
import type { JsonValue } from './sanitize';
|
|
3
3
|
/**
|
|
4
4
|
* A normalised audit event — one shape for a completed call on any surface
|
|
5
|
-
* (an HTTP request, an MCP tool call, an agent tool call). `
|
|
6
|
-
* produces it and hands it to the
|
|
5
|
+
* (an HTTP request, an MCP tool call, an agent tool call). `createObservability`
|
|
6
|
+
* produces it and hands it to the configured surface sink. The project maps it
|
|
7
7
|
* onto its own audit table; stitchkit owns the normalisation.
|
|
8
8
|
*/
|
|
9
9
|
export interface RequestEvent {
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
* `stitchkit/observability` — the audit layer one level above the raw hooks.
|
|
3
3
|
*
|
|
4
4
|
* W3C trace context, an `AsyncLocalStorage` request context, payload
|
|
5
|
-
* sanitisation, a normalised `RequestEvent`, and `
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* sanitisation, a normalised `RequestEvent`, and `createObservability` to wire
|
|
6
|
+
* framework-owned HTTP completion plus canonical tool hooks into independent
|
|
7
|
+
* sinks.
|
|
8
8
|
*/
|
|
9
|
-
export { type
|
|
9
|
+
export { createObservability, type HttpRequestCompletion, type HttpRequestObserver, type Observability, type ObservabilityConfig, type RequestEventSinkConfig, type RequestObservabilityConfig, } from './audit';
|
|
10
10
|
export { getRequestContext, getTraceId, getUserId, type RequestContext, runWithRequestContext, setRequestDimensions, setRequestEndpoint, setRequestError, setRequestUser, type WrapRequestContextOptions, wrapInRequestContext, } from './context';
|
|
11
11
|
export type { RequestEvent } from './event';
|
|
12
12
|
export { type JsonValue, measureSize, redact, type SanitizeOptions, type SizeMeasure, sanitizePayload, truncatePreview, } from './sanitize';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/observability/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/observability/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EACL,mBAAmB,EACnB,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,0BAA0B,GAChC,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,iBAAiB,EACjB,UAAU,EACV,SAAS,EACT,KAAK,cAAc,EACnB,qBAAqB,EACrB,oBAAoB,EACpB,kBAAkB,EAClB,eAAe,EACf,cAAc,EACd,KAAK,yBAAyB,EAC9B,oBAAoB,GACrB,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EACL,KAAK,SAAS,EACd,WAAW,EACX,MAAM,EACN,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,eAAe,EACf,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,SAAS,EACT,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,mBAAmB,EACnB,KAAK,YAAY,GAClB,MAAM,SAAS,CAAC"}
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
setRequestError,
|
|
17
17
|
setRequestUser,
|
|
18
18
|
wrapInRequestContext
|
|
19
|
-
} from "../index-
|
|
19
|
+
} from "../index-gex6gxhe.js";
|
|
20
20
|
|
|
21
21
|
// src/observability/sanitize.ts
|
|
22
22
|
var DEFAULT_SENSITIVE_KEYS = /(password|passwd|pwd|secret|token|apikey|api[-_ ]?key|auth|authorization|bearer|session|cookie|init[-_ ]?data|credential|private[-_ ]?key)/i;
|
|
@@ -100,7 +100,6 @@ function measureSize(value) {
|
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
// src/observability/audit.ts
|
|
103
|
-
var BODY_METHODS = new Set(["POST", "PUT", "PATCH", "DELETE"]);
|
|
104
103
|
function toolErrorMessage(result) {
|
|
105
104
|
const { details, hint } = result;
|
|
106
105
|
if (isRecord(details) && typeof details.message === "string") {
|
|
@@ -114,64 +113,71 @@ function auditErrorMessage(result, thrown) {
|
|
|
114
113
|
function readString(value) {
|
|
115
114
|
return typeof value === "string" ? value : undefined;
|
|
116
115
|
}
|
|
117
|
-
function
|
|
118
|
-
|
|
119
|
-
const emit = async (event) => {
|
|
116
|
+
function createEmitter(config) {
|
|
117
|
+
return async (event) => {
|
|
120
118
|
try {
|
|
121
|
-
if (filter && !filter(event))
|
|
119
|
+
if (config.filter && !config.filter(event))
|
|
122
120
|
return;
|
|
123
|
-
await write(event);
|
|
121
|
+
await config.write(event);
|
|
124
122
|
} catch {}
|
|
125
123
|
};
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
124
|
+
}
|
|
125
|
+
function createObservability(config) {
|
|
126
|
+
const request = config.request ? {
|
|
127
|
+
includePayload: config.request.includePayload ?? false,
|
|
128
|
+
complete: ({ context, statusCode, durationMs, payload }) => {
|
|
129
|
+
const requestConfig = config.request;
|
|
130
|
+
if (!requestConfig)
|
|
131
|
+
return;
|
|
132
|
+
const emit = createEmitter(requestConfig);
|
|
133
|
+
(async () => {
|
|
134
|
+
let body;
|
|
135
|
+
if (payload) {
|
|
136
|
+
try {
|
|
137
|
+
body = await payload;
|
|
138
|
+
} catch {
|
|
139
|
+
body = undefined;
|
|
141
140
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
141
|
+
}
|
|
142
|
+
await emit({
|
|
143
|
+
source: context.source,
|
|
144
|
+
method: context.method,
|
|
145
|
+
path: context.path,
|
|
146
|
+
...context.serviceName !== undefined && {
|
|
147
|
+
serviceName: context.serviceName
|
|
148
|
+
},
|
|
149
|
+
...context.action !== undefined && { action: context.action },
|
|
150
|
+
...context.dimensions !== undefined && {
|
|
151
|
+
dimensions: context.dimensions
|
|
152
|
+
},
|
|
153
|
+
traceId: context.trace.traceId,
|
|
154
|
+
spanId: context.trace.spanId,
|
|
155
|
+
parentSpanId: context.trace.parentSpanId,
|
|
156
|
+
ok: statusCode < 400,
|
|
157
|
+
statusCode,
|
|
158
|
+
durationMs,
|
|
159
|
+
errorCode: context.error?.code,
|
|
160
|
+
errorMessage: context.error?.message,
|
|
161
|
+
...context.error?.details !== undefined && {
|
|
162
|
+
errorDetail: sanitizePayload(context.error.details, requestConfig.sanitize)
|
|
163
|
+
},
|
|
164
|
+
payload: sanitizePayload(body, requestConfig.sanitize),
|
|
165
|
+
resultSize: null,
|
|
166
|
+
responseBytes: 0,
|
|
167
|
+
userId: context.userId,
|
|
168
|
+
ipAddress: context.ipAddress,
|
|
169
|
+
userAgent: context.userAgent,
|
|
170
|
+
startedAt: new Date(Date.now() - durationMs)
|
|
171
|
+
});
|
|
172
|
+
})();
|
|
173
|
+
}
|
|
174
|
+
} : undefined;
|
|
175
|
+
const toolCall = config.tools ? {
|
|
174
176
|
afterToolCall: ({ toolName, args, result, durationMs, context, endpoint, error }) => {
|
|
177
|
+
const toolConfig = config.tools;
|
|
178
|
+
if (!toolConfig)
|
|
179
|
+
return;
|
|
180
|
+
const emit = createEmitter(toolConfig);
|
|
175
181
|
const requestCtx = getRequestContext();
|
|
176
182
|
const span = requestCtx ? childSpan(requestCtx.trace) : createTraceContext();
|
|
177
183
|
const measure = result.ok ? measureSize(result.data) : { resultSize: null, responseBytes: 0 };
|
|
@@ -182,7 +188,9 @@ function createAuditHook(config) {
|
|
|
182
188
|
path: `/${context.source}/${toolName}`,
|
|
183
189
|
serviceName: endpoint.serviceName,
|
|
184
190
|
action: endpoint.key,
|
|
185
|
-
...requestCtx?.dimensions !== undefined && {
|
|
191
|
+
...requestCtx?.dimensions !== undefined && {
|
|
192
|
+
dimensions: requestCtx.dimensions
|
|
193
|
+
},
|
|
186
194
|
toolName,
|
|
187
195
|
traceId: span.traceId,
|
|
188
196
|
spanId: span.spanId,
|
|
@@ -193,9 +201,9 @@ function createAuditHook(config) {
|
|
|
193
201
|
errorCode: result.ok ? undefined : result.code,
|
|
194
202
|
errorMessage: result.ok ? undefined : auditErrorMessage(result, error),
|
|
195
203
|
...!result.ok && result.details !== undefined && {
|
|
196
|
-
errorDetail: sanitizePayload(result.details, sanitize)
|
|
204
|
+
errorDetail: sanitizePayload(result.details, toolConfig.sanitize)
|
|
197
205
|
},
|
|
198
|
-
payload: sanitizePayload(args, sanitize),
|
|
206
|
+
payload: sanitizePayload(args, toolConfig.sanitize),
|
|
199
207
|
resultSize: measure.resultSize,
|
|
200
208
|
responseBytes: measure.responseBytes,
|
|
201
209
|
userId: readString(context.userId),
|
|
@@ -206,8 +214,11 @@ function createAuditHook(config) {
|
|
|
206
214
|
startedAt: new Date(Date.now() - durationMs)
|
|
207
215
|
});
|
|
208
216
|
}
|
|
217
|
+
} : undefined;
|
|
218
|
+
return {
|
|
219
|
+
...request && { request },
|
|
220
|
+
toolCall: toolCall ?? {}
|
|
209
221
|
};
|
|
210
|
-
return { http, toolCall };
|
|
211
222
|
}
|
|
212
223
|
export {
|
|
213
224
|
wrapInRequestContext,
|
|
@@ -227,6 +238,6 @@ export {
|
|
|
227
238
|
getRequestContext,
|
|
228
239
|
formatTraceparent,
|
|
229
240
|
createTraceContext,
|
|
230
|
-
|
|
241
|
+
createObservability,
|
|
231
242
|
childSpan
|
|
232
243
|
};
|