silgi 0.53.0 → 0.53.1

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.
@@ -1,19 +1,45 @@
1
1
  import { SilgiError } from "./error.mjs";
2
2
  //#region src/core/input.ts
3
3
  /**
4
- * Request input parsing — JSON, MessagePack, devalue, query string.
4
+ * Request input parsing
5
+ * -----------------------
6
+ *
7
+ * Pulls the RPC input payload out of an incoming `Request` and returns
8
+ * it in its decoded form. The shape of the input depends on how the
9
+ * client chose to send it:
10
+ *
11
+ * - `GET` / no-body — JSON-encoded, URL-escaped, on `?data=` query.
12
+ * - `content-type: application/msgpack` — binary, via the msgpack codec.
13
+ * - `content-type: application/x-devalue` — text, via devalue.
14
+ * - anything else (typically JSON) — JSON body.
15
+ *
16
+ * Empty bodies always resolve to `undefined` so that a procedure with
17
+ * no input, or one whose schema allows `undefined`, works without the
18
+ * client having to send a payload at all. Malformed non-empty bodies
19
+ * throw `BAD_REQUEST` with a message the client can show to a user.
20
+ */
21
+ /**
22
+ * The msgpack and devalue codecs are pulled in on first use so that a
23
+ * handler that only ever sees JSON never pays the cost of loading
24
+ * them. `Promise`-cached at module scope: once the first request
25
+ * triggers the import, subsequent calls share the resolved module.
26
+ *
27
+ * Module-global is intentional and safe here — the cached value is a
28
+ * reference to an immutable ES module, not user data. Two silgi
29
+ * instances in the same process legitimately share it.
5
30
  */
6
- let _msgpack;
7
- let _devalue;
8
- /** Max allowed size for GET ?data= parameter (bytes). Prevents JSON bomb via URL. */
31
+ let msgpackModule;
32
+ let devalueModule;
33
+ /** Max bytes permitted in the `?data=` query param. Shields against JSON-bomb payloads in a URL. */
9
34
  const MAX_QUERY_DATA_LENGTH = 8192;
10
35
  /**
11
36
  * Find the value of the `data=` query parameter by key, not by substring.
12
37
  *
13
- * The previous `indexOf('data=')` implementation matched `userdata=...`,
14
- * `xdata=...`, or any other key ending in `data` silently returning the
15
- * wrong value. This scans for `data=` at a parameter boundary (either the
16
- * first param or after `&`).
38
+ * A naive `searchStr.indexOf('data=')` matches `userdata=`, `mydata=`,
39
+ * or any other key that merely ends in `data`, and silently returns
40
+ * the wrong value. This scans for `data=` only at a parameter
41
+ * boundary i.e. at the start of the search string, or right after
42
+ * an `&`.
17
43
  */
18
44
  function findDataParam(searchStr) {
19
45
  let i = 0;
@@ -29,31 +55,47 @@ function findDataParam(searchStr) {
29
55
  }
30
56
  return null;
31
57
  }
