@warlock.js/core 5.11.0 → 5.12.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.
Files changed (129) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/esm/application/app.d.mts +3 -3
  3. package/esm/application/app.mjs +6 -6
  4. package/esm/application/app.mjs.map +1 -1
  5. package/esm/cli/cli-commands.utils.mjs +7 -3
  6. package/esm/cli/cli-commands.utils.mjs.map +1 -1
  7. package/esm/cli/commands/doctor/checks/health.check.mjs +1 -1
  8. package/esm/cli/commands/doctor/checks/health.check.mjs.map +1 -1
  9. package/esm/connectors/socket-connector.mjs +13 -16
  10. package/esm/connectors/socket-connector.mjs.map +1 -1
  11. package/esm/container/index.d.mts +20 -7
  12. package/esm/container/index.d.mts.map +1 -1
  13. package/esm/container/index.mjs +8 -5
  14. package/esm/container/index.mjs.map +1 -1
  15. package/esm/dev-server/dev-logger.mjs +12 -1
  16. package/esm/dev-server/dev-logger.mjs.map +1 -1
  17. package/esm/dev-server/development-server.mjs +1 -1
  18. package/esm/dev-server/development-server.mjs.map +1 -1
  19. package/esm/dev-server/file-event-handler.mjs +49 -8
  20. package/esm/dev-server/file-event-handler.mjs.map +1 -1
  21. package/esm/dev-server/file-manager.d.mts +8 -0
  22. package/esm/dev-server/file-manager.d.mts.map +1 -1
  23. package/esm/dev-server/file-manager.mjs +26 -4
  24. package/esm/dev-server/file-manager.mjs.map +1 -1
  25. package/esm/dev-server/files-orchestrator.mjs +3 -3
  26. package/esm/dev-server/files-orchestrator.mjs.map +1 -1
  27. package/esm/dev-server/files-watcher.mjs +16 -2
  28. package/esm/dev-server/files-watcher.mjs.map +1 -1
  29. package/esm/dev-server/flags.mjs +16 -1
  30. package/esm/dev-server/flags.mjs.map +1 -1
  31. package/esm/dev-server/layer-executor.mjs +19 -2
  32. package/esm/dev-server/layer-executor.mjs.map +1 -1
  33. package/esm/errors/container-key-missing-error.mjs +18 -6
  34. package/esm/errors/container-key-missing-error.mjs.map +1 -1
  35. package/esm/generations/stubs.mjs +10 -10
  36. package/esm/generations/stubs.mjs.map +1 -1
  37. package/esm/http/context/request-context.d.mts +14 -11
  38. package/esm/http/context/request-context.d.mts.map +1 -1
  39. package/esm/http/context/request-context.mjs +13 -9
  40. package/esm/http/context/request-context.mjs.map +1 -1
  41. package/esm/http/createHttpApplication.d.mts.map +1 -1
  42. package/esm/http/createHttpApplication.mjs +2 -0
  43. package/esm/http/createHttpApplication.mjs.map +1 -1
  44. package/esm/http/csp.d.mts +139 -0
  45. package/esm/http/csp.d.mts.map +1 -0
  46. package/esm/http/csp.mjs +134 -0
  47. package/esm/http/csp.mjs.map +1 -0
  48. package/esm/http/errors/errors.d.mts +17 -1
  49. package/esm/http/errors/errors.d.mts.map +1 -1
  50. package/esm/http/errors/errors.mjs +20 -1
  51. package/esm/http/errors/errors.mjs.map +1 -1
  52. package/esm/http/index.d.mts +8 -3
  53. package/esm/http/index.mjs +6 -1
  54. package/esm/http/middleware/concurrency-limit.middleware.d.mts +2 -2
  55. package/esm/http/middleware/concurrency-limit.middleware.mjs +1 -1
  56. package/esm/http/middleware/concurrency-limit.middleware.mjs.map +1 -1
  57. package/esm/http/middleware/inject-request-context.d.mts +1 -1
  58. package/esm/http/middleware/inject-request-context.d.mts.map +1 -1
  59. package/esm/http/middleware/inject-request-context.mjs +10 -0
  60. package/esm/http/middleware/inject-request-context.mjs.map +1 -1
  61. package/esm/http/middleware/rate-limit.middleware.d.mts +1 -1
  62. package/esm/http/middleware/rate-limit.middleware.mjs.map +1 -1
  63. package/esm/http/middleware/utils/idempotency-key.d.mts.map +1 -1
  64. package/esm/http/middleware/utils/idempotency-key.mjs +13 -13
  65. package/esm/http/middleware/utils/idempotency-key.mjs.map +1 -1
  66. package/esm/http/request-controller.d.mts +1 -1
  67. package/esm/http/request.d.mts +32 -44
  68. package/esm/http/request.d.mts.map +1 -1
  69. package/esm/http/request.mjs +52 -44
  70. package/esm/http/request.mjs.map +1 -1
  71. package/esm/http/response.d.mts +18 -0
  72. package/esm/http/response.d.mts.map +1 -1
  73. package/esm/http/response.mjs +31 -0
  74. package/esm/http/response.mjs.map +1 -1
  75. package/esm/http/stream-react-response.d.mts +65 -0
  76. package/esm/http/stream-react-response.d.mts.map +1 -0
  77. package/esm/http/stream-react-response.mjs +46 -0
  78. package/esm/http/stream-react-response.mjs.map +1 -0
  79. package/esm/http/tracing/index.mjs +4 -0
  80. package/esm/http/tracing/trace-id.d.mts +14 -0
  81. package/esm/http/tracing/trace-id.d.mts.map +1 -0
  82. package/esm/http/tracing/trace-id.mjs +37 -0
  83. package/esm/http/tracing/trace-id.mjs.map +1 -0
  84. package/esm/http/tracing/tracing-dispatcher.d.mts +42 -0
  85. package/esm/http/tracing/tracing-dispatcher.d.mts.map +1 -0
  86. package/esm/http/tracing/tracing-dispatcher.mjs +94 -0
  87. package/esm/http/tracing/tracing-dispatcher.mjs.map +1 -0
  88. package/esm/http/tracing/tracing.type.d.mts +66 -0
  89. package/esm/http/tracing/tracing.type.d.mts.map +1 -0
  90. package/esm/http/types.d.mts +27 -21
  91. package/esm/http/types.d.mts.map +1 -1
  92. package/esm/index.d.mts +9 -4
  93. package/esm/index.mjs +6 -2
  94. package/esm/production/esbuild-preflight.mjs +47 -0
  95. package/esm/production/esbuild-preflight.mjs.map +1 -0
  96. package/esm/production/production-builder.mjs +19 -15
  97. package/esm/production/production-builder.mjs.map +1 -1
  98. package/esm/restful/restful.d.mts +1 -1
  99. package/esm/router/log-request-lifecycle.mjs +12 -2
  100. package/esm/router/log-request-lifecycle.mjs.map +1 -1
  101. package/esm/router/router.d.mts.map +1 -1
  102. package/esm/router/router.mjs +10 -0
  103. package/esm/router/router.mjs.map +1 -1
  104. package/esm/router/types.d.mts +1 -1
  105. package/esm/socket/utils.mjs +1 -2
  106. package/esm/socket/utils.mjs.map +1 -1
  107. package/esm/storage/drivers/cloud-driver.d.mts.map +1 -1
  108. package/esm/storage/drivers/cloud-driver.mjs +2 -5
  109. package/esm/storage/drivers/cloud-driver.mjs.map +1 -1
  110. package/esm/warlock-config/types.d.mts +12 -0
  111. package/esm/warlock-config/types.d.mts.map +1 -1
  112. package/llms-full.txt +290 -25
  113. package/llms.txt +1 -0
  114. package/package.json +12 -12
  115. package/skills/add-connector/SKILL.md +1 -1
  116. package/skills/build-restful/SKILL.md +2 -2
  117. package/skills/configure-app/SKILL.md +31 -0
  118. package/skills/create-controller/SKILL.md +4 -4
  119. package/skills/request-tracing/SKILL.md +208 -0
  120. package/skills/send-response/SKILL.md +16 -0
  121. package/skills/store-file/SKILL.md +1 -1
  122. package/skills/upload-file/SKILL.md +2 -2
  123. package/skills/use-app-context/SKILL.md +2 -2
  124. package/skills/use-middleware/SKILL.md +2 -2
  125. package/skills/use-repository/SKILL.md +1 -1
  126. package/skills/use-request-locals/SKILL.md +1 -1
  127. package/skills/wire-socket/SKILL.md +3 -3
  128. package/skills/write-middleware/SKILL.md +11 -5
  129. package/skills/write-use-case/SKILL.md +1 -1
