@daloyjs/core 0.35.2 → 0.36.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 CHANGED
@@ -466,6 +466,7 @@ The framework refuses to start (or to construct) when configuration is unsafe:
466
466
 
467
467
  - Weak session secrets, `cors({ origin: "*" })` with credentials, `session()` + state-changing route without `csrf()`, and unconfigured `X-Forwarded-*` in production.
468
468
  - `secureDefaults: false` in production unless `acknowledgeInsecureDefaults: true` is set, plus a once-per-process `error` log naming every disabled default.
469
+ - `preset: "internal-service"` topology preset for service-to-service deployments behind a mesh / sidecar / private network: turns OFF the browser-only guards (auto `secureHeaders`, `corsCrossOriginGuard`, `csrf` boot guard, unconfigured `X-Forwarded-*` guard) while keeping every input, parser, credential, SSRF, weak-secret, and refuse-to-boot guard ON. Per-knob options still win, the choice is logged at boot under `event: "security.preset.applied"`, and the live posture is auditable via `app.getSecurityPosture()`.
469
470
  - `createJwtSigner()` / `createJwtVerifier()` refuse `alg: "none"`, accept only an explicit allowlist, refuse HS + JWK combinations, refuse to sign without `exp`, and refuse HS-shaped secrets under 32 bytes (RFC 7518 §3.2).
470
471
  - `secureHeaders()` refuses to construct with `frameOptions: false` AND no CSP `frame-ancestors` directive (no clickjacking defense).
471
472
  - `cors()` refuses `methods: ['*']` at construction; default `allowMethods` narrowed to `[GET, HEAD, POST]` so `PUT` / `PATCH` / `DELETE` become explicit opt-ins.
package/dist/app.d.ts CHANGED
@@ -10,6 +10,33 @@ import { type BehindProxyConfig } from "./conn-info.js";
10
10
  export declare function _resetCrashHandlersForTests(): void;
11
11
  /** @internal Test-only helper to reset the latch between tests. */
12
12
  export declare function _resetInsecureDefaultsLogForTests(): void;
13
+ /**
14
+ * Named security posture preset. Currently only one value is supported:
15
+ *
16
+ * - `"internal-service"` — relaxes the *topology-dependent* defaults that
17
+ * only make sense when an HTTP boundary faces a browser or the public
18
+ * internet (auto `secureHeaders`, cross-origin write guard, the
19
+ * session+state-changing-route CSRF boot guard, and the unconfigured
20
+ * `X-Forwarded-*` guard). Everything that protects the service from
21
+ * malformed input, confused dependencies, or compromised callers —
22
+ * body limits, request timeouts, JWT algorithm allowlists, weak-secret
23
+ * refuse-to-boot, `cors({ origin: '*' })` refuse-to-boot, anonymous
24
+ * stateful plugin refuse-to-boot, `crashOnUnhandledRejection`, schema
25
+ * strictness, prototype-pollution-safe parsers, SSRF-safe `fetchGuard`
26
+ * defaults, RFC 9457 problem+json redaction — stays on. Per-knob
27
+ * options still win (`secureHeaders: { ... }` re-enables it on top of
28
+ * the preset). The preset choice is logged once at boot under the
29
+ * `security.preset.applied` event so operators can audit the posture
30
+ * without reading code.
31
+ *
32
+ * Topology presets are intentionally a small, curated set — they are NOT
33
+ * a master "disable everything" knob. If you really need to disable the
34
+ * entire secure-by-default surface, use the explicit
35
+ * {@link AppOptions.secureDefaults} `false` escape hatch.
36
+ *
37
+ * @since 0.34.0
38
+ */
39
+ export type SecurityPreset = "internal-service";
13
40
  /**
14
41
  * Configuration accepted by {@link App}'s constructor. Every field is
15
42
  * optional; sensible production defaults are applied.
@@ -21,6 +48,23 @@ export interface AppOptions {
21
48
  title?: string;
22
49
  version?: string;
23
50
  description?: string;
51
+ /**
52
+ * Topology-aware security posture preset. See {@link SecurityPreset}.
53
+ *
54
+ * - `"internal-service"` — for service-to-service deployments behind a
55
+ * service mesh, sidecar, or private network. Turns off the
56
+ * browser-/edge-only guards (auto `secureHeaders`, cross-origin write
57
+ * guard, session+state-changing CSRF boot guard, unconfigured
58
+ * `X-Forwarded-*` guard) while keeping every input-, parser-,
59
+ * credential-, and SSRF-level guard on. The choice is logged once at
60
+ * boot under the `security.preset.applied` event. Per-knob options
61
+ * you pass alongside the preset still win.
62
+ *
63
+ * Omit (default) for browser-facing / public APIs.
64
+ *
65
+ * @since 0.34.0
66
+ */
67
+ preset?: SecurityPreset;
24
68
  /** Validate handler responses against declared response schemas. Default: true. */
