@warlock.js/core 4.16.0 → 5.0.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 (180) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/bin/warlock.js +154 -1
  3. package/esm/application/application.d.mts +65 -1
  4. package/esm/application/application.d.mts.map +1 -1
  5. package/esm/application/application.mjs +62 -0
  6. package/esm/application/application.mjs.map +1 -1
  7. package/esm/application/index.d.mts +1 -1
  8. package/esm/cli/cli-commands.manager.mjs +60 -10
  9. package/esm/cli/cli-commands.manager.mjs.map +1 -1
  10. package/esm/cli/commands/dev-server.command.mjs +1 -0
  11. package/esm/cli/commands/dev-server.command.mjs.map +1 -1
  12. package/esm/cli/commands/doctor/checks/handler-signature.check.mjs +40 -0
  13. package/esm/cli/commands/doctor/checks/handler-signature.check.mjs.map +1 -0
  14. package/esm/cli/commands/doctor/checks/index.mjs +4 -2
  15. package/esm/cli/commands/doctor/checks/index.mjs.map +1 -1
  16. package/esm/commands/cli-command.d.mts +1 -1
  17. package/esm/commands/cli-command.d.mts.map +1 -1
  18. package/esm/commands/cli-command.mjs.map +1 -1
  19. package/esm/commands/types.d.mts +10 -2
  20. package/esm/commands/types.d.mts.map +1 -1
  21. package/esm/config/config-handlers.d.mts +1 -2
  22. package/esm/config/config-handlers.d.mts.map +1 -1
  23. package/esm/config/config-handlers.mjs +3 -2
  24. package/esm/config/config-handlers.mjs.map +1 -1
  25. package/esm/config/locale-configuration.mjs +29 -0
  26. package/esm/config/locale-configuration.mjs.map +1 -0
  27. package/esm/connectors/assert-no-reserved-connector-names.d.mts +7 -0
  28. package/esm/connectors/assert-no-reserved-connector-names.d.mts.map +1 -0
  29. package/esm/connectors/assert-no-reserved-connector-names.mjs +44 -0
  30. package/esm/connectors/assert-no-reserved-connector-names.mjs.map +1 -0
  31. package/esm/connectors/assert-unique-connector-names.d.mts +7 -0
  32. package/esm/connectors/assert-unique-connector-names.d.mts.map +1 -0
  33. package/esm/connectors/assert-unique-connector-names.mjs +20 -0
  34. package/esm/connectors/assert-unique-connector-names.mjs.map +1 -0
  35. package/esm/connectors/connectors-manager.d.mts +38 -0
  36. package/esm/connectors/connectors-manager.d.mts.map +1 -1
  37. package/esm/connectors/connectors-manager.mjs +45 -12
  38. package/esm/connectors/connectors-manager.mjs.map +1 -1
  39. package/esm/connectors/describe-server-address.mjs +75 -0
  40. package/esm/connectors/describe-server-address.mjs.map +1 -0
  41. package/esm/connectors/http-connector.d.mts.map +1 -1
  42. package/esm/connectors/http-connector.mjs +6 -3
  43. package/esm/connectors/http-connector.mjs.map +1 -1
  44. package/esm/connectors/index.d.mts +4 -1
  45. package/esm/connectors/index.mjs +4 -1
  46. package/esm/connectors/register-configured-connectors.d.mts +14 -0
  47. package/esm/connectors/register-configured-connectors.d.mts.map +1 -0
  48. package/esm/connectors/register-configured-connectors.mjs +99 -0
  49. package/esm/connectors/register-configured-connectors.mjs.map +1 -0
  50. package/esm/connectors/socket-connector.d.mts.map +1 -1
  51. package/esm/connectors/socket-connector.mjs +4 -1
  52. package/esm/connectors/socket-connector.mjs.map +1 -1
  53. package/esm/connectors/types.d.mts +76 -1
  54. package/esm/connectors/types.d.mts.map +1 -1
  55. package/esm/connectors/types.mjs.map +1 -1
  56. package/esm/database/models/database-log/database-log.d.mts +50 -2
  57. package/esm/database/models/database-log/database-log.d.mts.map +1 -1
  58. package/esm/database/utils.d.mts +2 -2
  59. package/esm/database/utils.d.mts.map +1 -1
  60. package/esm/dev-server/development-server.mjs +1 -0
  61. package/esm/dev-server/development-server.mjs.map +1 -1
  62. package/esm/dev-server/layer-executor.mjs +2 -1
  63. package/esm/dev-server/layer-executor.mjs.map +1 -1
  64. package/esm/dev-server/read-config-ast.mjs +48 -0
  65. package/esm/dev-server/read-config-ast.mjs.map +1 -0
  66. package/esm/dev-server/run-typings-generation.mjs +18 -0
  67. package/esm/dev-server/run-typings-generation.mjs.map +1 -0
  68. package/esm/dev-server/type-generator.mjs +30 -39
  69. package/esm/dev-server/type-generator.mjs.map +1 -1
  70. package/esm/generations/add-command.action.mjs +169 -1
  71. package/esm/generations/add-command.action.mjs.map +1 -1
  72. package/esm/generations/stubs.mjs +93 -1
  73. package/esm/generations/stubs.mjs.map +1 -1
  74. package/esm/http/context/request-context.d.mts +14 -3
  75. package/esm/http/context/request-context.d.mts.map +1 -1
  76. package/esm/http/context/request-context.mjs +11 -1
  77. package/esm/http/context/request-context.mjs.map +1 -1
  78. package/esm/http/context/request-memo.d.mts +21 -0
  79. package/esm/http/context/request-memo.d.mts.map +1 -0
  80. package/esm/http/context/request-memo.mjs +48 -0
  81. package/esm/http/context/request-memo.mjs.map +1 -0
  82. package/esm/http/database/RequestLog.d.mts +164 -2
  83. package/esm/http/database/RequestLog.d.mts.map +1 -1
  84. package/esm/http/index.d.mts +6 -5
  85. package/esm/http/index.mjs +2 -1
  86. package/esm/http/middleware/cache-response-middleware.d.mts +2 -2
  87. package/esm/http/middleware/cache-response-middleware.d.mts.map +1 -1
  88. package/esm/http/middleware/cache-response-middleware.mjs +1 -1
  89. package/esm/http/middleware/cache-response-middleware.mjs.map +1 -1
  90. package/esm/http/middleware/concurrency-limit.middleware.mjs +1 -1
  91. package/esm/http/middleware/concurrency-limit.middleware.mjs.map +1 -1
  92. package/esm/http/middleware/idempotency.middleware.mjs +1 -1
  93. package/esm/http/middleware/idempotency.middleware.mjs.map +1 -1
  94. package/esm/http/middleware/index.d.mts +1 -1
  95. package/esm/http/middleware/index.mjs +1 -1
  96. package/esm/http/middleware/inject-request-context.d.mts +1 -8
  97. package/esm/http/middleware/inject-request-context.d.mts.map +1 -1
  98. package/esm/http/middleware/inject-request-context.mjs +9 -22
  99. package/esm/http/middleware/inject-request-context.mjs.map +1 -1
  100. package/esm/http/middleware/ip-filter.middleware.mjs +1 -1
  101. package/esm/http/middleware/ip-filter.middleware.mjs.map +1 -1
  102. package/esm/http/middleware/maintenance.middleware.mjs +1 -1
  103. package/esm/http/middleware/maintenance.middleware.mjs.map +1 -1
  104. package/esm/http/middleware/max-body-size.middleware.mjs +1 -1
  105. package/esm/http/middleware/max-body-size.middleware.mjs.map +1 -1
  106. package/esm/http/middleware/rate-limit.middleware.mjs +1 -1
  107. package/esm/http/middleware/rate-limit.middleware.mjs.map +1 -1
  108. package/esm/http/middleware/utils/idempotency-key.d.mts +0 -14
  109. package/esm/http/middleware/utils/idempotency-key.d.mts.map +1 -1
  110. package/esm/http/middleware/utils/idempotency-key.mjs +19 -1
  111. package/esm/http/middleware/utils/idempotency-key.mjs.map +1 -1
  112. package/esm/http/request.d.mts +119 -30
  113. package/esm/http/request.d.mts.map +1 -1
  114. package/esm/http/request.mjs +98 -25
  115. package/esm/http/request.mjs.map +1 -1
  116. package/esm/http/response.mjs +1 -1
  117. package/esm/http/response.mjs.map +1 -1
  118. package/esm/http/types.d.mts +36 -1
  119. package/esm/http/types.d.mts.map +1 -1
  120. package/esm/http/uploaded-file.d.mts +51 -19
  121. package/esm/http/uploaded-file.d.mts.map +1 -1
  122. package/esm/http/uploaded-file.mjs +36 -8
  123. package/esm/http/uploaded-file.mjs.map +1 -1
  124. package/esm/index.d.mts +17 -9
  125. package/esm/index.mjs +10 -4
  126. package/esm/production/build-contributions.mjs +115 -0
  127. package/esm/production/build-contributions.mjs.map +1 -0
  128. package/esm/production/production-builder.mjs +97 -2
  129. package/esm/production/production-builder.mjs.map +1 -1
  130. package/esm/production/resolve-build-config.d.mts +25 -0
  131. package/esm/production/resolve-build-config.d.mts.map +1 -0
  132. package/esm/production/resolve-build-config.mjs +43 -1
  133. package/esm/production/resolve-build-config.mjs.map +1 -1
  134. package/esm/repositories/adapters/cascade/cascade-query-builder.d.mts +1 -1
  135. package/esm/repositories/adapters/cascade/filter-applicator.mjs +20 -9
  136. package/esm/repositories/adapters/cascade/filter-applicator.mjs.map +1 -1
  137. package/esm/restful/restful.d.mts +29 -8
  138. package/esm/restful/restful.d.mts.map +1 -1
  139. package/esm/restful/restful.mjs +31 -13
  140. package/esm/restful/restful.mjs.map +1 -1
  141. package/esm/router/describe-route-for-log.mjs +29 -0
  142. package/esm/router/describe-route-for-log.mjs.map +1 -0
  143. package/esm/router/index.d.mts +3 -1
  144. package/esm/router/index.mjs +2 -0
  145. package/esm/router/log-request-lifecycle.mjs +38 -0
  146. package/esm/router/log-request-lifecycle.mjs.map +1 -0
  147. package/esm/router/positional-handler-diagnostics.d.mts +90 -0
  148. package/esm/router/positional-handler-diagnostics.d.mts.map +1 -0
  149. package/esm/router/positional-handler-diagnostics.mjs +247 -0
  150. package/esm/router/positional-handler-diagnostics.mjs.map +1 -0
  151. package/esm/router/route-registry.d.mts +34 -0
  152. package/esm/router/route-registry.d.mts.map +1 -0
  153. package/esm/router/router.d.mts.map +1 -1
  154. package/esm/router/router.mjs +87 -63
  155. package/esm/router/router.mjs.map +1 -1
  156. package/esm/router/types.d.mts +23 -5
  157. package/esm/router/types.d.mts.map +1 -1
  158. package/esm/storage/storage.d.mts.map +1 -1
  159. package/esm/storage/storage.mjs +1 -1
  160. package/esm/storage/storage.mjs.map +1 -1
  161. package/esm/storage/utils/safe-fetch.d.mts +11 -1
  162. package/esm/storage/utils/safe-fetch.d.mts.map +1 -1
  163. package/esm/storage/utils/safe-fetch.mjs +51 -4
  164. package/esm/storage/utils/safe-fetch.mjs.map +1 -1
  165. package/esm/tests/vitest-setup.mjs +1 -1
  166. package/esm/utils/load-environment.mjs +33 -2
  167. package/esm/utils/load-environment.mjs.map +1 -1
  168. package/esm/utils/types.d.mts +4 -0
  169. package/esm/utils/types.d.mts.map +1 -1
  170. package/esm/validation/validateAll.mjs +4 -1
  171. package/esm/validation/validateAll.mjs.map +1 -1
  172. package/esm/validation/validators/file-validator.d.mts +25 -1
  173. package/esm/validation/validators/file-validator.d.mts.map +1 -1
  174. package/esm/warlock-config/index.mjs +1 -1
  175. package/esm/warlock-config/normalize-build-config.mjs.map +1 -1
  176. package/esm/warlock-config/types.d.mts +36 -2
  177. package/esm/warlock-config/types.d.mts.map +1 -1
  178. package/package.json +15 -12
  179. package/skills/request-memo/SKILL.md +142 -0
  180. package/skills/use-request-locals/SKILL.md +144 -0
@@ -574,7 +574,99 @@ router.group({ prefix: "/notifications", middleware: [authMiddleware([])] }, ()
574
574
  router.delete("/:id", deleteNotificationController);
575
575
  });
