@daloyjs/core 0.38.0 → 0.38.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 +44 -0
- package/dist/app.js +10 -3
- package/dist/jwt.js +8 -2
- package/dist/sbom.cdx.json +9 -9
- package/dist/sbom.spdx.json +5 -5
- package/dist/session.js +4 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
[](https://github.com/daloyjs/daloy/actions/workflows/release.yml)
|
|
16
16
|
[](https://github.com/daloyjs/daloy/actions/workflows/zizmor.yml)
|
|
17
17
|
[](https://github.com/daloyjs/daloy/commits/main)
|
|
18
|
+
[](https://www.npmjs.com/package/@daloyjs/core)
|
|
18
19
|
[](https://jsr.io/@daloyjs/daloy)
|
|
19
20
|
[](https://www.bestpractices.dev/projects/13058)
|
|
20
21
|
[](https://securityscorecards.dev/viewer/?uri=github.com/daloyjs/daloy)
|
|
@@ -29,6 +30,19 @@ DaloyJS is maintained in the GitHub organization at <https://github.com/daloyjs>
|
|
|
29
30
|
|
|
30
31
|
---
|
|
31
32
|
|
|
33
|
+
## Built for the vibe-coding era
|
|
34
|
+
|
|
35
|
+
Most backend code is now written with AI. Non-developers describe an app and ship whatever the model produces; engineers let agents install dependencies, run tests, and open PRs. The code works on the happy path and gets deployed within the hour — usually with no body limits, skippable input validation, admin routes left mounted on the public app, and an outbound `fetch` that will happily call cloud-metadata endpoints. At the same time, the dependency tree itself has become the attack surface: self-replicating npm worms, malicious `postinstall` scripts, CI cache-poisoning, and **slopsquatting** — where an attacker pre-registers a package name an AI assistant is likely to hallucinate.
|
|
36
|
+
|
|
37
|
+
DaloyJS is built for exactly this moment, from two directions at once:
|
|
38
|
+
|
|
39
|
+
- **A secure-by-default runtime.** Body limits, prototype-pollution-safe JSON, path-traversal rejection, request timeouts, header-injection guards, real `405`s, and RFC 9457 problem+json with prod-mode redaction are on in the constructor — the dangerous things are off when nobody remembered to turn them off. The app also *refuses to boot* on unsafe configuration (wildcard CORS with credentials, weak session secrets, a state-changing session route with no CSRF protection, unconfigured `X-Forwarded-*` in production).
|
|
40
|
+
- **A hardened supply chain.** `@daloyjs/core` ships **zero runtime dependencies**, is published with npm provenance and CycloneDX + SPDX SBOMs, and the pnpm posture (`ignore-scripts`, a 24-hour release-age cooldown, source-verified lockfiles) plus the CI `verify:*` gates shrink the blast radius of the campaigns making headlines.
|
|
41
|
+
|
|
42
|
+
The point is that none of this costs you developer experience or portability: you keep contract-first DX in the league of ts-rest, Elysia, and FastAPI, and Hono-grade portability across Node, Bun, Deno, Workers, Vercel, Fastly, and Lambda. The secure path is simply the path of least resistance. See [Vibe Coding Security: what DaloyJS already blocks](https://daloyjs.dev/blog/vibe-coding-security-what-daloyjs-already-blocks) and the [security docs](https://daloyjs.dev/docs/security).
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
32
46
|
DaloyJS exists to be the framework you'd build if you took the best ideas from each modern stack:
|
|
33
47
|
|
|
34
48
|
| You want | Today's best-of | What DaloyJS gives you |
|
|
@@ -38,6 +52,7 @@ DaloyJS exists to be the framework you'd build if you took the best ideas from e
|
|
|
38
52
|
| Mature **Swagger / docs / ops** in Node | [Fastify](https://fastify.dev/docs/latest/Reference/) | Encapsulated plugins, structured logger, graceful shutdown, request ids, and lifecycle hooks — all first-party. |
|
|
39
53
|
| Modern **TS-first DX**, Bun acceptable | [Elysia](https://elysiajs.com/at-glance.html) | End-to-end typed handlers, typed context, and a typed in-process client — no codegen step required. |
|
|
40
54
|
| Best-in-class **typed client codegen** for any consumer | [Hey API](https://heyapi.dev/openapi-ts/get-started) | One `pnpm gen` command emits a fully-typed fetch SDK from your live OpenAPI spec. |
|
|
55
|
+
| **Contract-first typed client, no codegen** | [ts-rest](https://ts-rest.com/) | Your route definition *is* the contract: an in-process typed client with zero codegen, plus OpenAPI 3.1 + a Hey API SDK for consumers that can't import your types. |
|
|
41
56
|
| Opinionated **DI / module architecture** for large teams | [NestJS](https://docs.nestjs.com/) | Plugin encapsulation, `register()` prefixes, and `defineDependency()` typed-DI with per-request dedup — no decorators. |
|
|
42
57
|
| Minimalist **async middleware cascade** | [Koa](https://koajs.com/) | Koa-style `Context` on a web-standard core, with validation, OpenAPI, errors, and security headers in-box. |
|
|
43
58
|
| **Services + real-time** API framework | [FeathersJS](https://feathersjs.com/) | First-party `app.ws()` with CSWSH refuse-to-boot guards, plus SSE / NDJSON streaming over explicit OpenAPI routes. |
|
|
@@ -61,6 +76,7 @@ Each existing stack is excellent at one thing and forces tradeoffs everywhere el
|
|
|
61
76
|
- Fastify has the best Node ops story but is Node-only and validation/types/docs are not unified.
|
|
62
77
|
- FastAPI has the best docs ergonomics — but it's Python.
|
|
63
78
|
- Hey API gives you the best typed client — but you still need a server that produces a clean spec.
|
|
79
|
+
- ts-rest gives lovely end-to-end types from a shared contract — but it rides on top of another server (Express/Fastify/Nest/Next), its safety is TypeScript-only, and OpenAPI and security are bring-your-own.
|
|
64
80
|
- npm leaves supply-chain protection up to you.
|
|
65
81
|
|
|
66
82
|
DaloyJS combines the wins:
|
|
@@ -339,6 +355,33 @@ deployment.
|
|
|
339
355
|
|
|
340
356
|
---
|
|
341
357
|
|
|
358
|
+
## Authentication, OAuth2 & OpenID Connect
|
|
359
|
+
|
|
360
|
+
DaloyJS is a **resource server** (and a toolkit for building a relying party),
|
|
361
|
+
**not** an identity provider. Like Hono, Express, Fastify, or ASP.NET Core, it
|
|
362
|
+
*verifies* and *enforces* tokens on each request — it does **not** ship a login
|
|
363
|
+
UI, a user database, or an OAuth2 authorization server. It is **not** an
|
|
364
|
+
"IdentityServer": it cannot, on its own, do what Duende IdentityServer,
|
|
365
|
+
Keycloak, or Auth0 do (run login pages, manage clients/consent, mint tokens).
|
|
366
|
+
|
|
367
|
+
To add login you bring an **OpenID Connect provider**. It does not have to be
|
|
368
|
+
Auth0/Okta/Clerk specifically — any standards-compliant IdP works, including
|
|
369
|
+
managed (Auth0, Okta, Clerk, Microsoft Entra ID, AWS Cognito) and **self-hosted
|
|
370
|
+
open source** (Keycloak, Zitadel, Ory, Authentik, Logto, SuperTokens, Dex).
|
|
371
|
+
Don't build your own authorization server — verify tokens from a vetted one.
|
|
372
|
+
|
|
373
|
+
- **API as a resource server (default):** verify JWTs with `jwk()` against the
|
|
374
|
+
provider's JWKS (asymmetric-only algorithm allowlist, `issuer`/`audience`
|
|
375
|
+
enforced), then authorize per route with `requireScopes()`.
|
|
376
|
+
- **Browser app:** use the back-end-for-frontend (BFF) pattern — run the
|
|
377
|
+
authorization-code + PKCE flow server-side, keep tokens in a `session()`
|
|
378
|
+
cookie (never in JavaScript), and protect mutations with `csrf()`.
|
|
379
|
+
|
|
380
|
+
Read [Auth architecture: where DaloyJS fits in OAuth2 & OpenID Connect](https://daloyjs.dev/docs/auth/architecture)
|
|
381
|
+
for the full picture, plus the per-provider guides under [`/docs/auth`](https://daloyjs.dev/docs/auth).
|
|
382
|
+
|
|
383
|
+
---
|
|
384
|
+
|
|
342
385
|
## Performance
|
|
343
386
|
|
|
344
387
|
```text
|
|
@@ -429,6 +472,7 @@ The core only ever sees `Request → Response`. Adapters live at the edge.
|
|
|
429
472
|
- Hono — portable web-standard router: <https://hono.dev/docs/>
|
|
430
473
|
- Elysia — TS-first DX & typed context: <https://elysiajs.com/at-glance.html>
|
|
431
474
|
- Fastify — production Node web framework: <https://fastify.dev/docs/latest/Reference/>
|
|
475
|
+
- ts-rest — contract-first, RPC-like client/server over REST: <https://ts-rest.com/>
|
|
432
476
|
- pnpm — strict, secure, content-addressable package manager: <https://pnpm.io/motivation>
|
|
433
477
|
- Standard Schema — universal validator interface: <https://github.com/standard-schema/standard-schema>
|
|
434
478
|
- RFC 9457 — Problem Details for HTTP APIs: <https://www.rfc-editor.org/rfc/rfc9457>
|
package/dist/app.js
CHANGED
|
@@ -351,7 +351,8 @@ export class App {
|
|
|
351
351
|
"If you really need this in production, also pass " +
|
|
352
352
|
"acknowledgeInsecureDefaults: true to confirm. Prefer per-feature opt-outs " +
|
|
353
353
|
"(secureHeaders: false, corsCrossOriginGuard: false, crashOnUnhandledRejection: false, " +
|
|
354
|
-
"trustProxy: false, csrf: \"off\") instead."
|
|
354
|
+
"trustProxy: false, csrf: \"off\") instead. " +
|
|
355
|
+
"See https://daloyjs.dev/docs/security/secure-defaults-enforcement.");
|
|
355
356
|
}
|
|
356
357
|
if (!insecureDefaultsLoggedThisProcess) {
|
|
357
358
|
insecureDefaultsLoggedThisProcess = true;
|
|
@@ -666,8 +667,11 @@ export class App {
|
|
|
666
667
|
return;
|
|
667
668
|
const err = new Error(`session() is registered in the hook chain for a state-changing route ` +
|
|
668
669
|
`(${stateChanging.method} ${stateChanging.path}) but no csrf() hook is installed. ` +
|
|
670
|
+
`Without CSRF protection a browser can be tricked into making authenticated ` +
|
|
671
|
+
`state-changing requests cross-site. ` +
|
|
669
672
|
`Register csrf() via app.use(csrf({ strategy: "fetch-metadata", allowedOrigins: [...] })), ` +
|
|
670
|
-
`or pass app({ csrf: "off" }) to acknowledge that this app is not browser-facing
|
|
673
|
+
`or pass app({ csrf: "off" }) to acknowledge that this app is not browser-facing. ` +
|
|
674
|
+
`See https://daloyjs.dev/docs/security/boot-guards.`);
|
|
671
675
|
this.bootGuard.error = err;
|
|
672
676
|
throw err;
|
|
673
677
|
}
|
|
@@ -717,9 +721,12 @@ export class App {
|
|
|
717
721
|
this.log.warn({ event: "trust-proxy.unconfigured", header: found }, `Request carried ${found} but app({ trustProxy }) is unset; refusing to honour spoofable proxy headers.`);
|
|
718
722
|
}
|
|
719
723
|
throw new InternalError(`Refusing to dispatch request: ${found} header is present but app({ trustProxy }) is unconfigured. ` +
|
|
724
|
+
`Honouring a spoofable forwarded header would let a client forge its source IP for the rate ` +
|
|
725
|
+
`limiter, audit log, and request-id propagation. ` +
|
|
720
726
|
`Pass app({ trustProxy: true }) when running behind a trusted reverse proxy, ` +
|
|
721
727
|
`or app({ trustProxy: false }) to ignore forwarded headers, ` +
|
|
722
|
-
`or app({ secureDefaults: false }) to disable this guard
|
|
728
|
+
`or app({ secureDefaults: false }) to disable this guard. ` +
|
|
729
|
+
`See https://daloyjs.dev/docs/security/boot-guards.`);
|
|
723
730
|
}
|
|
724
731
|
/**
|
|
725
732
|
* Resolve the {@link AppOptions.docs} option and, when enabled, register
|
package/dist/jwt.js
CHANGED
|
@@ -236,7 +236,10 @@ export function createJwtSigner(opts) {
|
|
|
236
236
|
}
|
|
237
237
|
const { alg } = opts;
|
|
238
238
|
if (alg === "none") {
|
|
239
|
-
throw new JwtError("alg_none_refused", 'jwt(): alg "none" is refused
|
|
239
|
+
throw new JwtError("alg_none_refused", 'jwt(): alg "none" is refused — it disables signature verification, so anyone could forge a ' +
|
|
240
|
+
'token by setting the header alg to "none" (the classic JWT signature-stripping / algorithm-' +
|
|
241
|
+
"confusion attack). Choose a real signing algorithm such as HS256 (shared secret) or " +
|
|
242
|
+
"RS256 / ES256 (key pair). See https://daloyjs.dev/docs/security/secure-defaults-enforcement.");
|
|
240
243
|
}
|
|
241
244
|
if (!ALL_ALGS.has(alg)) {
|
|
242
245
|
throw new JwtError("invalid_alg", `jwt(): unknown algorithm "${String(alg)}". Allowed: ${[...ALL_ALGS].sort().join(", ")}.`);
|
|
@@ -335,7 +338,10 @@ export function createJwtVerifier(opts) {
|
|
|
335
338
|
const allow = new Set();
|
|
336
339
|
for (const alg of opts.algorithms) {
|
|
337
340
|
if (alg === "none") {
|
|
338
|
-
throw new JwtError("alg_none_refused", 'jwt(): alg "none" cannot appear in the allowlist
|
|
341
|
+
throw new JwtError("alg_none_refused", 'jwt(): alg "none" cannot appear in the algorithms allowlist — it disables signature ' +
|
|
342
|
+
"verification and would let any caller forge a token. Remove it and list only real " +
|
|
343
|
+
"algorithms such as HS256, RS256, or ES256. " +
|
|
344
|
+
"See https://daloyjs.dev/docs/security/secure-defaults-enforcement.");
|
|
339
345
|
}
|
|
340
346
|
if (!ALL_ALGS.has(alg)) {
|
|
341
347
|
throw new JwtError("invalid_alg", `jwt(): unknown algorithm "${String(alg)}" in allowlist.`);
|
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:5b67e701-c198-5112-9457-127897c8cfb6",
|
|
5
5
|
"version": 1,
|
|
6
6
|
"metadata": {
|
|
7
|
-
"timestamp": "2026-06-
|
|
7
|
+
"timestamp": "2026-06-16T14:45:02.537Z",
|
|
8
8
|
"tools": [
|
|
9
9
|
{
|
|
10
10
|
"vendor": "DaloyJS",
|
|
11
11
|
"name": "daloy-generate-sbom",
|
|
12
|
-
"version": "0.38.
|
|
12
|
+
"version": "0.38.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@0.38.
|
|
22
|
+
"bom-ref": "pkg:npm/@daloyjs/core@0.38.2",
|
|
23
23
|
"name": "@daloyjs/core",
|
|
24
|
-
"version": "0.38.
|
|
24
|
+
"version": "0.38.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@0.38.
|
|
26
|
+
"purl": "pkg:npm/@daloyjs/core@0.38.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-0.38.
|
|
49
|
+
"tagId": "swidtag--daloyjs-core-0.38.2",
|
|
50
50
|
"name": "@daloyjs/core",
|
|
51
|
-
"version": "0.38.
|
|
51
|
+
"version": "0.38.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@0.38.
|
|
60
|
+
"ref": "pkg:npm/@daloyjs/core@0.38.2",
|
|
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.38.
|
|
6
|
-
"documentNamespace": "https://github.com/daloyjs/daloy/sbom/@daloyjs/core-0.38.
|
|
5
|
+
"name": "@daloyjs/core-0.38.2",
|
|
6
|
+
"documentNamespace": "https://github.com/daloyjs/daloy/sbom/@daloyjs/core-0.38.2-5b67e701-c198-5112-9457-127897c8cfb6",
|
|
7
7
|
"creationInfo": {
|
|
8
|
-
"created": "2026-06-
|
|
8
|
+
"created": "2026-06-16T14:45:02.537Z",
|
|
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.38.
|
|
19
|
+
"versionInfo": "0.38.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@0.38.
|
|
30
|
+
"referenceLocator": "pkg:npm/@daloyjs/core@0.38.2"
|
|
31
31
|
}
|
|
32
32
|
]
|
|
33
33
|
}
|
package/dist/session.js
CHANGED
|
@@ -57,7 +57,10 @@ function bytesToBase64Url(bytes) {
|
|
|
57
57
|
}
|
|
58
58
|
function makeSigner(secret) {
|
|
59
59
|
if (typeof secret !== "string" || secret.length < 16) {
|
|
60
|
-
throw new Error("session(): each secret must be a string of at least 16 characters
|
|
60
|
+
throw new Error("session(): each secret must be a string of at least 16 characters — it is the HMAC key " +
|
|
61
|
+
"that signs every session cookie, so a short or guessable value lets an attacker forge sessions. " +
|
|
62
|
+
"Generate one with `openssl rand -base64 32` and load it from an env var or secret manager " +
|
|
63
|
+
"(never hard-code or commit it). See https://daloyjs.dev/docs/security/session.");
|
|
61
64
|
}
|
|
62
65
|
let keyPromise = null;
|
|
63
66
|
const getKey = () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@daloyjs/core",
|
|
3
|
-
"version": "0.38.
|
|
3
|
+
"version": "0.38.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": {
|