@daloyjs/core 0.38.3 → 0.39.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 +23 -7
- package/dist/app.d.ts +17 -6
- package/dist/app.js +23 -8
- package/dist/docs.d.ts +129 -3
- package/dist/docs.js +54 -5
- package/dist/index.d.ts +1 -1
- package/dist/metrics.js +8 -5
- package/dist/sbom.cdx.json +9 -9
- package/dist/sbom.spdx.json +5 -5
- package/dist/tracing.js +7 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -249,7 +249,7 @@ const r = await client.getBookById({ params: { id: "1" } });
|
|
|
249
249
|
|
|
250
250
|
---
|
|
251
251
|
|
|
252
|
-
## Built-in docs UI (Scalar / Swagger UI)
|
|
252
|
+
## Built-in docs UI (Scalar / Swagger UI / Redoc)
|
|
253
253
|
|
|
254
254
|
FastAPI-style. One line on the `App` constructor mounts `GET /docs`, `GET /openapi.json`, and `GET /openapi.yaml` for you,
|
|
255
255
|
with a strict CSP and CDN-hosted assets:
|
|
@@ -269,7 +269,7 @@ Use `docs: "auto"` to mount only when `production: false`, or the object form fo
|
|
|
269
269
|
new App({
|
|
270
270
|
openapi: { info: { title: "My API", version: "1.0.0" } },
|
|
271
271
|
docs: {
|
|
272
|
-
ui: "scalar",
|
|
272
|
+
ui: "scalar", // "scalar" (default) | "swagger" | "redoc"
|
|
273
273
|
path: "/reference",
|
|
274
274
|
openapiPath: "/spec.json",
|
|
275
275
|
openapiYamlPath: "/spec.yaml", // or `false` to disable the YAML route
|
|
@@ -283,15 +283,30 @@ new App({
|
|
|
283
283
|
});
|
|
284
284
|
```
|
|
285
285
|
|
|
286
|
+
Switch UIs with one word. `ui: "redoc"` renders Redoc instead, and its
|
|
287
|
+
options are forwarded to `Redoc.init` via `docs.redoc`:
|
|
288
|
+
|
|
289
|
+
```ts
|
|
290
|
+
new App({
|
|
291
|
+
openapi: { info: { title: "My API", version: "1.0.0" } },
|
|
292
|
+
docs: {
|
|
293
|
+
ui: "redoc",
|
|
294
|
+
redoc: { hideDownloadButtons: true, sortPropsAlphabetically: true },
|
|
295
|
+
},
|
|
296
|
+
});
|
|
297
|
+
```
|
|
298
|
+
|
|
286
299
|
The `scalar` option is forwarded to Scalar's HTML API as JSON configuration,
|
|
287
300
|
with Daloy keeping the live `openapiPath` as the source. Use it for themes,
|
|
288
301
|
custom CSS, layout, auth defaults, and client visibility without copying the
|
|
289
|
-
HTML helper.
|
|
302
|
+
HTML helper. Redoc spins up a `blob:` Web Worker for search, so the
|
|
303
|
+
auto-mounted `/docs` page widens its CSP with `worker-src 'self' blob:` for
|
|
304
|
+
`ui: "redoc"` only — Scalar and Swagger UI keep the tighter default.
|
|
290
305
|
|
|
291
306
|
Prefer to mount manually? Import the helpers directly:
|
|
292
307
|
|
|
293
308
|
```ts
|
|
294
|
-
import { swaggerUiHtml, scalarHtml, htmlResponse } from "@daloyjs/core/docs";
|
|
309
|
+
import { swaggerUiHtml, scalarHtml, redocHtml, htmlResponse } from "@daloyjs/core/docs";
|
|
295
310
|
import { generateOpenAPI } from "@daloyjs/core/openapi";
|
|
296
311
|
```
|
|
297
312
|
|
|
@@ -488,7 +503,7 @@ DaloyJS is in **public preview** (`0.x`). The public API may still change betwee
|
|
|
488
503
|
### Routing, validation, and docs
|
|
489
504
|
|
|
490
505
|
- Contract-first routing with Standard Schema validation (Zod 4, Valibot, ArkType, TypeBox) and OpenAPI 3.1 generated from a single source of truth.
|
|
491
|
-
- Live OpenAPI 3.1 spec served as both JSON (`GET /openapi.json`) and YAML (`GET /openapi.yaml`) when `docs: true`, with Scalar UI
|
|
506
|
+
- 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`.
|
|
492
507
|
- Zero-config OpenAPI `info` autofill from `package.json` (Node / Bun) or `deno.json` / `deno.jsonc` (Deno); explicit `openapi.info` values always win.
|
|
493
508
|
- RFC 7231 + RFC 5789 HTTP-method allowlist enforced inside `app.route()` (WebDAV, `TRACE`, `CONNECT` rejected at the framework boundary).
|
|
494
509
|
- 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`.
|
|
@@ -549,7 +564,8 @@ The framework refuses to start (or to construct) when configuration is unsafe:
|
|
|
549
564
|
- `idempotency()` with `Idempotency-Key` fingerprinting + byte-for-byte response replay, in-flight `409`, `422` on key reuse with a different payload, and a pluggable `IdempotencyStore` (in-memory default) at `@daloyjs/core/idempotency`.
|
|
550
565
|
- `responseCache()` server-side body cache (cache-key + TTL with `s-maxage`/`max-age` orchestration, request `no-store`/`no-cache` directives, recursion-safe stale-while-revalidate, `Vary`-aware keying, `X-Cache` HIT/MISS/STALE marker, pluggable `ResponseCacheStore` in-memory default) at `@daloyjs/core/response-cache`. Never caches `Set-Cookie` or `private`/`no-store`/`no-cache` responses. Complements `etag()`/`compression()`, which do not cache bodies.
|
|
551
566
|
- `paginationQuery()` / `encodeCursor()` / `decodeCursor()` / `buildPageLinks()` / `buildLinkHeader()` cursor-pagination helpers at `@daloyjs/core/pagination`: opaque base64url cursors (length-capped, prototype-pollution-safe decode → `400` on tamper), RFC 8288 `Link` header emission with CRLF / header-injection guards, and a Standard Schema that validates `cursor`/`limit` and auto-wires both into the OpenAPI spec + typed client via `toJSONSchema()`.
|
|
552
|
-
- `app.metrics()` + `MetricsRegistry` / `httpMetrics()` Prometheus / OpenMetrics exposition at `@daloyjs/core/metrics`: dependency-free counters / gauges / histograms, RED instrumentation (`http_requests_total`, `http_request_duration_seconds`, `http_requests_in_flight`) plus process gauges, exposition-injection-safe name/label validation, a per-metric cardinality cap, and an opt-in `/metrics` route with the same hardened posture as `app.healthcheck()` (bearer token + `timingSafeEqual`, per-IP rate limit, refuse-to-boot unauthenticated in production).
|
|
567
|
+
- `app.metrics()` + `MetricsRegistry` / `httpMetrics()` Prometheus / OpenMetrics exposition at `@daloyjs/core/metrics`: dependency-free counters / gauges / histograms, RED instrumentation (`http_requests_total`, `http_request_duration_seconds`, `http_requests_in_flight`) plus process gauges, exposition-injection-safe name/label validation, a per-metric cardinality cap, and an opt-in `/metrics` route with the same hardened posture as `app.healthcheck()` (bearer token + `timingSafeEqual`, per-IP rate limit, refuse-to-boot unauthenticated in production). The repo ships an `examples/observability/` Docker Compose stack that starts a pre-configured Prometheus + Grafana pair (with an auto-provisioned RED + heatmap dashboard) against any local app via `docker compose -f examples/observability/docker-compose.yml up`.
|
|
568
|
+
- `otelTracing()` OpenTelemetry-compatible distributed tracing at `@daloyjs/core/tracing`: a dependency-free `Hooks` bundle that opens one `SERVER` span per request, attaches HTTP semantic-convention attributes (`http.request.method`, `url.path`, `server.address` / `server.port`, `http.response.status_code`, …), records exceptions + escalates `5xx` to `ERROR`, guarantees a single `span.end()`, and exposes the live span on `ctx.state.otelSpan`. Bring any tracer matching the small `TracingTracer` interface (the real `@opentelemetry/api` SDK on Node, or a custom exporter on Workers/Deno) plus your own propagator via `contextFromRequest` for `traceparent` continuation — no OTel SDK is forced into your install. The `examples/observability/` stack also runs **Jaeger**, and `examples/otel-tracing-demo.ts` ships a ~120-line dependency-free OTLP/HTTP exporter that streams spans straight to it.
|
|
553
569
|
- `resilientFetch()` + `CircuitBreaker` outbound resilience at `@daloyjs/core/fetch-resilience`: a dependency-free circuit breaker (`closed → open → half-open`), retry-with-backoff (exponential + full jitter, idempotent-method/transient-status scoped, honours `Retry-After`), and a per-call timeout (`AbortController` → `FetchTimeoutError`) designed to layer **on top of** `fetchGuard()` — an `SsrfBlockedError` is a terminal refusal that is never retried and never trips the breaker, so SSRF protection stays intact under the resilience layer.
|
|
554
570
|
- `createWebhookSender()` + `MemoryWebhookDeadLetterSink` outbound webhook delivery at `@daloyjs/core/webhook-delivery`: the outbound counterpart to `verifyWebhookSignature()` — timestamped HMAC-signed `POST`s (`webhook-id` / `webhook-timestamp` / `webhook-signature`, computed over `"<timestamp>.<body>"` and reused across retries for safe deduping), bounded retry-with-backoff (transient-status + network scoped, honours `Retry-After`), per-attempt timeout, and dead-letter semantics. Transport defaults to `fetchGuard()`, so a subscriber URL pointing at cloud metadata or a private range is refused with a terminal `SsrfBlockedError` (never retried, dead-lettered once). Zero runtime dependencies.
|
|
555
571
|
- `app.cron()` + standalone `Scheduler` in-process scheduled tasks at `@daloyjs/core/scheduler`: a queue-agnostic schedule primitive for periodic housekeeping (cache sweeps, token refresh, reconciliation). Fixed intervals or 5-field cron expressions (lists / ranges / steps / named months & days / `@hourly`–`@yearly` aliases / optional IANA `timeZone`), arithmetic cron parsing (no backtracking regex), fixed-rate **single-flight** (overlapping ticks are skipped, never run concurrently), per-run `timeoutMs` with `AbortSignal`, and graceful-shutdown integration (stop arming → await in-flight → abort after grace). Timers are `unref`'d. `parseCron()` / `nextCronRun()` exported standalone. Zero runtime dependencies.
|
|
@@ -561,7 +577,7 @@ The framework refuses to start (or to construct) when configuration is unsafe:
|
|
|
561
577
|
- `concurrencyLimit()` per-route / per-client concurrency limits + queueing at `@daloyjs/core/concurrency-limit`: HAProxy `maxconn`/queue parity at the app layer. Bounds in-flight requests through a surface with a per-bucket semaphore (`maxConcurrent`), a bounded FIFO queue (`maxQueue`) with an optional `queueTimeoutMs`, and a fast `503` + `Retry-After` once the queue is full or the wait times out. Partition the budget with `scope`: `"global"` (default), `"route"` (per `method + path`), `"client"` (per identity, needs `trustProxyHeaders`/`keyGenerator`), or a custom function (`undefined` skips limiting, fail-open). Acquires in `beforeHandle` and releases in `onSend`, so slots are freed on success, error, and short-circuit paths alike — never leaked. `onReject` observability hook, configurable `retryAfterSeconds`/`message`. Complements the `maxConnections` socket cap and `loadShedding()`. Zero runtime dependencies. HAProxy `maxconn`/queue parity at the app layer. Bounds in-flight requests through a surface with a per-bucket semaphore (`maxConcurrent`), a bounded FIFO queue (`maxQueue`) with an optional `queueTimeoutMs`, and a fast `503` + `Retry-After` once the queue is full or the wait times out. Partition the budget with `scope`: `"global"` (default), `"route"` (per `method + path`), `"client"` (per identity, needs `trustProxyHeaders`/`keyGenerator`), or a custom function (`undefined` skips limiting, fail-open). Acquires in `beforeHandle` and releases in `onSend`, so slots are freed on success, error, and short-circuit paths alike — never leaked. `onReject` observability hook, configurable `retryAfterSeconds`/`message`. Complements the `maxConnections` socket cap and `loadShedding()`. Zero runtime dependencies.
|
|
562
578
|
- `requestDecompression()` inbound decompression-bomb guard at `@daloyjs/core/request-decompression`: core is safe by omission (it never decompresses request bodies), so this is the opt-in middleware for services that must accept compressed uploads. Inflates `gzip` / `deflate` bodies behind two caps enforced **during** inflation so a zip bomb is aborted before it is fully materialised: an absolute `maxDecompressedBytes` (required) and an expansion-ratio `maxRatio` (default `100`), both rejecting with `413`. The compressed upload itself is bounded by `maxCompressedBytes` (default 1 MiB) before a byte is inflated. Unknown, non-allowlisted, runtime-unsupported, or **layered** (`gzip, gzip`) encodings are refused `415`; malformed streams `400`; bodyless / uncompressed / `identity` / `GET` / `HEAD` traffic passes through untouched. Runs in `onRequest` and stashes the inflated bytes so schema-validated bodies and raw-body handlers both see the decompressed payload. `onBomb` observability hook, exported `decompressRequestBody()` for custom flows. Built on web-standard `DecompressionStream` (brotli excluded — not in the spec). Zero runtime dependencies.
|
|
563
579
|
- `waf()` opt-in WAF-lite signature/anomaly inbound-inspection middleware at `@daloyjs/core/waf`: a first-party defense-in-depth layer for teams without an edge WAF (it does **not** replace ModSecurity / a CDN WAF). Wires DaloyJS' high-confidence injection signatures — SQLi, XSS, NoSQL-operator injection (reusing `hasMongoOperatorKeys` for a structural body check), and command injection — into a single scored inbound-inspection pass over the decoded path, the raw + decoded query string, an opt-in header allowlist, and the validated body. Each rule that fires adds an anomaly `score`; reaching `blockThreshold` (default `5`) rejects with a generic `403` (block mode) or merely reports via `onMatch` (log mode) so operators can tune against real traffic first. Per-rule enable/disable + score overrides, inspection-surface toggles, control-character-stripped log samples, and bounded scanning (`maxValueLength` / `maxBodyNodes`) keep a hostile payload from becoming CPU-DoS. The `403` body never names the rule that fired. Zero runtime dependencies.
|
|
564
|
-
- Built-in docs UI Subresource Integrity (SRI): `DocsAssetOptions` lets `scalarHtml()` / `swaggerUiHtml()` and the `docs: { assets }` auto-mount pin version-exact `*Integrity` hashes (`sha256`/`sha384`/`sha512`) plus a `crossOrigin` value (default `"anonymous"`) on the CDN-loaded Scalar / Swagger UI `<script>` / `<link>` tags, so a poisoned jsDelivr asset can't execute. Malformed SRI values throw a `TypeError` at startup (browsers ignore unparseable `integrity`, so failing loud avoids a false sense of protection); self-hosting the assets via the same `assets` URLs stays supported. Zero runtime dependencies.
|
|
580
|
+
- Built-in docs UI Subresource Integrity (SRI): `DocsAssetOptions` lets `scalarHtml()` / `swaggerUiHtml()` / `redocHtml()` and the `docs: { assets }` auto-mount pin version-exact `*Integrity` hashes (`sha256`/`sha384`/`sha512`) plus a `crossOrigin` value (default `"anonymous"`) on the CDN-loaded Scalar / Swagger UI / Redoc `<script>` / `<link>` tags, so a poisoned jsDelivr asset can't execute. Malformed SRI values throw a `TypeError` at startup (browsers ignore unparseable `integrity`, so failing loud avoids a false sense of protection); self-hosting the assets via the same `assets` URLs stays supported. Zero runtime dependencies.
|
|
565
581
|
- HTTP Message Signatures (RFC 9421) at `@daloyjs/core/http-signatures`: first-party sign/verify for server-to-server request authentication via the standard `Signature` / `Signature-Input` headers — complements the inbound-only webhook HMAC and `clientCertAuth()` mTLS. `signMessage()` / `signRequest()` build an RFC 9421 signature base over derived components (`@method`, `@target-uri`, `@authority`, `@scheme`, `@request-target`, `@path`, `@query`, `@query-param`, `@status`) and HTTP fields with Structured-Fields header serialization; `verifyMessage()` / `verifyRequest()` and the `httpSignatureAuth()` middleware check them. Algorithms `hmac-sha256` / `ed25519` / `ecdsa-p256-sha256` / `ecdsa-p384-sha384` / `rsa-pss-sha512` / `rsa-v1_5-sha256` via WebCrypto (no `node:` imports). Secure-by-default verify: a **mandatory `algorithms` allowlist** (no implicit "any alg"), optional per-key alg pinning to defeat algorithm-confusion, a required `created` timestamp with a 300s freshness window, `created`-in-future / `expires` skew rejection, configurable `requiredComponents`, a 32-byte raw-HMAC floor, and `nonce` replay defense; the middleware answers a missing/invalid signature with `401` + `Cache-Control: no-store` and stamps the verified result on `ctx.state.httpSignature`. Ships RFC 9530 `contentDigest()` / `verifyContentDigest()` to bind the request body. Zero runtime dependencies.
|
|
566
582
|
- `compression()` built on web-standard `CompressionStream` (prefers `br` > `gzip` > `deflate`), with BREACH-aware always-on guards (skips `Set-Cookie`, `Authorization`, session / CSRF cookies, already-compressed content types), `minimumSize: 1024`, negative-compression-ratio post-check, no configurable `compressLevel` knob (CPU-DoS defense — `level: 9` is refused at construction), always-on `Vary: Accept-Encoding`, and strong → weak ETag downgrade per RFC 9110 §8.8.3.
|
|
567
583
|
- `etag()` helper auto-skips on `Set-Cookie` and private / no-store / no-cache `Cache-Control` (cross-tenant fingerprinting defense).
|
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 ScalarReferenceConfiguration } from "./docs.js";
|
|
5
|
+
import { type DocsAssetOptions, type DocsContentSecurityPolicyOptions, type RedocConfiguration, type ScalarReferenceConfiguration } from "./docs.js";
|
|
6
6
|
import { type SecureHeadersOptions } from "./middleware.js";
|
|
7
7
|
import { type LoadSheddingOptions } from "./load-shedding.js";
|
|
8
8
|
import { MetricsRegistry, type HttpMetricsOptions } from "./metrics.js";
|
|
@@ -299,7 +299,7 @@ export interface AppOptions {
|
|
|
299
299
|
/**
|
|
300
300
|
* Enable the built-in API documentation surface — a `/openapi.json` route
|
|
301
301
|
* that serves the live OpenAPI 3.1 spec and a `/docs` route that serves a
|
|
302
|
-
* Scalar (default)
|
|
302
|
+
* Scalar (default), Swagger UI, or Redoc HTML page that loads it. This is the
|
|
303
303
|
* one-line equivalent of FastAPI's automatic `/docs` UI.
|
|
304
304
|
*
|
|
305
305
|
* - `false` (default) — never mount. Write your own with `generateOpenAPI`
|
|
@@ -309,7 +309,7 @@ export interface AppOptions {
|
|
|
309
309
|
* is a "secure by default" choice: production deployments should opt in
|
|
310
310
|
* explicitly so internal APIs do not accidentally publish a browsable
|
|
311
311
|
* schema.
|
|
312
|
-
* - object — full configuration (custom paths, UI choice, title,
|
|
312
|
+
* - object — full configuration (custom paths, UI choice, title, per-UI
|
|
313
313
|
* config, CSP overrides). The `enabled` field on the object can override the
|
|
314
314
|
* auto/prod rule.
|
|
315
315
|
*
|
|
@@ -353,10 +353,21 @@ export interface DocsRouteOptions {
|
|
|
353
353
|
* @since 0.13.1
|
|
354
354
|
*/
|
|
355
355
|
openapiYamlPath?: PathString | false;
|
|
356
|
-
/**
|
|
357
|
-
|
|
358
|
-
|
|
356
|
+
/**
|
|
357
|
+
* Which built-in UI to render. Default `"scalar"` (smaller payload, modern
|
|
358
|
+
* UI). `"swagger"` renders classic Swagger UI; `"redoc"` (since 0.39.0)
|
|
359
|
+
* renders Redoc.
|
|
360
|
+
*/
|
|
361
|
+
ui?: "scalar" | "swagger" | "redoc";
|
|
362
|
+
/** Scalar API reference UI configuration. Ignored unless `ui` is `"scalar"`. */
|
|
359
363
|
scalar?: ScalarReferenceConfiguration;
|
|
364
|
+
/**
|
|
365
|
+
* Redoc UI configuration, forwarded to `Redoc.init`. Ignored unless
|
|
366
|
+
* `ui: "redoc"`.
|
|
367
|
+
*
|
|
368
|
+
* @since 0.39.0
|
|
369
|
+
*/
|
|
370
|
+
redoc?: RedocConfiguration;
|
|
360
371
|
/** Page `<title>`. Defaults to the resolved OpenAPI `info.title`. */
|
|
361
372
|
title?: string;
|
|
362
373
|
/**
|
package/dist/app.js
CHANGED
|
@@ -5,7 +5,7 @@ import { validate } from "./schema.js";
|
|
|
5
5
|
import { readBodyLimited, safeJsonParse, randomId, assertNoDuplicateSingletonHeaders, assertNoReservedInternalHeaders, assertHeaderCountWithinLimit, DEFAULT_MAX_HEADER_COUNT, assertStrongSecret, timingSafeEqual, isForbiddenObjectKey } from "./security.js";
|
|
6
6
|
import { createLogger, noopLogger } from "./logger.js";
|
|
7
7
|
import { generateOpenAPI, openapiToYAML, } from "./openapi.js";
|
|
8
|
-
import { docsContentSecurityPolicy, scalarHtml, swaggerUiHtml, } from "./docs.js";
|
|
8
|
+
import { docsContentSecurityPolicy, redocHtml, scalarHtml, swaggerUiHtml, } from "./docs.js";
|
|
9
9
|
import { secureHeaders as secureHeadersMiddleware, CORS_HOOK_MARKER, CORS_ORIGIN_ALLOW_MARKER, CORS_WILDCARD_ORIGIN_MARKER, CSRF_HOOK_MARKER, REQUIRE_SCOPES_AGGREGATE_KEY, REQUIRE_SCOPES_HOOK_MARKER, SECURE_HEADERS_MARKER, } from "./middleware.js";
|
|
10
10
|
import { COMPRESSION_HOOK_MARKER } from "./compression.js";
|
|
11
11
|
import { SESSION_HOOK_MARKER, SESSION_SECRETS_MARKER, } from "./session.js";
|
|
@@ -830,6 +830,14 @@ export class App {
|
|
|
830
830
|
}),
|
|
831
831
|
});
|
|
832
832
|
}
|
|
833
|
+
// Redoc builds a Web Worker from a `blob:` URL, which the strict docs CSP
|
|
834
|
+
// would otherwise block; grant `worker-src 'self' blob:` for that UI only
|
|
835
|
+
// (unless the caller explicitly overrode it). Scalar / Swagger UI keep the
|
|
836
|
+
// tighter default. The policy is deterministic from `ui` + `opts.csp`, so
|
|
837
|
+
// compute it once at mount time instead of per request.
|
|
838
|
+
const docsCsp = docsContentSecurityPolicy(ui === "redoc"
|
|
839
|
+
? { ...opts.csp, allowBlobWorkers: opts.csp?.allowBlobWorkers ?? true }
|
|
840
|
+
: opts.csp);
|
|
833
841
|
this.route({
|
|
834
842
|
method: "GET",
|
|
835
843
|
path: docsPath,
|
|
@@ -847,18 +855,25 @@ export class App {
|
|
|
847
855
|
title,
|
|
848
856
|
assets: opts.assets,
|
|
849
857
|
})
|
|
850
|
-
:
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
858
|
+
: ui === "redoc"
|
|
859
|
+
? redocHtml({
|
|
860
|
+
specUrl: openapiPath,
|
|
861
|
+
title,
|
|
862
|
+
configuration: opts.redoc,
|
|
863
|
+
assets: opts.assets,
|
|
864
|
+
})
|
|
865
|
+
: scalarHtml({
|
|
866
|
+
specUrl: openapiPath,
|
|
867
|
+
title,
|
|
868
|
+
configuration: opts.scalar,
|
|
869
|
+
assets: opts.assets,
|
|
870
|
+
});
|
|
856
871
|
return {
|
|
857
872
|
status: 200,
|
|
858
873
|
body: html,
|
|
859
874
|
headers: {
|
|
860
875
|
"content-type": "text/html; charset=utf-8",
|
|
861
|
-
"content-security-policy":
|
|
876
|
+
"content-security-policy": docsCsp,
|
|
862
877
|
"x-content-type-options": "nosniff",
|
|
863
878
|
"referrer-policy": "no-referrer",
|
|
864
879
|
},
|
package/dist/docs.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Built-in API documentation handlers.
|
|
3
3
|
*
|
|
4
|
-
* Scalar
|
|
5
|
-
* at `specUrl` and fetches UI assets from a CDN by default. No build
|
|
6
|
-
* no extra deps.
|
|
4
|
+
* Scalar, Swagger UI, and Redoc helpers serve a single HTML page that loads
|
|
5
|
+
* the spec at `specUrl` and fetches UI assets from a CDN by default. No build
|
|
6
|
+
* step, no extra deps.
|
|
7
7
|
*
|
|
8
8
|
* (You can self-host the assets if your CSP forbids CDNs.)
|
|
9
9
|
*/
|
|
@@ -92,6 +92,89 @@ export interface ScalarReferenceConfiguration {
|
|
|
92
92
|
spec?: never;
|
|
93
93
|
url?: never;
|
|
94
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Subset of Redoc standalone configuration safe to serialize from the server.
|
|
97
|
+
*
|
|
98
|
+
* Every field is a plain JSON value, so the whole object is forwarded verbatim
|
|
99
|
+
* to `Redoc.init(specUrl, configuration, element)` in the generated page. The
|
|
100
|
+
* index signature accepts any additional Redoc option (the standalone bundle's
|
|
101
|
+
* option set drifts across versions); the named fields exist for editor
|
|
102
|
+
* autocompletion of the common, stable ones. Function-typed options are not
|
|
103
|
+
* representable here on purpose — they cannot cross the server→HTML boundary.
|
|
104
|
+
*
|
|
105
|
+
* @since 0.39.0
|
|
106
|
+
*/
|
|
107
|
+
export interface RedocConfiguration {
|
|
108
|
+
[key: string]: ScalarJsonValue | undefined;
|
|
109
|
+
/** Disable the search bar (also skips spawning the search Web Worker). */
|
|
110
|
+
disableSearch?: boolean;
|
|
111
|
+
/** Minimum query length before search runs. */
|
|
112
|
+
minCharacterLengthToInitSearch?: number;
|
|
113
|
+
/** Which responses to expand by default, e.g. `"200,201"` or `"all"`. */
|
|
114
|
+
expandResponses?: string;
|
|
115
|
+
/** Expand the single-property schema field instead of collapsing it. */
|
|
116
|
+
expandSingleSchemaField?: boolean;
|
|
117
|
+
/** Expand `default` server-variable values in the sidebar. */
|
|
118
|
+
expandDefaultServerVariables?: boolean;
|
|
119
|
+
/** How deep to expand generated JSON samples; a number or `"all"`. */
|
|
120
|
+
jsonSampleExpandLevel?: number | string;
|
|
121
|
+
/** How deep to expand nested schemas; a number or `"all"`. */
|
|
122
|
+
schemasExpansionLevel?: number | string;
|
|
123
|
+
/** Hide the "Download" button(s) for the spec. */
|
|
124
|
+
hideDownloadButtons?: boolean;
|
|
125
|
+
/** Override the URL used by the download button. */
|
|
126
|
+
downloadDefinitionUrl?: string;
|
|
127
|
+
/** Hide the API host from the docs. */
|
|
128
|
+
hideHostname?: boolean;
|
|
129
|
+
/** Hide the loading spinner. */
|
|
130
|
+
hideLoading?: boolean;
|
|
131
|
+
/** Hide the request payload sample tab. */
|
|
132
|
+
hideRequestPayloadSample?: boolean;
|
|
133
|
+
/** Hide the `pattern` shown for string schemas. */
|
|
134
|
+
hideSchemaPattern?: boolean;
|
|
135
|
+
/** Hide schema title captions. */
|
|
136
|
+
hideSchemaTitles?: boolean;
|
|
137
|
+
/** Hide the entire Security section. */
|
|
138
|
+
hideSecuritySection?: boolean;
|
|
139
|
+
/** Hide the single-sample tab when there is only one request sample. */
|
|
140
|
+
hideSingleRequestSampleTab?: boolean;
|
|
141
|
+
/** Largest number of enum values to show before collapsing. */
|
|
142
|
+
maxDisplayedEnumValues?: number;
|
|
143
|
+
/** Collapse sidebar items on selection (single-expanded menu behaviour). */
|
|
144
|
+
menuToggle?: boolean;
|
|
145
|
+
/** Use the browser's native scrollbars instead of custom ones. */
|
|
146
|
+
nativeScrollbars?: boolean;
|
|
147
|
+
/** Show only required fields in request samples. */
|
|
148
|
+
onlyRequiredInSamples?: boolean;
|
|
149
|
+
/** Render the path in the middle panel instead of the right one. */
|
|
150
|
+
pathInMiddlePanel?: boolean;
|
|
151
|
+
/** Index of the request sample shown first. */
|
|
152
|
+
payloadSampleIdx?: number;
|
|
153
|
+
/** Sort required properties before optional ones. */
|
|
154
|
+
requiredPropsFirst?: boolean;
|
|
155
|
+
/** Pixels of fixed offset for in-page anchor scrolling; a number or selector. */
|
|
156
|
+
scrollYOffset?: number | string;
|
|
157
|
+
/** Show vendor `x-` extensions; `true`/`false` or an allowlist of names. */
|
|
158
|
+
showExtensions?: boolean | string[];
|
|
159
|
+
/** Show object schema examples. */
|
|
160
|
+
showObjectSchemaExamples?: boolean;
|
|
161
|
+
/** Show the HTTP verb badge for webhooks. */
|
|
162
|
+
showWebhookVerb?: boolean;
|
|
163
|
+
/** Use a simple `oneOf` type label instead of an expandable selector. */
|
|
164
|
+
simpleOneOfTypeLabel?: boolean;
|
|
165
|
+
/** Sort enum values alphabetically. */
|
|
166
|
+
sortEnumValuesAlphabetically?: boolean;
|
|
167
|
+
/** Sort operations alphabetically. */
|
|
168
|
+
sortOperationsAlphabetically?: boolean;
|
|
169
|
+
/** Sort schema properties alphabetically. */
|
|
170
|
+
sortPropsAlphabetically?: boolean;
|
|
171
|
+
/** Sort tags alphabetically. */
|
|
172
|
+
sortTagsAlphabetically?: boolean;
|
|
173
|
+
/** Nested Redoc theme object (colors, typography, sidebar, etc.). */
|
|
174
|
+
theme?: {
|
|
175
|
+
[key: string]: ScalarJsonValue | undefined;
|
|
176
|
+
};
|
|
177
|
+
}
|
|
95
178
|
/**
|
|
96
179
|
* Override CDN URLs and pin Subresource Integrity (SRI) hashes for the docs
|
|
97
180
|
* UI assets.
|
|
@@ -134,6 +217,15 @@ export interface DocsAssetOptions {
|
|
|
134
217
|
* @since 0.37.0
|
|
135
218
|
*/
|
|
136
219
|
swaggerUiBundleIntegrity?: string;
|
|
220
|
+
/** Override the Redoc standalone bundle URL (useful for self-hosting). */
|
|
221
|
+
redocScriptUrl?: string;
|
|
222
|
+
/**
|
|
223
|
+
* SRI hash for {@link redocScriptUrl}. One or more space-separated
|
|
224
|
+
* `sha256-`/`sha384-`/`sha512-` base64 digests. Invalid values throw.
|
|
225
|
+
*
|
|
226
|
+
* @since 0.39.0
|
|
227
|
+
*/
|
|
228
|
+
redocScriptIntegrity?: string;
|
|
137
229
|
/**
|
|
138
230
|
* `crossorigin` attribute value emitted alongside any pinned integrity
|
|
139
231
|
* hash. SRI on a cross-origin asset requires CORS, so this defaults to
|
|
@@ -163,6 +255,15 @@ export interface ScalarHtmlOptions extends DocsOptions {
|
|
|
163
255
|
/** Forwarded to the Scalar `<script id="api-reference">` tag. */
|
|
164
256
|
configuration?: ScalarReferenceConfiguration;
|
|
165
257
|
}
|
|
258
|
+
/**
|
|
259
|
+
* Options for {@link redocHtml}; adds Redoc-specific UI configuration.
|
|
260
|
+
*
|
|
261
|
+
* @since 0.39.0
|
|
262
|
+
*/
|
|
263
|
+
export interface RedocHtmlOptions extends DocsOptions {
|
|
264
|
+
/** Forwarded as the options object to `Redoc.init(specUrl, configuration, element)`. */
|
|
265
|
+
configuration?: RedocConfiguration;
|
|
266
|
+
}
|
|
166
267
|
/** Options for {@link docsContentSecurityPolicy}. */
|
|
167
268
|
export interface DocsContentSecurityPolicyOptions {
|
|
168
269
|
/** Extra origins to allow for `script-src` / `style-src` (defaults to jsDelivr). */
|
|
@@ -171,6 +272,16 @@ export interface DocsContentSecurityPolicyOptions {
|
|
|
171
272
|
scriptNonce?: string;
|
|
172
273
|
/** When `false`, omits `'unsafe-inline'` from `style-src`. Defaults to `true`. */
|
|
173
274
|
allowInlineStyles?: boolean;
|
|
275
|
+
/**
|
|
276
|
+
* When `true`, append `worker-src 'self' blob:` so a UI that constructs a
|
|
277
|
+
* Web Worker from a `blob:` URL can run under this CSP. Redoc does this for
|
|
278
|
+
* its search index, so the auto-mounted docs route enables it automatically
|
|
279
|
+
* for `ui: "redoc"`; Scalar and Swagger UI do not need it. Defaults to
|
|
280
|
+
* `false`, leaving the policy unchanged.
|
|
281
|
+
*
|
|
282
|
+
* @since 0.39.0
|
|
283
|
+
*/
|
|
284
|
+
allowBlobWorkers?: boolean;
|
|
174
285
|
}
|
|
175
286
|
/** Options for {@link htmlResponse}. */
|
|
176
287
|
export interface HtmlResponseOptions extends DocsContentSecurityPolicyOptions {
|
|
@@ -190,6 +301,21 @@ export declare function scalarHtml(opts: ScalarHtmlOptions): string;
|
|
|
190
301
|
* {@link scalarHtml} but emits the classic Swagger UI bundle.
|
|
191
302
|
*/
|
|
192
303
|
export declare function swaggerUiHtml(opts: DocsOptions): string;
|
|
304
|
+
/**
|
|
305
|
+
* Render a Redoc HTML page that loads `opts.specUrl`. Same usage as
|
|
306
|
+
* {@link scalarHtml} / {@link swaggerUiHtml} but emits the Redoc standalone
|
|
307
|
+
* bundle and forwards {@link RedocHtmlOptions.configuration} to `Redoc.init`.
|
|
308
|
+
*
|
|
309
|
+
* Redoc constructs a Web Worker from a `blob:` URL for its search index, so
|
|
310
|
+
* serve this page with a CSP that allows `worker-src 'self' blob:` — pass
|
|
311
|
+
* `allowBlobWorkers: true` to {@link docsContentSecurityPolicy} /
|
|
312
|
+
* {@link htmlResponse} (the `docs: { ui: "redoc" }` auto-mount does this for
|
|
313
|
+
* you). The spec URL and configuration are embedded with `<`-escaped JSON so
|
|
314
|
+
* an attacker-controlled value cannot break out of the inline `<script>`.
|
|
315
|
+
*
|
|
316
|
+
* @since 0.39.0
|
|
317
|
+
*/
|
|
318
|
+
export declare function redocHtml(opts: RedocHtmlOptions): string;
|
|
193
319
|
/**
|
|
194
320
|
* Build a Content-Security-Policy string compatible with the docs HTML
|
|
195
321
|
* produced by {@link scalarHtml} / {@link swaggerUiHtml}.
|
package/dist/docs.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Built-in API documentation handlers.
|
|
3
3
|
*
|
|
4
|
-
* Scalar
|
|
5
|
-
* at `specUrl` and fetches UI assets from a CDN by default. No build
|
|
6
|
-
* no extra deps.
|
|
4
|
+
* Scalar, Swagger UI, and Redoc helpers serve a single HTML page that loads
|
|
5
|
+
* the spec at `specUrl` and fetches UI assets from a CDN by default. No build
|
|
6
|
+
* step, no extra deps.
|
|
7
7
|
*
|
|
8
8
|
* (You can self-host the assets if your CSP forbids CDNs.)
|
|
9
9
|
*/
|
|
@@ -90,6 +90,39 @@ export function swaggerUiHtml(opts) {
|
|
|
90
90
|
<script${nonce}>window.onload=()=>SwaggerUIBundle({url:"${url}",dom_id:"#swagger"});</script>
|
|
91
91
|
</body></html>`;
|
|
92
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* Render a Redoc HTML page that loads `opts.specUrl`. Same usage as
|
|
95
|
+
* {@link scalarHtml} / {@link swaggerUiHtml} but emits the Redoc standalone
|
|
96
|
+
* bundle and forwards {@link RedocHtmlOptions.configuration} to `Redoc.init`.
|
|
97
|
+
*
|
|
98
|
+
* Redoc constructs a Web Worker from a `blob:` URL for its search index, so
|
|
99
|
+
* serve this page with a CSP that allows `worker-src 'self' blob:` — pass
|
|
100
|
+
* `allowBlobWorkers: true` to {@link docsContentSecurityPolicy} /
|
|
101
|
+
* {@link htmlResponse} (the `docs: { ui: "redoc" }` auto-mount does this for
|
|
102
|
+
* you). The spec URL and configuration are embedded with `<`-escaped JSON so
|
|
103
|
+
* an attacker-controlled value cannot break out of the inline `<script>`.
|
|
104
|
+
*
|
|
105
|
+
* @since 0.39.0
|
|
106
|
+
*/
|
|
107
|
+
export function redocHtml(opts) {
|
|
108
|
+
const title = escapeHtml(opts.title ?? "API Docs");
|
|
109
|
+
const scriptUrl = escapeHtml(opts.assets?.redocScriptUrl ??
|
|
110
|
+
`${JSDELIVR_ORIGIN}/npm/redoc/bundles/redoc.standalone.js`);
|
|
111
|
+
const scriptSri = integrityAttr(opts.assets?.redocScriptIntegrity, opts.assets?.crossOrigin);
|
|
112
|
+
const nonce = nonceAttr(opts.scriptNonce);
|
|
113
|
+
const specArg = jsonForScript(opts.specUrl);
|
|
114
|
+
const optionsArg = jsonForScript(opts.configuration ?? {});
|
|
115
|
+
return `<!doctype html>
|
|
116
|
+
<html><head>
|
|
117
|
+
<meta charset="utf-8" />
|
|
118
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
119
|
+
<title>${title}</title>
|
|
120
|
+
</head><body>
|
|
121
|
+
<div id="redoc"></div>
|
|
122
|
+
<script src="${scriptUrl}"${scriptSri}${nonce}></script>
|
|
123
|
+
<script${nonce}>Redoc.init(${specArg},${optionsArg},document.getElementById("redoc"));</script>
|
|
124
|
+
</body></html>`;
|
|
125
|
+
}
|
|
93
126
|
/**
|
|
94
127
|
* Build a Content-Security-Policy string compatible with the docs HTML
|
|
95
128
|
* produced by {@link scalarHtml} / {@link swaggerUiHtml}.
|
|
@@ -107,13 +140,19 @@ export function docsContentSecurityPolicy(opts = {}) {
|
|
|
107
140
|
const styleSrc = ["'self'", ...assetOrigins];
|
|
108
141
|
if (opts.allowInlineStyles !== false)
|
|
109
142
|
styleSrc.push("'unsafe-inline'");
|
|
110
|
-
|
|
143
|
+
const directives = [
|
|
111
144
|
"default-src 'self'",
|
|
112
145
|
`script-src ${scriptSrc.join(" ")}`,
|
|
113
146
|
`style-src ${styleSrc.join(" ")}`,
|
|
114
147
|
"img-src 'self' data: https:",
|
|
115
148
|
"connect-src 'self'",
|
|
116
|
-
]
|
|
149
|
+
];
|
|
150
|
+
// Redoc spawns a Web Worker from a `blob:` URL; without an explicit
|
|
151
|
+
// worker-src the browser falls back to script-src, which forbids `blob:`
|
|
152
|
+
// and breaks the page. Scope this relaxation to callers that opt in.
|
|
153
|
+
if (opts.allowBlobWorkers)
|
|
154
|
+
directives.push("worker-src 'self' blob:");
|
|
155
|
+
return directives.join("; ");
|
|
117
156
|
}
|
|
118
157
|
/**
|
|
119
158
|
* Wrap a docs HTML string in a `Response` with safe defaults:
|
|
@@ -138,6 +177,16 @@ export function htmlResponse(html, opts = {}) {
|
|
|
138
177
|
function escapeHtml(s) {
|
|
139
178
|
return s.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]);
|
|
140
179
|
}
|
|
180
|
+
/**
|
|
181
|
+
* Serialize a value as a JSON literal safe to embed inside an inline
|
|
182
|
+
* `<script>` element. `JSON.stringify` already escapes quotes and backslashes;
|
|
183
|
+
* we additionally escape `<` (so `</script>`, `<!--`, and `<script` can't end
|
|
184
|
+
* or reopen the script) and the U+2028/U+2029 line separators that are illegal
|
|
185
|
+
* in older JS string literals. The result is valid JSON *and* valid JS.
|
|
186
|
+
*/
|
|
187
|
+
function jsonForScript(value) {
|
|
188
|
+
return JSON.stringify(value).replace(/[<\u2028\u2029]/g, (c) => ({ "<": "\\u003c", "\u2028": "\\u2028", "\u2029": "\\u2029" })[c]);
|
|
189
|
+
}
|
|
141
190
|
function scalarConfigurationAttr(specUrl, configuration) {
|
|
142
191
|
if (!configuration)
|
|
143
192
|
return "";
|
package/dist/index.d.ts
CHANGED
|
@@ -72,7 +72,7 @@ export type { RequestIdOptions, SecureHeadersOptions, CspDirectivesOptions, Cors
|
|
|
72
72
|
export type { BearerAuthOptions, BearerAuthVerifyHook } from "./middleware.js";
|
|
73
73
|
export { createLogger, noopLogger, DEFAULT_REDACT_KEYS } from "./logger.js";
|
|
74
74
|
export type { Logger, LogLevel, ConsoleLoggerOptions, LoggerRedactionOptions, } from "./logger.js";
|
|
75
|
-
export type { ScalarJsonPrimitive, ScalarJsonValue, ScalarReferenceConfiguration, ScalarTheme, DocsAssetOptions, } from "./docs.js";
|
|
75
|
+
export type { ScalarJsonPrimitive, ScalarJsonValue, ScalarReferenceConfiguration, ScalarTheme, RedocConfiguration, RedocHtmlOptions, DocsAssetOptions, } from "./docs.js";
|
|
76
76
|
export { formatStartupBanner, printStartupBanner } from "./banner.js";
|
|
77
77
|
export type { StartupBannerLink, StartupBannerOptions } from "./banner.js";
|
|
78
78
|
export { sseStream, sseResponse, ndjsonStream, ndjsonResponse, } from "./streaming.js";
|
package/dist/metrics.js
CHANGED
|
@@ -434,19 +434,22 @@ export function httpMetrics(opts) {
|
|
|
434
434
|
return "/";
|
|
435
435
|
}
|
|
436
436
|
})();
|
|
437
|
-
// Always balance the in-flight gauge, even for excluded paths whose
|
|
438
|
-
// onRequest already incremented it.
|
|
439
437
|
const started = START_TIMES.get(ctx.request);
|
|
440
438
|
START_TIMES.delete(ctx.request);
|
|
439
|
+
// Only balance in-flight and record metrics when onRequest was actually
|
|
440
|
+
// called for this Request. Framework-synthesised contexts such as an OPTIONS
|
|
441
|
+
// preflight handled via preflightHooks (no registered OPTIONS route) invoke
|
|
442
|
+
// onSend without a prior groupHook onRequest, so started is undefined and
|
|
443
|
+
// decrementing the gauge would drive it negative.
|
|
444
|
+
if (started === undefined)
|
|
445
|
+
return;
|
|
441
446
|
inFlight.dec();
|
|
442
447
|
if (opts.exclude && opts.exclude(path))
|
|
443
448
|
return;
|
|
444
449
|
const method = ctx.request.method.toUpperCase();
|
|
445
450
|
const route = routeLabel(ctx);
|
|
446
451
|
requests.inc({ method, route, status: res.status });
|
|
447
|
-
|
|
448
|
-
duration.observe({ method, route }, (nowMs() - started) / 1000);
|
|
449
|
-
}
|
|
452
|
+
duration.observe({ method, route }, (nowMs() - started) / 1000);
|
|
450
453
|
},
|
|
451
454
|
};
|
|
452
455
|
}
|
package/dist/sbom.cdx.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"bomFormat": "CycloneDX",
|
|
3
3
|
"specVersion": "1.5",
|
|
4
|
-
"serialNumber": "urn:uuid:
|
|
4
|
+
"serialNumber": "urn:uuid:deb5f2f9-6f46-5dd8-9ab5-2c301ddf6df0",
|
|
5
5
|
"version": 1,
|
|
6
6
|
"metadata": {
|
|
7
|
-
"timestamp": "2026-06-
|
|
7
|
+
"timestamp": "2026-06-17T18:06:01.531Z",
|
|
8
8
|
"tools": [
|
|
9
9
|
{
|
|
10
10
|
"vendor": "DaloyJS",
|
|
11
11
|
"name": "daloy-generate-sbom",
|
|
12
|
-
"version": "0.
|
|
12
|
+
"version": "0.39.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.
|
|
22
|
+
"bom-ref": "pkg:npm/@daloyjs/core@0.39.0",
|
|
23
23
|
"name": "@daloyjs/core",
|
|
24
|
-
"version": "0.
|
|
24
|
+
"version": "0.39.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.
|
|
26
|
+
"purl": "pkg:npm/@daloyjs/core@0.39.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.
|
|
49
|
+
"tagId": "swidtag--daloyjs-core-0.39.0",
|
|
50
50
|
"name": "@daloyjs/core",
|
|
51
|
-
"version": "0.
|
|
51
|
+
"version": "0.39.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.
|
|
60
|
+
"ref": "pkg:npm/@daloyjs/core@0.39.0",
|
|
61
61
|
"dependsOn": []
|
|
62
62
|
}
|
|
63
63
|
]
|
package/dist/sbom.spdx.json
CHANGED
|
@@ -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.
|
|
6
|
-
"documentNamespace": "https://github.com/daloyjs/daloy/sbom/@daloyjs/core-0.
|
|
5
|
+
"name": "@daloyjs/core-0.39.0",
|
|
6
|
+
"documentNamespace": "https://github.com/daloyjs/daloy/sbom/@daloyjs/core-0.39.0-deb5f2f9-6f46-5dd8-9ab5-2c301ddf6df0",
|
|
7
7
|
"creationInfo": {
|
|
8
|
-
"created": "2026-06-
|
|
8
|
+
"created": "2026-06-17T18:06:01.531Z",
|
|
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.
|
|
19
|
+
"versionInfo": "0.39.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.
|
|
30
|
+
"referenceLocator": "pkg:npm/@daloyjs/core@0.39.0"
|
|
31
31
|
}
|
|
32
32
|
]
|
|
33
33
|
}
|
package/dist/tracing.js
CHANGED
|
@@ -86,8 +86,13 @@ export function otelTracing(opts) {
|
|
|
86
86
|
if (url) {
|
|
87
87
|
attrs["url.path"] = url.pathname;
|
|
88
88
|
attrs["url.scheme"] = url.protocol.replace(/:$/, "");
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
// Per the OTel HTTP semantic conventions `server.address` is the host
|
|
90
|
+
// WITHOUT the port (which belongs in `server.port`), so derive both from
|
|
91
|
+
// `url.hostname`/`url.port` rather than `url.host` (which concatenates them).
|
|
92
|
+
if (url.hostname)
|
|
93
|
+
attrs["server.address"] = url.hostname;
|
|
94
|
+
if (url.port)
|
|
95
|
+
attrs["server.port"] = Number(url.port);
|
|
91
96
|
if (url.search)
|
|
92
97
|
attrs["url.query"] = url.search.replace(/^\?/, "");
|
|
93
98
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@daloyjs/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.39.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": {
|