576
576
  `;
577
+ /**
578
+ * `src/web/root.tsx` — the application root for the SSR page layer.
579
+ *
580
+ * Deliberately minimal. The framework ships a default root, so this exists to
581
+ * give you a place to start rather than because anything requires it. The
582
+ * reference app (`v5/app/src/web/root.tsx`) is where to look for the fuller
583
+ * shape: middleware, an app-level loader, locales, an ErrorBoundary.
584
+ */
585
+ const webRootStub = `import { Head, Scripts } from "@warlock.js/web";
586
+ import type { AppProps } from "@warlock.js/web";
587
+
588
+ /**
589
+ * The application root.
590
+ *
591
+ * NOT async, and it receives no request/response: it renders on the server and
592
+ * again in the browser during hydration, where neither exists.
593
+ */
594
+ export default function App({ children }: AppProps) {
595
+ return (
596
+ <html lang="en">
597
+ <head>
598
+ {/*
599
+ Placement only. The framework injects the page's \`metadata\`, the
600
+ stylesheet and preload tags for this route, and the canonical links
601
+ into <head> by default — <Head /> just says WHERE they land.
602
+
603
+ Do not add a <title> here: the page's \`metadata\` owns it, and a root
604
+ that emits one too produces two.
605
+ */}
606
+ <Head />
607
+ </head>
608
+ <body>
609
+ {/*
610
+ REQUIRED — this is the hydration mount point, not a styling wrapper.
611
+
612
+ The browser runtime looks up \`#root\` and hydrates that element only.
613
+ Remove this div, or rename the id, and the page still renders from the
614
+ server but never becomes interactive: the runtime throws in the console
615
+ and nothing on screen changes.
616
+
617
+ Wrap it in your own markup freely, and put anything that must live
618
+ outside the hydrated tree (a static footer, a portal target) outside
619
+ it — just keep an element with \`id="root"\` around {children}.
620
+ */}
621
+ <div id="root">{children}</div>
622
+ {/*
623
+ The hydration payload and module tags. Written explicitly because
624
+ placement occasionally matters — a CSP nonce, or ordering against
625
+ your own scripts.
626
+ */}
627
+ <Scripts />
628
+ </body>
629
+ </html>
630
+ );
631
+ }
632
+ `;
633
+ /**
634
+ * `src/web/home.page.tsx` — one page, so \`warlock dev\` has something to serve
635
+ * the moment this finishes.
636
+ */
637
+ const webHomePageStub = `import type { PageProps } from "@warlock.js/web";
638
+
639
+ /**
640
+ * A page route is an ordinary Warlock route whose handler renders React
641
+ * instead of returning JSON.
642
+ *
643
+ * The URL is DECLARED here rather than derived from the filename. Omit
644
+ * \`route\` and it is derived from this file's location instead — never both.
645
+ */
646
+ export const route = "/";
647
+
648
+ export const metadata = { title: "Home" };
649
+
650
+ /**
651
+ * Add a \`loader\` export to fetch data on the server, and it arrives here as
652
+ * \`data\`, typed:
653
+ *
654
+ * export const loader = (async () => ({ items: await itemsRepository.all() }));
655
+ * export default function HomePage({ data }: PageProps<typeof loader>) { ... }
656
+ */
657
+ export default function HomePage(_props: PageProps) {
658
+ return (
659
+ <>
660
+ <h1>It renders.</h1>
661
+ <p>
662
+ This page is <code>src/web/home.page.tsx</code>. Edit it and the browser
663
+ updates without losing state.
664
+ </p>
665
+ </>
666
+ );
667
+ }
668
+ `;
577
669
 
578
670
  //#endregion
579
- export { accessConfigStub, accessResolverStub, accessRoleMigrationStub, accessRoleModelIndexStub, accessRoleModelStub, accessUserRoleMigrationStub, accessUserRoleModelIndexStub, accessUserRoleModelStub, aiConfigStub, communicatorsConfigStub, notificationControllersStub, notificationMigrationStub, notificationModelStub, notificationRoutesStub, notificationsConfigStub, socketConfigStub };
671
+ export { accessConfigStub, accessResolverStub, accessRoleMigrationStub, accessRoleModelIndexStub, accessRoleModelStub, accessUserRoleMigrationStub, accessUserRoleModelIndexStub, accessUserRoleModelStub, aiConfigStub, communicatorsConfigStub, notificationControllersStub, notificationMigrationStub, notificationModelStub, notificationRoutesStub, notificationsConfigStub, socketConfigStub, webHomePageStub, webRootStub };
580
672
  //# sourceMappingURL=stubs.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"stubs.mjs","names":[],"sources":["../../../../../../../core/src/generations/stubs.ts"],"sourcesContent":["export const accessConfigStub = `import { type AccessConfigurations } from \"@warlock.js/access\";\r\nimport { DatabaseAccessResolver } from \"app/access/services/access-resolver\";\r\n\r\n/**\r\n * Authorization configuration — read by @warlock.js/access on boot.\r\n *\r\n * The resolver is the one required piece: it tells the engine how to read a\r\n * user's roles + permissions. The ejected DatabaseAccessResolver reads roles\r\n * from the user_roles table and maps them through the roles catalog table (so\r\n * roles + their permissions are managed at runtime, in the DB).\r\n *\r\n * For a fixed, code-defined catalog with no tables, swap in DefaultAccessResolver:\r\n * import { DefaultAccessResolver } from \"@warlock.js/access\";\r\n * resolver: new DefaultAccessResolver({ admin: [\"*\"], editor: [\"orders.*\"] }),\r\n *\r\n * Multi-tenant? Add a \\`resolveTenant()\\` to the resolver to read the active\r\n * tenant from the request; checks then scope to it automatically.\r\n */\r\nconst access: AccessConfigurations = {\r\n resolver: new DatabaseAccessResolver(),\r\n\r\n // Cache resolved permission sets (default \"10m\").\r\n // cache: { ttl: \"10m\" },\r\n};\r\n\r\nexport default access;\r\n`;\r\n\r\nexport const aiConfigStub = `import type { AIConfig } from \"@warlock.js/ai\";\r\n\r\n// >>> warlock:ai-packages (auto-managed) >>>\r\n// Satellite packages augment the \"ai\" object on import — e.g. ai.workspace,\r\n// ai.tools / ai.mcp, and panoptic's ai.config({ panoptic }) wiring. The command\r\n// \"warlock add ai-workspace | ai-tools | ai-panoptic\" adds the matching\r\n// side-effect import below; keep them so the augmentation + runtime registration\r\n// load before the ai connector applies this config.\r\n// <<< warlock:ai-packages <<<\r\n\r\n/**\r\n * AI configuration — applied on boot by the ai connector, which calls\r\n * ai.config(...) with the object below. Cross-cutting defaults live here\r\n * (shared cache / snapshot stores, observability); per-call options always win.\r\n *\r\n * Wire a default model from a provider you installed, e.g.:\r\n * import { OpenAISDK } from \"@warlock.js/ai-openai\";\r\n * const openai = OpenAISDK({ apiKey: env(\"OPENAI_API_KEY\") });\r\n * // then pass openai.model({ name: \"gpt-4o-mini\" }) into your agents.\r\n */\r\nconst ai: Partial<AIConfig> = {\r\n // Default cache driver for cache-backed AI features (semantic cache, rag / memory vector stores).\r\n // defaultStore: cache.driver(\"redis\", { client }),\r\n\r\n // Observability — requires \"warlock add ai-panoptic\". Exporters + the local dashboard.\r\n // panoptic: { exporters: [], dashboard: false, observeAll: false },\r\n};\r\n\r\nexport default ai;\r\n`;\r\n\r\nexport const accessRoleModelStub = `import { Model, RegisterModel } from \"@warlock.js/cascade\";\r\nimport { type Infer, v } from \"@warlock.js/seal\";\r\n\r\n/**\r\n * Validation schema for the roles catalog — mirrors the migration columns\r\n * (snake_case). Each row is a role name plus the permission strings it grants;\r\n * wildcards work (\"orders.*\", \"*\"). The DatabaseAccessResolver maps a user's\r\n * assigned role names through this table to their effective permissions.\r\n */\r\nexport const roleSchema = v.object({\r\n name: v.string(),\r\n permissions: v.array(v.string()).default([]),\r\n});\r\n\r\nexport type RoleSchema = Infer<typeof roleSchema>;\r\n\r\n/**\r\n * The roles catalog — role name → the permissions it grants. Managed at runtime\r\n * (admins add roles + edit their permissions), unlike a fixed code map. Read by\r\n * DatabaseAccessResolver.resolvePermissions to expand a user's roles to permissions.\r\n */\r\n@RegisterModel()\r\nexport class Role extends Model<RoleSchema> {\r\n public static table = \"roles\";\r\n\r\n public static schema = roleSchema;\r\n\r\n /** The permission strings this role grants. */\r\n public get permissions(): string[] {\r\n return this.get<string[]>(\"permissions\", []);\r\n }\r\n}\r\n`;\r\n\r\nexport const accessRoleModelIndexStub = `export * from \"./role.model\";\r\n`;\r\n\r\nexport const accessRoleMigrationStub = `import { arrayText, Migration, text } from \"@warlock.js/cascade\";\r\nimport { Role } from \"../role.model\";\r\n\r\n/**\r\n * Roles catalog table. \\`name\\` is unique (one row per role); \\`permissions\\` is a\r\n * text array of the permission strings the role grants.\r\n */\r\nexport default Migration.create(Role, {\r\n name: text().notNullable().unique(),\r\n permissions: arrayText().nullable(),\r\n});\r\n`;\r\n\r\nexport const accessUserRoleModelStub = `import { access } from \"@warlock.js/access\";\r\nimport type { Auth } from \"@warlock.js/auth\";\r\nimport { Model, RegisterModel } from \"@warlock.js/cascade\";\r\nimport { type Infer, v } from \"@warlock.js/seal\";\r\n\r\n/**\r\n * Validation schema for a role assignment — mirrors the migration columns\r\n * (snake_case). \\`tenant\\` is nullable: a null tenant is a GLOBAL assignment.\r\n */\r\nexport const userRoleSchema = v.object({\r\n user_id: v.string(),\r\n user_type: v.string(),\r\n role: v.string(),\r\n tenant: v.string().optional(),\r\n});\r\n\r\nexport type UserRoleSchema = Infer<typeof userRoleSchema>;\r\n\r\n/**\r\n * The role-assignment table — which roles a user holds, optionally per tenant.\r\n * Read by DatabaseAccessResolver.resolveRoles; mutated via the statics below.\r\n * \\`assign\\` / \\`revoke\\` flush the cached permission set automatically, so callers\r\n * never need to call \\`access.flush(user, tenant)\\` themselves.\r\n */\r\n@RegisterModel()\r\nexport class UserRole extends Model<UserRoleSchema> {\r\n public static table = \"user_roles\";\r\n\r\n public static schema = userRoleSchema;\r\n\r\n /**\r\n * Role names assigned to the user in the given tenant.\r\n *\r\n * An unresolved tenant (\\`undefined\\`) scopes to GLOBAL roles only — the rows\r\n * stored with no tenant (\\`null\\`) — never the union across every tenant. The\r\n * union would be a privilege-escalation: a user who is \\`owner\\` in one tenant\r\n * must not be treated as \\`owner\\` everywhere just because a check didn't carry\r\n * a tenant. This mirrors how \\`assign(user, role)\\` stores a global row.\r\n */\r\n public static async rolesFor(user: Auth, tenant?: string): Promise<string[]> {\r\n const rows = await this.query()\r\n .where({\r\n user_id: user.id,\r\n user_type: user.userType,\r\n tenant: tenant ?? null,\r\n })\r\n .get();\r\n\r\n // De-dupe so a duplicate row (a concurrent assign that slipped past the\r\n // existence check) can't distort the resolved set.\r\n return [...new Set(rows.map((row) => row.get(\"role\") as string))];\r\n }\r\n\r\n /**\r\n * Assign a role to the user. No-op if the assignment already exists.\r\n * Flushes the user's cached permission set automatically.\r\n */\r\n public static async assign(user: Auth, role: string, tenant?: string): Promise<void> {\r\n const existing = await this.first({\r\n user_id: user.id,\r\n user_type: user.userType,\r\n role,\r\n tenant: tenant ?? null,\r\n });\r\n\r\n if (existing) return;\r\n\r\n await this.create({\r\n user_id: user.id,\r\n user_type: user.userType,\r\n role,\r\n tenant,\r\n });\r\n\r\n await access.flush(user, tenant);\r\n }\r\n\r\n /**\r\n * Remove a role assignment from the user.\r\n * Flushes the user's cached permission set automatically.\r\n */\r\n public static async revoke(user: Auth, role: string, tenant?: string): Promise<void> {\r\n await this.delete({\r\n user_id: user.id,\r\n user_type: user.userType,\r\n role,\r\n tenant: tenant ?? null,\r\n });\r\n\r\n await access.flush(user, tenant);\r\n }\r\n}\r\n`;\r\n\r\nexport const accessUserRoleModelIndexStub = `export * from \"./user-role.model\";\r\n`;\r\n\r\nexport const accessUserRoleMigrationStub = `import { Migration, text, uuid } from \"@warlock.js/cascade\";\r\nimport { UserRole } from \"../user-role.model\";\r\n\r\n/**\r\n * Role-assignment table. \\`user_id\\` is a UUID — override this migration if your\r\n * user ids are integers. The composite index powers the per-user (per-tenant)\r\n * lookup the resolver runs on every check.\r\n */\r\nexport default Migration.create(\r\n UserRole,\r\n {\r\n user_id: uuid().notNullable().index(),\r\n user_type: text().notNullable(),\r\n role: text().notNullable().index(),\r\n tenant: text().nullable().index(),\r\n },\r\n {\r\n index: [{ columns: [\"user_id\", \"user_type\", \"tenant\"] }],\r\n },\r\n);\r\n`;\r\n\r\nexport const accessResolverStub = `import type { AccessResolver } from \"@warlock.js/access\";\r\nimport type { Auth } from \"@warlock.js/auth\";\r\nimport { Role } from \"app/access/models/role\";\r\nimport { UserRole } from \"app/access/models/user-role\";\r\n\r\n/**\r\n * The app's access adapter — connects @warlock.js/access to the ejected role\r\n * tables. Roles come from the user_roles assignment table; permissions are\r\n * expanded by mapping those role names through the roles catalog table. Both\r\n * are managed at runtime (in the DB), so admins can add roles + edit their\r\n * permissions without a deploy.\r\n *\r\n * The engine owns the hard parts (wildcard matching, caching, fail-closed); this\r\n * resolver only fetches — keep it dumb, never cache inside it.\r\n */\r\nexport class DatabaseAccessResolver implements AccessResolver {\r\n /** The role names this user holds (powers \\`hasRole\\` / \\`hasAnyRole\\`). */\r\n public async resolveRoles(user: Auth, tenant?: string): Promise<string[]> {\r\n return UserRole.rolesFor(user, tenant);\r\n }\r\n\r\n /** The effective permission strings this user has (powers \\`can\\` / \\`authorize\\`). */\r\n public async resolvePermissions(user: Auth, tenant?: string): Promise<string[]> {\r\n const names = await this.resolveRoles(user, tenant);\r\n\r\n if (names.length === 0) return [];\r\n\r\n const roles = await Role.query().whereIn(\"name\", names).get();\r\n\r\n // Flatten + de-dupe so two roles granting the same permission yield one entry.\r\n return [...new Set(roles.flatMap((role) => role.permissions))];\r\n }\r\n\r\n /**\r\n * Optional. Resolve the ambient tenant when a check doesn't pass one\r\n * explicitly — derive it from the authenticated user (safer than reading\r\n * client request input, which a caller could spoof). Uncomment + adapt for a\r\n * multi-tenant app (single-tenant apps leave this off and return undefined).\r\n */\r\n // public resolveTenant(user: Auth): string | undefined {\r\n // return user.get(\"organization_id\");\r\n // }\r\n}\r\n`;\r\n\r\nexport const socketConfigStub = `import type { SocketOptions } from \"@warlock.js/core\";\r\n\r\n/**\r\n * Socket.IO configuration — read by the framework's socket connector\r\n * on boot. When the HTTP server is running the socket server attaches\r\n * to it; otherwise it listens on its own configured port.\r\n *\r\n * Remove this file to disable the socket server entirely.\r\n */\r\nexport default {\r\n options: {\r\n cors: {\r\n origin: \"*\",\r\n },\r\n },\r\n} as SocketOptions;\r\n`;\r\n\r\nexport const communicatorsConfigStub = `import { env } from \"@warlock.js/core\";\r\nimport type { BrokerConfigurations, RabbitMQClientOptions } from \"@warlock.js/herald\";\r\n\r\nconst heraldConfigurations: BrokerConfigurations<RabbitMQClientOptions> = {\r\n driver: \"rabbitmq\",\r\n name: \"default\",\r\n isDefault: true,\r\n\r\n // ============================================================================\r\n // Connection Settings\r\n // ============================================================================\r\n\r\n host: env(\"RABBITMQ_HOST\", \"localhost\"),\r\n port: env(\"RABBITMQ_PORT\", 5672),\r\n username: env(\"RABBITMQ_USERNAME\", \"guest\"),\r\n password: env(\"RABBITMQ_PASSWORD\", \"guest\"),\r\n vhost: env(\"RABBITMQ_VHOST\", \"/\"),\r\n\r\n // Or use connection URI (takes precedence over host/port)\r\n // uri: env(\"RABBITMQ_URL\"),\r\n\r\n // ============================================================================\r\n // Connection Options\r\n // ============================================================================\r\n\r\n /** Heartbeat interval in seconds */\r\n heartbeat: 60,\r\n\r\n /** Connection timeout in milliseconds */\r\n connectionTimeout: 10000,\r\n\r\n /** Enable automatic reconnection on disconnect */\r\n reconnect: true,\r\n\r\n /** Delay between reconnection attempts in milliseconds */\r\n reconnectDelay: 5_000,\r\n\r\n // ============================================================================\r\n // Consumer Options\r\n // ============================================================================\r\n\r\n /** Default prefetch count (number of unacknowledged messages per consumer) */\r\n prefetch: 10,\r\n\r\n // ============================================================================\r\n // Client Options (Native amqplib options)\r\n // ============================================================================\r\n // These options are passed directly to amqplib.connect()\r\n // for low-level configuration like frame size, TLS, socket options, etc.\r\n // ============================================================================\r\n clientOptions: {\r\n // Frame max size in bytes (0 = no limit)\r\n // frameMax: 0,\r\n\r\n // Channel max (0 = unlimited)\r\n // channelMax: 0,\r\n\r\n // Socket options\r\n socket: {\r\n // Enable TCP keep-alive\r\n keepAlive: true,\r\n\r\n // Disable Nagle's algorithm for lower latency\r\n noDelay: true,\r\n\r\n // Socket timeout (in addition to heartbeat)\r\n // timeout: 30000,\r\n },\r\n\r\n // TLS/SSL options (uncomment for secure connections)\r\n // socket: {\r\n // ca: fs.readFileSync('/path/to/ca.pem'),\r\n // cert: fs.readFileSync('/path/to/cert.pem'),\r\n // key: fs.readFileSync('/path/to/key.pem'),\r\n // rejectUnauthorized: true,\r\n // },\r\n },\r\n};\r\n\r\nexport default heraldConfigurations;\r\n`;\r\n\r\nexport const notificationsConfigStub = `import { type NotificationConfig, inApp, mailChannel } from \"@warlock.js/notifications\";\r\nimport { Notification } from \"app/notifications/notification.model\";\r\n\r\n/**\r\n * Notifications configuration. Auto-loaded from src/config on boot — the\r\n * framework's notifications connector reads this default export and hands it to\r\n * setNotificationConfig, so this file stays declarative (no side-effect call).\r\n *\r\n * Each channel is payload-typed, so notify.mail(...) / notify.database(...)\r\n * and defineNotification are type-checked against the registry.\r\n *\r\n * Channels enabled here:\r\n * - mail wraps @warlock.js/core sendMail; route is notifiable.email.\r\n * The \"from\" address defaults to config/mail.ts; override per\r\n * channel with mailChannel({ from: \"no-reply@yourapp.com\" }).\r\n * - database in-app store backed by the Notification model. The \"inApp\"\r\n * facade exposes the recipient-scoped read API: listUnread,\r\n * countUnread, markAsRead, dismiss, ...\r\n *\r\n * Async delivery (.queue()) is OPTIONAL: run \"npx warlock add herald\",\r\n * import { heraldQueue } from \"@warlock.js/notifications\", and uncomment the\r\n * queue line below.\r\n */\r\nconst config: NotificationConfig = {\r\n channels: {\r\n mail: mailChannel(),\r\n database: inApp.configure({ model: Notification }),\r\n },\r\n\r\n // Async queue — requires @warlock.js/herald (npx warlock add herald):\r\n // queue: heraldQueue(),\r\n};\r\n\r\nexport default config;\r\n`;\r\n\r\nexport const notificationModelStub = `import { RegisterModel } from \"@warlock.js/cascade\";\r\nimport { DatabaseNotification, type NotificationColumnMap } from \"@warlock.js/notifications\";\r\nimport { v } from \"@warlock.js/seal\";\r\n\r\n/**\r\n * Validation schema for the notifications table — mirrors the migration\r\n * columns (snake_case). Cascade validates + casts every write against it:\r\n * nullable columns use .nullish() (may be absent or null), and payload is\r\n * free-form JSON. Keep this in sync with the migration + columnMap when you\r\n * add or rename columns.\r\n */\r\nconst notificationSchema = v.object({\r\n user_id: v.string(),\r\n type: v.string(),\r\n title: v.string(),\r\n body: v.string().nullish(),\r\n payload: v.record(v.any()).nullish(),\r\n read_at: v.date().nullish(),\r\n idempotency_key: v.string().nullish(),\r\n});\r\n\r\n/**\r\n * In-app notification model.\r\n *\r\n * Extends the package's DatabaseNotification base, which provides the stable\r\n * accessors (recipientId, tenantId, isRead, readAt, markRead) — all derived\r\n * from the columnMap below. The read/write API lives on the inApp facade\r\n * (configured in config/notifications.ts); you rarely touch this class directly.\r\n */\r\n@RegisterModel()\r\nexport class Notification extends DatabaseNotification {\r\n public static table = \"notifications\";\r\n public static schema = notificationSchema;\r\n\r\n /**\r\n * Maps the in-app store's roles to your columns. This default is\r\n * single-tenant + read_at-only. Add tenant: \"organization_id\" for\r\n * multi-tenant; use isRead: \"is_read\" (instead of, or alongside, readAt) to\r\n * track a boolean read flag. The migration + accessors all follow this map.\r\n */\r\n public static columnMap: NotificationColumnMap = { readAt: \"read_at\" };\r\n}\r\n`;\r\n\r\nexport const notificationMigrationStub = `import { Migration } from \"@warlock.js/cascade\";\r\nimport { notificationColumns } from \"@warlock.js/notifications\";\r\nimport { Notification } from \"../notification.model\";\r\n\r\n/**\r\n * Notifications table.\r\n *\r\n * Columns come from notificationColumns(Notification) — the recipient / tenant\r\n * / read-state names follow the model's columnMap; type / title / body /\r\n * payload / idempotency_key are fixed. Spread it to add your own columns\r\n * (remember to mirror them in the model schema):\r\n *\r\n * import { uuid } from \"@warlock.js/cascade\";\r\n *\r\n * export default Migration.create(Notification, {\r\n * ...notificationColumns(Notification),\r\n * // category_id: uuid().index().nullable(),\r\n * });\r\n */\r\nexport default Migration.create(Notification, notificationColumns(Notification));\r\n`;\r\n\r\nexport const notificationControllersStub = `import { type Request, type RequestHandler, type Response } from \"@warlock.js/core\";\r\nimport { inApp } from \"@warlock.js/notifications\";\r\n\r\n/**\r\n * The authenticated user's notification HTTP surface — thin wrappers over the\r\n * recipient-scoped \\`inApp\\` facade (a foreign id can never touch another user's\r\n * rows). Notifications are produced by domain events, never over HTTP, so there\r\n * is no create. Trim or split these as your app grows.\r\n */\r\n\r\n/** GET /notifications — list, most recent first (page / limit / type / unread via query). */\r\nexport const listNotificationsController: RequestHandler = async (\r\n request: Request,\r\n response: Response,\r\n) => {\r\n const { data, pagination } = await inApp.list(request.user!, request.all());\r\n\r\n return response.success({ notifications: data, pagination });\r\n};\r\n\r\nlistNotificationsController.description = \"List notifications\";\r\n\r\n/** GET /notifications/unread-count — drives the bell badge. */\r\nexport const unreadNotificationsCountController: RequestHandler = async (\r\n request: Request,\r\n response: Response,\r\n) => {\r\n const count = await inApp.countUnread(request.user!);\r\n\r\n return response.success({ count });\r\n};\r\n\r\nunreadNotificationsCountController.description = \"Unread notifications count\";\r\n\r\n/** PATCH /notifications/:id/read — mark one read, return the updated row. */\r\nexport const markNotificationReadController: RequestHandler = async (\r\n request: Request,\r\n response: Response,\r\n) => {\r\n const id = request.input(\"id\");\r\n\r\n await inApp.markAsRead(request.user!, id);\r\n const notification = await inApp.find(request.user!, id);\r\n\r\n return response.success({ notification });\r\n};\r\n\r\nmarkNotificationReadController.description = \"Mark notification read\";\r\n\r\n/** PATCH /notifications/read-all — mark every unread one read. */\r\nexport const markAllNotificationsReadController: RequestHandler = async (\r\n request: Request,\r\n response: Response,\r\n) => {\r\n const count = await inApp.markAsRead(request.user!);\r\n\r\n return response.success({ count });\r\n};\r\n\r\nmarkAllNotificationsReadController.description = \"Mark all notifications read\";\r\n\r\n/** DELETE /notifications — dismiss all for the user. */\r\nexport const clearNotificationsController: RequestHandler = async (\r\n request: Request,\r\n response: Response,\r\n) => {\r\n await inApp.dismiss(request.user!);\r\n\r\n return response.noContent();\r\n};\r\n\r\nclearNotificationsController.description = \"Clear notifications\";\r\n\r\n/** DELETE /notifications/:id — dismiss one. */\r\nexport const deleteNotificationController: RequestHandler = async (\r\n request: Request,\r\n response: Response,\r\n) => {\r\n await inApp.dismiss(request.user!, request.input(\"id\"));\r\n\r\n return response.noContent();\r\n};\r\n\r\ndeleteNotificationController.description = \"Delete notification\";\r\n`;\r\n\r\nexport const notificationRoutesStub = `import { authMiddleware } from \"@warlock.js/auth\";\r\nimport { router } from \"@warlock.js/core\";\r\nimport {\r\n clearNotificationsController,\r\n deleteNotificationController,\r\n listNotificationsController,\r\n markAllNotificationsReadController,\r\n markNotificationReadController,\r\n unreadNotificationsCountController,\r\n} from \"./controllers/notifications.controller\";\r\n\r\n/**\r\n * Notification routes — the authenticated user's read + dismiss surface.\r\n *\r\n * Notifications are produced by domain events (never created over HTTP), so\r\n * there is no POST. Every route is gated by \\`authMiddleware\\` and recipient-\r\n * scoped by \\`inApp\\` (a foreign id touches zero rows). Delete any endpoint you\r\n * don't need; if your app reads notifications over sockets/GraphQL instead,\r\n * delete this file + the controllers entirely.\r\n */\r\nrouter.group({ prefix: \"/notifications\", middleware: [authMiddleware([])] }, () => {\r\n router.get(\"/\", listNotificationsController);\r\n router.get(\"/unread-count\", unreadNotificationsCountController);\r\n router.patch(\"/read-all\", markAllNotificationsReadController);\r\n router.patch(\"/:id/read\", markNotificationReadController);\r\n router.delete(\"/\", clearNotificationsController);\r\n router.delete(\"/:id\", deleteNotificationController);\r\n});\r\n`;\r\n"],"mappings":";AAAA,MAAa,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BhC,MAAa,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+B5B,MAAa,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCnC,MAAa,2BAA2B;;AAGxC,MAAa,0BAA0B;;;;;;;;;;;;AAavC,MAAa,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8FvC,MAAa,+BAA+B;;AAG5C,MAAa,8BAA8B;;;;;;;;;;;;;;;;;;;;;AAsB3C,MAAa,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6ClC,MAAa,mBAAmB;;;;;;;;;;;;;;;;;AAkBhC,MAAa,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkFvC,MAAa,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCvC,MAAa,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4CrC,MAAa,4BAA4B;;;;;;;;;;;;;;;;;;;;;AAsBzC,MAAa,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsF3C,MAAa,yBAAyB"}