@@ -36,24 +36,22 @@ function hashBody(body) {
36
36
  * user B used a guessable value — while still letting the primitive work on
37
37
  * public endpoints.
38
38
  *
39
- * Idempotency middleware must run **after** `authMiddleware` so `request.user`
40
- * and `request.decodedAccessToken` are populated.
39
+ * Idempotency middleware must run **after** `authMiddleware` so
40
+ * `request.locals.user` (written by `@warlock.js/auth`) and
41
+ * `request.decodedAccessToken` are populated.
41
42
  *
42
43
  * @example
43
44
  * buildIdempotencyCacheKey(request, "01J9XZQ-ABC"); // "idem:client:user_123:01J9XZQ-ABC"
44
45
  */
45
46
  /**
46
- * Read `id` off `request.user` without assuming every app's `RequestUser`
47
- * augmentation declares it.
47
+ * Read `id` off `request.locals.user` without assuming any app's
48
+ * `RequestUser` augmentation (declared by `@warlock.js/auth`, which core
49
+ * cannot import) declares it, and without assuming `@warlock.js/auth` is
50
+ * even installed — hence the untyped `unknown` read here rather than a
51
+ * typed `RequestLocals["user"]` access.
48
52
  *
49
- * `RequestUser` (`core/src/http/types.ts`) is empty by defaultapps narrow
50
- * it to their own model shape. Adding `id` directly to `RequestUser` here
51
- * would force that exact field (and type) onto every app: TypeScript
52
- * interface merging requires all declarations of a shared member to have an
53
- * identical type, so an app augmenting `RequestUser` with, say, `id: string`
54
- * only would conflict with a core-declared `id?: string | number`. A local,
55
- * narrow read survives any augmentation shape (eed20184 step (b) —
56
- * `implementation/2026-08-20-A2-request-locals.md` §6.2) — no `as any`.
53
+ * A local, narrow runtime read survives any augmentation shape no
54
+ * `as any`.
57
55
  */
58
56
  function readUserId(user) {
59
57
  if (!user || typeof user !== "object" || !("id" in user)) return void 0;
@@ -61,7 +59,9 @@ function readUserId(user) {
61
59
  return typeof id === "string" || typeof id === "number" ? id : void 0;
62
60
  }
63
61
  function buildIdempotencyCacheKey(request, idempotencyKey) {
64
- return `idem:${request.decodedAccessToken?.userType || "anonymous"}:${readUserId(request.user) || request.detectIp() || "unknown"}:${idempotencyKey}`;
62
+ const userType = request.decodedAccessToken?.userType || "anonymous";
63
+ const locals = request.locals;
64
+ return `idem:${userType}:${readUserId(locals.user) || request.detectIp() || "unknown"}:${idempotencyKey}`;
65
65
  }
66
66
 
67
67
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"idempotency-key.mjs","names":[],"sources":["../../../../../../../../../core/src/http/middleware/utils/idempotency-key.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport type { Request } from \"../../request\";\nimport type { RequestUser } from \"../../types\";\n\n/**\n * Idempotency-key validation rules.\n *\n * RFC draft-ietf-httpapi-idempotency-key suggests UUIDs or ULIDs but doesn't\n * mandate a format. We accept any printable ASCII string up to 255 chars —\n * tight enough to reject log-injection (no control characters / newlines),\n * loose enough to accept whatever ID scheme the client picks.\n */\nconst MAX_KEY_LENGTH = 255;\nconst PRINTABLE_ASCII = /^[\\x21-\\x7e]+$/;\n\nexport function isValidIdempotencyKey(value: unknown): value is string {\n return (\n typeof value === \"string\" &&\n value.length > 0 &&\n value.length <= MAX_KEY_LENGTH &&\n PRINTABLE_ASCII.test(value)\n );\n}\n\n/**\n * Hash a request body to a stable hex digest.\n *\n * Used to detect \"same key, different body\" — that's a client bug, not a retry.\n * sha256 is overkill for collision resistance here, but it's the dependency-free\n * choice and avoids importing a faster hasher.\n *\n * @example\n * hashBody({ amount: 100, currency: \"USD\" }); // \"a1b2c3...\"\n */\nexport function hashBody(body: unknown): string {\n const serialized = typeof body === \"string\" ? body : JSON.stringify(body ?? {});\n\n return createHash(\"sha256\").update(serialized).digest(\"hex\");\n}\n\n/**\n * Build the cache key for an idempotency record.\n *\n * Scope is `userType:userId` when authenticated, falling back to the client IP\n * when anonymous. This prevents user A from replaying user B's key — even if\n * user B used a guessable value — while still letting the primitive work on\n * public endpoints.\n *\n * Idempotency middleware must run **after** `authMiddleware` so `request.user`\n * and `request.decodedAccessToken` are populated.\n *\n * @example\n * buildIdempotencyCacheKey(request, \"01J9XZQ-ABC\"); // \"idem:client:user_123:01J9XZQ-ABC\"\n */\n/**\n * Read `id` off `request.user` without assuming every app's `RequestUser`\n * augmentation declares it.\n *\n * `RequestUser` (`core/src/http/types.ts`) is empty by default apps narrow\n * it to their own model shape. Adding `id` directly to `RequestUser` here\n * would force that exact field (and type) onto every app: TypeScript\n * interface merging requires all declarations of a shared member to have an\n * identical type, so an app augmenting `RequestUser` with, say, `id: string`\n * only would conflict with a core-declared `id?: string | number`. A local,\n * narrow read survives any augmentation shape (eed20184 step (b) —\n * `implementation/2026-08-20-A2-request-locals.md` §6.2) — no `as any`.\n */\nfunction readUserId(user: RequestUser | undefined): string | number | undefined {\n if (!user || typeof user !== \"object\" || !(\"id\" in user)) return undefined;\n\n const id = (user as { id?: unknown }).id;\n\n return typeof id === \"string\" || typeof id === \"number\" ? id : undefined;\n}\n\nexport function buildIdempotencyCacheKey(request: Request, idempotencyKey: string): string {\n const userType = request.decodedAccessToken?.userType || \"anonymous\";\n const userId = readUserId(request.user) || request.detectIp() || \"unknown\";\n\n return `idem:${userType}:${userId}:${idempotencyKey}`;\n}\n"],"mappings":";;;;;;;;;;;AAYA,MAAM,iBAAiB;AACvB,MAAM,kBAAkB;AAExB,SAAgB,sBAAsB,OAAiC;CACrE,OACE,OAAO,UAAU,YACjB,MAAM,SAAS,KACf,MAAM,UAAU,kBAChB,gBAAgB,KAAK,KAAK;AAE9B;;;;;;;;;;;AAYA,SAAgB,SAAS,MAAuB;CAC9C,MAAM,aAAa,OAAO,SAAS,WAAW,OAAO,KAAK,UAAU,QAAQ,CAAC,CAAC;CAE9E,OAAO,WAAW,QAAQ,CAAC,CAAC,OAAO,UAAU,CAAC,CAAC,OAAO,KAAK;AAC7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,SAAS,WAAW,MAA4D;CAC9E,IAAI,CAAC,QAAQ,OAAO,SAAS,YAAY,EAAE,QAAQ,OAAO,OAAO;CAEjE,MAAM,KAAM,KAA0B;CAEtC,OAAO,OAAO,OAAO,YAAY,OAAO,OAAO,WAAW,KAAK;AACjE;AAEA,SAAgB,yBAAyB,SAAkB,gBAAgC;CAIzF,OAAO,QAHU,QAAQ,oBAAoB,YAAY,YAGjC,GAFT,WAAW,QAAQ,IAAI,KAAK,QAAQ,SAAS,KAAK,UAE/B,GAAG;AACvC"}
1
+ {"version":3,"file":"idempotency-key.mjs","names":[],"sources":["../../../../../../../../../core/src/http/middleware/utils/idempotency-key.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport type { Request } from \"../../request\";\n\n/**\n * Idempotency-key validation rules.\n *\n * RFC draft-ietf-httpapi-idempotency-key suggests UUIDs or ULIDs but doesn't\n * mandate a format. We accept any printable ASCII string up to 255 chars —\n * tight enough to reject log-injection (no control characters / newlines),\n * loose enough to accept whatever ID scheme the client picks.\n */\nconst MAX_KEY_LENGTH = 255;\nconst PRINTABLE_ASCII = /^[\\x21-\\x7e]+$/;\n\nexport function isValidIdempotencyKey(value: unknown): value is string {\n return (\n typeof value === \"string\" &&\n value.length > 0 &&\n value.length <= MAX_KEY_LENGTH &&\n PRINTABLE_ASCII.test(value)\n );\n}\n\n/**\n * Hash a request body to a stable hex digest.\n *\n * Used to detect \"same key, different body\" — that's a client bug, not a retry.\n * sha256 is overkill for collision resistance here, but it's the dependency-free\n * choice and avoids importing a faster hasher.\n *\n * @example\n * hashBody({ amount: 100, currency: \"USD\" }); // \"a1b2c3...\"\n */\nexport function hashBody(body: unknown): string {\n const serialized = typeof body === \"string\" ? body : JSON.stringify(body ?? {});\n\n return createHash(\"sha256\").update(serialized).digest(\"hex\");\n}\n\n/**\n * Build the cache key for an idempotency record.\n *\n * Scope is `userType:userId` when authenticated, falling back to the client IP\n * when anonymous. This prevents user A from replaying user B's key — even if\n * user B used a guessable value — while still letting the primitive work on\n * public endpoints.\n *\n * Idempotency middleware must run **after** `authMiddleware` so\n * `request.locals.user` (written by `@warlock.js/auth`) and\n * `request.decodedAccessToken` are populated.\n *\n * @example\n * buildIdempotencyCacheKey(request, \"01J9XZQ-ABC\"); // \"idem:client:user_123:01J9XZQ-ABC\"\n */\n/**\n * Read `id` off `request.locals.user` without assuming any app's\n * `RequestUser` augmentation (declared by `@warlock.js/auth`, which core\n * cannot import) declares it, and without assuming `@warlock.js/auth` is\n * even installed hence the untyped `unknown` read here rather than a\n * typed `RequestLocals[\"user\"]` access.\n *\n * A local, narrow runtime read survives any augmentation shape no\n * `as any`.\n */\nfunction readUserId(user: unknown): string | number | undefined {\n if (!user || typeof user !== \"object\" || !(\"id\" in user)) return undefined;\n\n const id = (user as { id?: unknown }).id;\n\n return typeof id === \"string\" || typeof id === \"number\" ? id : undefined;\n}\n\nexport function buildIdempotencyCacheKey(request: Request, idempotencyKey: string): string {\n const userType = request.decodedAccessToken?.userType || \"anonymous\";\n const locals = request.locals as Record<string, unknown>;\n const userId = readUserId(locals.user) || request.detectIp() || \"unknown\";\n\n return `idem:${userType}:${userId}:${idempotencyKey}`;\n}\n"],"mappings":";;;;;;;;;;;AAWA,MAAM,iBAAiB;AACvB,MAAM,kBAAkB;AAExB,SAAgB,sBAAsB,OAAiC;CACrE,OACE,OAAO,UAAU,YACjB,MAAM,SAAS,KACf,MAAM,UAAU,kBAChB,gBAAgB,KAAK,KAAK;AAE9B;;;;;;;;;;;AAYA,SAAgB,SAAS,MAAuB;CAC9C,MAAM,aAAa,OAAO,SAAS,WAAW,OAAO,KAAK,UAAU,QAAQ,CAAC,CAAC;CAE9E,OAAO,WAAW,QAAQ,CAAC,CAAC,OAAO,UAAU,CAAC,CAAC,OAAO,KAAK;AAC7D;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,SAAS,WAAW,MAA4C;CAC9D,IAAI,CAAC,QAAQ,OAAO,SAAS,YAAY,EAAE,QAAQ,OAAO,OAAO;CAEjE,MAAM,KAAM,KAA0B;CAEtC,OAAO,OAAO,OAAO,YAAY,OAAO,OAAO,WAAW,KAAK;AACjE;AAEA,SAAgB,yBAAyB,SAAkB,gBAAgC;CACzF,MAAM,WAAW,QAAQ,oBAAoB,YAAY;CACzD,MAAM,SAAS,QAAQ;CAGvB,OAAO,QAAQ,SAAS,GAFT,WAAW,OAAO,IAAI,KAAK,QAAQ,SAAS,KAAK,UAE9B,GAAG;AACvC"}
@@ -1,5 +1,5 @@
1
- import { Request } from "./request.mjs";
2
1
  import { Response } from "./response.mjs";
2
+ import { Request } from "./request.mjs";
3
3
  import { ReturnedResponse } from "./types.mjs";
4
4
  import { RequestControllerContract } from "../router/types.mjs";
5
5
 
@@ -1,6 +1,6 @@
1
- import { UploadedFile } from "./uploaded-file.mjs";
2
1
  import { Response } from "./response.mjs";
3
- import { DecodedAccessToken, RequestEvent, RequestLocals, RequestUser } from "./types.mjs";
2
+ import { UploadedFile } from "./uploaded-file.mjs";
3
+ import { DecodedAccessToken, RequestEvent, RequestLocals } from "./types.mjs";
4
4
  import { Middleware, RequestHandler, Route } from "../router/types.mjs";
5
5
  import { BaseValidator } from "@warlock.js/seal";
6
6
  import { trans } from "@mongez/localization";
@@ -18,7 +18,8 @@ declare class Request<RequestValidation = any> {
18
18
  *
19
19
  * **Prefer framework methods first**: `request.input()`, `request.header()`,
20
20
  * `request.body`, `request.query`, `request.params`, `request.file()`,
21
- * `request.user`, `request.detectIp()`, etc. They handle locale, parsing,
21
+ * `request.locals.user` (set by `@warlock.js/auth`), `request.detectIp()`,
22
+ * etc. They handle locale, parsing,
22
23
  * trust-proxy, and validation pipeline integration correctly.
23
24
  *
24
25
  * **Reach for `baseRequest` only** when the framework genuinely lacks a
@@ -54,38 +55,23 @@ declare class Request<RequestValidation = any> {
54
55
  get decodedAccessToken(): DecodedAccessToken | undefined;
55
56
  set decodedAccessToken(value: DecodedAccessToken | undefined);
56
57
  /**
57
- * Backing field for `user` see the accessor below.
58
- */
59
- private _user?;
60
- /**
61
- * The authenticated user attached to this request, if any.
62
- *
63
- * `RequestUser` is empty by default, so ANY shape is assignable here at the
64
- * declaration site — the app or auth package declares its real fields via
65
- * module augmentation:
58
+ * REMOVED in 5.12.0 — the authenticated user now lives at
59
+ * `request.locals.user`, a key `@warlock.js/auth` declares via module
60
+ * augmentation on `RequestLocals` and writes from its middleware after a
61
+ * successful token resolution. `RequestUser` moved out of core to
62
+ * `@warlock.js/auth` alongside it.
66
63
  *
67
- * ```typescript
68
- * declare module "@warlock.js/core" {
69
- * interface RequestUser {
70
- * id: string | number;
71
- * }
72
- * }
73
- * ```
64
+ * This getter is a development-time diagnostic only, kept for one release
65
+ * so a call site that still reads `request.user` fails loudly at runtime
66
+ * instead of silently reading `undefined`. It is typed `never` so it
67
+ * cannot reintroduce an auth-shaped type into core, and it throws
68
+ * unconditionally outside production so the failure is impossible to miss
69
+ * in local dev — see `RequestUserMovedError`.
74
70
  *
75
- * Replaces the v4 `GuardedRequest` convention
76
- * (`create-warlock/.../guarded.request.ts` — `Request<T> & { user: User }`,
77
- * an intersection type hand-declared per app) with a property core itself
78
- * declares and types. `clearCurrentUser()` below is the one place core
79
- * writes it directly; auth middleware writes it after a successful token
80
- * resolution.
81
- *
82
- * A prototype accessor, not a plain field: the setter also marks the
83
- * request `authDerived` (see below), so `clearCurrentUser()`'s
84
- * `this.user = undefined` still counts as touching auth state rather than
85
- * un-marking it.
71
+ * There is no setter: nothing in core or downstream packages should ever
72
+ * assign to `request.user` again.
86
73
  */
87
- get user(): RequestUser | undefined;
88
- set user(value: RequestUser | undefined);
74
+ get user(): never;
89
75
  /**
90
76
  * Private, server-only, per-request data bag.
91
77
  *
@@ -115,18 +101,6 @@ declare class Request<RequestValidation = any> {
115
101
  * a prior one.
116
102
  */
117
103
  locals: RequestLocals;
118
- /**
119
- * Forget the authenticated user for this request.
120
- *
121
- * The `user` documentation above already names this as "the one place core
122
- * writes it directly", and `@warlock.js/auth`'s middleware already calls it when
123
- * a token is forged, malformed, expired, or of the wrong type. It was designed,
124
- * documented and called — just never written, so `auth` could not typecheck.
125
- *
126
- * Clearing rather than leaving a stale value matters: a request that failed
127
- * authentication must not carry the identity of whoever this object last held.
128
- */
129
- clearCurrentUser(): void;
130
104
  /**
131
105
  * Backing field for the lazily-generated CSP nonce. Left `undefined` until
132
106
  * the first `request.nonce` read; see the `nonce` getter below.
@@ -180,6 +154,12 @@ declare class Request<RequestValidation = any> {
180
154
  * Request id
181
155
  */
182
156
  id: string;
157
+ /**
158
+ * Trace id. The inbound `traceparent` header's trace id
159
+ * when valid, otherwise `id`. Resolved once in `setRequest`, alongside
160
+ * `id` itself — see `resolveTraceId`.
161
+ */
162
+ traceId: string;
183
163
  /**
184
164
  * Start Time
185
165
  */
@@ -202,6 +182,14 @@ declare class Request<RequestValidation = any> {
202
182
  * default is used regardless of any incoming header.
203
183
  */
204
184
  protected resolveRequestId(): void;
185
+ /**
186
+ * Derive `traceId`: the inbound
187
+ * `traceparent` header's trace id when it is a valid W3C traceparent,
188
+ * otherwise `id`. Always runs — unlike request-id inheritance this has no
189
+ * `enabled: false` escape hatch, since `traceId` is only ever read when
190
+ * tracing hooks are enabled (see `./tracing`).
191
+ */
192
+ protected resolveTraceId(): void;
205
193
  /**
206
194
  * Validate a candidate request-id value. Accepts non-empty printable ASCII
207
195
  * up to 128 characters — tight enough to reject newline / control-character
@@ -1 +1 @@
1
- {"version":3,"file":"request.d.mts","names":[],"sources":["../../../../../../../core/src/http/request.ts"],"mappings":"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"}
1
+ {"version":3,"file":"request.d.mts","names":[],"sources":["../../../../../../../core/src/http/request.ts"],"mappings":"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"}
@@ -1,6 +1,11 @@
1
1
  import { config } from "../config/config-getter.mjs";
2
2
  import { LOCALE_COOKIE_NAME, resolveLocaleConfiguration } from "../config/locale-configuration.mjs";
3
+ import { RequestUserMovedError } from "./errors/errors.mjs";
4
+ import { deriveTraceId } from "./tracing/trace-id.mjs";
5
+ import { buildTracingContext, dispatchPhase, isTracingEnabled } from "./tracing/tracing-dispatcher.mjs";
6
+ import "./tracing/index.mjs";
3
7
  import { createRequestStore } from "./middleware/inject-request-context.mjs";
8
+ import { Application } from "../application/application.mjs";
4
9
  import { validateAll } from "../validation/validateAll.mjs";
5
10
  import { Response } from "./response.mjs";
6
11
  import { UploadedFile } from "./uploaded-file.mjs";
@@ -22,6 +27,7 @@ var Request = class Request {
22
27
  this.t = trans;
23
28
  this._locale = "";
24
29
  this.id = Random.string(32);
30
+ this.traceId = "";
25
31
  this.startTime = Date.now();
26
32
  }
27
33
  /**
@@ -39,52 +45,24 @@ var Request = class Request {
39
45
  this.locals.authDerived = true;
40
46
  }
41
47
  /**
42
- * The authenticated user attached to this request, if any.
48
+ * REMOVED in 5.12.0 the authenticated user now lives at
49
+ * `request.locals.user`, a key `@warlock.js/auth` declares via module
50
+ * augmentation on `RequestLocals` and writes from its middleware after a
51
+ * successful token resolution. `RequestUser` moved out of core to
52
+ * `@warlock.js/auth` alongside it.
43
53
  *
44
- * `RequestUser` is empty by default, so ANY shape is assignable here at the
45
- * declaration site the app or auth package declares its real fields via
46
- * module augmentation:
54
+ * This getter is a development-time diagnostic only, kept for one release
55
+ * so a call site that still reads `request.user` fails loudly at runtime
56
+ * instead of silently reading `undefined`. It is typed `never` so it
57
+ * cannot reintroduce an auth-shaped type into core, and it throws
58
+ * unconditionally outside production so the failure is impossible to miss
59
+ * in local dev — see `RequestUserMovedError`.
47
60
  *
48
- * ```typescript
49
- * declare module "@warlock.js/core" {
50
- * interface RequestUser {
51
- * id: string | number;
52
- * }
53
- * }
54
- * ```
55
- *
56
- * Replaces the v4 `GuardedRequest` convention
57
- * (`create-warlock/.../guarded.request.ts` — `Request<T> & { user: User }`,
58
- * an intersection type hand-declared per app) with a property core itself
59
- * declares and types. `clearCurrentUser()` below is the one place core
60
- * writes it directly; auth middleware writes it after a successful token
61
- * resolution.
62
- *
63
- * A prototype accessor, not a plain field: the setter also marks the
64
- * request `authDerived` (see below), so `clearCurrentUser()`'s
65
- * `this.user = undefined` still counts as touching auth state rather than
66
- * un-marking it.
61
+ * There is no setter: nothing in core or downstream packages should ever
62
+ * assign to `request.user` again.
67
63
  */
68
64
  get user() {
69
- return this._user;
70
- }
71
- set user(value) {
72
- this._user = value;
73
- this.locals.authDerived = true;
74
- }
75
- /**
76
- * Forget the authenticated user for this request.
77
- *
78
- * The `user` documentation above already names this as "the one place core
79
- * writes it directly", and `@warlock.js/auth`'s middleware already calls it when
80
- * a token is forged, malformed, expired, or of the wrong type. It was designed,
81
- * documented and called — just never written, so `auth` could not typecheck.
82
- *
83
- * Clearing rather than leaving a stale value matters: a request that failed
84
- * authentication must not carry the identity of whoever this object last held.
85
- */
86
- clearCurrentUser() {
87
- this.user = void 0;
65
+ if (Application.isDevelopment) throw new RequestUserMovedError();
88
66
  }
89
67
  /**
90
68
  * Per-request Content-Security-Policy nonce — a fresh, unguessable value
@@ -118,6 +96,7 @@ var Request = class Request {
118
96
  setRequest(request) {
119
97
  this.baseRequest = request;
120
98
  this.resolveRequestId();
99
+ this.resolveTraceId();
121
100
  this.parsePayload();
122
101
  this.trans = this.t = (keyword, placeholders) => transFrom(this.getLocaleCode(), keyword, placeholders);
123
102
  return this;
@@ -143,6 +122,18 @@ var Request = class Request {
143
122
  if (typeof requestIdConfig.generator === "function") this.id = requestIdConfig.generator();
144
123
  }
145
124
  /**
125
+ * Derive `traceId`: the inbound
126
+ * `traceparent` header's trace id when it is a valid W3C traceparent,
127
+ * otherwise `id`. Always runs — unlike request-id inheritance this has no
128
+ * `enabled: false` escape hatch, since `traceId` is only ever read when
129
+ * tracing hooks are enabled (see `./tracing`).
130
+ */
131
+ resolveTraceId() {
132
+ const header = this.baseRequest.headers.traceparent;
133
+ const traceparent = Array.isArray(header) ? header[0] : header;
134
+ this.traceId = deriveTraceId(traceparent, this.id);
135
+ }
136
+ /**
146
137
  * Validate a candidate request-id value. Accepts non-empty printable ASCII
147
138
  * up to 128 characters — tight enough to reject newline / control-character
148
139
  * log-injection, loose enough to accept UUIDs, ULIDs, snowflakes, etc.
@@ -483,7 +474,14 @@ var Request = class Request {
483
474
  }
484
475
  const handler = this.route.handler;
485
476
  if (!handler.validation) return;
486
- return await validateAll(handler.validation, this, this.response);
477
+ const tracingEnabled = isTracingEnabled();
478
+ const validationStartedAt = tracingEnabled ? performance.now() : 0;
479
+ const validationOutput = await validateAll(handler.validation, this, this.response);
480
+ if (tracingEnabled) dispatchPhase(buildTracingContext(this), {
481
+ name: "validation",
482
+ durationMs: performance.now() - validationStartedAt
483
+ });
484
+ return validationOutput;
487
485
  }
488
486
  /**
489
487
  * Return the request handler attached to the current route.
@@ -540,12 +538,22 @@ var Request = class Request {
540
538
  if (middlewares.length === 0) return;
541
539
  this.log("About to execute request middlewares");
542
540
  this.trigger("executingMiddleware", middlewares, this.route);
543
- for (const middleware of middlewares) {
541
+ const tracingEnabled = isTracingEnabled();
542
+ for (const [index, middleware] of middlewares.entries()) {
544
543
  this.log("Executing middleware " + colors.yellowBright(middleware.name));
544
+ const middlewareStartedAt = tracingEnabled ? performance.now() : 0;
545
545
  const output = await middleware({
546
546
  request: this,
547
547
  response: this.response
548
548
  });
549
+ if (tracingEnabled) dispatchPhase(buildTracingContext(this), {
550
+ name: "middleware",
551
+ durationMs: performance.now() - middlewareStartedAt,
552
+ attrs: {
553
+ name: middleware.name,
554
+ index
555
+ }
556
+ });
549
557
  this.log("Executed middleware " + colors.yellowBright(middleware.name), "success");
550
558
  if (output !== void 0) {
551
559
  this.log(colors.yellow("request intercepted by middleware ") + colors.cyanBright(middleware.name), "warn");