25
69
  validateResponses?: boolean;
26
70
  /** Hard cap on request body size in bytes. Default: 1 MiB. */
@@ -642,6 +686,41 @@ export declare class App {
642
686
  * set deep in shared configuration.
643
687
  */
644
688
  private assertInsecureDefaultsAcknowledged;
689
+ /**
690
+ * Emit the one-time boot audit entry for an applied security preset.
691
+ * Called from the constructor with the *original* (pre-preset) options
692
+ * so the log captures which fields the preset filled in vs. which the
693
+ * caller set explicitly. Logged at `info` so the line shows up in
694
+ * standard production log shipping without being noisy.
695
+ *
696
+ * Operators can audit the live posture at any time through
697
+ * {@link App.getSecurityPosture}.
698
+ *
699
+ * @since 0.34.0
700
+ */
701
+ private logSecurityPresetIfApplied;
702
+ /**
703
+ * Structured snapshot of the live security posture. Returns the same
704
+ * data the constructor logs under the `security.preset.applied` audit
705
+ * event plus the resolved values of every secure-by-default knob, so
706
+ * operators can build a `/__security` introspection route or a CI
707
+ * audit without parsing the framework source.
708
+ *
709
+ * @since 0.34.0
710
+ */
711
+ getSecurityPosture(): {
712
+ preset: SecurityPreset | undefined;
713
+ secureDefaults: boolean;
714
+ secureHeaders: boolean;
715
+ corsCrossOriginGuard: boolean;
716
+ csrf: "off" | "on";
717
+ crashOnUnhandledRejection: boolean | "default";
718
+ trustProxy: true | false | "unconfigured";
719
+ bodyLimitBytes: number;
720
+ requestTimeoutMs: number;
721
+ stripServerHeaders: boolean;
722
+ production: boolean;
723
+ };
645
724
  /**
646
725
  * Install the secure-by-default global hooks. Currently:
647
726
  * - {@link secureHeaders} as a group-level hook so every response carries
package/dist/app.js CHANGED
@@ -40,6 +40,36 @@ let insecureDefaultsLoggedThisProcess = false;
40
40
  export function _resetInsecureDefaultsLogForTests() {
41
41
  insecureDefaultsLoggedThisProcess = false;
42
42
  }
43
+ /**
44
+ * The exact set of fields the `"internal-service"` preset flips off when
45
+ * the caller has not set them explicitly. Surfaced through the boot
46
+ * audit log entry so operators can see which guards the preset turned
47
+ * off without re-reading the framework source.
48
+ */
49
+ const INTERNAL_SERVICE_PRESET_DISABLED = Object.freeze([
50
+ "secureHeaders auto-install",
51
+ "corsCrossOriginGuard (state-changing cross-origin write rejection)",
52
+ "csrf boot guard (session() + state-changing route)",
53
+ "unconfigured X-Forwarded-* / trustProxy guard",
54
+ ]);
55
+ /**
56
+ * Defaults that the `"internal-service"` preset keeps on. Logged at boot
57
+ * alongside the disabled list so the audit entry shows the full posture.
58
+ */
59
+ const INTERNAL_SERVICE_PRESET_KEPT = Object.freeze([
60
+ "bodyLimitBytes (1 MiB default)",
61
+ "requestTimeoutMs (30 s default)",
62
+ "crashOnUnhandledRejection (production)",
63
+ "weak session secret refuse-to-boot",
64
+ "cors({ origin: '*' }) refuse-to-boot",
65
+ "anonymous stateful plugin refuse-to-boot",
66
+ "stripServerHeaders",
67
+ "RFC 9457 problem+json prod redaction",
68
+ "JWT algorithm allowlist + timingSafeEqual credential comparison",
69
+ "prototype-pollution-safe parsers + isForbiddenObjectKey",
70
+ "fetchGuard() SSRF defaults",
71
+ "schema .strict() + response validation when enabled",
72
+ ]);
43
73
  /**
44
74
  * List of secure-by-default surfaces disabled when `secureDefaults: false`
45
75
  * is set. Surfaced through the once-per-process `error` log so the operator
@@ -74,6 +104,46 @@ const CANONICAL_HTTP_METHODS = new Set([
74
104
  "HEAD",
75
105
  "OPTIONS",
76
106
  ]);
107
+ /**
108
+ * Apply a topology-aware security preset on top of caller-supplied
109
+ * options. Returns a new options object where preset defaults fill in
110
+ * any field the caller left `undefined`; explicit caller values always
111
+ * win. Pure / no side effects — the boot audit log is emitted
112
+ * separately by {@link App.logSecurityPresetIfApplied} so this helper is
113
+ * safe to call from `new App({ preset: ... })` in test setups.
114
+ *
115
+ * The `"internal-service"` preset turns off:
116
+ * - `secureHeaders` auto-install (browser-only headers)
117
+ * - `corsCrossOriginGuard` (no browser Origin to guard against)
118
+ * - `csrf` (set to `"off"` — service-to-service callers aren't browsers)
119
+ * - `trustProxy` (set to `false` — explicitly ignore `X-Forwarded-*`
120
+ * and silence the unconfigured-proxy 500 guard; the immediate peer
121
+ * inside the mesh *is* the caller)
122
+ *
123
+ * Everything else (body limits, request timeouts, JWT allowlist,
124
+ * `crashOnUnhandledRejection`, weak-secret refuse-to-boot, cors-wildcard
125
+ * refuse-to-boot, anonymous stateful plugin refuse-to-boot,
126
+ * `stripServerHeaders`, RFC 9457 prod redaction, schema strictness,
127
+ * `fetchGuard`, parser safety) stays at its standard secure-by-default
128
+ * value.
129
+ *
130
+ * @internal
131
+ */
132
+ function applySecurityPreset(options) {
133
+ if (options.preset !== "internal-service")
134
+ return options;
135
+ const out = { ...options };
136
+ if (out.secureHeaders === undefined)
137
+ out.secureHeaders = false;
138
+ if (out.corsCrossOriginGuard === undefined)
139
+ out.corsCrossOriginGuard = false;
140
+ if (out.csrf === undefined)
141
+ out.csrf = "off";
142
+ if (out.trustProxy === undefined && out.behindProxy === undefined) {
143
+ out.trustProxy = false;
144
+ }
145
+ return out;
146
+ }
77
147
  const DEFAULTS = {
78
148
  bodyLimitBytes: 1024 * 1024,
79
149
  requestTimeoutMs: 30_000,
@@ -245,11 +315,12 @@ export class App {
245
315
  return this._globalCorsAllowsCache;
246
316
  }
247
317
  constructor(options = {}) {
318
+ const resolved = applySecurityPreset(options);
248
319
  this.options = {
249
- validateResponses: options.validateResponses ?? DEFAULTS.validateResponses,
250
- bodyLimitBytes: options.bodyLimitBytes ?? DEFAULTS.bodyLimitBytes,
251
- requestTimeoutMs: options.requestTimeoutMs ?? DEFAULTS.requestTimeoutMs,
252
- ...options,
320
+ validateResponses: resolved.validateResponses ?? DEFAULTS.validateResponses,
321
+ bodyLimitBytes: resolved.bodyLimitBytes ?? DEFAULTS.bodyLimitBytes,
322
+ requestTimeoutMs: resolved.requestTimeoutMs ?? DEFAULTS.requestTimeoutMs,
323
+ ...resolved,
253
324
  };
254
325
  this.log =
255
326
  options.logger === false
@@ -264,6 +335,7 @@ export class App {
264
335
  if (this.options.hooks)
265
336
  this.assertSecureHookConfig(this.options.hooks);
266
337
  this.assertInsecureDefaultsAcknowledged();
338
+ this.logSecurityPresetIfApplied(options);
267
339
  this.installSecureDefaults();
268
340
  this.maybeInstallCrashHandlers();
269
341
  this.maybeMountDocs();
@@ -318,6 +390,68 @@ export class App {
318
390
  }, `app({ secureDefaults: false }) disables: ${DISABLED_BY_INSECURE_DEFAULTS.join(", ")}.`);
319
391
  }
320
392
  }
393
+ /**
394
+ * Emit the one-time boot audit entry for an applied security preset.
395
+ * Called from the constructor with the *original* (pre-preset) options
396
+ * so the log captures which fields the preset filled in vs. which the
397
+ * caller set explicitly. Logged at `info` so the line shows up in
398
+ * standard production log shipping without being noisy.
399
+ *
400
+ * Operators can audit the live posture at any time through
401
+ * {@link App.getSecurityPosture}.
402
+ *
403
+ * @since 0.34.0
404
+ */
405
+ logSecurityPresetIfApplied(originalOptions) {
406
+ if (originalOptions.preset !== "internal-service")
407
+ return;
408
+ const userOverrode = [];
409
+ if (originalOptions.secureHeaders !== undefined)
410
+ userOverrode.push("secureHeaders");
411
+ if (originalOptions.corsCrossOriginGuard !== undefined) {
412
+ userOverrode.push("corsCrossOriginGuard");
413
+ }
414
+ if (originalOptions.csrf !== undefined)
415
+ userOverrode.push("csrf");
416
+ if (originalOptions.trustProxy !== undefined)
417
+ userOverrode.push("trustProxy");
418
+ if (originalOptions.behindProxy !== undefined)
419
+ userOverrode.push("behindProxy");
420
+ this.log.info({
421
+ event: "security.preset.applied",
422
+ preset: "internal-service",
423
+ disabled: INTERNAL_SERVICE_PRESET_DISABLED,
424
+ kept: INTERNAL_SERVICE_PRESET_KEPT,
425
+ userOverrode,
426
+ }, `Applied security preset "internal-service": disabled ${INTERNAL_SERVICE_PRESET_DISABLED.length} topology-dependent guards; kept ${INTERNAL_SERVICE_PRESET_KEPT.length} input/credential/SSRF guards on. See app.getSecurityPosture() for the live snapshot.`);
427
+ }
428
+ /**
429
+ * Structured snapshot of the live security posture. Returns the same
430
+ * data the constructor logs under the `security.preset.applied` audit
431
+ * event plus the resolved values of every secure-by-default knob, so
432
+ * operators can build a `/__security` introspection route or a CI
433
+ * audit without parsing the framework source.
434
+ *
435
+ * @since 0.34.0
436
+ */
437
+ getSecurityPosture() {
438
+ const o = this.options;
439
+ return Object.freeze({
440
+ preset: o.preset,
441
+ secureDefaults: o.secureDefaults !== false,
442
+ secureHeaders: o.secureDefaults !== false && o.secureHeaders !== false,
443
+ corsCrossOriginGuard: o.secureDefaults !== false && o.corsCrossOriginGuard !== false,
444
+ csrf: o.csrf === "off" ? "off" : "on",
445
+ crashOnUnhandledRejection: o.crashOnUnhandledRejection === undefined
446
+ ? "default"
447
+ : o.crashOnUnhandledRejection,
448
+ trustProxy: o.trustProxy === undefined ? "unconfigured" : o.trustProxy,
449
+ bodyLimitBytes: this.options.bodyLimitBytes,
450
+ requestTimeoutMs: this.options.requestTimeoutMs,
451
+ stripServerHeaders: o.stripServerHeaders !== false,
452
+ production: this.isProduction(),
453
+ });
454
+ }
321
455
  /**
322
456
  * Install the secure-by-default global hooks. Currently:
323
457
  * - {@link secureHeaders} as a group-level hook so every response carries
@@ -1499,7 +1633,7 @@ export class App {
1499
1633
  });
1500
1634
  }
1501
1635
  this.inflight++;
1502
- const requestId = randomId();
1636
+ let requestId = randomId();
1503
1637
  // Skip the per-request child-logger allocation when the app was
1504
1638
  // constructed with `{ logger: false }`. noopLogger.child() returns
1505
1639
  // itself, so the binding is wasted work on every request.
@@ -1662,6 +1796,13 @@ export class App {
1662
1796
  if (allHooks.beforeHandle !== undefined) {
1663
1797
  const beforeResult = allHooks.beforeHandle(ctx);
1664
1798
  const before = isPromiseLike(beforeResult) ? await beforeResult : beforeResult;
1799
+ // Honor any request id override applied by middleware (e.g. the
1800
+ // `requestId()` Hooks bundle replaces the framework-generated value
1801
+ // with a trusted incoming header or a user-supplied generator).
1802
+ const overriddenId = state.requestId;
1803
+ if (typeof overriddenId === "string" && overriddenId.length > 0) {
1804
+ requestId = overriddenId;
1805
+ }
1665
1806
  if (before instanceof Response) {
1666
1807
  copyContextHeaders(ctx, before);
1667
1808
  if (!before.headers.has("x-request-id"))
@@ -1686,8 +1827,10 @@ export class App {
1686
1827
  const serializeResultRes = serializeResult(result, def, this.options.validateResponses ?? true);
1687
1828
  let response = isPromiseLike(serializeResultRes) ? await serializeResultRes : serializeResultRes;
1688
1829
  copyContextHeaders(ctx, response);
1689
- if (!response.headers.has("x-request-id"))
1690
- response.headers.set("x-request-id", requestId);
1830
+ // `serializeResult` always builds a fresh Response with no request id
1831
+ // skip the `has()` probe and set directly. Saves one undici contains()
1832
+ // call per request on the hot path.
1833
+ response.headers.set("x-request-id", requestId);
1691
1834
  let finalized;
1692
1835
  if (hasFinalizeHook) {
1693
1836
  const fin = finalizeResponse(response, ctx, allHooks, stripFingerprint);
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ export { createApp } from "./app.js";
3
3
  export { _resetPackageJsonCacheForTests } from "./app.js";
4
4
  export { _resetCrashHandlersForTests } from "./app.js";
5
5
  export { _resetInsecureDefaultsLogForTests } from "./app.js";
6
- export type { AppOptions, AppOpenAPIOptions, DocsRouteOptions, HealthRouteOptions, CspReportRouteOptions, IntrospectedRoute, PluginInstalledEvent, PluginExtension, ShutdownEvent, } from "./app.js";
6
+ export type { AppOptions, AppOpenAPIOptions, DocsRouteOptions, HealthRouteOptions, CspReportRouteOptions, IntrospectedRoute, PluginInstalledEvent, PluginExtension, ShutdownEvent, SecurityPreset, } from "./app.js";
7
7
  export { getConnInfo, setConnInfo, assertBehindProxy, resolveClientIp, readRemoteAddress, readRemotePort, pickForwardedForByHops, } from "./conn-info.js";
8
8
  export type { BehindProxyConfig, ConnInfo } from "./conn-info.js";
9
9
  export { subdomains, PSL_SNAPSHOT_DATE, PSL_PUBLIC_SUFFIXES, MAX_SNAPSHOT_AGE_DAYS, } from "./subdomains.js";
@@ -38,7 +38,7 @@
38
38
  * });
39
39
  * ```
40
40
  *
41
- * @since 0.35.2
41
+ * @since 0.36.0
42
42
  */
43
43
  /** Reason an open-redirect candidate was refused. */
44
44
  export type SafeRedirectBlockReason = "empty-target" | "invalid-control-characters" | "protocol-relative" | "backslash-path" | "path-not-allowed" | "origin-not-allowed" | "scheme-not-allowed" | "parse-failed";
@@ -86,6 +86,6 @@ export interface SafeRedirectOptions {
86
86
  * @param target - User-supplied URL candidate (path or absolute URL).
87
87
  * @param options - Allowlist + response configuration.
88
88
  *
89
- * @since 0.35.2
89
+ * @since 0.36.0
90
90
  */
91
91
  export declare function safeRedirect(target: string, options?: SafeRedirectOptions): Response;
@@ -38,7 +38,7 @@
38
38
  * });