1
+ {"version":3,"file":"stubs.mjs","names":[],"sources":["../../../../../../../core/src/generations/stubs.ts"],"sourcesContent":["export const accessConfigStub = `import { type AccessConfigurations } from \"@warlock.js/access\";\r\nimport { DatabaseAccessResolver } from \"app/access/services/access-resolver\";\r\n\r\n/**\r\n * Authorization configuration — read by @warlock.js/access on boot.\r\n *\r\n * The resolver is the one required piece: it tells the engine how to read a\r\n * user's roles + permissions. The ejected DatabaseAccessResolver reads roles\r\n * from the user_roles table and maps them through the roles catalog table (so\r\n * roles + their permissions are managed at runtime, in the DB).\r\n *\r\n * For a fixed, code-defined catalog with no tables, swap in DefaultAccessResolver:\r\n * import { DefaultAccessResolver } from \"@warlock.js/access\";\r\n * resolver: new DefaultAccessResolver({ admin: [\"*\"], editor: [\"orders.*\"] }),\r\n *\r\n * Multi-tenant? Add a \\`resolveTenant()\\` to the resolver to read the active\r\n * tenant from the request; checks then scope to it automatically.\r\n */\r\nconst access: AccessConfigurations = {\r\n resolver: new DatabaseAccessResolver(),\r\n\r\n // Cache resolved permission sets (default \"10m\").\r\n // cache: { ttl: \"10m\" },\r\n};\r\n\r\nexport default access;\r\n`;\r\n\r\nexport const aiConfigStub = `import type { AIConfig } from \"@warlock.js/ai\";\r\n\r\n// >>> warlock:ai-packages (auto-managed) >>>\r\n// Satellite packages augment the \"ai\" object on import — e.g. ai.workspace,\r\n// ai.tools / ai.mcp, and panoptic's ai.config({ panoptic }) wiring. The command\r\n// \"warlock add ai-workspace | ai-tools | ai-panoptic\" adds the matching\r\n// side-effect import below; keep them so the augmentation + runtime registration\r\n// load before the ai connector applies this config.\r\n// <<< warlock:ai-packages <<<\r\n\r\n/**\r\n * AI configuration — applied on boot by the ai connector, which calls\r\n * ai.config(...) with the object below. Cross-cutting defaults live here\r\n * (shared cache / snapshot stores, observability); per-call options always win.\r\n *\r\n * Wire a default model from a provider you installed, e.g.:\r\n * import { OpenAISDK } from \"@warlock.js/ai-openai\";\r\n * const openai = OpenAISDK({ apiKey: env(\"OPENAI_API_KEY\") });\r\n * // then pass openai.model({ name: \"gpt-4o-mini\" }) into your agents.\r\n */\r\nconst ai: Partial<AIConfig> = {\r\n // Default cache driver for cache-backed AI features (semantic cache, rag / memory vector stores).\r\n // defaultStore: cache.driver(\"redis\", { client }),\r\n\r\n // Observability — requires \"warlock add ai-panoptic\". Exporters + the local dashboard.\r\n // panoptic: { exporters: [], dashboard: false, observeAll: false },\r\n};\r\n\r\nexport default ai;\r\n`;\r\n\r\nexport const accessRoleModelStub = `import { Model, RegisterModel } from \"@warlock.js/cascade\";\r\nimport { type Infer, v } from \"@warlock.js/seal\";\r\n\r\n/**\r\n * Validation schema for the roles catalog — mirrors the migration columns\r\n * (snake_case). Each row is a role name plus the permission strings it grants;\r\n * wildcards work (\"orders.*\", \"*\"). The DatabaseAccessResolver maps a user's\r\n * assigned role names through this table to their effective permissions.\r\n */\r\nexport const roleSchema = v.object({\r\n name: v.string(),\r\n permissions: v.array(v.string()).default([]),\r\n});\r\n\r\nexport type RoleSchema = Infer<typeof roleSchema>;\r\n\r\n/**\r\n * The roles catalog — role name → the permissions it grants. Managed at runtime\r\n * (admins add roles + edit their permissions), unlike a fixed code map. Read by\r\n * DatabaseAccessResolver.resolvePermissions to expand a user's roles to permissions.\r\n */\r\n@RegisterModel()\r\nexport class Role extends Model<RoleSchema> {\r\n public static table = \"roles\";\r\n\r\n public static schema = roleSchema;\r\n\r\n /** The permission strings this role grants. */\r\n public get permissions(): string[] {\r\n return this.get<string[]>(\"permissions\", []);\r\n }\r\n}\r\n`;\r\n\r\nexport const accessRoleModelIndexStub = `export * from \"./role.model\";\r\n`;\r\n\r\nexport const accessRoleMigrationStub = `import { arrayText, Migration, text } from \"@warlock.js/cascade\";\r\nimport { Role } from \"../role.model\";\r\n\r\n/**\r\n * Roles catalog table. \\`name\\` is unique (one row per role); \\`permissions\\` is a\r\n * text array of the permission strings the role grants.\r\n */\r\nexport default Migration.create(Role, {\r\n name: text().notNullable().unique(),\r\n permissions: arrayText().nullable(),\r\n});\r\n`;\r\n\r\nexport const accessUserRoleModelStub = `import { access } from \"@warlock.js/access\";\r\nimport type { Auth } from \"@warlock.js/auth\";\r\nimport { Model, RegisterModel } from \"@warlock.js/cascade\";\r\nimport { type Infer, v } from \"@warlock.js/seal\";\r\n\r\n/**\r\n * Validation schema for a role assignment — mirrors the migration columns\r\n * (snake_case). \\`tenant\\` is nullable: a null tenant is a GLOBAL assignment.\r\n */\r\nexport const userRoleSchema = v.object({\r\n user_id: v.string(),\r\n user_type: v.string(),\r\n role: v.string(),\r\n tenant: v.string().optional(),\r\n});\r\n\r\nexport type UserRoleSchema = Infer<typeof userRoleSchema>;\r\n\r\n/**\r\n * The role-assignment table — which roles a user holds, optionally per tenant.\r\n * Read by DatabaseAccessResolver.resolveRoles; mutated via the statics below.\r\n * \\`assign\\` / \\`revoke\\` flush the cached permission set automatically, so callers\r\n * never need to call \\`access.flush(user, tenant)\\` themselves.\r\n */\r\n@RegisterModel()\r\nexport class UserRole extends Model<UserRoleSchema> {\r\n public static table = \"user_roles\";\r\n\r\n public static schema = userRoleSchema;\r\n\r\n /**\r\n * Role names assigned to the user in the given tenant.\r\n *\r\n * An unresolved tenant (\\`undefined\\`) scopes to GLOBAL roles only — the rows\r\n * stored with no tenant (\\`null\\`) — never the union across every tenant. The\r\n * union would be a privilege-escalation: a user who is \\`owner\\` in one tenant\r\n * must not be treated as \\`owner\\` everywhere just because a check didn't carry\r\n * a tenant. This mirrors how \\`assign(user, role)\\` stores a global row.\r\n */\r\n public static async rolesFor(user: Auth, tenant?: string): Promise<string[]> {\r\n const rows = await this.query()\r\n .where({\r\n user_id: user.id,\r\n user_type: user.userType,\r\n tenant: tenant ?? null,\r\n })\r\n .get();\r\n\r\n // De-dupe so a duplicate row (a concurrent assign that slipped past the\r\n // existence check) can't distort the resolved set.\r\n return [...new Set(rows.map((row) => row.get(\"role\") as string))];\r\n }\r\n\r\n /**\r\n * Assign a role to the user. No-op if the assignment already exists.\r\n * Flushes the user's cached permission set automatically.\r\n */\r\n public static async assign(user: Auth, role: string, tenant?: string): Promise<void> {\r\n const existing = await this.first({\r\n user_id: user.id,\r\n user_type: user.userType,\r\n role,\r\n tenant: tenant ?? null,\r\n });\r\n\r\n if (existing) return;\r\n\r\n await this.create({\r\n user_id: user.id,\r\n user_type: user.userType,\r\n role,\r\n tenant,\r\n });\r\n\r\n await access.flush(user, tenant);\r\n }\r\n\r\n /**\r\n * Remove a role assignment from the user.\r\n * Flushes the user's cached permission set automatically.\r\n */\r\n public static async revoke(user: Auth, role: string, tenant?: string): Promise<void> {\r\n await this.delete({\r\n user_id: user.id,\r\n user_type: user.userType,\r\n role,\r\n tenant: tenant ?? null,\r\n });\r\n\r\n await access.flush(user, tenant);\r\n }\r\n}\r\n`;\r\n\r\nexport const accessUserRoleModelIndexStub = `export * from \"./user-role.model\";\r\n`;\r\n\r\nexport const accessUserRoleMigrationStub = `import { Migration, text, uuid } from \"@warlock.js/cascade\";\r\nimport { UserRole } from \"../user-role.model\";\r\n\r\n/**\r\n * Role-assignment table. \\`user_id\\` is a UUID — override this migration if your\r\n * user ids are integers. The composite index powers the per-user (per-tenant)\r\n * lookup the resolver runs on every check.\r\n */\r\nexport default Migration.create(\r\n UserRole,\r\n {\r\n user_id: uuid().notNullable().index(),\r\n user_type: text().notNullable(),\r\n role: text().notNullable().index(),\r\n tenant: text().nullable().index(),\r\n },\r\n {\r\n index: [{ columns: [\"user_id\", \"user_type\", \"tenant\"] }],\r\n },\r\n);\r\n`;\r\n\r\nexport const accessResolverStub = `import type { AccessResolver } from \"@warlock.js/access\";\r\nimport type { Auth } from \"@warlock.js/auth\";\r\nimport { Role } from \"app/access/models/role\";\r\nimport { UserRole } from \"app/access/models/user-role\";\r\n\r\n/**\r\n * The app's access adapter — connects @warlock.js/access to the ejected role\r\n * tables. Roles come from the user_roles assignment table; permissions are\r\n * expanded by mapping those role names through the roles catalog table. Both\r\n * are managed at runtime (in the DB), so admins can add roles + edit their\r\n * permissions without a deploy.\r\n *\r\n * The engine owns the hard parts (wildcard matching, caching, fail-closed); this\r\n * resolver only fetches — keep it dumb, never cache inside it.\r\n */\r\nexport class DatabaseAccessResolver implements AccessResolver {\r\n /** The role names this user holds (powers \\`hasRole\\` / \\`hasAnyRole\\`). */\r\n public async resolveRoles(user: Auth, tenant?: string): Promise<string[]> {\r\n return UserRole.rolesFor(user, tenant);\r\n }\r\n\r\n /** The effective permission strings this user has (powers \\`can\\` / \\`authorize\\`). */\r\n public async resolvePermissions(user: Auth, tenant?: string): Promise<string[]> {\r\n const names = await this.resolveRoles(user, tenant);\r\n\r\n if (names.length === 0) return [];\r\n\r\n const roles = await Role.query().whereIn(\"name\", names).get();\r\n\r\n // Flatten + de-dupe so two roles granting the same permission yield one entry.\r\n return [...new Set(roles.flatMap((role) => role.permissions))];\r\n }\r\n\r\n /**\r\n * Optional. Resolve the ambient tenant when a check doesn't pass one\r\n * explicitly — derive it from the authenticated user (safer than reading\r\n * client request input, which a caller could spoof). Uncomment + adapt for a\r\n * multi-tenant app (single-tenant apps leave this off and return undefined).\r\n */\r\n // public resolveTenant(user: Auth): string | undefined {\r\n // return user.get(\"organization_id\");\r\n // }\r\n}\r\n`;\r\n\r\nexport const socketConfigStub = `import type { SocketOptions } from \"@warlock.js/core\";\r\n\r\n/**\r\n * Socket.IO configuration — read by the framework's socket connector\r\n * on boot. When the HTTP server is running the socket server attaches\r\n * to it; otherwise it listens on its own configured port.\r\n *\r\n * Remove this file to disable the socket server entirely.\r\n */\r\nexport default {\r\n options: {\r\n cors: {\r\n origin: \"*\",\r\n },\r\n },\r\n} as SocketOptions;\r\n`;\r\n\r\nexport const communicatorsConfigStub = `import { env } from \"@warlock.js/core\";\r\nimport type { BrokerConfigurations, RabbitMQClientOptions } from \"@warlock.js/herald\";\r\n\r\nconst heraldConfigurations: BrokerConfigurations<RabbitMQClientOptions> = {\r\n driver: \"rabbitmq\",\r\n name: \"default\",\r\n isDefault: true,\r\n\r\n // ============================================================================\r\n // Connection Settings\r\n // ============================================================================\r\n\r\n host: env(\"RABBITMQ_HOST\", \"localhost\"),\r\n port: env(\"RABBITMQ_PORT\", 5672),\r\n username: env(\"RABBITMQ_USERNAME\", \"guest\"),\r\n password: env(\"RABBITMQ_PASSWORD\", \"guest\"),\r\n vhost: env(\"RABBITMQ_VHOST\", \"/\"),\r\n\r\n // Or use connection URI (takes precedence over host/port)\r\n // uri: env(\"RABBITMQ_URL\"),\r\n\r\n // ============================================================================\r\n // Connection Options\r\n // ============================================================================\r\n\r\n /** Heartbeat interval in seconds */\r\n heartbeat: 60,\r\n\r\n /** Connection timeout in milliseconds */\r\n connectionTimeout: 10000,\r\n\r\n /** Enable automatic reconnection on disconnect */\r\n reconnect: true,\r\n\r\n /** Delay between reconnection attempts in milliseconds */\r\n reconnectDelay: 5_000,\r\n\r\n // ============================================================================\r\n // Consumer Options\r\n // ============================================================================\r\n\r\n /** Default prefetch count (number of unacknowledged messages per consumer) */\r\n prefetch: 10,\r\n\r\n // ============================================================================\r\n // Client Options (Native amqplib options)\r\n // ============================================================================\r\n // These options are passed directly to amqplib.connect()\r\n // for low-level configuration like frame size, TLS, socket options, etc.\r\n // ============================================================================\r\n clientOptions: {\r\n // Frame max size in bytes (0 = no limit)\r\n // frameMax: 0,\r\n\r\n // Channel max (0 = unlimited)\r\n // channelMax: 0,\r\n\r\n // Socket options\r\n socket: {\r\n // Enable TCP keep-alive\r\n keepAlive: true,\r\n\r\n // Disable Nagle's algorithm for lower latency\r\n noDelay: true,\r\n\r\n // Socket timeout (in addition to heartbeat)\r\n // timeout: 30000,\r\n },\r\n\r\n // TLS/SSL options (uncomment for secure connections)\r\n // socket: {\r\n // ca: fs.readFileSync('/path/to/ca.pem'),\r\n // cert: fs.readFileSync('/path/to/cert.pem'),\r\n // key: fs.readFileSync('/path/to/key.pem'),\r\n // rejectUnauthorized: true,\r\n // },\r\n },\r\n};\r\n\r\nexport default heraldConfigurations;\r\n`;\r\n\r\nexport const notificationsConfigStub = `import { type NotificationConfig, inApp, mailChannel } from \"@warlock.js/notifications\";\r\nimport { Notification } from \"app/notifications/notification.model\";\r\n\r\n/**\r\n * Notifications configuration. Auto-loaded from src/config on boot — the\r\n * framework's notifications connector reads this default export and hands it to\r\n * setNotificationConfig, so this file stays declarative (no side-effect call).\r\n *\r\n * Each channel is payload-typed, so notify.mail(...) / notify.database(...)\r\n * and defineNotification are type-checked against the registry.\r\n *\r\n * Channels enabled here:\r\n * - mail wraps @warlock.js/core sendMail; route is notifiable.email.\r\n * The \"from\" address defaults to config/mail.ts; override per\r\n * channel with mailChannel({ from: \"no-reply@yourapp.com\" }).\r\n * - database in-app store backed by the Notification model. The \"inApp\"\r\n * facade exposes the recipient-scoped read API: listUnread,\r\n * countUnread, markAsRead, dismiss, ...\r\n *\r\n * Async delivery (.queue()) is OPTIONAL: run \"npx warlock add herald\",\r\n * import { heraldQueue } from \"@warlock.js/notifications\", and uncomment the\r\n * queue line below.\r\n */\r\nconst config: NotificationConfig = {\r\n channels: {\r\n mail: mailChannel(),\r\n database: inApp.configure({ model: Notification }),\r\n },\r\n\r\n // Async queue — requires @warlock.js/herald (npx warlock add herald):\r\n // queue: heraldQueue(),\r\n};\r\n\r\nexport default config;\r\n`;\r\n\r\nexport const notificationModelStub = `import { RegisterModel } from \"@warlock.js/cascade\";\r\nimport { DatabaseNotification, type NotificationColumnMap } from \"@warlock.js/notifications\";\r\nimport { v } from \"@warlock.js/seal\";\r\n\r\n/**\r\n * Validation schema for the notifications table — mirrors the migration\r\n * columns (snake_case). Cascade validates + casts every write against it:\r\n * nullable columns use .nullish() (may be absent or null), and payload is\r\n * free-form JSON. Keep this in sync with the migration + columnMap when you\r\n * add or rename columns.\r\n */\r\nconst notificationSchema = v.object({\r\n user_id: v.string(),\r\n type: v.string(),\r\n title: v.string(),\r\n body: v.string().nullish(),\r\n payload: v.record(v.any()).nullish(),\r\n read_at: v.date().nullish(),\r\n idempotency_key: v.string().nullish(),\r\n});\r\n\r\n/**\r\n * In-app notification model.\r\n *\r\n * Extends the package's DatabaseNotification base, which provides the stable\r\n * accessors (recipientId, tenantId, isRead, readAt, markRead) — all derived\r\n * from the columnMap below. The read/write API lives on the inApp facade\r\n * (configured in config/notifications.ts); you rarely touch this class directly.\r\n */\r\n@RegisterModel()\r\nexport class Notification extends DatabaseNotification {\r\n public static table = \"notifications\";\r\n public static schema = notificationSchema;\r\n\r\n /**\r\n * Maps the in-app store's roles to your columns. This default is\r\n * single-tenant + read_at-only. Add tenant: \"organization_id\" for\r\n * multi-tenant; use isRead: \"is_read\" (instead of, or alongside, readAt) to\r\n * track a boolean read flag. The migration + accessors all follow this map.\r\n */\r\n public static columnMap: NotificationColumnMap = { readAt: \"read_at\" };\r\n}\r\n`;\r\n\r\nexport const notificationMigrationStub = `import { Migration } from \"@warlock.js/cascade\";\r\nimport { notificationColumns } from \"@warlock.js/notifications\";\r\nimport { Notification } from \"../notification.model\";\r\n\r\n/**\r\n * Notifications table.\r\n *\r\n * Columns come from notificationColumns(Notification) — the recipient / tenant\r\n * / read-state names follow the model's columnMap; type / title / body /\r\n * payload / idempotency_key are fixed. Spread it to add your own columns\r\n * (remember to mirror them in the model schema):\r\n *\r\n * import { uuid } from \"@warlock.js/cascade\";\r\n *\r\n * export default Migration.create(Notification, {\r\n * ...notificationColumns(Notification),\r\n * // category_id: uuid().index().nullable(),\r\n * });\r\n */\r\nexport default Migration.create(Notification, notificationColumns(Notification));\r\n`;\r\n\r\nexport const notificationControllersStub = `import { type Request, type RequestHandler, type Response } from \"@warlock.js/core\";\r\nimport { inApp } from \"@warlock.js/notifications\";\r\n\r\n/**\r\n * The authenticated user's notification HTTP surface — thin wrappers over the\r\n * recipient-scoped \\`inApp\\` facade (a foreign id can never touch another user's\r\n * rows). Notifications are produced by domain events, never over HTTP, so there\r\n * is no create. Trim or split these as your app grows.\r\n */\r\n\r\n/** GET /notifications — list, most recent first (page / limit / type / unread via query). */\r\nexport const listNotificationsController: RequestHandler = async (\r\n request: Request,\r\n response: Response,\r\n) => {\r\n const { data, pagination } = await inApp.list(request.user!, request.all());\r\n\r\n return response.success({ notifications: data, pagination });\r\n};\r\n\r\nlistNotificationsController.description = \"List notifications\";\r\n\r\n/** GET /notifications/unread-count — drives the bell badge. */\r\nexport const unreadNotificationsCountController: RequestHandler = async (\r\n request: Request,\r\n response: Response,\r\n) => {\r\n const count = await inApp.countUnread(request.user!);\r\n\r\n return response.success({ count });\r\n};\r\n\r\nunreadNotificationsCountController.description = \"Unread notifications count\";\r\n\r\n/** PATCH /notifications/:id/read — mark one read, return the updated row. */\r\nexport const markNotificationReadController: RequestHandler = async (\r\n request: Request,\r\n response: Response,\r\n) => {\r\n const id = request.input(\"id\");\r\n\r\n await inApp.markAsRead(request.user!, id);\r\n const notification = await inApp.find(request.user!, id);\r\n\r\n return response.success({ notification });\r\n};\r\n\r\nmarkNotificationReadController.description = \"Mark notification read\";\r\n\r\n/** PATCH /notifications/read-all — mark every unread one read. */\r\nexport const markAllNotificationsReadController: RequestHandler = async (\r\n request: Request,\r\n response: Response,\r\n) => {\r\n const count = await inApp.markAsRead(request.user!);\r\n\r\n return response.success({ count });\r\n};\r\n\r\nmarkAllNotificationsReadController.description = \"Mark all notifications read\";\r\n\r\n/** DELETE /notifications — dismiss all for the user. */\r\nexport const clearNotificationsController: RequestHandler = async (\r\n request: Request,\r\n response: Response,\r\n) => {\r\n await inApp.dismiss(request.user!);\r\n\r\n return response.noContent();\r\n};\r\n\r\nclearNotificationsController.description = \"Clear notifications\";\r\n\r\n/** DELETE /notifications/:id — dismiss one. */\r\nexport const deleteNotificationController: RequestHandler = async (\r\n request: Request,\r\n response: Response,\r\n) => {\r\n await inApp.dismiss(request.user!, request.input(\"id\"));\r\n\r\n return response.noContent();\r\n};\r\n\r\ndeleteNotificationController.description = \"Delete notification\";\r\n`;\r\n\r\nexport const notificationRoutesStub = `import { authMiddleware } from \"@warlock.js/auth\";\r\nimport { router } from \"@warlock.js/core\";\r\nimport {\r\n clearNotificationsController,\r\n deleteNotificationController,\r\n listNotificationsController,\r\n markAllNotificationsReadController,\r\n markNotificationReadController,\r\n unreadNotificationsCountController,\r\n} from \"./controllers/notifications.controller\";\r\n\r\n/**\r\n * Notification routes — the authenticated user's read + dismiss surface.\r\n *\r\n * Notifications are produced by domain events (never created over HTTP), so\r\n * there is no POST. Every route is gated by \\`authMiddleware\\` and recipient-\r\n * scoped by \\`inApp\\` (a foreign id touches zero rows). Delete any endpoint you\r\n * don't need; if your app reads notifications over sockets/GraphQL instead,\r\n * delete this file + the controllers entirely.\r\n */\r\nrouter.group({ prefix: \"/notifications\", middleware: [authMiddleware([])] }, () => {\r\n router.get(\"/\", listNotificationsController);\r\n router.get(\"/unread-count\", unreadNotificationsCountController);\r\n router.patch(\"/read-all\", markAllNotificationsReadController);\r\n router.patch(\"/:id/read\", markNotificationReadController);\r\n router.delete(\"/\", clearNotificationsController);\r\n router.delete(\"/:id\", deleteNotificationController);\r\n});\r\n`;\r\n\r\n/**\r\n * `src/web/root.tsx` — the application root for the SSR page layer.\r\n *\r\n * Deliberately minimal. The framework ships a default root, so this exists to\r\n * give you a place to start rather than because anything requires it. The\r\n * reference app (`v5/app/src/web/root.tsx`) is where to look for the fuller\r\n * shape: middleware, an app-level loader, locales, an ErrorBoundary.\r\n */\r\nexport const webRootStub = `import { Head, Scripts } from \"@warlock.js/web\";\r\nimport type { AppProps } from \"@warlock.js/web\";\r\n\r\n/**\r\n * The application root.\r\n *\r\n * NOT async, and it receives no request/response: it renders on the server and\r\n * again in the browser during hydration, where neither exists.\r\n */\r\nexport default function App({ children }: AppProps) {\r\n return (\r\n <html lang=\"en\">\r\n <head>\r\n {/*\r\n Placement only. The framework injects the page's \\`metadata\\`, the\r\n stylesheet and preload tags for this route, and the canonical links\r\n into <head> by default — <Head /> just says WHERE they land.\r\n\r\n Do not add a <title> here: the page's \\`metadata\\` owns it, and a root\r\n that emits one too produces two.\r\n */}\r\n <Head />\r\n </head>\r\n <body>\r\n {/*\r\n REQUIRED — this is the hydration mount point, not a styling wrapper.\r\n\r\n The browser runtime looks up \\`#root\\` and hydrates that element only.\r\n Remove this div, or rename the id, and the page still renders from the\r\n server but never becomes interactive: the runtime throws in the console\r\n and nothing on screen changes.\r\n\r\n Wrap it in your own markup freely, and put anything that must live\r\n outside the hydrated tree (a static footer, a portal target) outside\r\n it — just keep an element with \\`id=\"root\"\\` around {children}.\r\n */}\r\n <div id=\"root\">{children}</div>\r\n {/*\r\n The hydration payload and module tags. Written explicitly because\r\n placement occasionally matters — a CSP nonce, or ordering against\r\n your own scripts.\r\n */}\r\n <Scripts />\r\n </body>\r\n </html>\r\n );\r\n}\r\n`;\r\n\r\n/**\r\n * `src/web/home.page.tsx` — one page, so \\`warlock dev\\` has something to serve\r\n * the moment this finishes.\r\n */\r\nexport const webHomePageStub = `import type { PageProps } from \"@warlock.js/web\";\r\n\r\n/**\r\n * A page route is an ordinary Warlock route whose handler renders React\r\n * instead of returning JSON.\r\n *\r\n * The URL is DECLARED here rather than derived from the filename. Omit\r\n * \\`route\\` and it is derived from this file's location instead — never both.\r\n */\r\nexport const route = \"/\";\r\n\r\nexport const metadata = { title: \"Home\" };\r\n\r\n/**\r\n * Add a \\`loader\\` export to fetch data on the server, and it arrives here as\r\n * \\`data\\`, typed:\r\n *\r\n * export const loader = (async () => ({ items: await itemsRepository.all() }));\r\n * export default function HomePage({ data }: PageProps<typeof loader>) { ... }\r\n */\r\nexport default function HomePage(_props: PageProps) {\r\n return (\r\n <>\r\n <h1>It renders.</h1>\r\n <p>\r\n This page is <code>src/web/home.page.tsx</code>. Edit it and the browser\r\n updates without losing state.\r\n </p>\r\n </>\r\n );\r\n}\r\n`;\r\n"],"mappings":";AAAA,MAAa,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BhC,MAAa,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+B5B,MAAa,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCnC,MAAa,2BAA2B;;AAGxC,MAAa,0BAA0B;;;;;;;;;;;;AAavC,MAAa,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8FvC,MAAa,+BAA+B;;AAG5C,MAAa,8BAA8B;;;;;;;;;;;;;;;;;;;;;AAsB3C,MAAa,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6ClC,MAAa,mBAAmB;;;;;;;;;;;;;;;;;AAkBhC,MAAa,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkFvC,MAAa,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCvC,MAAa,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4CrC,MAAa,4BAA4B;;;;;;;;;;;;;;;;;;;;;AAsBzC,MAAa,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsF3C,MAAa,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCtC,MAAa,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqD3B,MAAa,kBAAkB"}
@@ -1,5 +1,6 @@
1
1
  import { Request } from "../request.mjs";
