@daloyjs/core 1.0.0-rc.2 → 1.0.0-rc.3

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
@@ -28,6 +28,12 @@ Disclosure](https://img.shields.io/badge/Security-Responsible%20Disclosure-yello
28
28
 
29
29
  DaloyJS is maintained in the GitHub organization at <https://github.com/daloyjs>; the canonical framework repository is <https://github.com/daloyjs/daloy>.
30
30
 
31
+ ## Partners
32
+
33
+ <a href="https://snyk.io">
34
+ <img src="https://github.com/user-attachments/assets/da58db43-67cc-45d4-ade5-bdaa7b041465" alt="Snyk's Secure Developer Program" width="160">
35
+ </a>
36
+
31
37
  ---
32
38
 
33
39
  ## Built for the vibe-coding era
@@ -509,7 +515,7 @@ The core only ever sees `Request → Response`. Adapters live at the edge.
509
515
 
510
516
  ## Status
511
517
 
512
- DaloyJS is at **`1.0.0-rc.2`**, a security-hardening release candidate. Because the framework has no external users yet, this RC ships a few intentional breaking changes (see the [CHANGELOG](CHANGELOG.md)) to get the secure-by-default posture right before GA rather than deferring them; the generated OpenAPI contract is unchanged. From `1.0.0` GA onward, breaking changes follow SemVer with deprecations getting at least one minor cycle. The framework is already in use for production trials.
518
+ DaloyJS is at **`1.0.0-rc.3`**, a security-hardening release candidate. Because the framework has no external users yet, this RC makes a few intentional changes (see the [CHANGELOG](CHANGELOG.md)) to get the secure-by-default posture right before GA rather than deferring them; the generated OpenAPI contract is unchanged. From `1.0.0` GA onward, the API follows SemVer with deprecations getting at least one minor cycle. The framework is already in use for production trials.
513
519
 
514
520
  **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.
515
521
 
@@ -1,13 +1,15 @@
1
1
  /**
2
2
  * Vercel / web-standard handler.
3
3
  *
4
- * Vercel now recommends the Node.js runtime over Edge for new functions. The
5
- * runtime is web-standard, but the export shape differs by integration: Node
6
- * `/api` functions use a default `{ fetch }` object, while Edge functions use a
7
- * bare function export. If you are hosting a DaloyJS app inside an existing
8
- * Next.js app, App Router route handlers use named method exports.
4
+ * Vercel recommends the Node.js runtime for new functions (it runs on Fluid
5
+ * Compute with full Node APIs) and has deprecated standalone Edge Functions.
6
+ * The runtime is web-standard, but the export shape differs by integration:
7
+ * Node `/api` functions use a default `{ fetch }` object, App Router route
8
+ * handlers use named method exports, and the deprecated Edge runtime expects a
9
+ * bare function export — {@link toWebHandler} — plus `export const runtime =
10
+ * "edge"`.
9
11
  *
10
- * // Vercel Functions (`api/[...path].ts`)
12
+ * // Vercel Functions (`api/[...path].ts`) — recommended
11
13
  * import { toFetchHandler } from "@daloyjs/core/vercel";
12
14
  * export default toFetchHandler(app);
13
15
  *
@@ -15,8 +17,6 @@
15
17
  * import { toRouteHandlers } from "@daloyjs/core/vercel";
16
18
  * export const { GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD } =
17
19
  * toRouteHandlers(app);
18
- *
19
- * `toEdgeHandler` is kept as a backward-compatible alias of `toWebHandler`.
20
20
  */
21
21
  import type { App } from "../app.js";
22
22
  /** Web-standard handler shape used by Vercel Functions, Next.js route handlers, and middleware. */
@@ -44,8 +44,6 @@ export declare function toWebHandler(app: App): WebHandler;
44
44
  * @returns A {@link FetchHandler} object suitable as the module's `export default`.
45
45
  */
46
46
  export declare function toFetchHandler(app: App): FetchHandler;
47
- /** Backward-compatible alias for {@link toWebHandler}. */
48
- export declare const toEdgeHandler: typeof toWebHandler;
49
47
  /**
50
48
  * Build the `{ GET, POST, ... }` object expected by Next.js App Router
51
49
  * `route.ts` files when a DaloyJS app is mounted inside an existing Next app.
@@ -18,8 +18,6 @@ export function toWebHandler(app) {
18
18
  export function toFetchHandler(app) {
19
19
  return { fetch: toWebHandler(app) };
20
20
  }
21
- /** Backward-compatible alias for {@link toWebHandler}. */
22
- export const toEdgeHandler = toWebHandler;
23
21
  /**
24
22
  * Build the `{ GET, POST, ... }` object expected by Next.js App Router
25
23
  * `route.ts` files when a DaloyJS app is mounted inside an existing Next app.
package/dist/app.d.ts CHANGED
@@ -1401,6 +1401,25 @@ export declare class App<Routes extends readonly RouteDefinition<any, any, any,
1401
1401
  * });
1402
1402
  * ```
1403
1403
  *
1404
+ * ### Scoping (Fastify-style encapsulation)
1405
+ *
1406
+ * Decorations are **scoped to the app instance they are declared on**, and
1407
+ * are captured per route at registration time:
1408
+ *
1409
+ * - Calling `decorate()` on the root app makes the value visible to every
1410
+ * route, including routes inside plugins/groups registered *afterwards*
1411
+ * (app-level decorations flow inward).
1412
+ * - Calling `decorate()` on the child app passed to {@link App.register} /
1413
+ * {@link App.group} scopes the value to **that plugin's routes only** — it
1414
+ * does not leak to sibling plugins or back to the root.
1415
+ *
1416
+ * Each route binds to its scope's decorations when it is registered, so
1417
+ * **decorate before registering the routes that consume the value** (the same
1418
+ * ordering Fastify requires). Adding a decoration to a scope that already had
1419
+ * at least one is picked up by that scope's existing routes; but the first
1420
+ * decoration added to a scope *after* its routes were registered will not
1421
+ * reach them.
1422
+ *
1404
1423
  * @param key - Property name on `ctx.state`.
1405
1424
  * @param value - Value bound to that property on every request.
1406
1425
  * @param opts - Pass `{ override: true }` to replace an existing decoration (logged as a warning).
package/dist/app.js CHANGED
@@ -1433,7 +1433,22 @@ export class App {
1433
1433
  ...corsOriginAllows,
1434
1434
  ];
1435
1435
  const securityMarkers = securityMarkersFromHooks([globalHookLayer, ...sources]);
1436
- this.router.add(def.method, fullPath, { def: merged, hooks, mergedHooks, hasFinalizeHook, corsOriginAllows, fullCorsOriginAllows }, def.operationId);
1436
+ // Capture the decorations of this route's scope at registration time so the
1437
+ // dispatch hot path reads the scope-local bag rather than the root app's.
1438
+ // `this.decorations` is this scope's own bag (the root's, or the child's
1439
+ // copy created in `group()`), so plugin-local decorations never leak to
1440
+ // sibling plugins or the root. `undefined` when empty keeps the per-request
1441
+ // `Object.assign` skipped for the common no-decoration case.
1442
+ const decorations = this.decorationsCount === 0 ? undefined : this.decorations;
1443
+ this.router.add(def.method, fullPath, {
1444
+ def: merged,
1445
+ hooks,
1446
+ mergedHooks,
1447
+ hasFinalizeHook,
1448
+ corsOriginAllows,
1449
+ fullCorsOriginAllows,
1450
+ decorations,
1451
+ }, def.operationId);
1437
1452
  // `routes` is statically a readonly tuple so the typed client can infer
1438
1453
  // per-route methods; at runtime it is a growable array, so we push through
1439
1454
  // a mutable view.
@@ -1946,7 +1961,15 @@ export class App {
1946
1961
  child.corsOriginAllows = corsOriginAllowsFromHooks(child.groupHooks);
1947
1962
  child.groupTags = [...this.groupTags, ...(config.tags ?? [])];
1948
1963
  child.groupAuth = config.auth ?? this.groupAuth;
1949
- child.decorations = this.decorations;
1964
+ // Encapsulate decorations (Fastify-style): the child gets its OWN bag
1965
+ // seeded with a copy of the parent's current decorations. App-level
1966
+ // decorations therefore flow inward, while `child.decorate()` mutates only
1967
+ // this copy — it never leaks back to the parent or sideways to sibling
1968
+ // plugins (each sibling copies the parent bag at its own registration).
1969
+ // Routes snapshot this bag in `route()`; the shared reference used before
1970
+ // made decorators app-global instead of scoped.
1971
+ child.decorations = { ...this.decorations };
1972
+ child.decorationsCount = this.decorationsCount;
1950
1973
  child.installedPlugins = this.installedPlugins;
1951
1974
  child.closeHooks = this.closeHooks;
1952
1975
  child.idleConnectionCloseHooks = this.idleConnectionCloseHooks;
@@ -2032,6 +2055,25 @@ export class App {
2032
2055
  * });
2033
2056
  * ```
2034
2057
  *
2058
+ * ### Scoping (Fastify-style encapsulation)
2059
+ *
2060
+ * Decorations are **scoped to the app instance they are declared on**, and
2061
+ * are captured per route at registration time:
2062
+ *
2063
+ * - Calling `decorate()` on the root app makes the value visible to every
2064
+ * route, including routes inside plugins/groups registered *afterwards*
2065
+ * (app-level decorations flow inward).
2066
+ * - Calling `decorate()` on the child app passed to {@link App.register} /
2067
+ * {@link App.group} scopes the value to **that plugin's routes only** — it
2068
+ * does not leak to sibling plugins or back to the root.
2069
+ *
2070
+ * Each route binds to its scope's decorations when it is registered, so
2071
+ * **decorate before registering the routes that consume the value** (the same
2072
+ * ordering Fastify requires). Adding a decoration to a scope that already had
2073
+ * at least one is picked up by that scope's existing routes; but the first
2074
+ * decoration added to a scope *after* its routes were registered will not
2075
+ * reach them.
2076
+ *
2035
2077
  * @param key - Property name on `ctx.state`.
2036
2078
  * @param value - Value bound to that property on every request.
2037
2079
  * @param opts - Pass `{ override: true }` to replace an existing decoration (logged as a warning).
@@ -2466,8 +2508,13 @@ export class App {
2466
2508
  const state = ctx.state;
2467
2509
  state.requestId = requestId;
2468
2510
  state.log = log;
2469
- if (this.decorationsCount !== 0)
2470
- Object.assign(state, this.decorations);
2511
+ // Apply the decorations captured for THIS route's scope (see
2512
+ // `CompiledRoute.decorations`) rather than the root app's bag, so a
2513
+ // plugin's decorations reach only that plugin's routes. `undefined` when
2514
+ // the scope had none keeps the common case allocation-free.
2515
+ const routeDecorations = match.handler.decorations;
2516
+ if (routeDecorations !== undefined)
2517
+ Object.assign(state, routeDecorations);
2471
2518
  if (allHooks.beforeHandle !== undefined) {
2472
2519
  const beforeResult = allHooks.beforeHandle(ctx);
2473
2520
  const before = isPromiseLike(beforeResult) ? await beforeResult : beforeResult;
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "bomFormat": "CycloneDX",
3
3
  "specVersion": "1.5",
4
- "serialNumber": "urn:uuid:8448de36-3a72-553d-882f-b5b36bbd1426",
4
+ "serialNumber": "urn:uuid:50e7cfc8-73fe-5f9a-96b0-38df20d401b5",
5
5
  "version": 1,
6
6
  "metadata": {
7
- "timestamp": "2026-07-07T09:24:36.816Z",
7
+ "timestamp": "2026-07-09T19:29:34.036Z",
8
8
  "tools": [
9
9
  {
10
10
  "vendor": "DaloyJS",
11
11
  "name": "daloy-generate-sbom",
12
- "version": "1.0.0-rc.2"
12
+ "version": "1.0.0-rc.3"
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-rc.2",
22
+ "bom-ref": "pkg:npm/@daloyjs/core@1.0.0-rc.3",
23
23
  "name": "@daloyjs/core",
24
- "version": "1.0.0-rc.2",
24
+ "version": "1.0.0-rc.3",
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-rc.2",
26
+ "purl": "pkg:npm/@daloyjs/core@1.0.0-rc.3",
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-rc.2",
49
+ "tagId": "swidtag--daloyjs-core-1.0.0-rc.3",
50
50
  "name": "@daloyjs/core",
51
- "version": "1.0.0-rc.2",
51
+ "version": "1.0.0-rc.3",
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-rc.2",
60
+ "ref": "pkg:npm/@daloyjs/core@1.0.0-rc.3",
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-rc.2",
6
- "documentNamespace": "https://github.com/daloyjs/daloy/sbom/@daloyjs/core-1.0.0-rc.2-8448de36-3a72-553d-882f-b5b36bbd1426",
5
+ "name": "@daloyjs/core-1.0.0-rc.3",
6
+ "documentNamespace": "https://github.com/daloyjs/daloy/sbom/@daloyjs/core-1.0.0-rc.3-50e7cfc8-73fe-5f9a-96b0-38df20d401b5",
7
7
  "creationInfo": {
8
- "created": "2026-07-07T09:24:36.816Z",
8
+ "created": "2026-07-09T19:29:34.036Z",
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-rc.2",
19
+ "versionInfo": "1.0.0-rc.3",
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-rc.2"
30
+ "referenceLocator": "pkg:npm/@daloyjs/core@1.0.0-rc.3"
31
31
  }
32
32
  ]
33
33
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@daloyjs/core",
3
- "version": "1.0.0-rc.2",
3
+ "version": "1.0.0-rc.3",
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": {