silgi 0.51.7 → 0.51.8
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 +47 -0
- package/dist/adapters/_fetch-adapter.d.mts +6 -0
- package/dist/adapters/_fetch-adapter.mjs +14 -8
- package/dist/adapters/astro.mjs +1 -1
- package/dist/adapters/nextjs.mjs +1 -1
- package/dist/adapters/remix.mjs +1 -1
- package/dist/adapters/solidstart.mjs +1 -1
- package/dist/adapters/sveltekit.mjs +1 -1
- package/dist/client/client.d.mts +42 -4
- package/dist/client/client.mjs +42 -4
- package/dist/client/server.d.mts +27 -2
- package/dist/client/server.mjs +27 -2
- package/dist/compile.d.mts +10 -1
- package/dist/compile.mjs +13 -4
- package/dist/core/context-bridge.d.mts +49 -0
- package/dist/core/context-bridge.mjs +43 -7
- package/dist/core/context.d.mts +26 -0
- package/dist/core/ctx-symbols.mjs +21 -0
- package/dist/core/error.d.mts +183 -2
- package/dist/core/error.mjs +259 -16
- package/dist/core/handler.d.mts +15 -1
- package/dist/core/handler.mjs +33 -17
- package/dist/core/schema-converter.d.mts +131 -0
- package/dist/core/schema-converter.mjs +82 -0
- package/dist/core/serve.d.mts +2 -2
- package/dist/core/serve.mjs +9 -2
- package/dist/core/task.mjs +2 -2
- package/dist/index.d.mts +5 -2
- package/dist/index.mjs +4 -2
- package/dist/integrations/better-auth/index.d.mts +22 -1
- package/dist/integrations/better-auth/index.mjs +79 -11
- package/dist/integrations/drizzle/index.mjs +22 -5
- package/dist/integrations/zod/converter.d.mts +1 -1
- package/dist/integrations/zod/index.d.mts +29 -2
- package/dist/integrations/zod/index.mjs +60 -1
- package/dist/lazy.d.mts +40 -3
- package/dist/lazy.mjs +40 -3
- package/dist/map-input.mjs +1 -1
- package/dist/plugins/analytics/collector.d.mts +1 -1
- package/dist/plugins/analytics/trace.mjs +1 -1
- package/dist/plugins/analytics/types.d.mts +3 -3
- package/dist/plugins/analytics/utils.mjs +1 -4
- package/dist/plugins/analytics.d.mts +5 -3
- package/dist/plugins/analytics.mjs +16 -29
- package/dist/plugins/cache.mjs +1 -1
- package/dist/plugins/coerce.mjs +1 -1
- package/dist/scalar.d.mts +2 -1
- package/dist/scalar.mjs +9 -30
- package/dist/silgi.d.mts +165 -18
- package/dist/silgi.mjs +47 -11
- package/package.json +6 -2
- package/dist/core/trace-map.d.mts +0 -13
- package/dist/core/trace-map.mjs +0 -13
|
@@ -1,2 +1,29 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { SchemaConverter } from "../../core/schema-converter.mjs";
|
|
2
|
+
import { CompositeSchemaConverter, ConvertOptions, JSONSchema, ZodSchemaConverter } from "./converter.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/integrations/zod/index.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Pre-built Zod → JSON Schema converter for use with
|
|
7
|
+
* `silgi({ schemaConverters })`.
|
|
8
|
+
*
|
|
9
|
+
* @remarks
|
|
10
|
+
* Pass this to the `schemaConverters` option of `silgi()` to enable
|
|
11
|
+
* OpenAPI spec generation and analytics schema extraction for Zod
|
|
12
|
+
* schemas. Supports Zod v3 and v4.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* import { zodConverter } from 'silgi/zod'
|
|
17
|
+
* import { silgi } from 'silgi'
|
|
18
|
+
*
|
|
19
|
+
* const k = silgi({
|
|
20
|
+
* context: (req) => ({ db: getDB() }),
|
|
21
|
+
* schemaConverters: [zodConverter],
|
|
22
|
+
* })
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* @category Schema
|
|
26
|
+
*/
|
|
27
|
+
declare const zodConverter: SchemaConverter;
|
|
28
|
+
//#endregion
|
|
29
|
+
export { CompositeSchemaConverter, type ConvertOptions, type JSONSchema, type SchemaConverter, ZodSchemaConverter, zodConverter };
|
|
@@ -1,2 +1,61 @@
|
|
|
1
1
|
import { CompositeSchemaConverter, ZodSchemaConverter } from "./converter.mjs";
|
|
2
|
-
|
|
2
|
+
//#region src/integrations/zod/index.ts
|
|
3
|
+
/**
|
|
4
|
+
* Zod integration for Silgi — explicit injection model.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* **Breaking change:** the old side-effect import (`import 'silgi/zod'`)
|
|
8
|
+
* that auto-registered the Zod converter globally is removed. Import
|
|
9
|
+
* {@link zodConverter} and pass it explicitly to
|
|
10
|
+
* `silgi({ schemaConverters: [zodConverter] })`.
|
|
11
|
+
*
|
|
12
|
+
* Migration:
|
|
13
|
+
* ```ts
|
|
14
|
+
* // Before:
|
|
15
|
+
* import 'silgi/zod'
|
|
16
|
+
* const k = silgi({ context: ... })
|
|
17
|
+
*
|
|
18
|
+
* // After:
|
|
19
|
+
* import { zodConverter } from 'silgi/zod'
|
|
20
|
+
* const k = silgi({ context: ..., schemaConverters: [zodConverter] })
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* Note: Zod v4 schemas expose a native `jsonSchema.input()` fast path, so
|
|
24
|
+
* most Zod v4 users do not strictly need this converter. Passing
|
|
25
|
+
* `zodConverter` is still recommended as a safety net for schemas that
|
|
26
|
+
* bypass the fast path.
|
|
27
|
+
*
|
|
28
|
+
* @category Schema
|
|
29
|
+
*/
|
|
30
|
+
const _zodConverterInstance = new ZodSchemaConverter();
|
|
31
|
+
/**
|
|
32
|
+
* Pre-built Zod → JSON Schema converter for use with
|
|
33
|
+
* `silgi({ schemaConverters })`.
|
|
34
|
+
*
|
|
35
|
+
* @remarks
|
|
36
|
+
* Pass this to the `schemaConverters` option of `silgi()` to enable
|
|
37
|
+
* OpenAPI spec generation and analytics schema extraction for Zod
|
|
38
|
+
* schemas. Supports Zod v3 and v4.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* import { zodConverter } from 'silgi/zod'
|
|
43
|
+
* import { silgi } from 'silgi'
|
|
44
|
+
*
|
|
45
|
+
* const k = silgi({
|
|
46
|
+
* context: (req) => ({ db: getDB() }),
|
|
47
|
+
* schemaConverters: [zodConverter],
|
|
48
|
+
* })
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* @category Schema
|
|
52
|
+
*/
|
|
53
|
+
const zodConverter = {
|
|
54
|
+
vendor: "zod",
|
|
55
|
+
toJsonSchema(schema, opts) {
|
|
56
|
+
const [, json] = _zodConverterInstance.convert(schema, opts);
|
|
57
|
+
return json;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
//#endregion
|
|
61
|
+
export { CompositeSchemaConverter, ZodSchemaConverter, zodConverter };
|
package/dist/lazy.d.mts
CHANGED
|
@@ -9,14 +9,51 @@ interface LazyRouter {
|
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
11
|
* Wrap a dynamic import for lazy loading.
|
|
12
|
-
*
|
|
12
|
+
*
|
|
13
|
+
* @remarks
|
|
14
|
+
* The module must export its router or procedure as `default`. The
|
|
15
|
+
* returned handle is resolved on demand by {@link resolveLazy} and
|
|
16
|
+
* cached via module-local `WeakMap`s so concurrent resolutions share a
|
|
17
|
+
* single in-flight `Promise`.
|
|
18
|
+
*
|
|
19
|
+
* @param loader - A function returning `import('…')` — typically a
|
|
20
|
+
* dynamic import expression pointing at a module whose default export
|
|
21
|
+
* is a `RouterDef` or `ProcedureDef`.
|
|
22
|
+
* @returns A {@link LazyRouter} handle usable as a value inside
|
|
23
|
+
* `silgi.router({ … })`.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* const appRouter = k.router({
|
|
28
|
+
* users: lazy(() => import('./routes/users.ts')),
|
|
29
|
+
* admin: lazy(() => import('./routes/admin.ts')),
|
|
30
|
+
* })
|
|
31
|
+
* ```
|
|
13
32
|
*/
|
|
14
33
|
declare function lazy(loader: () => Promise<{
|
|
15
34
|
default: RouterDef | ProcedureDef;
|
|
16
35
|
}>): LazyRouter;
|
|
17
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* Type guard: `true` when `value` is a {@link LazyRouter} produced by
|
|
38
|
+
* {@link lazy}.
|
|
39
|
+
*
|
|
40
|
+
* @param value - Any value.
|
|
41
|
+
* @returns `true` when `value` carries the `__lazy` brand.
|
|
42
|
+
*/
|
|
18
43
|
declare function isLazy(value: unknown): value is LazyRouter;
|
|
19
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* Resolve a lazy router and cache the result.
|
|
46
|
+
*
|
|
47
|
+
* @remarks
|
|
48
|
+
* The resolved module is cached in a `WeakMap` keyed on the
|
|
49
|
+
* {@link LazyRouter} handle, so repeat calls return synchronously after
|
|
50
|
+
* the first. Concurrent calls share a single in-flight `Promise` to
|
|
51
|
+
* avoid duplicate imports — the race cache is cleared once the import
|
|
52
|
+
* settles.
|
|
53
|
+
*
|
|
54
|
+
* @param value - A handle returned by {@link lazy}.
|
|
55
|
+
* @returns The default export of the imported module.
|
|
56
|
+
*/
|
|
20
57
|
declare function resolveLazy(value: LazyRouter): Promise<RouterDef | ProcedureDef>;
|
|
21
58
|
//#endregion
|
|
22
59
|
export { LazyRouter, isLazy, lazy, resolveLazy };
|
package/dist/lazy.mjs
CHANGED
|
@@ -3,7 +3,26 @@ const resolved = /* @__PURE__ */ new WeakMap();
|
|
|
3
3
|
const loading = /* @__PURE__ */ new WeakMap();
|
|
4
4
|
/**
|
|
5
5
|
* Wrap a dynamic import for lazy loading.
|
|
6
|
-
*
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* The module must export its router or procedure as `default`. The
|
|
9
|
+
* returned handle is resolved on demand by {@link resolveLazy} and
|
|
10
|
+
* cached via module-local `WeakMap`s so concurrent resolutions share a
|
|
11
|
+
* single in-flight `Promise`.
|
|
12
|
+
*
|
|
13
|
+
* @param loader - A function returning `import('…')` — typically a
|
|
14
|
+
* dynamic import expression pointing at a module whose default export
|
|
15
|
+
* is a `RouterDef` or `ProcedureDef`.
|
|
16
|
+
* @returns A {@link LazyRouter} handle usable as a value inside
|
|
17
|
+
* `silgi.router({ … })`.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* const appRouter = k.router({
|
|
22
|
+
* users: lazy(() => import('./routes/users.ts')),
|
|
23
|
+
* admin: lazy(() => import('./routes/admin.ts')),
|
|
24
|
+
* })
|
|
25
|
+
* ```
|
|
7
26
|
*/
|
|
8
27
|
function lazy(loader) {
|
|
9
28
|
return {
|
|
@@ -11,11 +30,29 @@ function lazy(loader) {
|
|
|
11
30
|
load: loader
|
|
12
31
|
};
|
|
13
32
|
}
|
|
14
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* Type guard: `true` when `value` is a {@link LazyRouter} produced by
|
|
35
|
+
* {@link lazy}.
|
|
36
|
+
*
|
|
37
|
+
* @param value - Any value.
|
|
38
|
+
* @returns `true` when `value` carries the `__lazy` brand.
|
|
39
|
+
*/
|
|
15
40
|
function isLazy(value) {
|
|
16
41
|
return typeof value === "object" && value !== null && value.__lazy === true;
|
|
17
42
|
}
|
|
18
|
-
/**
|
|
43
|
+
/**
|
|
44
|
+
* Resolve a lazy router and cache the result.
|
|
45
|
+
*
|
|
46
|
+
* @remarks
|
|
47
|
+
* The resolved module is cached in a `WeakMap` keyed on the
|
|
48
|
+
* {@link LazyRouter} handle, so repeat calls return synchronously after
|
|
49
|
+
* the first. Concurrent calls share a single in-flight `Promise` to
|
|
50
|
+
* avoid duplicate imports — the race cache is cleared once the import
|
|
51
|
+
* settles.
|
|
52
|
+
*
|
|
53
|
+
* @param value - A handle returned by {@link lazy}.
|
|
54
|
+
* @returns The default export of the imported module.
|
|
55
|
+
*/
|
|
19
56
|
async function resolveLazy(value) {
|
|
20
57
|
const cached = resolved.get(value);
|
|
21
58
|
if (cached) return cached;
|
package/dist/map-input.mjs
CHANGED
|
@@ -15,7 +15,7 @@ declare class AnalyticsCollector {
|
|
|
15
15
|
alertEngine: AlertEngine | null;
|
|
16
16
|
/** Cost tracker */
|
|
17
17
|
costTracker: CostTracker;
|
|
18
|
-
constructor(options?: AnalyticsOptions);
|
|
18
|
+
constructor(options?: Omit<AnalyticsOptions, 'auth'>);
|
|
19
19
|
/** Set procedure schemas extracted from router definition. */
|
|
20
20
|
setProcedureSchemas(schemas: Map<string, {
|
|
21
21
|
input?: Record<string, unknown>;
|
|
@@ -90,12 +90,12 @@ interface AnalyticsOptions {
|
|
|
90
90
|
/** Time-series history in seconds (default: 120) */
|
|
91
91
|
historySeconds?: number;
|
|
92
92
|
/**
|
|
93
|
-
* Protect dashboard access.
|
|
93
|
+
* Protect dashboard access. **Required** — the analytics dashboard exposes
|
|
94
|
+
* request bodies, headers, and stack traces, so authentication is mandatory.
|
|
94
95
|
* - `string` — secret token checked against `Authorization: Bearer <token>` header or `?token=` query param
|
|
95
96
|
* - `(req: Request) => boolean | Promise<boolean>` — custom auth function
|
|
96
|
-
* - `undefined` — no auth (open access, NOT recommended in production)
|
|
97
97
|
*/
|
|
98
|
-
auth
|
|
98
|
+
auth: string | ((req: Request) => boolean | Promise<boolean>);
|
|
99
99
|
/** Interval in ms between storage flushes (default: 5000) */
|
|
100
100
|
flushInterval?: number;
|
|
101
101
|
/** Days to retain entries in storage (default: 30). Entries older than this are pruned on flush. */
|
|
@@ -13,11 +13,8 @@ const SENSITIVE_HEADER_KEYS = new Set([
|
|
|
13
13
|
"x-auth-token",
|
|
14
14
|
"proxy-authorization"
|
|
15
15
|
]);
|
|
16
|
-
function shouldRedactSensitiveData() {
|
|
17
|
-
return process.env.NODE_ENV === "production";
|
|
18
|
-
}
|
|
19
16
|
function redactHeaderValue(key, value) {
|
|
20
|
-
return
|
|
17
|
+
return SENSITIVE_HEADER_KEYS.has(key.toLowerCase()) ? REDACTED : value;
|
|
21
18
|
}
|
|
22
19
|
function round(n) {
|
|
23
20
|
return Math.round(n * 100) / 100;
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { RouterDef } from "../types.mjs";
|
|
2
2
|
import { AnalyticsOptions, AnalyticsSnapshot, ErrorEntry, ProcedureCall, ProcedureSnapshot, RequestEntry, SpanKind, TaskExecution, TaskSnapshot, TraceSpan } from "./analytics/types.mjs";
|
|
3
|
+
import { SchemaRegistry } from "../core/schema-converter.mjs";
|
|
3
4
|
import { AnalyticsCollector } from "./analytics/collector.mjs";
|
|
4
5
|
import { RequestTrace, trace } from "./analytics/trace.mjs";
|
|
5
6
|
import { analyticsAuthResponse, analyticsHTML, checkAnalyticsAuth, serveAnalyticsRoute } from "./analytics/routes.mjs";
|
|
6
7
|
import { errorToMarkdown, requestToMarkdown } from "./analytics/export.mjs";
|
|
7
8
|
import { sanitizeHeaders } from "./analytics/utils.mjs";
|
|
8
9
|
import { RequestAccumulator } from "./analytics/accumulator.mjs";
|
|
9
|
-
import {
|
|
10
|
+
import { SilgiHooks } from "../silgi.mjs";
|
|
10
11
|
import { FetchHandler } from "../core/handler.mjs";
|
|
12
|
+
import { Hookable } from "hookable";
|
|
11
13
|
|
|
12
14
|
//#region src/plugins/analytics.d.ts
|
|
13
15
|
interface ProcedureSchemaInfo {
|
|
@@ -18,6 +20,6 @@ interface ProcedureSchemaInfo {
|
|
|
18
20
|
* Wrap a fetch handler with analytics collection.
|
|
19
21
|
* Intercepts analytics dashboard routes and instruments every request.
|
|
20
22
|
*/
|
|
21
|
-
declare function wrapWithAnalytics(handler: FetchHandler, router: RouterDef | undefined, options?:
|
|
23
|
+
declare function wrapWithAnalytics(handler: FetchHandler, router: RouterDef | undefined, options: AnalyticsOptions, registry?: SchemaRegistry, hooks?: Hookable<SilgiHooks>): FetchHandler;
|
|
22
24
|
//#endregion
|
|
23
|
-
export { AnalyticsCollector, type AnalyticsOptions, type AnalyticsSnapshot, type ErrorEntry, type ProcedureCall, ProcedureSchemaInfo, type ProcedureSnapshot, RequestAccumulator, type RequestEntry, RequestTrace, type SpanKind, type TaskExecution, type TaskSnapshot, type TraceSpan, analyticsAuthResponse, analyticsHTML,
|
|
25
|
+
export { AnalyticsCollector, type AnalyticsOptions, type AnalyticsSnapshot, type ErrorEntry, type ProcedureCall, ProcedureSchemaInfo, type ProcedureSnapshot, RequestAccumulator, type RequestEntry, RequestTrace, type SpanKind, type TaskExecution, type TaskSnapshot, type TraceSpan, analyticsAuthResponse, analyticsHTML, checkAnalyticsAuth, errorToMarkdown, requestToMarkdown, sanitizeHeaders, serveAnalyticsRoute, trace, wrapWithAnalytics };
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { ValidationError } from "../core/schema.mjs";
|
|
2
2
|
import { SilgiError, toSilgiError } from "../core/error.mjs";
|
|
3
|
-
import { analyticsTraceMap } from "../core/trace-map.mjs";
|
|
4
3
|
import { parseUrlPathname } from "../core/url.mjs";
|
|
5
|
-
import {
|
|
4
|
+
import { schemaToJsonSchema } from "../core/schema-converter.mjs";
|
|
6
5
|
import { generateRequestId } from "./analytics/request-id.mjs";
|
|
7
6
|
import { isTrackedRequestPath, normalizeAnalyticsPath, round, sanitizeHeaders } from "./analytics/utils.mjs";
|
|
8
7
|
import { RequestAccumulator } from "./analytics/accumulator.mjs";
|
|
@@ -84,29 +83,18 @@ function extractResponseError(output, status, fallback) {
|
|
|
84
83
|
function isProcedureDef(value) {
|
|
85
84
|
return typeof value === "object" && value !== null && "type" in value && "resolve" in value && typeof value.resolve === "function";
|
|
86
85
|
}
|
|
87
|
-
|
|
88
|
-
function schemaToJson(schema, strategy) {
|
|
86
|
+
function schemaToJson(schema, strategy, registry) {
|
|
89
87
|
if (!schema) return void 0;
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
const result = std.jsonSchema.input({ target: "draft-2020-12" });
|
|
93
|
-
if (result && typeof result === "object") {
|
|
94
|
-
const { $schema: _, ...rest } = result;
|
|
95
|
-
return rest;
|
|
96
|
-
}
|
|
97
|
-
} catch {}
|
|
98
|
-
if (_zodConverter.condition(schema)) try {
|
|
99
|
-
const [, json] = _zodConverter.convert(schema, { strategy });
|
|
100
|
-
return json;
|
|
101
|
-
} catch {}
|
|
88
|
+
const json = schemaToJsonSchema(schema, strategy, registry);
|
|
89
|
+
return Object.keys(json).length > 0 ? json : void 0;
|
|
102
90
|
}
|
|
103
|
-
function extractProcedureSchemas(router) {
|
|
91
|
+
function extractProcedureSchemas(router, registry) {
|
|
104
92
|
const schemas = /* @__PURE__ */ new Map();
|
|
105
93
|
function walk(node, path) {
|
|
106
94
|
if (isProcedureDef(node)) {
|
|
107
95
|
const info = {};
|
|
108
|
-
if (node.input) info.input = schemaToJson(node.input, "input");
|
|
109
|
-
if (node.output) info.output = schemaToJson(node.output, "output");
|
|
96
|
+
if (node.input) info.input = schemaToJson(node.input, "input", registry);
|
|
97
|
+
if (node.output) info.output = schemaToJson(node.output, "output", registry);
|
|
110
98
|
if (info.input || info.output) schemas.set(path.join("/"), info);
|
|
111
99
|
return;
|
|
112
100
|
}
|
|
@@ -119,12 +107,13 @@ function extractProcedureSchemas(router) {
|
|
|
119
107
|
* Wrap a fetch handler with analytics collection.
|
|
120
108
|
* Intercepts analytics dashboard routes and instruments every request.
|
|
121
109
|
*/
|
|
122
|
-
function wrapWithAnalytics(handler, router, options
|
|
110
|
+
function wrapWithAnalytics(handler, router, options, registry, hooks) {
|
|
123
111
|
const collector = new AnalyticsCollector(options);
|
|
124
|
-
const procedureSchemas = router ? extractProcedureSchemas(router) : void 0;
|
|
112
|
+
const procedureSchemas = router ? extractProcedureSchemas(router, registry) : void 0;
|
|
125
113
|
if (procedureSchemas) collector.setProcedureSchemas(procedureSchemas);
|
|
126
114
|
const dashboardHtml = analyticsHTML();
|
|
127
115
|
const auth = options.auth;
|
|
116
|
+
if (!auth) throw new Error("[silgi analytics] `options.auth` is required. The analytics dashboard exposes request bodies, headers, and stack traces. Provide an auth token via `analytics: { auth: \"your-secret-token\" }` to protect it.");
|
|
128
117
|
import("../core/task.mjs").then(({ setTaskAnalytics }) => {
|
|
129
118
|
setTaskAnalytics((entry) => collector.recordTask({
|
|
130
119
|
...entry,
|
|
@@ -136,10 +125,8 @@ function wrapWithAnalytics(handler, router, options = {}) {
|
|
|
136
125
|
const analyticsSub = normalizeAnalyticsPath(pathname);
|
|
137
126
|
if (analyticsSub !== null) {
|
|
138
127
|
const canonical = analyticsSub === "" ? "api/analytics" : `api/analytics/${analyticsSub}`;
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
if (!(authResult instanceof Promise ? await authResult : authResult)) return analyticsAuthResponse(canonical);
|
|
142
|
-
}
|
|
128
|
+
const authResult = checkAnalyticsAuth(request, auth);
|
|
129
|
+
if (!(authResult instanceof Promise ? await authResult : authResult)) return analyticsAuthResponse(canonical);
|
|
143
130
|
return serveAnalyticsRoute(canonical, request, collector, dashboardHtml);
|
|
144
131
|
}
|
|
145
132
|
if (!isTrackedRequestPath(pathname) || collector.isIgnored(pathname)) return handler(request);
|
|
@@ -149,7 +136,9 @@ function wrapWithAnalytics(handler, router, options = {}) {
|
|
|
149
136
|
const acc = new RequestAccumulator(request, collector, traceId, parentRequestId ?? void 0);
|
|
150
137
|
const reqTrace = new RequestTrace();
|
|
151
138
|
const t0 = performance.now();
|
|
152
|
-
|
|
139
|
+
if (hooks) hooks.hookOnce("request:prepare", (event) => {
|
|
140
|
+
if (event.request === request) event.ctx.trace = reqTrace;
|
|
141
|
+
});
|
|
153
142
|
let response;
|
|
154
143
|
try {
|
|
155
144
|
response = await handler(request);
|
|
@@ -206,8 +195,6 @@ function wrapWithAnalytics(handler, router, options = {}) {
|
|
|
206
195
|
spans: reqTrace.spans ?? []
|
|
207
196
|
});
|
|
208
197
|
throw error;
|
|
209
|
-
} finally {
|
|
210
|
-
analyticsTraceMap.delete(request);
|
|
211
198
|
}
|
|
212
199
|
const headers = new Headers(response.headers);
|
|
213
200
|
headers.set("x-request-id", acc.requestId);
|
|
@@ -224,4 +211,4 @@ function wrapWithAnalytics(handler, router, options = {}) {
|
|
|
224
211
|
};
|
|
225
212
|
}
|
|
226
213
|
//#endregion
|
|
227
|
-
export { AnalyticsCollector, RequestAccumulator, RequestTrace, analyticsAuthResponse, analyticsHTML,
|
|
214
|
+
export { AnalyticsCollector, RequestAccumulator, RequestTrace, analyticsAuthResponse, analyticsHTML, checkAnalyticsAuth, errorToMarkdown, requestToMarkdown, sanitizeHeaders, serveAnalyticsRoute, trace, wrapWithAnalytics };
|
package/dist/plugins/cache.mjs
CHANGED
package/dist/plugins/coerce.mjs
CHANGED
package/dist/scalar.d.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { RouterDef } from "./types.mjs";
|
|
2
|
+
import { SchemaRegistry } from "./core/schema-converter.mjs";
|
|
2
3
|
//#region src/scalar.d.ts
|
|
3
4
|
interface ScalarOptions {
|
|
4
5
|
title?: string;
|
|
@@ -43,7 +44,7 @@ interface ScalarOptions {
|
|
|
43
44
|
*/
|
|
44
45
|
cdn?: 'cdn' | 'unpkg' | 'local' | (string & {});
|
|
45
46
|
}
|
|
46
|
-
declare function generateOpenAPI(router: RouterDef, options?: ScalarOptions, basePath?: string): Record<string, unknown>;
|
|
47
|
+
declare function generateOpenAPI(router: RouterDef, options?: ScalarOptions, basePath?: string, registry?: SchemaRegistry): Record<string, unknown>;
|
|
47
48
|
declare function scalarHTML(specUrl: string, options?: ScalarOptions): string;
|
|
48
49
|
//#endregion
|
|
49
50
|
export { ScalarOptions, generateOpenAPI, scalarHTML };
|
package/dist/scalar.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { schemaToJsonSchema } from "./core/schema-converter.mjs";
|
|
2
2
|
//#region src/scalar.ts
|
|
3
3
|
/**
|
|
4
4
|
* Scalar API Reference — v2 OpenAPI integration.
|
|
@@ -31,7 +31,8 @@ function toOpenAPIPath(raw) {
|
|
|
31
31
|
pathParams
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
|
-
function generateOpenAPI(router, options = {}, basePath = "") {
|
|
34
|
+
function generateOpenAPI(router, options = {}, basePath = "", registry) {
|
|
35
|
+
const schemaToJsonSchema$1 = (schema, strategy = "input") => schemaToJsonSchema(schema, strategy, registry);
|
|
35
36
|
const paths = {};
|
|
36
37
|
const tags = /* @__PURE__ */ new Map();
|
|
37
38
|
collectProcedures(router, [], (path, proc) => {
|
|
@@ -74,7 +75,7 @@ function generateOpenAPI(router, options = {}, basePath = "") {
|
|
|
74
75
|
if (route?.security === false) operation.security = [];
|
|
75
76
|
else if (route?.security) operation.security = route.security.map((s) => ({ [s]: [] }));
|
|
76
77
|
else if (options.security) operation.security = [{ auth: [] }];
|
|
77
|
-
const inputSchema = proc.input ? schemaToJsonSchema(proc.input, "input") : null;
|
|
78
|
+
const inputSchema = proc.input ? schemaToJsonSchema$1(proc.input, "input") : null;
|
|
78
79
|
const successStatus = route?.successStatus ?? 200;
|
|
79
80
|
const successDesc = route?.successDescription ?? "Successful response";
|
|
80
81
|
const guards = (proc.use ?? []).filter((m) => m.kind === "guard" && m.errors);
|
|
@@ -107,7 +108,7 @@ function generateOpenAPI(router, options = {}, basePath = "") {
|
|
|
107
108
|
};
|
|
108
109
|
if (params.length > 0) op.parameters = params;
|
|
109
110
|
if (proc.type === "subscription") {
|
|
110
|
-
const outputSchema = proc.output ? schemaToJsonSchema(proc.output, "output") : { type: "string" };
|
|
111
|
+
const outputSchema = proc.output ? schemaToJsonSchema$1(proc.output, "output") : { type: "string" };
|
|
111
112
|
op.responses[String(successStatus)] = {
|
|
112
113
|
description: "SSE event stream",
|
|
113
114
|
content: { "text/event-stream": { schema: {
|
|
@@ -117,7 +118,7 @@ function generateOpenAPI(router, options = {}, basePath = "") {
|
|
|
117
118
|
};
|
|
118
119
|
} else if (proc.output) op.responses[String(successStatus)] = {
|
|
119
120
|
description: successDesc,
|
|
120
|
-
content: { "application/json": { schema: schemaToJsonSchema(proc.output, "output") } }
|
|
121
|
+
content: { "application/json": { schema: schemaToJsonSchema$1(proc.output, "output") } }
|
|
121
122
|
};
|
|
122
123
|
else op.responses[String(successStatus)] = { description: successDesc };
|
|
123
124
|
if (proc.input) op.responses["400"] = {
|
|
@@ -154,7 +155,7 @@ function generateOpenAPI(router, options = {}, basePath = "") {
|
|
|
154
155
|
const entry = { code };
|
|
155
156
|
if (typeof def === "object") {
|
|
156
157
|
if (def.message) entry.message = def.message;
|
|
157
|
-
if (def.data) entry.schema = schemaToJsonSchema(def.data);
|
|
158
|
+
if (def.data) entry.schema = schemaToJsonSchema$1(def.data);
|
|
158
159
|
}
|
|
159
160
|
byStatus.get(status).push(entry);
|
|
160
161
|
}
|
|
@@ -269,28 +270,6 @@ function collectProcedures(node, path, cb) {
|
|
|
269
270
|
}
|
|
270
271
|
if (typeof node === "object" && node !== null) for (const [key, child] of Object.entries(node)) collectProcedures(child, [...path, key], cb);
|
|
271
272
|
}
|
|
272
|
-
/**
|
|
273
|
-
* Convert a Standard Schema to JSON Schema.
|
|
274
|
-
*
|
|
275
|
-
* Fast path: `~standard.jsonSchema.input()` (StandardJSONSchemaV1 implementors).
|
|
276
|
-
* Fallback: vendor-specific converters (Zod v4 via ZodSchemaConverter).
|
|
277
|
-
*/
|
|
278
|
-
const _zodConverter = new ZodSchemaConverter();
|
|
279
|
-
function schemaToJsonSchema(schema, strategy = "input") {
|
|
280
|
-
const std = schema["~standard"];
|
|
281
|
-
if (std?.jsonSchema?.input) try {
|
|
282
|
-
const result = std.jsonSchema.input({ target: "draft-2020-12" });
|
|
283
|
-
if (result && typeof result === "object") {
|
|
284
|
-
const { $schema: _, ...rest } = result;
|
|
285
|
-
return rest;
|
|
286
|
-
}
|
|
287
|
-
} catch {}
|
|
288
|
-
if (_zodConverter.condition(schema)) try {
|
|
289
|
-
const [, jsonSchema] = _zodConverter.convert(schema, { strategy });
|
|
290
|
-
return jsonSchema;
|
|
291
|
-
} catch {}
|
|
292
|
-
return {};
|
|
293
|
-
}
|
|
294
273
|
function objectSchemaToParams(schema) {
|
|
295
274
|
if (schema.type !== "object" || !schema.properties) return [];
|
|
296
275
|
const required = new Set(schema.required ?? []);
|
|
@@ -306,9 +285,9 @@ function objectSchemaToParams(schema) {
|
|
|
306
285
|
* Wrap a fetch handler to serve Scalar API Reference at /api/reference and /api/openapi.json.
|
|
307
286
|
* Scalar routes are intercepted before the handler — zero overhead for normal requests.
|
|
308
287
|
*/
|
|
309
|
-
function wrapWithScalar(handler, routerDef, options = {}, prefix = "/api") {
|
|
288
|
+
function wrapWithScalar(handler, routerDef, options = {}, prefix = "/api", registry) {
|
|
310
289
|
const normPrefix = (prefix.startsWith("/") ? prefix : "/" + prefix).replace(/\/+$/, "");
|
|
311
|
-
const specJson = JSON.stringify(generateOpenAPI(routerDef, options, normPrefix));
|
|
290
|
+
const specJson = JSON.stringify(generateOpenAPI(routerDef, options, normPrefix, registry));
|
|
312
291
|
const specHtml = scalarHTML(`${normPrefix}/openapi.json`, options);
|
|
313
292
|
const openapiMatch = `${normPrefix.slice(1)}/openapi.json`;
|
|
314
293
|
const referenceMatch = `${normPrefix.slice(1)}/reference`;
|