2
2
  import { Response } from "../response.mjs";
3
+ import { RequestUser } from "../types.mjs";
3
4
  import { Model } from "@warlock.js/cascade";
4
5
  import { Context } from "@warlock.js/context";
5
6
 
@@ -27,9 +28,19 @@ declare class RequestContext<User extends Model = Model> extends Context<Request
27
28
  */
28
29
  getResponse(): Response | undefined;
29
30
  /**
30
- * Get the current user
31
+ * Get the current user.
32
+ *
33
+ * Reads `request.user`, typed via the `RequestUser` module-augmentation
34
+ * surface (`core/src/http/types.ts`) — NOT the `User` generic above. That
35
+ * generic actually binds `Request<User>`'s `RequestValidation` parameter
36
+ * (see `RequestContextStore`), not "the user type"; there is no `Request`
37
+ * generic for the user today, so returning it as `User` was never sound
38
+ * (eed20184 step (b) inventory, `implementation/2026-08-20-A2-request-locals.md`
39
+ * §6.1). Callers that need a concrete model type — e.g.
40
+ * `useCurrentUser<MyUserModel>()` — cast at the call site; the app owns
41
+ * that shape via its own `RequestUser` augmentation.
31
42
  */
32
- getUser(): User | undefined;
43
+ getUser(): RequestUser | undefined;
33
44
  /**
34
45
  * Build the initial request store from HTTP context
35
46
  */
