@daloyjs/core 1.0.0-beta.1 → 1.0.0-beta.2

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
@@ -278,13 +278,21 @@ new App({
278
278
  customCss: ":root { --scalar-color-accent: #2563eb; }",
279
279
  hideTestRequestButton: true,
280
280
  },
281
+ swagger: {
282
+ docExpansion: "none",
283
+ displayRequestDuration: true,
284
+ },
281
285
  tags: ["Docs"],
282
286
  },
283
287
  });
284
288
  ```
285
289
 
286
- Switch UIs with one word. `ui: "redoc"` renders Redoc instead, and its
287
- options are forwarded to `Redoc.init` via `docs.redoc`:
290
+ Switch UIs with one word. Scalar and Swagger UI include developer request
291
+ consoles for authenticated endpoints: Scalar selects the first configured
292
+ OpenAPI security scheme by default, and Swagger UI keeps values from the
293
+ Authorize dialog across reloads. `ui: "redoc"` renders Redoc instead, and its
294
+ options are forwarded to `Redoc.init` via `docs.redoc`; Redoc displays security
295
+ requirements but is a read-only reference UI, not a Try It console:
288
296
 
289
297
  ```ts
290
298
  new App({
@@ -299,7 +307,8 @@ new App({
299
307
  The `scalar` option is forwarded to Scalar's HTML API as JSON configuration,
300
308
  with Daloy keeping the live `openapiPath` as the source. Use it for themes,
301
309
  custom CSS, layout, auth defaults, and client visibility without copying the
302
- HTML helper. Redoc spins up a `blob:` Web Worker for search, so the
310
+ HTML helper. The `swagger` option is forwarded to `SwaggerUIBundle` with Daloy
311
+ owning `url` / `dom_id`. Redoc spins up a `blob:` Web Worker for search, so the
303
312
  auto-mounted `/docs` page widens its CSP with `worker-src 'self' blob:` for
304
313
  `ui: "redoc"` only — Scalar and Swagger UI keep the tighter default.
305
314
 
@@ -413,7 +422,7 @@ miss 4,763,878 ops/sec
413
422
 
414
423
  ### Cold-start tip (serverless / edge)
415
424
 
416
- For deployments where every millisecond of startup matters (Lambda, Vercel Edge, Cloudflare Workers, Fastly Compute), import `App` from the deep entry point instead of the barrel:
425
+ For deployments where every millisecond of startup matters (Lambda, Vercel, Cloudflare Workers, Fastly Compute), import `App` from the deep entry point instead of the barrel:
417
426
 
418
427
  ```ts
419
428
  import { App } from "@daloyjs/core/app"; // ~13 ms faster cold start than "@daloyjs/core"
@@ -498,14 +507,14 @@ The core only ever sees `Request → Response`. Adapters live at the edge.
498
507
 
499
508
  ## Status
500
509
 
501
- DaloyJS is now in the **`1.0.0` beta** (`1.0.0-beta.1`). The public API is feature-complete and stable for the 1.0 line; from `1.0.0` onward, breaking changes follow SemVer and deprecations get at least one minor cycle. Small adjustments are still possible before the `1.0.0` GA if beta feedback surfaces something. The framework is already in use for production trials.
510
+ DaloyJS is now in the **`1.0.0` beta** (`1.0.0-beta.2`). The public API is feature-complete and stable for the 1.0 line; from `1.0.0` onward, breaking changes follow SemVer and deprecations get at least one minor cycle. Small adjustments are still possible before the `1.0.0` GA if beta feedback surfaces something. The framework is already in use for production trials.
502
511
 
503
512
  **Release quality bar.** Every release ships with **≥90% line + function coverage and ≥90% branch coverage**, strict TypeScript, OpenSSF Scorecard, CodeQL + Opengrep dual SAST, zizmor workflow linting, and npm provenance. Coverage was relaxed from a former 100% gate so complex security work isn't blocked chasing throwaway tests for unreachable defensive branches or tsx source-map phantoms; see [AGENTS.md](AGENTS.md) for the policy.
504
513
 
505
514
  ### Routing, validation, and docs
506
515
 
507
516
  - Contract-first routing with Standard Schema validation (Zod 4, Valibot, ArkType, TypeBox) and OpenAPI 3.1 generated from a single source of truth.
508
- - Live OpenAPI 3.1 spec served as both JSON (`GET /openapi.json`) and YAML (`GET /openapi.yaml`) when `docs: true`, with a choice of Scalar (default), Swagger UI, or Redoc via `docs.ui`, plus Scalar theming/custom CSS via `docs.scalar` and Redoc options via `docs.redoc`.
517
+ - Live OpenAPI 3.1 spec served as both JSON (`GET /openapi.json`) and YAML (`GET /openapi.yaml`) when `docs: true`, with a choice of Scalar (default), Swagger UI, or Redoc via `docs.ui`, plus Scalar theming/custom CSS/auth defaults via `docs.scalar`, Swagger UI options via `docs.swagger` (including persisted Authorize credentials), and Redoc options via `docs.redoc`.
509
518
  - Zero-config OpenAPI `info` autofill from `package.json` (Node / Bun) or `deno.json` / `deno.jsonc` (Deno); explicit `openapi.info` values always win.
510
519
  - RFC 7231 + RFC 5789 HTTP-method allowlist enforced inside `app.route()` (WebDAV, `TRACE`, `CONNECT` rejected at the framework boundary).
511
520
  - AI-friendly route metadata via optional `meta: { examples, extensions, summary, description, tags }`; examples are validated against your schemas at build time, surfaced as OpenAPI `examples` + `x-daloy-*` extensions, and dumped as `routes.json` / `routes.yaml` via `daloy inspect --ai`.
@@ -516,7 +525,7 @@ DaloyJS is now in the **`1.0.0` beta** (`1.0.0-beta.1`). The public API is featu
516
525
 
517
526
  - Adapters for Node (Heroku, Railway, Render, Fly.io), Bun, Deno, Cloudflare Workers, Vercel Node / Edge / Next.js / Netlify Edge, Fastly Compute, and AWS Lambda / Netlify Functions / Lambda Function URLs.
518
527
  - `daloy dev` watch loop delegates to the host runtime's native watcher (`node --import tsx --watch`, `bun --hot`, or `deno run --watch`) with a `--runtime` override for cross-runtime `package.json` scripts.
519
- - `pnpm create daloy` scaffolder with Node, Bun, Deno, Cloudflare Worker, and Vercel Edge templates, plus optional `--with-ci` GitHub Actions / Dependabot / CODEOWNERS / SECURITY.md hardening. The completion summary surfaces official install links (nodejs.org, pnpm.io, bun.sh) for any runtime or package manager your selections need but that is missing from `PATH`, and skips a doomed dependency install when the chosen package manager is absent.
528
+ - `pnpm create daloy` scaffolder with Node, Bun, Deno, Cloudflare Worker, and Vercel templates, plus optional `--with-ci` GitHub Actions / Dependabot / CODEOWNERS / SECURITY.md hardening. The completion summary surfaces official install links (nodejs.org, pnpm.io, bun.sh) for any runtime or package manager your selections need but that is missing from `PATH`, and skips a doomed dependency install when the chosen package manager is absent.
520
529
  - Container-first templates: `HEALTHCHECK` to `/readyz`, `STOPSIGNAL SIGTERM`, non-root user, `tini` as PID 1.
521
530
  - Generated `deploy.yml` for container templates signs every pushed GHCR image with **Sigstore Cosign** (keyless OIDC) and attaches an **SPDX SBOM attestation** so consumers can `cosign verify` and `cosign verify-attestation --type spdxjson` instead of trusting the registry alone.
522
531
  - Pretty `printStartupBanner()` / `formatStartupBanner()` helpers at `@daloyjs/core/banner`, used by every starter template (TTY + `NO_COLOR` / `FORCE_COLOR` aware, ASCII fallback for dumb terminals).
@@ -19,7 +19,7 @@
19
19
  * `toEdgeHandler` is kept as a backward-compatible alias of `toWebHandler`.
20
20
  */
21
21
  import type { App } from "../app.js";
22
- /** Web-standard handler shape used by Vercel Edge Functions, Next.js route handlers, and middleware. */
22
+ /** Web-standard handler shape used by Vercel Functions, Next.js route handlers, and middleware. */
23
23
  export type WebHandler = (req: Request) => Promise<Response>;
24
24
  /** Default export shape for Vercel's web-standard `{ fetch }` runtime. */
25
25
  export interface FetchHandler {
package/dist/app.d.ts CHANGED
@@ -2,7 +2,7 @@ import { WebSocketRegistry, type WebSocketHandler } from "./websocket.js";
2
2
  import { type Logger } from "./logger.js";
3
3
  import type { HttpMethod, Hooks, PathString, RequestSchemas, ResponsesMap, RouteDefinition } from "./types.js";
4
4
  import { type OpenAPIInfo, type OpenAPIOptions } from "./openapi.js";
5
- import { type DocsAssetOptions, type DocsContentSecurityPolicyOptions, type RedocConfiguration, type ScalarJsonValue, type ScalarReferenceConfiguration } from "./docs.js";
5
+ import { type DocsAssetOptions, type DocsContentSecurityPolicyOptions, type RedocConfiguration, type ScalarJsonValue, type ScalarReferenceConfiguration, type SwaggerUiConfiguration } from "./docs.js";
6
6
  import { type AsyncAPIServer } from "./asyncapi.js";
7
7
  import { type SecureHeadersOptions } from "./middleware.js";
8
8
  import { type LoadSheddingOptions } from "./load-shedding.js";
@@ -246,7 +246,7 @@ export interface AppOptions {
246
246
  * {@link AppOptions.secureDefaults} is not `false`. Pass `false` to opt
247
247
  * out (long-running CLI processes / test harnesses that intentionally
248
248
  * swallow rejections may want this). No-op on runtimes without
249
- * `process.on` (Cloudflare Workers / Vercel Edge / Fastly).
249
+ * `process.on` (Cloudflare Workers / Vercel / Fastly).
250
250
  *
251
251
  * @since 0.18.0
252
252
  */
@@ -380,8 +380,21 @@ export interface DocsRouteOptions {
380
380
  * renders Redoc.
381
381
  */
382
382
  ui?: "scalar" | "swagger" | "redoc";
383
- /** Scalar API reference UI configuration. Ignored unless `ui` is `"scalar"`. */
383
+ /**
384
+ * Scalar API reference UI configuration. Ignored unless `ui` is `"scalar"`.
385
+ * When OpenAPI security schemes are configured and this object does not set
386
+ * `authentication.preferredSecurityScheme`, DaloyJS selects the first scheme
387
+ * so Scalar's developer console opens with an auth type ready to fill.
388
+ */
384
389
  scalar?: ScalarReferenceConfiguration;
390
+ /**
391
+ * Swagger UI configuration, forwarded to `SwaggerUIBundle`. Ignored unless
392
+ * `ui: "swagger"`. DaloyJS owns `url` and `dom_id`; credentials entered in
393
+ * the Authorize dialog persist across reloads by default.
394
+ *
395
+ * @since 0.42.0
396
+ */
397
+ swagger?: SwaggerUiConfiguration;
385
398
  /**
386
399
  * Redoc UI configuration, forwarded to `Redoc.init`. Ignored unless
387
400
  * `ui: "redoc"`.
@@ -730,7 +743,7 @@ export declare const DALOY_RAW_STREAM: unique symbol;
730
743
  * - graceful shutdown and lifecycle observability
731
744
  *
732
745
  * `App` is **runtime-agnostic**: the same instance runs on Node, Bun, Deno,
733
- * Cloudflare Workers, Vercel Edge, AWS Lambda, and Fastly Compute via the
746
+ * Cloudflare Workers, Vercel, AWS Lambda, and Fastly Compute via the
734
747
  * dedicated adapters.
735
748
  *
736
749
  * @example
@@ -998,7 +1011,7 @@ export declare class App<Routes extends readonly RouteDefinition<any, any, any,
998
1011
  * Whether the resolved runtime environment is indeterminate: no explicit
999
1012
  * {@link AppOptions.env} / {@link AppOptions.production} was provided AND
1000
1013
  * `process.env.NODE_ENV` is unset or empty. This is the common default on
1001
- * edge runtimes (Cloudflare Workers, Deno Deploy, Vercel Edge), which have no
1014
+ * edge runtimes (Cloudflare Workers, Deno Deploy), which have no
1002
1015
  * `NODE_ENV`. Deliberately does not sniff the runtime — it only reports
1003
1016
  * whether the environment signal is absent — so it stays runtime-portable.
1004
1017
  *
package/dist/app.js CHANGED
@@ -630,7 +630,7 @@ export class App {
630
630
  // The refusals below are production-only. When the environment is
631
631
  // *indeterminate* (no explicit `env` / `production` option and no
632
632
  // `NODE_ENV` — the common default on edge runtimes such as Workers /
633
- // Deno Deploy / Vercel Edge), those refusals never fire, so a risky
633
+ // Deno Deploy / Vercel), those refusals never fire, so a risky
634
634
  // config could ship unguarded. Surface that once as a warning when such
635
635
  // a config is actually present. Enforcement is unchanged — this only
636
636
  // makes the silent skip observable. The runtime itself is deliberately
@@ -659,7 +659,7 @@ export class App {
659
659
  * Whether the resolved runtime environment is indeterminate: no explicit
660
660
  * {@link AppOptions.env} / {@link AppOptions.production} was provided AND
661
661
  * `process.env.NODE_ENV` is unset or empty. This is the common default on
662
- * edge runtimes (Cloudflare Workers, Deno Deploy, Vercel Edge), which have no
662
+ * edge runtimes (Cloudflare Workers, Deno Deploy), which have no
663
663
  * `NODE_ENV`. Deliberately does not sniff the runtime — it only reports
664
664
  * whether the environment signal is absent — so it stays runtime-portable.
665
665
  *
@@ -934,9 +934,16 @@ export class App {
934
934
  // (unless the caller explicitly overrode it). Scalar / Swagger UI keep the
935
935
  // tighter default. The policy is deterministic from `ui` + `opts.csp`, so
936
936
  // compute it once at mount time instead of per request.
937
- const docsCsp = docsContentSecurityPolicy(ui === "redoc"
938
- ? { ...opts.csp, allowBlobWorkers: opts.csp?.allowBlobWorkers ?? true }
939
- : opts.csp);
937
+ const docsConnectOrigins = [
938
+ ...(opts.csp?.connectOrigins ?? []),
939
+ ...serverConnectOrigins(this.options.openapi?.servers),
940
+ ];
941
+ const docsCsp = docsContentSecurityPolicy({
942
+ ...(ui === "redoc"
943
+ ? { ...opts.csp, allowBlobWorkers: opts.csp?.allowBlobWorkers ?? true }
944
+ : opts.csp),
945
+ ...(docsConnectOrigins.length ? { connectOrigins: docsConnectOrigins } : {}),
946
+ });
940
947
  this.route({
941
948
  method: "GET",
942
949
  path: docsPath,
@@ -952,6 +959,7 @@ export class App {
952
959
  ? swaggerUiHtml({
953
960
  specUrl: openapiPath,
954
961
  title,
962
+ configuration: opts.swagger,
955
963
  assets: opts.assets,
956
964
  })
957
965
  : ui === "redoc"
@@ -964,7 +972,7 @@ export class App {
964
972
  : scalarHtml({
965
973
  specUrl: openapiPath,
966
974
  title,
967
- configuration: opts.scalar,
975
+ configuration: scalarConfigurationWithPreferredAuth(opts.scalar, this.options.openapi?.securitySchemes),
968
976
  assets: opts.assets,
969
977
  });
970
978
  return {
@@ -2773,6 +2781,47 @@ function responsePipeline(fns) {
2773
2781
  return current;
2774
2782
  };
2775
2783
  }
2784
+ function serverConnectOrigins(servers) {
2785
+ if (!servers)
2786
+ return [];
2787
+ const origins = [];
2788
+ for (const server of servers) {
2789
+ try {
2790
+ const url = new URL(server.url);
2791
+ if ((url.protocol === "http:" || url.protocol === "https:") &&
2792
+ !origins.includes(url.origin)) {
2793
+ origins.push(url.origin);
2794
+ }
2795
+ }
2796
+ catch {
2797
+ // Relative server URLs are already covered by connect-src 'self'.
2798
+ }
2799
+ }
2800
+ return origins;
2801
+ }
2802
+ function scalarConfigurationWithPreferredAuth(configuration, schemes) {
2803
+ const preferredSecurityScheme = schemes ? Object.keys(schemes)[0] : undefined;
2804
+ if (!preferredSecurityScheme)
2805
+ return configuration;
2806
+ const authentication = configuration?.authentication;
2807
+ if (authentication &&
2808
+ typeof authentication === "object" &&
2809
+ !Array.isArray(authentication) &&
2810
+ "preferredSecurityScheme" in authentication) {
2811
+ return configuration;
2812
+ }
2813
+ return {
2814
+ ...(configuration ?? {}),
2815
+ authentication: {
2816
+ ...(authentication &&
2817
+ typeof authentication === "object" &&
2818
+ !Array.isArray(authentication)
2819
+ ? authentication
2820
+ : {}),
2821
+ preferredSecurityScheme,
2822
+ },
2823
+ };
2824
+ }
2776
2825
  function finalizeResponse(res, ctx, hooks, stripFingerprint = true) {
2777
2826
  let final = res;
2778
2827
  const finish = (f) => {
@@ -3133,7 +3182,7 @@ async function readBody(req, ct, limit, multipart) {
3133
3182
  // chunked upload sends none, and a lying small value slips past the check —
3134
3183
  // in both cases the platform parser would otherwise buffer the whole body
3135
3184
  // in memory on runtimes whose adapter does not cap at the socket layer
3136
- // (Workers / Deno / Vercel Edge). `readBodyLimited` streams the body and
3185
+ // (Workers / Deno / Vercel). `readBodyLimited` streams the body and
3137
3186
  // throws `PayloadTooLargeError` the instant it exceeds `limit`; we then
3138
3187
  // re-parse the bounded bytes with the standard `formData()` parser,
3139
3188
  // preserving the multipart boundary via the original Content-Type. This is
@@ -3360,7 +3409,7 @@ const PACKAGE_JSON_CACHE = {};
3360
3409
  * none is found while walking up from `process.cwd()`, falls back to
3361
3410
  * `deno.json` / `deno.jsonc` so Deno projects get the same DX without a
3362
3411
  * `package.json`. Returns an empty object on edge runtimes (Cloudflare
3363
- * Workers, Vercel Edge) where `node:fs` is absent, on any I/O or parse
3412
+ * Workers) where `node:fs` is absent, on any I/O or parse
3364
3413
  * error, and when nothing is found.
3365
3414
  *
3366
3415
  * The result is memoized at module scope: manifests do not change
@@ -3,7 +3,7 @@
3
3
  * BREACH-aware safe defaults.
4
4
  *
5
5
  * Built on the web-standard `CompressionStream` API so the same line works
6
- * on Node, Bun, Deno, Cloudflare Workers, Vercel Edge, and Fastly Compute.
6
+ * on Node, Bun, Deno, Cloudflare Workers, Vercel, and Fastly Compute.
7
7
  * The middleware deliberately refuses to expose a configurable compression
8
8
  * level — `CompressionStream` uses the runtime's documented default (gzip
9
9
  * level 6 on Node / V8); a `level: 9` opt-in is rejected at construction
@@ -68,10 +68,10 @@ export interface CompressionOptions {
68
68
  */
69
69
  authCookieNames?: readonly string[];
70
70
  /**
71
- * Deliberately unsupported. Provided as a `never`-typed trap so the type
72
- * system flags accidental opt-in. The actual `CompressionStream` never
73
- * exposes a level knob — passing any value triggers a construction-time
74
- * refusal.
71
+ * Deliberately unsupported. Provided as a `never`-typed trap so the type
72
+ * system flags accidental opt-in. The actual `CompressionStream` never
73
+ * exposes a level knob — passing any value triggers a construction-time
74
+ * refusal.
75
75
  *
76
76
  * @internal
77
77
  */
@@ -3,7 +3,7 @@
3
3
  * BREACH-aware safe defaults.
4
4
  *
5
5
  * Built on the web-standard `CompressionStream` API so the same line works
6
- * on Node, Bun, Deno, Cloudflare Workers, Vercel Edge, and Fastly Compute.
6
+ * on Node, Bun, Deno, Cloudflare Workers, Vercel, and Fastly Compute.
7
7
  * The middleware deliberately refuses to expose a configurable compression
8
8
  * level — `CompressionStream` uses the runtime's documented default (gzip
9
9
  * level 6 on Node / V8); a `level: 9` opt-in is rejected at construction
package/dist/docs.d.ts CHANGED
@@ -254,7 +254,7 @@ export interface DocsAssetOptions {
254
254
  */
255
255
  crossOrigin?: "anonymous" | "use-credentials";
256
256
  }
257
- /** Shared options for {@link scalarHtml} and {@link swaggerUiHtml}. */
257
+ /** Shared options for {@link scalarHtml}, {@link swaggerUiHtml}, and {@link redocHtml}. */
258
258
  export interface DocsOptions {
259
259
  /** Absolute or relative URL of the OpenAPI document to render. */
260
260
  specUrl: string;
@@ -273,6 +273,53 @@ export interface ScalarHtmlOptions extends DocsOptions {
273
273
  /** Forwarded to the Scalar `<script id="api-reference">` tag. */
274
274
  configuration?: ScalarReferenceConfiguration;
275
275
  }
276
+ /** JSON-only subset of Swagger UI's `SwaggerUIBundle(...)` configuration. */
277
+ export interface SwaggerUiConfiguration {
278
+ [key: string]: ScalarJsonValue | undefined;
279
+ /**
280
+ * Preserve developer-entered API credentials across reloads. Defaults to
281
+ * `true` in {@link swaggerUiHtml} so the built-in docs behave like a local
282
+ * developer console instead of forgetting the bearer token on every refresh.
283
+ */
284
+ persistAuthorization?: boolean;
285
+ /** Expand operations by default: `"list"`, `"full"`, or `"none"`. */
286
+ docExpansion?: "list" | "full" | "none";
287
+ /** Enable filtering operations by tag/path text. */
288
+ filter?: boolean | string;
289
+ /** Show operation extension fields such as `x-*`. */
290
+ showExtensions?: boolean;
291
+ /** Show common extension fields. */
292
+ showCommonExtensions?: boolean;
293
+ /** Render request duration after "Try it out" calls. */
294
+ displayRequestDuration?: boolean;
295
+ /** Sort operations by HTTP method, alphabetically, or with no sorter. */
296
+ operationsSorter?: "alpha" | "method";
297
+ /** Sort tags alphabetically or with no sorter. */
298
+ tagsSorter?: "alpha";
299
+ /**
300
+ * Internal URL wiring owned by DaloyJS. Use {@link DocsOptions.specUrl}
301
+ * instead of passing Swagger UI's `url` directly.
302
+ */
303
+ url?: never;
304
+ /**
305
+ * Internal DOM mount point owned by DaloyJS. The generated HTML always mounts
306
+ * Swagger UI into `#swagger`.
307
+ */
308
+ dom_id?: never;
309
+ /** Function-valued Swagger UI plugins cannot cross the server-to-HTML boundary. */
310
+ plugins?: never;
311
+ /** Function-valued presets cannot cross the server-to-HTML boundary. */
312
+ presets?: never;
313
+ /** Function-valued request interceptors cannot cross the server-to-HTML boundary. */
314
+ requestInterceptor?: never;
315
+ /** Function-valued response interceptors cannot cross the server-to-HTML boundary. */
316
+ responseInterceptor?: never;
317
+ }
318
+ /** Options for {@link swaggerUiHtml}; adds Swagger UI-specific configuration. */
319
+ export interface SwaggerUiHtmlOptions extends DocsOptions {
320
+ /** Forwarded into `SwaggerUIBundle(...)` after `url` and `dom_id`. */
321
+ configuration?: SwaggerUiConfiguration;
322
+ }
276
323
  /**
277
324
  * Options for {@link redocHtml}; adds Redoc-specific UI configuration.
278
325
  *
@@ -300,6 +347,14 @@ export interface AsyncApiHtmlOptions extends DocsOptions {
300
347
  export interface DocsContentSecurityPolicyOptions {
301
348
  /** Extra origins to allow for `script-src` / `style-src` (defaults to jsDelivr). */
302
349
  assetOrigins?: string[];
350
+ /**
351
+ * Extra origins to allow for `connect-src`. Add API origins here when the
352
+ * docs UI should send "Try it" requests somewhere other than the same origin
353
+ * serving the docs page.
354
+ *
355
+ * @since 0.42.0
356
+ */
357
+ connectOrigins?: string[];
303
358
  /** When set, allows nonce-protected inline scripts instead of `'unsafe-inline'`. */
304
359
  scriptNonce?: string;
305
360
  /** When `false`, omits `'unsafe-inline'` from `style-src`. Defaults to `true`. */
@@ -331,8 +386,12 @@ export declare function scalarHtml(opts: ScalarHtmlOptions): string;
331
386
  /**
332
387
  * Render a Swagger UI HTML page that loads `opts.specUrl`. Same usage as
333
388
  * {@link scalarHtml} but emits the classic Swagger UI bundle.
389
+ *
390
+ * Developer-entered credentials are persisted by default
391
+ * (`persistAuthorization: true`) so routes with OpenAPI security requirements
392
+ * can be exercised after using Swagger UI's Authorize dialog.
334
393
  */
335
- export declare function swaggerUiHtml(opts: DocsOptions): string;
394
+ export declare function swaggerUiHtml(opts: SwaggerUiHtmlOptions): string;
336
395
  /**
337
396
  * Render a Redoc HTML page that loads `opts.specUrl`. Same usage as
338
397
  * {@link scalarHtml} / {@link swaggerUiHtml} but emits the Redoc standalone
package/dist/docs.js CHANGED
@@ -67,10 +67,13 @@ export function scalarHtml(opts) {
67
67
  /**
68
68
  * Render a Swagger UI HTML page that loads `opts.specUrl`. Same usage as
69
69
  * {@link scalarHtml} but emits the classic Swagger UI bundle.
70
+ *
71
+ * Developer-entered credentials are persisted by default
72
+ * (`persistAuthorization: true`) so routes with OpenAPI security requirements
73
+ * can be exercised after using Swagger UI's Authorize dialog.
70
74
  */
71
75
  export function swaggerUiHtml(opts) {
72
76
  const title = escapeHtml(opts.title ?? "API Docs");
73
- const url = escapeHtml(opts.specUrl);
74
77
  const cssUrl = escapeHtml(opts.assets?.swaggerUiCssUrl ??
75
78
  `${JSDELIVR_ORIGIN}/npm/swagger-ui-dist/swagger-ui.css`);
76
79
  const bundleUrl = escapeHtml(opts.assets?.swaggerUiBundleUrl ??
@@ -78,6 +81,12 @@ export function swaggerUiHtml(opts) {
78
81
  const cssSri = integrityAttr(opts.assets?.swaggerUiCssIntegrity, opts.assets?.crossOrigin);
79
82
  const bundleSri = integrityAttr(opts.assets?.swaggerUiBundleIntegrity, opts.assets?.crossOrigin);
80
83
  const nonce = nonceAttr(opts.scriptNonce);
84
+ const configuration = jsonForScript({
85
+ persistAuthorization: true,
86
+ ...(opts.configuration ?? {}),
87
+ url: opts.specUrl,
88
+ dom_id: "#swagger",
89
+ });
81
90
  return `<!doctype html>
82
91
  <html><head>
83
92
  <meta charset="utf-8" />
@@ -87,7 +96,7 @@ export function swaggerUiHtml(opts) {
87
96
  </head><body>
88
97
  <div id="swagger"></div>
89
98
  <script src="${bundleUrl}"${bundleSri}${nonce}></script>
90
- <script${nonce}>window.onload=()=>SwaggerUIBundle({url:"${url}",dom_id:"#swagger"});</script>
99
+ <script${nonce}>window.onload=()=>SwaggerUIBundle(${configuration});</script>
91
100
  </body></html>`;
92
101
  }
93
102
  /**
@@ -179,12 +188,13 @@ export function docsContentSecurityPolicy(opts = {}) {
179
188
  const styleSrc = ["'self'", ...assetOrigins];
180
189
  if (opts.allowInlineStyles !== false)
181
190
  styleSrc.push("'unsafe-inline'");
191
+ const connectSrc = ["'self'", ...(opts.connectOrigins ?? [])];
182
192
  const directives = [
183
193
  "default-src 'self'",
184
194
  `script-src ${scriptSrc.join(" ")}`,
185
195
  `style-src ${styleSrc.join(" ")}`,
186
196
  "img-src 'self' data: https:",
187
- "connect-src 'self'",
197
+ `connect-src ${connectSrc.join(" ")}`,
188
198
  ];
189
199
  // Redoc spawns a Web Worker from a `blob:` URL; without an explicit
190
200
  // worker-src the browser falls back to script-src, which forbids `blob:`
@@ -205,6 +215,7 @@ export function htmlResponse(html, opts = {}) {
205
215
  "content-security-policy": opts.contentSecurityPolicy ??
206
216
  docsContentSecurityPolicy({
207
217
  assetOrigins: opts.assetOrigins,
218
+ connectOrigins: opts.connectOrigins,
208
219
  scriptNonce: opts.scriptNonce,
209
220
  allowInlineStyles: opts.allowInlineStyles,
210
221
  }),
@@ -26,7 +26,7 @@
26
26
  *
27
27
  * This module is dependency-free and uses only Web Crypto + Web Standard
28
28
  * `Request`/`Response`, so it runs unchanged on Node, Bun, Deno, Cloudflare
29
- * Workers, and Vercel Edge.
29
+ * Workers, and Vercel.
30
30
  *
31
31
  * @module
32
32
  * @since 0.37.0
@@ -26,7 +26,7 @@
26
26
  *
27
27
  * This module is dependency-free and uses only Web Crypto + Web Standard
28
28
  * `Request`/`Response`, so it runs unchanged on Node, Bun, Deno, Cloudflare
29
- * Workers, and Vercel Edge.
29
+ * Workers, and Vercel.
30
30
  *
31
31
  * @module
32
32
  * @since 0.37.0
package/dist/index.d.ts CHANGED
@@ -74,7 +74,7 @@ export type { RequestIdOptions, SecureHeadersOptions, CspDirectivesOptions, Cors
74
74
  export type { BearerAuthOptions, BearerAuthVerifyHook } from "./middleware.js";
75
75
  export { createLogger, noopLogger, DEFAULT_REDACT_KEYS } from "./logger.js";
76
76
  export type { Logger, LogLevel, ConsoleLoggerOptions, LoggerRedactionOptions, } from "./logger.js";
77
- export type { ScalarJsonPrimitive, ScalarJsonValue, ScalarReferenceConfiguration, ScalarTheme, RedocConfiguration, RedocHtmlOptions, AsyncApiHtmlOptions, DocsAssetOptions, } from "./docs.js";
77
+ export type { ScalarJsonPrimitive, ScalarJsonValue, ScalarReferenceConfiguration, ScalarTheme, RedocConfiguration, RedocHtmlOptions, SwaggerUiConfiguration, SwaggerUiHtmlOptions, AsyncApiHtmlOptions, DocsAssetOptions, } from "./docs.js";
78
78
  export { formatStartupBanner, printStartupBanner } from "./banner.js";
79
79
  export type { StartupBannerLink, StartupBannerOptions } from "./banner.js";
80
80
  export { sseStream, sseResponse, ndjsonStream, ndjsonResponse, } from "./streaming.js";
package/dist/metrics.d.ts CHANGED
@@ -18,7 +18,7 @@
18
18
  *
19
19
  * Everything is built on Web-standard primitives (plus optional `process.*`
20
20
  * gauges guarded for non-Node runtimes), so it runs unchanged on Node, Bun,
21
- * Deno, Cloudflare Workers, and Vercel Edge.
21
+ * Deno, Cloudflare Workers, and Vercel.
22
22
  *
23
23
  * @module
24
24
  * @since 0.37.0
package/dist/metrics.js CHANGED
@@ -18,7 +18,7 @@
18
18
  *
19
19
  * Everything is built on Web-standard primitives (plus optional `process.*`
20
20
  * gauges guarded for non-Node runtimes), so it runs unchanged on Node, Bun,
21
- * Deno, Cloudflare Workers, and Vercel Edge.
21
+ * Deno, Cloudflare Workers, and Vercel.
22
22
  *
23
23
  * @module
24
24
  * @since 0.37.0
@@ -231,10 +231,7 @@ export class Histogram extends Metric {
231
231
  }
232
232
  /** @internal */
233
233
  render() {
234
- const lines = [
235
- `# HELP ${this.name} ${escapeHelp(this.help)}`,
236
- `# TYPE ${this.name} histogram`,
237
- ];
234
+ const lines = [`# HELP ${this.name} ${escapeHelp(this.help)}`, `# TYPE ${this.name} histogram`];
238
235
  for (const s of this.series.values()) {
239
236
  for (let i = 0; i < this.bounds.length; i++) {
240
237
  lines.push(`${this.name}_bucket${renderLabelBlock(s.labels, ["le", String(this.bounds[i])])} ${s.counts[i]}`);
@@ -72,7 +72,7 @@ export interface CspDirectivesOptions {
72
72
  * Pair with {@link SecureHeadersOptions.reportingEndpoints} (or
73
73
  * {@link App.cspReportRoute}) to register the receiver.
74
74
  *
75
- * @since 0.20.0
75
+ * @since 0.20.0
76
76
  */
77
77
  reportTo?: string;
78
78
  }
@@ -112,7 +112,7 @@ export interface SecureHeadersOptions {
112
112
  * `reportTo: "csp-endpoint"` here as a shortcut) to direct CSP
113
113
  * violation reports there.
114
114
  *
115
- * @since 0.20.0
115
+ * @since 0.20.0
116
116
  */
117
117
  reportingEndpoints?: Record<string, string>;
118
118
  /**
@@ -121,7 +121,7 @@ export interface SecureHeadersOptions {
121
121
  * header so violation reports land at the matching
122
122
  * `reportingEndpoints` URL.
123
123
  *
124
- * @since 0.20.0
124
+ * @since 0.20.0
125
125
  */
126
126
  reportTo?: string;
127
127
  }
@@ -500,9 +500,9 @@ export interface BearerAuthOptions {
500
500
  realm?: string;
501
501
  /**
502
502
  * Optional per-request revalidation hook. Called after `validate`
503
- * accepts the token. Returning `false` rejects the request with `403`;
504
- * returning `true` or `undefined` accepts. Use for revocation lists,
505
- * token-version counters, etc.
503
+ * accepts the token. Returning `false` rejects the request with `403`;
504
+ * returning `true` or `undefined` accepts. Use for revocation lists,
505
+ * token-version counters, etc.
506
506
  *
507
507
  * @since 0.22.0
508
508
  */
@@ -44,7 +44,7 @@ function generateCspNonce() {
44
44
  const cryptoApi = globalThis.crypto;
45
45
  if (!cryptoApi?.getRandomValues) {
46
46
  throw new Error("secureHeaders(): WebCrypto is required to generate a CSP nonce. " +
47
- "Run on Node 20+, Bun, Deno, Cloudflare Workers, or Vercel Edge.");
47
+ "Run on Node 20+, Bun, Deno, Cloudflare Workers, or Vercel.");
48
48
  }
49
49
  const nonceBytes = new Uint8Array(16);
50
50
  cryptoApi.getRandomValues(nonceBytes);
@@ -193,10 +193,7 @@ export function secureHeaders(opts = {}) {
193
193
  }
194
194
  cspOpt = { directives, reportTo: opts.reportTo };
195
195
  }
196
- else if (opts.reportTo &&
197
- cspOpt !== false &&
198
- typeof cspOpt === "object" &&
199
- !cspOpt.reportTo) {
196
+ else if (opts.reportTo && cspOpt !== false && typeof cspOpt === "object" && !cspOpt.reportTo) {
200
197
  cspOpt = { ...cspOpt, reportTo: opts.reportTo };
201
198
  }
202
199
  const cspIsDynamic = cspOpt !== false && typeof cspOpt === "object";
@@ -479,10 +476,9 @@ export function cors(opts) {
479
476
  // produces broken-but-not-obviously-broken behavior in production. Fail
480
477
  // closed at construction time instead.
481
478
  if (opts.credentials) {
482
- const includesWildcard = opts.origin === "*" ||
483
- (Array.isArray(opts.origin) && opts.origin.includes("*"));
479
+ const includesWildcard = opts.origin === "*" || (Array.isArray(opts.origin) && opts.origin.includes("*"));
484
480
  if (includesWildcard) {
485
- throw new Error("cors(): origin: \"*\" cannot be combined with credentials: true. " +
481
+ throw new Error('cors(): origin: "*" cannot be combined with credentials: true. ' +
486
482
  "Pass an explicit origin string, an array of allowed origins, or a predicate function instead.");
487
483
  }
488
484
  }
@@ -558,8 +554,7 @@ export function cors(opts) {
558
554
  };
559
555
  hooks[CORS_HOOK_MARKER] = true;
560
556
  hooks[CORS_ORIGIN_ALLOW_MARKER] = (origin) => allow(origin) !== null;
561
- const hasWildcard = opts.origin === "*" ||
562
- (Array.isArray(opts.origin) && opts.origin.includes("*"));
557
+ const hasWildcard = opts.origin === "*" || (Array.isArray(opts.origin) && opts.origin.includes("*"));
563
558
  if (hasWildcard) {
564
559
  hooks[CORS_WILDCARD_ORIGIN_MARKER] = true;
565
560
  }
@@ -732,9 +727,7 @@ export function loginThrottle(opts = {}) {
732
727
  groupId,
733
728
  keyGenerator,
734
729
  ...(opts.store ? { store: opts.store } : {}),
735
- ...(opts.trustProxyHeaders !== undefined
736
- ? { trustProxyHeaders: opts.trustProxyHeaders }
737
- : {}),
730
+ ...(opts.trustProxyHeaders !== undefined ? { trustProxyHeaders: opts.trustProxyHeaders } : {}),
738
731
  ...(opts.retryAfter !== undefined ? { retryAfter: opts.retryAfter } : {}),
739
732
  });
740
733
  let slowdownBuckets = SHARED_LOGIN_THROTTLE_BUCKETS.get(groupId);
@@ -1229,9 +1222,7 @@ export function requireScopes(scopes) {
1229
1222
  });
1230
1223
  }
1231
1224
  const owned = readUserScopes(user);
1232
- const missing = owned === null
1233
- ? aggregate.slice()
1234
- : aggregate.filter((s) => !owned.has(s));
1225
+ const missing = owned === null ? aggregate.slice() : aggregate.filter((s) => !owned.has(s));
1235
1226
  if (missing.length > 0) {
1236
1227
  throw new ForbiddenError(`Missing required scope(s): ${missing.join(", ")}.`);
1237
1228
  }
@@ -22,7 +22,7 @@
22
22
  *
23
23
  * Everything here is built on Web-standard `URL` / `Request` and `btoa` /
24
24
  * `atob`, so it runs unchanged on Node, Bun, Deno, Cloudflare Workers, and
25
- * Vercel Edge.
25
+ * Vercel.
26
26
  *
27
27
  * @module
28
28
  * @since 0.37.0
@@ -22,7 +22,7 @@
22
22
  *
23
23
  * Everything here is built on Web-standard `URL` / `Request` and `btoa` /
24
24
  * `atob`, so it runs unchanged on Node, Bun, Deno, Cloudflare Workers, and
25
- * Vercel Edge.
25
+ * Vercel.
26
26
  *
27
27
  * @module
28
28
  * @since 0.37.0
@@ -24,7 +24,7 @@
24
24
  *
25
25
  * The compressed input itself is bounded by `maxCompressedBytes` before a single
26
26
  * byte is inflated. Built on the web-standard `DecompressionStream`, so the same
27
- * line works on Node, Bun, Deno, Cloudflare Workers, and Vercel Edge. Zero
27
+ * line works on Node, Bun, Deno, Cloudflare Workers, and Vercel. Zero
28
28
  * runtime dependencies.
29
29
  *
30
30
  * The middleware runs in the {@link "./types.js".Hooks.onRequest} phase — before
@@ -24,7 +24,7 @@
24
24
  *
25
25
  * The compressed input itself is bounded by `maxCompressedBytes` before a single
26
26
  * byte is inflated. Built on the web-standard `DecompressionStream`, so the same
27
- * line works on Node, Bun, Deno, Cloudflare Workers, and Vercel Edge. Zero
27
+ * line works on Node, Bun, Deno, Cloudflare Workers, and Vercel. Zero
28
28
  * runtime dependencies.
29
29
  *
30
30
  * The middleware runs in the {@link "./types.js".Hooks.onRequest} phase — before
@@ -34,7 +34,7 @@
34
34
  *
35
35
  * This module is dependency-free and uses only Web Standard
36
36
  * `Request`/`Response` + `Headers`, so it runs unchanged on Node, Bun, Deno,
37
- * Cloudflare Workers, and Vercel Edge.
37
+ * Cloudflare Workers, and Vercel.
38
38
  *
39
39
  * @module
40
40
  * @since 0.37.0
@@ -34,7 +34,7 @@
34
34
  *
35
35
  * This module is dependency-free and uses only Web Standard
36
36
  * `Request`/`Response` + `Headers`, so it runs unchanged on Node, Bun, Deno,
37
- * Cloudflare Workers, and Vercel Edge.
37
+ * Cloudflare Workers, and Vercel.
38
38
  *
39
39
  * @module
40
40
  * @since 0.37.0
@@ -304,9 +304,7 @@ export function responseCache(opts = {}) {
304
304
  const reqCc = parseCacheControl(ctx.request.headers.get("cache-control"));
305
305
  if (reqCc.has("no-store"))
306
306
  return undefined;
307
- const rawKey = opts.keyGenerator
308
- ? opts.keyGenerator(ctx)
309
- : defaultKey(ctx, varyHeaders);
307
+ const rawKey = opts.keyGenerator ? opts.keyGenerator(ctx) : defaultKey(ctx, varyHeaders);
310
308
  if (rawKey === null)
311
309
  return undefined;
312
310
  const key = `${keyPrefix}${rawKey}`;
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "bomFormat": "CycloneDX",
3
3
  "specVersion": "1.5",
4
- "serialNumber": "urn:uuid:3322791e-92a4-5b93-a136-5a15b3fe53aa",
4
+ "serialNumber": "urn:uuid:086b2383-b351-57fb-bd98-0fbf04f2342b",
5
5
  "version": 1,
6
6
  "metadata": {
7
- "timestamp": "2026-06-21T13:50:39.481Z",
7
+ "timestamp": "2026-06-22T21:07:28.471Z",
8
8
  "tools": [
9
9
  {
10
10
  "vendor": "DaloyJS",
11
11
  "name": "daloy-generate-sbom",
12
- "version": "1.0.0-beta.1"
12
+ "version": "1.0.0-beta.2"
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@1.0.0-beta.1",
22
+ "bom-ref": "pkg:npm/@daloyjs/core@1.0.0-beta.2",
23
23
  "name": "@daloyjs/core",
24
- "version": "1.0.0-beta.1",
24
+ "version": "1.0.0-beta.2",
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@1.0.0-beta.1",
26
+ "purl": "pkg:npm/@daloyjs/core@1.0.0-beta.2",
27
27
  "licenses": [
28
28
  {
29
29
  "license": {
@@ -46,9 +46,9 @@
46
46
  }
47
47
  ],
48
48
  "swid": {
49
- "tagId": "swidtag--daloyjs-core-1.0.0-beta.1",
49
+ "tagId": "swidtag--daloyjs-core-1.0.0-beta.2",
50
50
  "name": "@daloyjs/core",
51
- "version": "1.0.0-beta.1",
51
+ "version": "1.0.0-beta.2",
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@1.0.0-beta.1",
60
+ "ref": "pkg:npm/@daloyjs/core@1.0.0-beta.2",
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-1.0.0-beta.1",
6
- "documentNamespace": "https://github.com/daloyjs/daloy/sbom/@daloyjs/core-1.0.0-beta.1-3322791e-92a4-5b93-a136-5a15b3fe53aa",
5
+ "name": "@daloyjs/core-1.0.0-beta.2",
6
+ "documentNamespace": "https://github.com/daloyjs/daloy/sbom/@daloyjs/core-1.0.0-beta.2-086b2383-b351-57fb-bd98-0fbf04f2342b",
7
7
  "creationInfo": {
8
- "created": "2026-06-21T13:50:39.481Z",
8
+ "created": "2026-06-22T21:07:28.471Z",
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": "1.0.0-beta.1",
19
+ "versionInfo": "1.0.0-beta.2",
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@1.0.0-beta.1"
30
+ "referenceLocator": "pkg:npm/@daloyjs/core@1.0.0-beta.2"
31
31
  }
32
32
  ]
33
33
  }
@@ -22,7 +22,7 @@
22
22
  *
23
23
  * Everything is built on Web-standard primitives (`AbortController`,
24
24
  * `Intl.DateTimeFormat` for timezone wall-clock math, `setTimeout`), so it
25
- * runs unchanged on Node, Bun, Deno, Cloudflare Workers, and Vercel Edge, with
25
+ * runs unchanged on Node, Bun, Deno, Cloudflare Workers, and Vercel, with
26
26
  * zero runtime dependencies. Pair it with {@link App.cron} for an app-managed
27
27
  * scheduler whose lifecycle is tied to graceful shutdown, or drive a
28
28
  * {@link Scheduler} directly.
package/dist/scheduler.js CHANGED
@@ -22,7 +22,7 @@
22
22
  *
23
23
  * Everything is built on Web-standard primitives (`AbortController`,
24
24
  * `Intl.DateTimeFormat` for timezone wall-clock math, `setTimeout`), so it
25
- * runs unchanged on Node, Bun, Deno, Cloudflare Workers, and Vercel Edge, with
25
+ * runs unchanged on Node, Bun, Deno, Cloudflare Workers, and Vercel, with
26
26
  * zero runtime dependencies. Pair it with {@link App.cron} for an app-managed
27
27
  * scheduler whose lifecycle is tied to graceful shutdown, or drive a
28
28
  * {@link Scheduler} directly.
@@ -63,11 +63,27 @@ const CRON_ALIASES = {
63
63
  "@hourly": "0 * * * *",
64
64
  };
65
65
  const MONTH_NAMES = {
66
- jan: 1, feb: 2, mar: 3, apr: 4, may: 5, jun: 6,
67
- jul: 7, aug: 8, sep: 9, oct: 10, nov: 11, dec: 12,
66
+ jan: 1,
67
+ feb: 2,
68
+ mar: 3,
69
+ apr: 4,
70
+ may: 5,
71
+ jun: 6,
72
+ jul: 7,
73
+ aug: 8,
74
+ sep: 9,
75
+ oct: 10,
76
+ nov: 11,
77
+ dec: 12,
68
78
  };
69
79
  const DAY_NAMES = {
70
- sun: 0, mon: 1, tue: 2, wed: 3, thu: 4, fri: 5, sat: 6,
80
+ sun: 0,
81
+ mon: 1,
82
+ tue: 2,
83
+ wed: 3,
84
+ thu: 4,
85
+ fri: 5,
86
+ sat: 6,
71
87
  };
72
88
  function resolveNamed(token, names) {
73
89
  const lower = token.toLowerCase();
@@ -163,7 +179,13 @@ export function parseCron(expression) {
163
179
  };
164
180
  }
165
181
  const WEEKDAY_INDEX = {
166
- Sun: 0, Mon: 1, Tue: 2, Wed: 3, Thu: 4, Fri: 5, Sat: 6,
182
+ Sun: 0,
183
+ Mon: 1,
184
+ Tue: 2,
185
+ Wed: 3,
186
+ Thu: 4,
187
+ Fri: 5,
188
+ Sat: 6,
167
189
  };
168
190
  function wallClockOf(date, timeZone) {
169
191
  if (timeZone === undefined || timeZone === "UTC") {
@@ -323,7 +345,12 @@ export class Scheduler {
323
345
  skipped: 0,
324
346
  };
325
347
  this.#tasks.set(def.name, task);
326
- this.#logger?.debug({ event: "scheduler.task.defined", task: def.name, cron: def.cron, intervalMs: def.intervalMs }, `Scheduled task "${def.name}" defined`);
348
+ this.#logger?.debug({
349
+ event: "scheduler.task.defined",
350
+ task: def.name,
351
+ cron: def.cron,
352
+ intervalMs: def.intervalMs,
353
+ }, `Scheduled task "${def.name}" defined`);
327
354
  if (this.#started && !this.#stopped)
328
355
  this.#arm(task, def.runOnStart === true);
329
356
  return this;
@@ -518,7 +545,13 @@ export class Scheduler {
518
545
  catch (error) {
519
546
  task.failures++;
520
547
  task.lastError = error;
521
- this.#logger?.error({ event: "scheduler.task.failed", task: task.def.name, runCount, timedOut, err: serializeError(error) }, `Scheduled task "${task.def.name}" failed`);
548
+ this.#logger?.error({
549
+ event: "scheduler.task.failed",
550
+ task: task.def.name,
551
+ runCount,
552
+ timedOut,
553
+ err: serializeError(error),
554
+ }, `Scheduled task "${task.def.name}" failed`);
522
555
  try {
523
556
  task.def.onError?.(error, { name: task.def.name, runCount, timedOut });
524
557
  }
package/dist/session.d.ts CHANGED
@@ -13,7 +13,7 @@
13
13
  * backed by Redis, Cloudflare KV, Vercel KV, or any other shared store.
14
14
  *
15
15
  * The module is dependency-free and uses Web Crypto (`crypto.subtle`), so it
16
- * works on Node, Bun, Deno, Cloudflare Workers, and Vercel Edge.
16
+ * works on Node, Bun, Deno, Cloudflare Workers, and Vercel.
17
17
  */
18
18
  import type { BaseContext, Hooks } from "./types.js";
19
19
  /**
package/dist/session.js CHANGED
@@ -13,7 +13,7 @@
13
13
  * backed by Redis, Cloudflare KV, Vercel KV, or any other shared store.
14
14
  *
15
15
  * The module is dependency-free and uses Web Crypto (`crypto.subtle`), so it
16
- * works on Node, Bun, Deno, Cloudflare Workers, and Vercel Edge.
16
+ * works on Node, Bun, Deno, Cloudflare Workers, and Vercel.
17
17
  */
18
18
  import { assertCookieAttributes, readRequestCookie, serializeClearCookie, serializeCookie, } from "./cookie.js";
19
19
  import { timingSafeEqual } from "./security.js";
@@ -385,7 +385,9 @@ const DEFAULT_ROTATE_SESSION_KEYS = [
385
385
  ];
386
386
  function sessionFromContext(ctx) {
387
387
  const value = ctx.state[STATE_KEY];
388
- if (!value || typeof value !== "object" || typeof value.regenerate !== "function") {
388
+ if (!value ||
389
+ typeof value !== "object" ||
390
+ typeof value.regenerate !== "function") {
389
391
  throw new Error("rotateSession(): session() must run before rotateSession().");
390
392
  }
391
393
  return value;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@daloyjs/core",
3
- "version": "1.0.0-beta.1",
3
+ "version": "1.0.0-beta.2",
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": {