32
- /** Parse request input from body or query string */
33
- async function parseInput(request, url, qMark) {
34
- if (request.method === "GET" || !request.body) {
35
- if (qMark !== -1) {
36
- const encoded = findDataParam(url.slice(qMark + 1));
37
- if (encoded !== null) {
38
- if (encoded.length > MAX_QUERY_DATA_LENGTH) throw new SilgiError("BAD_REQUEST", { message: "Query data parameter too large" });
39
- return JSON.parse(decodeURIComponent(encoded));
40
- }
41
- }
42
- return;
43
- }
44
- const ct = request.headers.get("content-type");
45
- if (ct) {
46
- if (ct.includes("msgpack")) {
47
- _msgpack ??= await import("../codec/msgpack.mjs");
48
- const buf = new Uint8Array(await request.arrayBuffer());
49
- return buf.length > 0 ? _msgpack.decode(buf) : void 0;
50
- }
51
- if (ct.includes("x-devalue")) {
52
- _devalue ??= await import("../codec/devalue.mjs");
53
- const text = await request.text();
54
- return text ? _devalue.decode(text) : void 0;
55
- }
56
- }
58
+ /**
59
+ * Decode the `?data=` query param as JSON. Returns `undefined` when
60
+ * the query is missing or has no `data=` field. Throws `BAD_REQUEST`
61
+ * when the payload is oversized or unparsable.
62
+ */
63
+ function decodeQueryInput(url, qMark) {
64
+ if (qMark === -1) return void 0;
65
+ const encoded = findDataParam(url.slice(qMark + 1));
66
+ if (encoded === null) return void 0;
67
+ if (encoded.length > MAX_QUERY_DATA_LENGTH) throw new SilgiError("BAD_REQUEST", { message: "Query data parameter too large" });
68
+ return JSON.parse(decodeURIComponent(encoded));
69
+ }
70
+ /**
71
+ * Decode a MessagePack-encoded request body. Empty bodies resolve to
72
+ * `undefined` (procedures with no input work without a payload).
73
+ */
74
+ async function decodeMsgpackBody(request) {
75
+ msgpackModule ??= await import("../codec/msgpack.mjs");
76
+ const buf = new Uint8Array(await request.arrayBuffer());
77
+ return buf.length > 0 ? msgpackModule.decode(buf) : void 0;
78
+ }
79
+ /** Decode a devalue-encoded request body. Empty bodies resolve to `undefined`. */
80
+ async function decodeDevalueBody(request) {
81
+ devalueModule ??= await import("../codec/devalue.mjs");
82
+ const text = await request.text();
83
+ return text ? devalueModule.decode(text) : void 0;
84
+ }
85
+ /**
86
+ * Decode a JSON-encoded request body.
87
+ *
88
+ * Empty bodies resolve to `undefined` so the input schema sees the
89
+ * same value whether or not the client sent a body at all. Malformed
90
+ * non-empty bodies throw `BAD_REQUEST`.
91
+ *
92
+ * Why we `text()` first and then `JSON.parse` — instead of
93
+ * `request.json()`: Bun's `request.json()` is a fast path, but it
94
+ * throws a generic `SyntaxError` for **both** empty and malformed
95
+ * bodies, so we cannot tell them apart. Reading text first keeps the
96
+ * two cases distinct (and Bun's `text()` is also fast).
97
+ */
98
+ async function decodeJsonBody(request) {
57
99
  const text = await request.text();
58
100
  if (!text) return void 0;
59
101
  try {
@@ -62,5 +104,25 @@ async function parseInput(request, url, qMark) {
62
104
  throw new SilgiError("BAD_REQUEST", { message: "Malformed JSON body" });
63
105
  }
64
106
  }
107
+ /**
108
+ * Decode the input payload off a Fetch `Request`.
109
+ *
110
+ * @param request The incoming request.
111
+ * @param url The full request URL (reused by the caller — we avoid
112
+ * re-parsing it here).
113
+ * @param qMark Byte offset of the `?` in `url`, or `-1` when absent.
114
+ *
115
+ * @returns The decoded input value, or `undefined` when the request
116
+ * carries no payload.
117
+ */
118
+ async function parseInput(request, url, qMark) {
119
+ if (request.method === "GET" || !request.body) return decodeQueryInput(url, qMark);
120
+ const contentType = request.headers.get("content-type");
121
+ if (contentType) {
122
+ if (contentType.includes("msgpack")) return decodeMsgpackBody(request);
123
+ if (contentType.includes("x-devalue")) return decodeDevalueBody(request);
124
+ }
125
+ return decodeJsonBody(request);
126
+ }
65
127
  //#endregion
66
128
  export { parseInput };
@@ -2,7 +2,9 @@ import { AnySchema } from "./schema.mjs";
2
2
 
3
3
  //#region src/core/schema-converter.d.ts
4
4
  /**
5
- * JSON Schema subset used for OpenAPI / analytics output.
5
+ * JSON Schema subset used across silgi's OpenAPI and analytics output.
6
+ * Intentionally broad (`[key: string]: unknown`) so library-specific
7
+ * fields (e.g. Zod's `x-native-type`) pass through untouched.
6
8
  *
7
9
  * @category Schema
8
10
  */
@@ -22,54 +24,68 @@ interface JSONSchema {
22
24
  default?: unknown;
23
25
  [key: string]: unknown;
24
26
  }
27
+ /**
28
+ * JSON Schema dialect passed through to the schema library. Matches the
29
+ * `target` field of the Standard JSON Schema spec. Unknown strings are
30
+ * allowed so new dialects can be threaded through without a silgi
31
+ * release; libraries that do not recognise the value should throw and
32
+ * the conversion falls back to an empty schema.
33
+ *
34
+ * @category Schema
35
+ */
36
+ type JSONSchemaTarget = 'draft-2020-12' | 'draft-07' | 'openapi-3.0' | (string & {});
25
37
  /**
26
38
  * Options passed to a converter's `toJsonSchema` method.
27
39
  *
28
40
  * @category Schema
29
41
  */
30
42
  interface ConvertOptions {
43
+ /** `'input'` for pre-transform types, `'output'` for post-transform. */
31
44
  strategy: 'input' | 'output';
45
+ /** JSON Schema dialect to target. Defaults to `'draft-2020-12'`. */
46
+ target?: JSONSchemaTarget;
47
+ /** Opaque options the converter may forward to its underlying library. */
48
+ libraryOptions?: Record<string, unknown>;
32
49
  }
33
50
  /**
34
- * A converter that translates a specific Standard Schema vendor's schemas
35
- * into JSON Schema. Pass instances via `silgi({ schemaConverters: [...] })`.
51
+ * Fallback converter for a schema library that has not adopted the
52
+ * Standard JSON Schema extension yet. Pass instances via
53
+ * `silgi({ schemaConverters: [...] })`.
36
54
  *
37
55
  * @remarks
38
- * Implement this interface to add OpenAPI / analytics support for a custom
39
- * schema library. The `vendor` string must match the `~standard.vendor`
40
- * property reported by the schema library's Standard Schema implementation.
56
+ * Libraries that *do* implement the extension (Zod v4.2+, ArkType
57
+ * v2.1.28+, Valibot v1.2+) are handled without a converter silgi
58
+ * calls their native `~standard.jsonSchema` directly. Write a converter
59
+ * only when you need to support a vendor that has not yet adopted the
60
+ * spec.
41
61
  *
42
62
  * @example
43
- * ```ts
44
- * import type { SchemaConverter } from 'silgi'
63
+ * import type { SchemaConverter } from 'silgi'
45
64
  *
46
- * const myConverter: SchemaConverter = {
47
- * vendor: 'my-lib',
48
- * toJsonSchema(schema, opts) {
49
- * return { type: 'string' }
50
- * },
51
- * }
52
- * ```
65
+ * const myConverter: SchemaConverter = {
66
+ * vendor: 'my-lib',
67
+ * toJsonSchema(schema, opts) {
68
+ * return { type: 'string' }
69
+ * },
70
+ * }
53
71
  *
54
72
  * @category Schema
55
73
  */
56
74
  interface SchemaConverter {
57
- /** The Standard Schema `~standard.vendor` string this converter handles (e.g. `"zod"`). */
75
+ /** Matches the `~standard.vendor` reported by the schema library. */
58
76
  vendor: string;
59
77
  /**
60
- * Convert a schema to a JSON Schema object.
61
- *
62
- * @param schema - The schema to convert.
63
- * @param opts - Conversion options including `strategy` (`'input'` | `'output'`).
64
- * @returns A JSON Schema object. Return `{}` for unsupported/unknown schemas.
78
+ * Convert a schema to a JSON Schema object. Return `{}` for schemas
79
+ * the converter does not understand.
65
80
  */
66
81
  toJsonSchema(schema: AnySchema, opts: ConvertOptions): JSONSchema;
67
82
  }
68
83
  /**
69
- * A per-instance registry mapping vendor strings to their converters.
70
- *
71
- * Built by {@link createSchemaRegistry} and threaded through the handler
72
- * pipeline to scalar and analytics wrappers.
84
+ * Per-instance mapping of vendor string fallback converter. Built by
85
+ * {@link createSchemaRegistry} and threaded through the handler pipeline
86
+ * to the scalar and analytics wrappers. Using `Map` gives O(1) lookup
87
+ * and keyed-by-vendor semantics that match the spec's own extension
88
+ * contract.
73
89
  *
74
90
  * @category Schema
75
91
  */
@@ -77,55 +93,44 @@ type SchemaRegistry = Map<string, SchemaConverter>;
77
93
  /**
78
94
  * Build a {@link SchemaRegistry} from an array of converters.
79
95
  *
80
- * @param converters - Array of {@link SchemaConverter} objects, each
81
- * declaring their own `vendor`.
82
- * @returns A `Map<string, SchemaConverter>` keyed by `converter.vendor`.
83
- *
84
96
  * @example
85
- * ```ts
86
- * import { zodConverter } from 'silgi/zod'
87
- * import { createSchemaRegistry } from 'silgi'
88
- *
89
- * const registry = createSchemaRegistry([zodConverter])
90
- * ```
97
+ * import { zodConverter } from 'silgi/zod'
98
+ * const registry = createSchemaRegistry([zodConverter])
91
99
  *
92
100
  * @category Schema
93
101
  */
94
102
  declare function createSchemaRegistry(converters?: SchemaConverter[]): SchemaRegistry;
95
103
  /**
96
- * Convert any Standard Schema to JSON Schema.
97
- *
98
- * @remarks
99
- * Resolution order:
100
- * 1. **Native fast path** `schema['~standard'].jsonSchema.input()`
101
- * (Valibot, ArkType, Zod v4, …). No registry needed.
102
- * 2. **Registry lookup** finds a converter by
103
- * `schema['~standard'].vendor`. Registry must be passed explicitly;
104
- * there is no global mutable state.
105
- * 3. **Empty schema `{}`** — emits a one-time `console.warn` per vendor
106
- * when a registry was provided but contained no matching converter.
107
- * No warn when no registry was passed (caller opted out).
108
- *
109
- * @param schema - Any Standard Schema compatible schema object.
110
- * @param strategy - `'input'` (default) for pre-transform types; `'output'`
111
- * for post-transform.
112
- * @param registry - Optional {@link SchemaRegistry} built from
113
- * {@link createSchemaRegistry}. When omitted the function still handles
114
- * schemas that expose the native `jsonSchema.input()` fast path.
115
- * @returns A JSON Schema object. Returns `{}` when conversion is not possible.
104
+ * Convert any Standard Schema to a JSON Schema object.
105
+ *
106
+ * @param schema The schema to convert.
107
+ * @param strategy `'input'` (default) for pre-transform types, `'output'`
108
+ * for post-transform. Matters for schemas that coerce
109
+ * (e.g. `z.coerce.number()` takes a string and yields a
110
+ * number input and output schemas differ).
111
+ * @param registry Optional fallback registry built by
112
+ * {@link createSchemaRegistry}. Omit to rely solely on
113
+ * the native Standard JSON Schema extension.
114
+ * @param options Extra knobs: `target` dialect (default
115
+ * `'draft-2020-12'`), opaque `libraryOptions`
116
+ * forwarded to the underlying library.
117
+ *
118
+ * @returns A JSON Schema object. `{}` when the schema cannot be
119
+ * converted (silent fallback — analytics / OpenAPI output
120
+ * still renders, just without schema detail for that field).
116
121
  *
117
122
  * @example
118
- * ```ts
119
- * import { zodConverter } from 'silgi/zod'
120
- * import { createSchemaRegistry, schemaToJsonSchema } from 'silgi'
121
- * import { z } from 'zod'
123
+ * import { zodConverter } from 'silgi/zod'
124
+ * import { createSchemaRegistry, schemaToJsonSchema } from 'silgi'
122
125
  *
123
- * const registry = createSchemaRegistry([zodConverter])
124
- * const json = schemaToJsonSchema(z.object({ name: z.string() }), 'input', registry)
125
- * ```
126
+ * const registry = createSchemaRegistry([zodConverter])
127
+ * const json = schemaToJsonSchema(MySchema, 'input', registry)
126
128
  *
127
129
  * @category Schema
128
130
  */
129
- declare function schemaToJsonSchema(schema: AnySchema, strategy?: 'input' | 'output', registry?: SchemaRegistry): JSONSchema;
131
+ declare function schemaToJsonSchema(schema: AnySchema, strategy?: 'input' | 'output', registry?: SchemaRegistry, options?: {
132
+ target?: JSONSchemaTarget;
133
+ libraryOptions?: Record<string, unknown>;
134
+ }): JSONSchema;
130
135
  //#endregion
131
136
  export { ConvertOptions, JSONSchema, SchemaConverter, SchemaRegistry, createSchemaRegistry, schemaToJsonSchema };
@@ -2,17 +2,9 @@
2
2
  /**
3
3
  * Build a {@link SchemaRegistry} from an array of converters.
4
4
  *
5
- * @param converters - Array of {@link SchemaConverter} objects, each
6
- * declaring their own `vendor`.
7
- * @returns A `Map<string, SchemaConverter>` keyed by `converter.vendor`.
8
- *
9
5
  * @example
10
- * ```ts
11
- * import { zodConverter } from 'silgi/zod'
12
- * import { createSchemaRegistry } from 'silgi'
13
- *
14
- * const registry = createSchemaRegistry([zodConverter])
15
- * ```
6
+ * import { zodConverter } from 'silgi/zod'
7
+ * const registry = createSchemaRegistry([zodConverter])
16
8
  *
17
9
  * @category Schema
18
10
  */
@@ -21,62 +13,99 @@ function createSchemaRegistry(converters = []) {
21
13
  for (const converter of converters) map.set(converter.vendor, converter);
22
14
  return map;
23
15
  }
24
- const _warnedVendors = /* @__PURE__ */ new Set();
25
16
  /**
26
- * Convert any Standard Schema to JSON Schema.
17
+ * Vendor names we've already warned about. Module-scoped so the
18
+ * warning fires at most once per vendor per process lifetime — across
19
+ * every silgi instance in the same process. The set only holds bounded
20
+ * vendor strings (no schema data, no user input), so sharing it
21
+ * globally is safe.
22
+ */
23
+ const warnedVendors = /* @__PURE__ */ new Set();
24
+ /** Read the `~standard` slot off an AnySchema in one typed step. */
25
+ const readStandardSlot = (schema) => schema["~standard"];
26
+ /**
27
+ * Call the Standard JSON Schema extension on a schema that exposes it.
28
+ * Returns `null` when the extension does not handle this strategy or
29
+ * throws (in which case the caller falls back to the registry).
27
30
  *
28
- * @remarks
29
- * Resolution order:
30
- * 1. **Native fast path** — `schema['~standard'].jsonSchema.input()`
31
- * (Valibot, ArkType, Zod v4, …). No registry needed.
32
- * 2. **Registry lookup** finds a converter by
33
- * `schema['~standard'].vendor`. Registry must be passed explicitly;
34
- * there is no global mutable state.
35
- * 3. **Empty schema `{}`** — emits a one-time `console.warn` per vendor
36
- * when a registry was provided but contained no matching converter.
37
- * No warn when no registry was passed (caller opted out).
31
+ * We strip the `$schema` meta field because it is a JSON Schema marker,
32
+ * not a member of the schema itself — silgi never propagates it.
33
+ */
34
+ function callNativeJsonSchema(std, opts) {
35
+ const generator = opts.strategy === "output" ? std.jsonSchema?.output : std.jsonSchema?.input;
36
+ if (!generator) return null;
37
+ try {
38
+ const result = generator({
39
+ target: opts.target,
40
+ libraryOptions: opts.libraryOptions
41
+ });
42
+ if (!result || typeof result !== "object") return null;
43
+ const { $schema: _ignored, ...rest } = result;
44
+ return rest;
45
+ } catch {
46
+ return null;
47
+ }
48
+ }
49
+ /** Warn once per vendor when a registry was passed but had no match. */
50
+ function warnMissingConverter(vendor) {
51
+ if (warnedVendors.has(vendor)) return;
52
+ warnedVendors.add(vendor);
53
+ console.warn(`[silgi] No schema converter registered for vendor "${vendor}". Pass schemaConverters: [${vendor}Converter] to silgi() to enable OpenAPI / analytics schema generation.`);
54
+ }
55
+ /**
56
+ * Convert any Standard Schema to a JSON Schema object.
38
57
  *
39
- * @param schema - Any Standard Schema compatible schema object.
40
- * @param strategy - `'input'` (default) for pre-transform types; `'output'`
41
- * for post-transform.
42
- * @param registry - Optional {@link SchemaRegistry} built from
43
- * {@link createSchemaRegistry}. When omitted the function still handles
44
- * schemas that expose the native `jsonSchema.input()` fast path.
45
- * @returns A JSON Schema object. Returns `{}` when conversion is not possible.
58
+ * @param schema The schema to convert.
59
+ * @param strategy `'input'` (default) for pre-transform types, `'output'`
60
+ * for post-transform. Matters for schemas that coerce
61
+ * (e.g. `z.coerce.number()` takes a string and yields a
62
+ * number input and output schemas differ).
63
+ * @param registry Optional fallback registry built by
64
+ * {@link createSchemaRegistry}. Omit to rely solely on
65
+ * the native Standard JSON Schema extension.
66
+ * @param options Extra knobs: `target` dialect (default
67
+ * `'draft-2020-12'`), opaque `libraryOptions`
68
+ * forwarded to the underlying library.
69
+ *
70
+ * @returns A JSON Schema object. `{}` when the schema cannot be
71
+ * converted (silent fallback — analytics / OpenAPI output
72
+ * still renders, just without schema detail for that field).
46
73
  *
47
74
  * @example
48
- * ```ts
49
- * import { zodConverter } from 'silgi/zod'
50
- * import { createSchemaRegistry, schemaToJsonSchema } from 'silgi'
51
- * import { z } from 'zod'
75
+ * import { zodConverter } from 'silgi/zod'
76
+ * import { createSchemaRegistry, schemaToJsonSchema } from 'silgi'
52
77
  *
53
- * const registry = createSchemaRegistry([zodConverter])
54
- * const json = schemaToJsonSchema(z.object({ name: z.string() }), 'input', registry)
55
- * ```
78
+ * const registry = createSchemaRegistry([zodConverter])
79
+ * const json = schemaToJsonSchema(MySchema, 'input', registry)
56
80
  *
57
81
  * @category Schema
58
82
  */
59
- function schemaToJsonSchema(schema, strategy = "input", registry) {
60
- const std = schema?.["~standard"];
61
- if (std?.jsonSchema?.input) try {
62
- const result = std.jsonSchema.input({ target: "draft-2020-12" });
63
- if (result && typeof result === "object") {
64
- const { $schema: _, ...rest } = result;
65
- return rest;
66
- }
67
- } catch {}
68
- const vendor = typeof std?.vendor === "string" ? std.vendor : void 0;
69
- if (vendor && registry) {
70
- const converter = registry.get(vendor);
71
- if (converter) try {
72
- return converter.toJsonSchema(schema, { strategy });
73
- } catch {}
74
- else if (!_warnedVendors.has(vendor)) {
75
- _warnedVendors.add(vendor);
76
- console.warn(`[silgi] No schema converter registered for vendor "${vendor}". Pass schemaConverters: [${vendor}Converter] to silgi() to enable OpenAPI / analytics schema generation.`);
77
- }
83
+ function schemaToJsonSchema(schema, strategy = "input", registry, options = {}) {
84
+ const std = readStandardSlot(schema);
85
+ if (!std) return {};
86
+ const target = options.target ?? "draft-2020-12";
87
+ const native = callNativeJsonSchema(std, {
88
+ strategy,
89
+ target,
90
+ libraryOptions: options.libraryOptions
91
+ });
92
+ if (native !== null) return native;
93
+ const vendor = typeof std.vendor === "string" ? std.vendor : void 0;
94
+ if (!vendor || !registry) return {};
95
+ const converter = registry.get(vendor);
96
+ if (!converter) {
97
+ warnMissingConverter(vendor);
98
+ return {};
99
+ }
100
+ try {
101
+ return converter.toJsonSchema(schema, {
102
+ strategy,
103
+ target,
104
+ libraryOptions: options.libraryOptions
105
+ });
106
+ } catch {
107
+ return {};
78
108
  }
79
- return {};
80
109
  }
81
110
  //#endregion
82
111
  export { createSchemaRegistry, schemaToJsonSchema };
@@ -5,52 +5,53 @@ import { Hookable } from "hookable";
5
5
 
6
6
  //#region src/core/serve.d.ts
7
7
  interface SilgiServer {
8
- /** Server URL (e.g. "http://127.0.0.1:3000") */
8
+ /** Full server URL, e.g. `http://127.0.0.1:3000`. */
9
9
  readonly url: string;
10
- /** Configured port */
10
+ /** Port the server actually bound to (may differ from requested when `0`). */
11
11
  readonly port: number;
12
- /** Configured hostname */
12
+ /** Hostname the server bound to. */
13
13
  readonly hostname: string;
14
14
  /**
15
- * Gracefully shut down the server.
15
+ * Gracefully shut the server down.
16
16
  *
17
- * By default waits for in-flight requests to complete.
18
- * Pass `true` to forcefully terminate all active connections immediately.
17
+ * Waits for in-flight requests by default. Pass `true` to drop
18
+ * active connections immediately.
19
19
  */
20
20
  close(forceCloseConnections?: boolean): Promise<void>;
21
21
  }
22
22
  interface ServeOptions {
23
- /** URL path prefix (e.g. "/api"). Only requests matching this prefix are handled; others return 404. */
23
+ /** URL path prefix (e.g. `/api`). Requests outside the prefix 404. */
24
24
  basePath?: string;
25
25
  port?: number;
26
26
  hostname?: string;
27
- /** Enable Scalar API Reference UI at /api/reference and /api/openapi.json */
27
+ /** Mount the Scalar API Reference UI at `/api/reference`. */
28
28
  scalar?: boolean | ScalarOptions;
29
- /** Enable analytics dashboard at /api/analytics requires `auth` to be set */
29
+ /** Mount the analytics dashboard at `/api/analytics` (requires `auth`). */
30
30
  analytics?: AnalyticsOptions;
31
31
  /**
32
32
  * WebSocket RPC configuration.
33
33
  *
34
- * Defaults to auto-enabled when the router contains any subscription procedure.
35
- * Pass `false` to disable, or an options object to fine-tune crossws (compression, keepalive, maxPayload).
34
+ * Auto-enabled when the router contains any subscription procedure.
35
+ * Pass `false` to disable, or an options object to tune crossws
36
+ * (compression, keepalive, maxPayload).
36
37
  */
37
38
  ws?: false | WSAdapterOptions;
38
- /** Enable HTTP/2 (requires cert + key for TLS) */
39
+ /** TLS material for HTTP/2. When set, the server serves HTTPS. */
39
40
  http2?: {
40
41
  cert: string;
41
42
  key: string;
42
43
  };
43
44
  /**
44
- * Graceful shutdown on SIGINT/SIGTERM signals.
45
+ * Graceful shutdown on `SIGINT` / `SIGTERM`.
45
46
  *
46
- * - `true` (default): enables graceful shutdown with srvx defaults
47
- * - `false`: disables automatic signal handling
48
- * - `object`: fine-tune timeouts
47
+ * - `true` enable with srvx defaults (recommended).
48
+ * - `false` — disable automatic signal handling.
49
+ * - object fine-tune timeouts.
49
50
  *
50
51
  * @default true
51
52
  */
52
53
  gracefulShutdown?: boolean | {
53
- /** Max time (ms) to wait for in-flight requests before force-closing */timeout?: number; /** Max time (ms) after graceful period before process.exit */
54
+ /** Max ms to wait for in-flight requests before force-closing. */timeout?: number; /** Max ms after graceful period before `process.exit`. */
54
55
  forceTimeout?: number;
55
56
  };
56
57
  }