@@ -38,7 +49,7 @@ declare class RequestContext<User extends Model = Model> extends Context<Request
38
49
  /**
39
50
  * Global request context instance
40
51
  */
41
- declare const requestContext: RequestContext<Model>;
52
+ declare const requestContext: RequestContext<Model<import("@warlock.js/cascade").ModelSchema>>;
42
53
  /**
43
54
  * Use request store (for backward compatibility)
44
55
  */
@@ -1 +1 @@
1
- {"version":3,"file":"request-context.d.mts","names":[],"sources":["../../../../../../../../core/src/http/context/request-context.ts"],"mappings":";;;;;;;;AAQA;KAAY,mBAAA,cAAiC,KAAA,GAAQ,KAAA;EACnD,OAAA,EAAS,OAAA,CAAQ,IAAA;EACjB,QAAA,EAAU,QAAA;AAAA;;;;;;;cASN,cAAA,cAA4B,KAAA,GAAQ,KAAA,UAAe,OAAA,CAAQ,mBAAA,CAAoB,IAAA;EAXhC;;;EAe5C,UAAA,IAAc,OAAA,CAAQ,IAAA;EAb7B;;;EAoBO,WAAA,IAAe,QAAA;EAXlB;;;EAkBG,OAAA,IAAW,IAAA;EAlBsB;;;EAyBjC,UAAA,CAAW,OAAA,GAAU,MAAA,gBAAsB,mBAAA,CAAoB,IAAA;AAAA;;;;cAW3D,cAAA,EAAc,cAAA,CAAA,KAAA;;;;iBAOX,eAAA,kBAAiC,KAAA,GAAQ,KAAA,KACX,mBAAA,CAAoB,QAAA;AAAA,iBAGlD,UAAA,kBAA4B,KAAA,GAAQ,KAAA,KACZ,OAAA,CAAQ,QAAA;AAAA,iBAGhC,cAAA,kBAAgC,KAAA,GAAQ,KAAA,KACnB,QAAA"}
1
+ {"version":3,"file":"request-context.d.mts","names":[],"sources":["../../../../../../../../core/src/http/context/request-context.ts"],"mappings":";;;;;;;;;AASA;KAAY,mBAAA,cAAiC,KAAA,GAAQ,KAAA;EACnD,OAAA,EAAS,OAAA,CAAQ,IAAA;EACjB,QAAA,EAAU,QAAA;AAAA;;;;;;;cASN,cAAA,cAA4B,KAAA,GAAQ,KAAA,UAAe,OAAA,CAAQ,mBAAA,CAAoB,IAAA;EAXhC;;;EAe5C,UAAA,IAAc,OAAA,CAAQ,IAAA;EAb7B;;;EAoBO,WAAA,IAAe,QAAA;EAXlB;;;;;;;;;;;;;EA4BG,OAAA,IAAW,WAAA;EA5BqC;;;EAmChD,UAAA,CAAW,OAAA,GAAU,MAAA,gBAAsB,mBAAA,CAAoB,IAAA;AAAA;;;;cAW3D,cAAA,EAAc,cAAA,CAAA,KAAA,+BAAA,WAAA;;;;iBAOX,eAAA,kBAAiC,KAAA,GAAQ,KAAA,KACX,mBAAA,CAAoB,QAAA;AAAA,iBAGlD,UAAA,kBAA4B,KAAA,GAAQ,KAAA,KACZ,OAAA,CAAQ,QAAA;AAAA,iBAGhC,cAAA,kBAAgC,KAAA,GAAQ,KAAA,KAOR,QAAA"}
@@ -21,7 +21,17 @@ var RequestContext = class extends Context {
21
21
  return this.get("response");
22
22
  }