39
39
  * ```
40
40
  *
41
- * @since 0.35.2
41
+ * @since 0.36.0
42
42
  */
43
43
  /** Thrown when {@link safeRedirect} refuses a candidate URL and no `fallback` is configured. */
44
44
  export class OpenRedirectBlockedError extends Error {
@@ -126,7 +126,7 @@ function classify(target, allowedPaths, allowedOrigins) {
126
126
  * @param target - User-supplied URL candidate (path or absolute URL).
127
127
  * @param options - Allowlist + response configuration.
128
128
  *
129
- * @since 0.35.2
129
+ * @since 0.36.0
130
130
  */
131
131
  export function safeRedirect(target, options = {}) {
132
132
  const allowedPaths = options.allowedPaths ?? [];
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "bomFormat": "CycloneDX",
3
3
  "specVersion": "1.5",
4
- "serialNumber": "urn:uuid:1d9fa950-874b-5a89-9b39-98f1bbdfdb30",
4
+ "serialNumber": "urn:uuid:7079f846-98da-5195-89ad-4f54c8938289",
5
5
  "version": 1,
6
6
  "metadata": {
7
- "timestamp": "2026-05-28T07:51:16.251Z",
7
+ "timestamp": "2026-05-28T20:50:52.884Z",
8
8
  "tools": [
9
9
  {
10
10
  "vendor": "DaloyJS",
11
11
  "name": "daloy-generate-sbom",
12
- "version": "0.35.2"
12
+ "version": "0.36.0"
13
13
  }
14
14
  ],
15
15
  "authors": [
@@ -19,11 +19,11 @@
19
19
  ],
20
20
  "component": {
21
21
  "type": "library",
22
- "bom-ref": "pkg:npm/@daloyjs/core@0.35.2",
22
+ "bom-ref": "pkg:npm/@daloyjs/core@0.36.0",
23
23
  "name": "@daloyjs/core",
24
- "version": "0.35.2",
24
+ "version": "0.36.0",
25
25
  "description": "DaloyJS is a runtime-portable, contract-first TypeScript web framework with built-in OpenAPI (Hey API), typed client generation, large-scale maintainability, and security-first defaults. Hono-grade portability, Elysia-grade DX, FastAPI-grade docs, Fastify-grade ops — distributed via pnpm.",
26
- "purl": "pkg:npm/@daloyjs/core@0.35.2",
26
+ "purl": "pkg:npm/@daloyjs/core@0.36.0",
27
27
  "licenses": [
28
28
  {
29
29
  "license": {
@@ -46,9 +46,9 @@
46
46
  }
47
47
  ],
48
48
  "swid": {
49
- "tagId": "swidtag--daloyjs-core-0.35.2",
49
+ "tagId": "swidtag--daloyjs-core-0.36.0",
50
50
  "name": "@daloyjs/core",
51
- "version": "0.35.2",
51
+ "version": "0.36.0",
52
52
  "tagVersion": 0,
53
53
  "patch": false
54
54
  }
@@ -57,7 +57,7 @@
57
57
  "components": [],
58
58
  "dependencies": [
59
59
  {
60
- "ref": "pkg:npm/@daloyjs/core@0.35.2",
60
+ "ref": "pkg:npm/@daloyjs/core@0.36.0",
61
61
  "dependsOn": []
62
62
  }
63
63
  ]
@@ -2,10 +2,10 @@
2
2
  "spdxVersion": "SPDX-2.3",
3
3
  "dataLicense": "CC0-1.0",
4
4
  "SPDXID": "SPDXRef-DOCUMENT",
5
- "name": "@daloyjs/core-0.35.2",
6
- "documentNamespace": "https://github.com/daloyjs/daloy/sbom/@daloyjs/core-0.35.2-1d9fa950-874b-5a89-9b39-98f1bbdfdb30",
5
+ "name": "@daloyjs/core-0.36.0",
6
+ "documentNamespace": "https://github.com/daloyjs/daloy/sbom/@daloyjs/core-0.36.0-7079f846-98da-5195-89ad-4f54c8938289",
7
7
  "creationInfo": {
8
- "created": "2026-05-28T07:51:16.251Z",
8
+ "created": "2026-05-28T20:50:52.884Z",
9
9
  "creators": [
10
10
  "Tool: daloy-generate-sbom",
11
11
  "Organization: DaloyJS"
@@ -16,7 +16,7 @@
16
16
  {
17
17
  "SPDXID": "SPDXRef-Package--daloyjs-core",
18
18
  "name": "@daloyjs/core",
19
- "versionInfo": "0.35.2",
19
+ "versionInfo": "0.36.0",
20
20
  "downloadLocation": "https://github.com/daloyjs/daloy",
21
21
  "filesAnalyzed": false,
22
22
  "licenseConcluded": "MIT",
@@ -27,7 +27,7 @@
27
27
  {
28
28
  "referenceCategory": "PACKAGE-MANAGER",
29
29
  "referenceType": "purl",
30
- "referenceLocator": "pkg:npm/@daloyjs/core@0.35.2"
30
+ "referenceLocator": "pkg:npm/@daloyjs/core@0.36.0"
31
31
  }
32
32
  ]
33
33
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@daloyjs/core",
3
- "version": "0.35.2",
3
+ "version": "0.36.0",
4
4
  "description": "DaloyJS is a runtime-portable, contract-first TypeScript web framework with built-in OpenAPI (Hey API), typed client generation, large-scale maintainability, and security-first defaults. Hono-grade portability, Elysia-grade DX, FastAPI-grade docs, Fastify-grade ops — distributed via pnpm.",
5
5
  "type": "module",
6
6
  "publishConfig": {