23
23
  /**
24
- * Get the current user
24
+ * Get the current user.
25
+ *
26
+ * Reads `request.user`, typed via the `RequestUser` module-augmentation
27
+ * surface (`core/src/http/types.ts`) — NOT the `User` generic above. That
28
+ * generic actually binds `Request<User>`'s `RequestValidation` parameter
29
+ * (see `RequestContextStore`), not "the user type"; there is no `Request`
30
+ * generic for the user today, so returning it as `User` was never sound
31
+ * (eed20184 step (b) inventory, `implementation/2026-08-20-A2-request-locals.md`
32
+ * §6.1). Callers that need a concrete model type — e.g.
33
+ * `useCurrentUser<MyUserModel>()` — cast at the call site; the app owns
34
+ * that shape via its own `RequestUser` augmentation.
25
35
  */
26
36
  getUser() {
27
37
  return this.getRequest()?.user;
@@ -1 +1 @@
1
- {"version":3,"file":"request-context.mjs","names":[],"sources":["../../../../../../../../core/src/http/context/request-context.ts"],"sourcesContent":["import type { Model } from \"@warlock.js/cascade\";\nimport { Context, contextManager } from \"@warlock.js/context\";\nimport type { Request } from \"../request\";\nimport type { Response } from \"../response\";\n\n/**\n * Request Context Store\n */\nexport type RequestContextStore<User extends Model = Model> = {\n request: Request<User>;\n response: Response;\n};\n\n/**\n * Request Context\n *\n * Manages request-scoped data (request, response, user) using AsyncLocalStorage.\n * Extends the base Context class for consistent API.\n */\nclass RequestContext<User extends Model = Model> extends Context<RequestContextStore<User>> {\n /**\n * Get the current request\n */\n public getRequest(): Request<User> | undefined {\n return this.get(\"request\");\n }\n\n /**\n * Get the current response\n */\n public getResponse(): Response | undefined {\n return this.get(\"response\");\n }\n\n /**\n * Get the current user\n */\n public getUser(): User | undefined {\n return this.getRequest()?.user;\n }\n\n /**\n * Build the initial request store from HTTP context\n */\n public buildStore(payload?: Record<string, any>): RequestContextStore<User> {\n return {\n request: payload?.request,\n response: payload?.response,\n };\n }\n}\n\n/**\n * Global request context instance\n */\nexport const requestContext = new RequestContext();\n\ncontextManager.register(\"request\", requestContext);\n\n/**\n * Use request store (for backward compatibility)\n */\nexport function useRequestStore<UserType extends Model = Model>() {\n return (requestContext.getStore() || {}) as RequestContextStore<UserType>;\n}\n\nexport function useRequest<UserType extends Model = Model>() {\n return requestContext.getRequest() as Request<UserType>;\n}\n\nexport function useCurrentUser<UserType extends Model = Model>() {\n return requestContext.getUser() as UserType;\n}\n"],"mappings":";;;;;;;;;AAmBA,IAAM,iBAAN,cAAyD,QAAmC;;;;CAI1F,AAAO,aAAwC;EAC7C,OAAO,KAAK,IAAI,SAAS;CAC3B;;;;CAKA,AAAO,cAAoC;EACzC,OAAO,KAAK,IAAI,UAAU;CAC5B;;;;CAKA,AAAO,UAA4B;EACjC,OAAO,KAAK,WAAW,CAAC,EAAE;CAC5B;;;;CAKA,AAAO,WAAW,SAA0D;EAC1E,OAAO;GACL,SAAS,SAAS;GAClB,UAAU,SAAS;EACrB;CACF;AACF;;;;AAKA,MAAa,iBAAiB,IAAI,eAAe;AAEjD,eAAe,SAAS,WAAW,cAAc;;;;AAKjD,SAAgB,kBAAkD;CAChE,OAAQ,eAAe,SAAS,KAAK,CAAC;AACxC;AAEA,SAAgB,aAA6C;CAC3D,OAAO,eAAe,WAAW;AACnC;AAEA,SAAgB,iBAAiD;CAC/D,OAAO,eAAe,QAAQ;AAChC"}
1
+ {"version":3,"file":"request-context.mjs","names":[],"sources":["../../../../../../../../core/src/http/context/request-context.ts"],"sourcesContent":["import type { Model } from \"@warlock.js/cascade\";\nimport { Context, contextManager } from \"@warlock.js/context\";\nimport type { Request } from \"../request\";\nimport type { Response } from \"../response\";\nimport type { RequestUser } from \"../types\";\n\n/**\n * Request Context Store\n */\nexport type RequestContextStore<User extends Model = Model> = {\n request: Request<User>;\n response: Response;\n};\n\n/**\n * Request Context\n *\n * Manages request-scoped data (request, response, user) using AsyncLocalStorage.\n * Extends the base Context class for consistent API.\n */\nclass RequestContext<User extends Model = Model> extends Context<RequestContextStore<User>> {\n /**\n * Get the current request\n */\n public getRequest(): Request<User> | undefined {\n return this.get(\"request\");\n }\n\n /**\n * Get the current response\n */\n public getResponse(): Response | undefined {\n return this.get(\"response\");\n }\n\n /**\n * Get the current user.\n *\n * Reads `request.user`, typed via the `RequestUser` module-augmentation\n * surface (`core/src/http/types.ts`) — NOT the `User` generic above. That\n * generic actually binds `Request<User>`'s `RequestValidation` parameter\n * (see `RequestContextStore`), not \"the user type\"; there is no `Request`\n * generic for the user today, so returning it as `User` was never sound\n * (eed20184 step (b) inventory, `implementation/2026-08-20-A2-request-locals.md`\n * §6.1). Callers that need a concrete model type — e.g.\n * `useCurrentUser<MyUserModel>()` — cast at the call site; the app owns\n * that shape via its own `RequestUser` augmentation.\n */\n public getUser(): RequestUser | undefined {\n return this.getRequest()?.user;\n }\n\n /**\n * Build the initial request store from HTTP context\n */\n public buildStore(payload?: Record<string, any>): RequestContextStore<User> {\n return {\n request: payload?.request,\n response: payload?.response,\n };\n }\n}\n\n/**\n * Global request context instance\n */\nexport const requestContext = new RequestContext();\n\ncontextManager.register(\"request\", requestContext);\n\n/**\n * Use request store (for backward compatibility)\n */\nexport function useRequestStore<UserType extends Model = Model>() {\n return (requestContext.getStore() || {}) as RequestContextStore<UserType>;\n}\n\nexport function useRequest<UserType extends Model = Model>() {\n return requestContext.getRequest() as Request<UserType>;\n}\n\nexport function useCurrentUser<UserType extends Model = Model>() {\n // `getUser()` returns the declared `RequestUser` surface (empty by\n // default), which is structurally unrelated to `Model` — this is the app\n // boundary where the caller's own `RequestUser` augmentation is expected\n // to actually be its `UserType` model instance. `unknown` is the\n // documented double-assertion for \"these ARE the same value, TS just\n // can't see the two augmented shapes are related\" — not `any`.\n return requestContext.getUser() as unknown as UserType;\n}\n"],"mappings":";;;;;;;;;AAoBA,IAAM,iBAAN,cAAyD,QAAmC;;;;CAI1F,AAAO,aAAwC;EAC7C,OAAO,KAAK,IAAI,SAAS;CAC3B;;;;CAKA,AAAO,cAAoC;EACzC,OAAO,KAAK,IAAI,UAAU;CAC5B;;;;;;;;;;;;;;CAeA,AAAO,UAAmC;EACxC,OAAO,KAAK,WAAW,CAAC,EAAE;CAC5B;;;;CAKA,AAAO,WAAW,SAA0D;EAC1E,OAAO;GACL,SAAS,SAAS;GAClB,UAAU,SAAS;EACrB;CACF;AACF;;;;AAKA,MAAa,iBAAiB,IAAI,eAAe;AAEjD,eAAe,SAAS,WAAW,cAAc;;;;AAKjD,SAAgB,kBAAkD;CAChE,OAAQ,eAAe,SAAS,KAAK,CAAC;AACxC;AAEA,SAAgB,aAA6C;CAC3D,OAAO,eAAe,WAAW;AACnC;AAEA,SAAgB,iBAAiD;CAO/D,OAAO,eAAe,QAAQ;AAChC"}
@@ -0,0 +1,21 @@
1
+ //#region ../core/src/http/context/request-memo.d.ts
2
+ /**
3
+ * Single-flight, request-scoped memoization.
4
+ *
5
+ * The first call for a given `key` within the current request runs `fn` once
6
+ * and caches the resulting promise; any call for the same `key` issued while
7
+ * that promise is still pending — or after it has resolved — gets that exact
8
+ * promise back, so parallel loaders that all need the same value never issue
9
+ * duplicate work. A rejection is evicted immediately, so the next call for
10
+ * that `key` retries `fn` from scratch instead of caching the failure.
11
+ *
12
+ * Must run inside a request context (established by
13
+ * `createRequestStore`/`contextManager.runAll` for every HTTP request). There
14
+ * is deliberately no process-global fallback — that is the exact cross-request
15
+ * leak this helper exists to prevent — so calling it outside a request
16
+ * context throws instead of silently memoizing across requests.
17
+ */
18
+ declare function requestMemo<T>(key: string, fn: () => Promise<T>): Promise<T>;
19
+ //#endregion
20
+ export { requestMemo };
21
+ //# sourceMappingURL=request-memo.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-memo.d.mts","names":[],"sources":["../../../../../../../../core/src/http/context/request-memo.ts"],"mappings":";;AA2BA;;;;;;;;;;;;;;;iBAAgB,WAAA,IAAe,GAAA,UAAa,EAAA,QAAU,OAAA,CAAQ,CAAA,IAAK,OAAA,CAAQ,CAAA"}
@@ -0,0 +1,48 @@
1
+ import { requestContext } from "./request-context.mjs";
2
+
3
+ //#region ../core/src/http/context/request-memo.ts
4
+ /**
5
+ * In-flight/settled promises for the current request, keyed by the caller's
6
+ * key. Never a process-global map: the outer key is the request's own store
7
+ * object (fresh per request, see `RequestContext.buildStore`), so two
8
+ * concurrent requests can never share — or even see — each other's entries,
9
+ * and everything is GC-eligible once the request's ALS frame ends.
10
+ */
11
+ const requestMemoStores = /* @__PURE__ */ new WeakMap();
12
+ /**
13
+ * Single-flight, request-scoped memoization.
14
+ *
15
+ * The first call for a given `key` within the current request runs `fn` once
16
+ * and caches the resulting promise; any call for the same `key` issued while
17
+ * that promise is still pending — or after it has resolved — gets that exact
18
+ * promise back, so parallel loaders that all need the same value never issue
19
+ * duplicate work. A rejection is evicted immediately, so the next call for
20
+ * that `key` retries `fn` from scratch instead of caching the failure.
21
+ *
22
+ * Must run inside a request context (established by
23
+ * `createRequestStore`/`contextManager.runAll` for every HTTP request). There
24
+ * is deliberately no process-global fallback — that is the exact cross-request
25
+ * leak this helper exists to prevent — so calling it outside a request
26
+ * context throws instead of silently memoizing across requests.
27
+ */
28
+ function requestMemo(key, fn) {
29
+ const store = requestContext.getStore();
30
+ if (!store) throw new Error("requestMemo() was called outside a request context. It caches work for the lifetime of a single request and must run inside code executed by the HTTP request pipeline (middleware, a loader, or a controller) — call it from there. If you need to share a value across requests, use a process-level cache instead; requestMemo will not fall back to one.");
31
+ let entries = requestMemoStores.get(store);
32
+ if (!entries) {
33
+ entries = /* @__PURE__ */ new Map();
34
+ requestMemoStores.set(store, entries);
35
+ }
36
+ const cached = entries.get(key);
37
+ if (cached) return cached;
38
+ const promise = (async () => fn())().catch((error) => {
39
+ entries.delete(key);
40
+ throw error;
41
+ });
42
+ entries.set(key, promise);
43
+ return promise;
44
+ }
45
+
46
+ //#endregion
47
+ export { requestMemo };
48
+ //# sourceMappingURL=request-memo.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-memo.mjs","names":[],"sources":["../../../../../../../../core/src/http/context/request-memo.ts"],"sourcesContent":["import { requestContext, type RequestContextStore } from \"./request-context\";\n\n/**\n * In-flight/settled promises for the current request, keyed by the caller's\n * key. Never a process-global map: the outer key is the request's own store\n * object (fresh per request, see `RequestContext.buildStore`), so two\n * concurrent requests can never share — or even see — each other's entries,\n * and everything is GC-eligible once the request's ALS frame ends.\n */\nconst requestMemoStores = new WeakMap<RequestContextStore, Map<string, Promise<any>>>();\n\n/**\n * Single-flight, request-scoped memoization.\n *\n * The first call for a given `key` within the current request runs `fn` once\n * and caches the resulting promise; any call for the same `key` issued while\n * that promise is still pending — or after it has resolved — gets that exact\n * promise back, so parallel loaders that all need the same value never issue\n * duplicate work. A rejection is evicted immediately, so the next call for\n * that `key` retries `fn` from scratch instead of caching the failure.\n *\n * Must run inside a request context (established by\n * `createRequestStore`/`contextManager.runAll` for every HTTP request). There\n * is deliberately no process-global fallback — that is the exact cross-request\n * leak this helper exists to prevent — so calling it outside a request\n * context throws instead of silently memoizing across requests.\n */\nexport function requestMemo<T>(key: string, fn: () => Promise<T>): Promise<T> {\n const store = requestContext.getStore();\n\n if (!store) {\n throw new Error(\n \"requestMemo() was called outside a request context. \" +\n \"It caches work for the lifetime of a single request and must run inside \" +\n \"code executed by the HTTP request pipeline (middleware, a loader, or a \" +\n \"controller) — call it from there. If you need to share a value across \" +\n \"requests, use a process-level cache instead; requestMemo will not fall \" +\n \"back to one.\",\n );\n }\n\n let entries = requestMemoStores.get(store);\n\n if (!entries) {\n entries = new Map();\n requestMemoStores.set(store, entries);\n }\n\n const cached = entries.get(key);\n\n if (cached) return cached;\n\n const promise = (async () => fn())().catch(error => {\n entries!.delete(key);\n throw error;\n });\n\n entries.set(key, promise);\n\n return promise;\n}\n"],"mappings":";;;;;;;;;;AASA,MAAM,oCAAoB,IAAI,QAAwD;;;;;;;;;;;;;;;;;AAkBtF,SAAgB,YAAe,KAAa,IAAkC;CAC5E,MAAM,QAAQ,eAAe,SAAS;CAEtC,IAAI,CAAC,OACH,MAAM,IAAI,MACR,8VAMF;CAGF,IAAI,UAAU,kBAAkB,IAAI,KAAK;CAEzC,IAAI,CAAC,SAAS;EACZ,0BAAU,IAAI,IAAI;EAClB,kBAAkB,IAAI,OAAO,OAAO;CACtC;CAEA,MAAM,SAAS,QAAQ,IAAI,GAAG;CAE9B,IAAI,QAAQ,OAAO;CAEnB,MAAM,WAAW,YAAY,GAAG,EAAC,CAAE,CAAC,CAAC,OAAM,UAAS;EAClD,QAAS,OAAO,GAAG;EACnB,MAAM;CACR,CAAC;CAED,QAAQ,IAAI,KAAK,OAAO;CAExB,OAAO;AACT"}
@@ -2,7 +2,88 @@ import { Infer } from "@warlock.js/seal";
2
2
  import { Model } from "@warlock.js/cascade";
3
3
 
4
4
  //#region ../core/src/http/database/RequestLog.d.ts
5
- declare const schema: any;
5
+ declare const schema: import("@warlock.js/seal").ObjectValidator<{
6
+ statusCode: import("@warlock.js/seal").NumberValidator & import("@warlock.js/seal").StandardSchemaV1<number, number>;
7
+ responseTime: import("@warlock.js/seal").NumberValidator & import("@warlock.js/seal").StandardSchemaV1<number, number>;
8
+ responseSize: import("@warlock.js/seal").NumberValidator & import("@warlock.js/seal").StandardSchemaV1<number, number>;
9
+ responseBody: import("@warlock.js/seal").RecordValidator & {
10
+ valueValidator: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
11
+ } & import("@warlock.js/seal").StandardSchemaV1<Record<string, any>, Record<string, any>>;
12
+ responseHeaders: import("@warlock.js/seal").RecordValidator & {
13
+ valueValidator: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
14
+ } & import("@warlock.js/seal").StandardSchemaV1<Record<string, any>, Record<string, any>>;
15
+ ip: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
16
+ method: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
17
+ route: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
18
+ requestHeaders: import("@warlock.js/seal").RecordValidator & {
19
+ valueValidator: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
20
+ } & import("@warlock.js/seal").StandardSchemaV1<Record<string, any>, Record<string, any>>;
21
+ userAgent: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
22
+ referer: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
23
+ requestBody: import("@warlock.js/seal").RecordValidator & {
24
+ valueValidator: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
25
+ } & import("@warlock.js/seal").StandardSchemaV1<Record<string, any>, Record<string, any>>;
26
+ requestParams: import("@warlock.js/seal").RecordValidator & {
27
+ valueValidator: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
28
+ } & import("@warlock.js/seal").StandardSchemaV1<Record<string, any>, Record<string, any>>;
29
+ requestQuery: import("@warlock.js/seal").RecordValidator & {
30
+ valueValidator: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
31
+ } & import("@warlock.js/seal").StandardSchemaV1<Record<string, any>, Record<string, any>>;
32
+ }> & import("@warlock.js/seal").StandardSchemaV1<import("@warlock.js/seal").InferInputObjectShape<{
33
+ statusCode: import("@warlock.js/seal").NumberValidator & import("@warlock.js/seal").StandardSchemaV1<number, number>;
34
+ responseTime: import("@warlock.js/seal").NumberValidator & import("@warlock.js/seal").StandardSchemaV1<number, number>;
35
+ responseSize: import("@warlock.js/seal").NumberValidator & import("@warlock.js/seal").StandardSchemaV1<number, number>;
36
+ responseBody: import("@warlock.js/seal").RecordValidator & {
37
+ valueValidator: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
38
+ } & import("@warlock.js/seal").StandardSchemaV1<Record<string, any>, Record<string, any>>;
39
+ responseHeaders: import("@warlock.js/seal").RecordValidator & {
40
+ valueValidator: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
41
+ } & import("@warlock.js/seal").StandardSchemaV1<Record<string, any>, Record<string, any>>;
42
+ ip: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
43
+ method: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
44
+ route: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
45
+ requestHeaders: import("@warlock.js/seal").RecordValidator & {
46
+ valueValidator: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
47
+ } & import("@warlock.js/seal").StandardSchemaV1<Record<string, any>, Record<string, any>>;
48
+ userAgent: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
49
+ referer: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
50
+ requestBody: import("@warlock.js/seal").RecordValidator & {
51
+ valueValidator: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
52
+ } & import("@warlock.js/seal").StandardSchemaV1<Record<string, any>, Record<string, any>>;
53
+ requestParams: import("@warlock.js/seal").RecordValidator & {
54
+ valueValidator: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
55
+ } & import("@warlock.js/seal").StandardSchemaV1<Record<string, any>, Record<string, any>>;
56
+ requestQuery: import("@warlock.js/seal").RecordValidator & {
57
+ valueValidator: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
58
+ } & import("@warlock.js/seal").StandardSchemaV1<Record<string, any>, Record<string, any>>;
59
+ }>, import("@warlock.js/seal").InferInputObjectShape<{
60
+ statusCode: import("@warlock.js/seal").NumberValidator & import("@warlock.js/seal").StandardSchemaV1<number, number>;
61
+ responseTime: import("@warlock.js/seal").NumberValidator & import("@warlock.js/seal").StandardSchemaV1<number, number>;
62
+ responseSize: import("@warlock.js/seal").NumberValidator & import("@warlock.js/seal").StandardSchemaV1<number, number>;
63
+ responseBody: import("@warlock.js/seal").RecordValidator & {
64
+ valueValidator: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
65
+ } & import("@warlock.js/seal").StandardSchemaV1<Record<string, any>, Record<string, any>>;
66
+ responseHeaders: import("@warlock.js/seal").RecordValidator & {
67
+ valueValidator: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
68
+ } & import("@warlock.js/seal").StandardSchemaV1<Record<string, any>, Record<string, any>>;
69
+ ip: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
70
+ method: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
71
+ route: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
72
+ requestHeaders: import("@warlock.js/seal").RecordValidator & {
73
+ valueValidator: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
74
+ } & import("@warlock.js/seal").StandardSchemaV1<Record<string, any>, Record<string, any>>;
75
+ userAgent: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
76
+ referer: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
77
+ requestBody: import("@warlock.js/seal").RecordValidator & {
78
+ valueValidator: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
79
+ } & import("@warlock.js/seal").StandardSchemaV1<Record<string, any>, Record<string, any>>;
80
+ requestParams: import("@warlock.js/seal").RecordValidator & {
81
+ valueValidator: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
82
+ } & import("@warlock.js/seal").StandardSchemaV1<Record<string, any>, Record<string, any>>;
83
+ requestQuery: import("@warlock.js/seal").RecordValidator & {
84
+ valueValidator: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
85
+ } & import("@warlock.js/seal").StandardSchemaV1<Record<string, any>, Record<string, any>>;
86
+ }>>;
6
87
  type RequestLogSchema = Infer.Output<typeof schema>;
7
88
  declare class RequestLog extends Model<RequestLogSchema> {
8
89
  /**
@@ -12,7 +93,88 @@ declare class RequestLog extends Model<RequestLogSchema> {
12
93
  /**
13
94
  * {@inheritdoc}
14
95
  */
15
- static schema: any;
96
+ static schema: import("@warlock.js/seal").ObjectValidator<{
97
+ statusCode: import("@warlock.js/seal").NumberValidator & import("@warlock.js/seal").StandardSchemaV1<number, number>;
98
+ responseTime: import("@warlock.js/seal").NumberValidator & import("@warlock.js/seal").StandardSchemaV1<number, number>;
99
+ responseSize: import("@warlock.js/seal").NumberValidator & import("@warlock.js/seal").StandardSchemaV1<number, number>;
100
+ responseBody: import("@warlock.js/seal").RecordValidator & {
101
+ valueValidator: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
102
+ } & import("@warlock.js/seal").StandardSchemaV1<Record<string, any>, Record<string, any>>;
103
+ responseHeaders: import("@warlock.js/seal").RecordValidator & {
104
+ valueValidator: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
105
+ } & import("@warlock.js/seal").StandardSchemaV1<Record<string, any>, Record<string, any>>;
106
+ ip: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
107
+ method: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
108
+ route: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
109
+ requestHeaders: import("@warlock.js/seal").RecordValidator & {
110
+ valueValidator: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
111
+ } & import("@warlock.js/seal").StandardSchemaV1<Record<string, any>, Record<string, any>>;
112
+ userAgent: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
113
+ referer: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
114
+ requestBody: import("@warlock.js/seal").RecordValidator & {
115
+ valueValidator: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
116
+ } & import("@warlock.js/seal").StandardSchemaV1<Record<string, any>, Record<string, any>>;
117
+ requestParams: import("@warlock.js/seal").RecordValidator & {
118
+ valueValidator: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
119
+ } & import("@warlock.js/seal").StandardSchemaV1<Record<string, any>, Record<string, any>>;
120
+ requestQuery: import("@warlock.js/seal").RecordValidator & {
121
+ valueValidator: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
122
+ } & import("@warlock.js/seal").StandardSchemaV1<Record<string, any>, Record<string, any>>;
123
+ }> & import("@warlock.js/seal").StandardSchemaV1<import("@warlock.js/seal").InferInputObjectShape<{
124
+ statusCode: import("@warlock.js/seal").NumberValidator & import("@warlock.js/seal").StandardSchemaV1<number, number>;
125
+ responseTime: import("@warlock.js/seal").NumberValidator & import("@warlock.js/seal").StandardSchemaV1<number, number>;
126
+ responseSize: import("@warlock.js/seal").NumberValidator & import("@warlock.js/seal").StandardSchemaV1<number, number>;
127
+ responseBody: import("@warlock.js/seal").RecordValidator & {
128
+ valueValidator: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
129
+ } & import("@warlock.js/seal").StandardSchemaV1<Record<string, any>, Record<string, any>>;
130
+ responseHeaders: import("@warlock.js/seal").RecordValidator & {
131
+ valueValidator: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
132
+ } & import("@warlock.js/seal").StandardSchemaV1<Record<string, any>, Record<string, any>>;
133
+ ip: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
134
+ method: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
135
+ route: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
136
+ requestHeaders: import("@warlock.js/seal").RecordValidator & {
137
+ valueValidator: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
138
+ } & import("@warlock.js/seal").StandardSchemaV1<Record<string, any>, Record<string, any>>;
139
+ userAgent: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
140
+ referer: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
141
+ requestBody: import("@warlock.js/seal").RecordValidator & {
142
+ valueValidator: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
143
+ } & import("@warlock.js/seal").StandardSchemaV1<Record<string, any>, Record<string, any>>;
144
+ requestParams: import("@warlock.js/seal").RecordValidator & {
145
+ valueValidator: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
146
+ } & import("@warlock.js/seal").StandardSchemaV1<Record<string, any>, Record<string, any>>;
147
+ requestQuery: import("@warlock.js/seal").RecordValidator & {
148
+ valueValidator: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
149
+ } & import("@warlock.js/seal").StandardSchemaV1<Record<string, any>, Record<string, any>>;
150
+ }>, import("@warlock.js/seal").InferInputObjectShape<{
151
+ statusCode: import("@warlock.js/seal").NumberValidator & import("@warlock.js/seal").StandardSchemaV1<number, number>;
152
+ responseTime: import("@warlock.js/seal").NumberValidator & import("@warlock.js/seal").StandardSchemaV1<number, number>;
153
+ responseSize: import("@warlock.js/seal").NumberValidator & import("@warlock.js/seal").StandardSchemaV1<number, number>;
154
+ responseBody: import("@warlock.js/seal").RecordValidator & {
155
+ valueValidator: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
156
+ } & import("@warlock.js/seal").StandardSchemaV1<Record<string, any>, Record<string, any>>;
157
+ responseHeaders: import("@warlock.js/seal").RecordValidator & {
158
+ valueValidator: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
159
+ } & import("@warlock.js/seal").StandardSchemaV1<Record<string, any>, Record<string, any>>;
160
+ ip: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
161
+ method: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
162
+ route: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
163
+ requestHeaders: import("@warlock.js/seal").RecordValidator & {
164
+ valueValidator: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
165
+ } & import("@warlock.js/seal").StandardSchemaV1<Record<string, any>, Record<string, any>>;
166
+ userAgent: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
167
+ referer: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
168
+ requestBody: import("@warlock.js/seal").RecordValidator & {
169
+ valueValidator: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
170
+ } & import("@warlock.js/seal").StandardSchemaV1<Record<string, any>, Record<string, any>>;
171
+ requestParams: import("@warlock.js/seal").RecordValidator & {
172
+ valueValidator: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
173
+ } & import("@warlock.js/seal").StandardSchemaV1<Record<string, any>, Record<string, any>>;
174
+ requestQuery: import("@warlock.js/seal").RecordValidator & {
175
+ valueValidator: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
176
+ } & import("@warlock.js/seal").StandardSchemaV1<Record<string, any>, Record<string, any>>;
177
+ }>>;
16
178
  }
17
179
  //#endregion
18
180
  export { RequestLog };
@@ -1 +1 @@
1
- {"version":3,"file":"RequestLog.d.mts","names":[],"sources":["../../../../../../../../core/src/http/database/RequestLog.ts"],"mappings":";;;;cAGM,MAAA;AAAA,KAiBD,gBAAA,GAAmB,KAAA,CAAM,MAAM,QAAQ,MAAA;AAAA,cAE/B,UAAA,SAAmB,KAAK,CAAC,gBAAA;EAJpC;;;EAAA,OAQc,KAAA;EANX;;;EAAA,OAWW,MAAA;AAAA"}
1
+ {"version":3,"file":"RequestLog.d.mts","names":[],"sources":["../../../../../../../../core/src/http/database/RequestLog.ts"],"mappings":";;;;cAGM,MAAA,6BAAM,eAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAiBP,gBAAA,GAAmB,KAAA,CAAM,MAAM,QAAQ,MAAA;AAAA,cAE/B,UAAA,SAAmB,KAAA,CAAM,gBAAA;;;;SAItB,KAAA;;;;SAKA,MAAA,6BAAM,eAAA"}