@warlock.js/core 5.34.3 → 5.35.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 (244) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/esm/cli/cli-commands.manager.mjs +1 -1
  3. package/esm/cli/cli-commands.manager.mjs.map +1 -1
  4. package/esm/cli/commands/add.command.mjs +1 -0
  5. package/esm/cli/commands/add.command.mjs.map +1 -1
  6. package/esm/cli/commands/build.command.mjs +30 -3
  7. package/esm/cli/commands/build.command.mjs.map +1 -1
  8. package/esm/cli/commands/check/check-command.action.mjs +1 -0
  9. package/esm/cli/commands/check/check-command.action.mjs.map +1 -1
  10. package/esm/cli/commands/check.command.mjs +5 -0
  11. package/esm/cli/commands/check.command.mjs.map +1 -1
  12. package/esm/cli/commands/explain/explain-command.action.mjs +21 -0
  13. package/esm/cli/commands/explain/explain-command.action.mjs.map +1 -1
  14. package/esm/cli/commands/explain.command.mjs +14 -4
  15. package/esm/cli/commands/explain.command.mjs.map +1 -1
  16. package/esm/cli/commands/generate/generate.command.mjs +19 -1
  17. package/esm/cli/commands/generate/generate.command.mjs.map +1 -1
  18. package/esm/cli/commands/generate/generators/endpoint.generator.mjs +1 -1
  19. package/esm/cli/commands/generate/generators/endpoint.generator.mjs.map +1 -1
  20. package/esm/cli/commands/generate/generators/web.generator.mjs +169 -0
  21. package/esm/cli/commands/generate/generators/web.generator.mjs.map +1 -0
  22. package/esm/cli/commands/generate/templates/web-stubs.mjs +231 -0
  23. package/esm/cli/commands/generate/templates/web-stubs.mjs.map +1 -0
  24. package/esm/cli/commands/inspect/inspect-command.action.mjs +4 -3
  25. package/esm/cli/commands/inspect/inspect-command.action.mjs.map +1 -1
  26. package/esm/cli/commands/inspect.command.mjs +13 -8
  27. package/esm/cli/commands/inspect.command.mjs.map +1 -1
  28. package/esm/cli/commands/mcp/mcp-command.action.mjs +1 -1
  29. package/esm/cli/commands/mcp/mcp-command.action.mjs.map +1 -1
  30. package/esm/config/config-loader.d.mts.map +1 -1
  31. package/esm/config/config-loader.mjs +2 -0
  32. package/esm/config/config-loader.mjs.map +1 -1
  33. package/esm/config/load-config-files.mjs +4 -0
  34. package/esm/config/load-config-files.mjs.map +1 -1
  35. package/esm/dev-server/file-event-handler.mjs +11 -1
  36. package/esm/dev-server/file-event-handler.mjs.map +1 -1
  37. package/esm/dev-server/file-manager.d.mts +1 -1
  38. package/esm/dev-server/file-manager.d.mts.map +1 -1
  39. package/esm/dev-server/file-manager.mjs.map +1 -1
  40. package/esm/dev-server/files-orchestrator.mjs +16 -1
  41. package/esm/dev-server/files-orchestrator.mjs.map +1 -1
  42. package/esm/dev-server/files-watcher.mjs +13 -4
  43. package/esm/dev-server/files-watcher.mjs.map +1 -1
  44. package/esm/dev-server/loader/loader-port-acks.mjs +21 -9
  45. package/esm/dev-server/loader/loader-port-acks.mjs.map +1 -1
  46. package/esm/dev-server/loader/register-loader.mjs +31 -12
  47. package/esm/dev-server/loader/register-loader.mjs.map +1 -1
  48. package/esm/dev-server/module-loader.mjs +14 -0
  49. package/esm/dev-server/module-loader.mjs.map +1 -1
  50. package/esm/dev-server/special-file-patterns.mjs +4 -1
  51. package/esm/dev-server/special-file-patterns.mjs.map +1 -1
  52. package/esm/e2e/index.d.mts +2 -2
  53. package/esm/e2e/page.d.mts +6 -1
  54. package/esm/e2e/page.d.mts.map +1 -1
  55. package/esm/e2e/page.mjs +36 -1
  56. package/esm/e2e/page.mjs.map +1 -1
  57. package/esm/e2e/types.d.mts +31 -0
  58. package/esm/e2e/types.d.mts.map +1 -1
  59. package/esm/e2e/types.mjs.map +1 -1
  60. package/esm/errors/error-codes.d.mts +48 -0
  61. package/esm/errors/error-codes.d.mts.map +1 -1
  62. package/esm/errors/error-codes.mjs +48 -0
  63. package/esm/errors/error-codes.mjs.map +1 -1
  64. package/esm/errors/index.d.mts +2 -2
  65. package/esm/errors/index.mjs +2 -2
  66. package/esm/generations/add-command.action.mjs +2 -0
  67. package/esm/generations/add-command.action.mjs.map +1 -1
  68. package/esm/generations/design-system-payload.mjs +53 -0
  69. package/esm/generations/design-system-payload.mjs.map +1 -0
  70. package/esm/generations/features/design-system.feature.mjs +38 -0
  71. package/esm/generations/features/design-system.feature.mjs.map +1 -0
  72. package/esm/generations/features/index.mjs +3 -1
  73. package/esm/generations/features/index.mjs.map +1 -1
  74. package/esm/generations/features/notifications.feature.mjs +13 -2
  75. package/esm/generations/features/notifications.feature.mjs.map +1 -1
  76. package/esm/generations/features/test.feature.mjs +48 -13
  77. package/esm/generations/features/test.feature.mjs.map +1 -1
  78. package/esm/generations/stubs.mjs +9 -5
  79. package/esm/generations/stubs.mjs.map +1 -1
  80. package/esm/http/error-mapper.d.mts +1 -1
  81. package/esm/http/error-mapper.mjs +3 -3
  82. package/esm/http/error-mapper.mjs.map +1 -1
  83. package/esm/http/error-response.d.mts +3 -0
  84. package/esm/http/error-response.d.mts.map +1 -1
  85. package/esm/http/error-response.mjs +8 -2
  86. package/esm/http/error-response.mjs.map +1 -1
  87. package/esm/http/errors/errors.d.mts +8 -0
  88. package/esm/http/errors/errors.d.mts.map +1 -1
  89. package/esm/http/errors/errors.mjs +13 -1
  90. package/esm/http/errors/errors.mjs.map +1 -1
  91. package/esm/http/index.d.mts +3 -2
  92. package/esm/http/index.mjs +3 -2
  93. package/esm/http/list-endpoint.d.mts +35 -0
  94. package/esm/http/list-endpoint.d.mts.map +1 -0
  95. package/esm/http/list-endpoint.mjs +28 -0
  96. package/esm/http/list-endpoint.mjs.map +1 -0
  97. package/esm/http/middleware/inject-request-context.d.mts.map +1 -1
  98. package/esm/http/middleware/inject-request-context.mjs +12 -2
  99. package/esm/http/middleware/inject-request-context.mjs.map +1 -1
  100. package/esm/http/server.mjs +1 -1
  101. package/esm/index.d.mts +8 -6
  102. package/esm/index.mjs +3 -2
  103. package/esm/openapi/security.mjs +12 -4
  104. package/esm/openapi/security.mjs.map +1 -1
  105. package/esm/openapi/types.d.mts +2 -0
  106. package/esm/openapi/types.d.mts.map +1 -1
  107. package/esm/production/build-budgets.d.mts +46 -0
  108. package/esm/production/build-budgets.d.mts.map +1 -0
  109. package/esm/production/build-budgets.mjs +109 -0
  110. package/esm/production/build-budgets.mjs.map +1 -0
  111. package/esm/production/production-builder.mjs +13 -1
  112. package/esm/production/production-builder.mjs.map +1 -1
  113. package/esm/production/resolve-build-config.d.mts +1 -1
  114. package/esm/production/resolve-build-config.d.mts.map +1 -1
  115. package/esm/production/resolve-build-config.mjs.map +1 -1
  116. package/esm/restful/restful.d.mts +1 -1
  117. package/esm/tests/fakes/storage-fake.d.mts +3 -1
  118. package/esm/tests/fakes/storage-fake.d.mts.map +1 -1
  119. package/esm/tests/fakes/storage-fake.mjs +3 -3
  120. package/esm/tests/fakes/storage-fake.mjs.map +1 -1
  121. package/esm/tests/fakes/test-fakes.d.mts +3 -1
  122. package/esm/tests/fakes/test-fakes.d.mts.map +1 -1
  123. package/esm/tests/fakes/test-fakes.mjs +3 -2
  124. package/esm/tests/fakes/test-fakes.mjs.map +1 -1
  125. package/esm/tests/index.d.mts +4 -2
  126. package/esm/tests/index.mjs +3 -1
  127. package/esm/tests/load-unit-config.mjs +24 -0
  128. package/esm/tests/load-unit-config.mjs.map +1 -0
  129. package/esm/tests/response-contract.d.mts +32 -0
  130. package/esm/tests/response-contract.d.mts.map +1 -0
  131. package/esm/tests/response-contract.mjs +116 -0
  132. package/esm/tests/response-contract.mjs.map +1 -0
  133. package/esm/tests/start-http-development-server.d.mts.map +1 -1
  134. package/esm/tests/start-http-development-server.mjs +10 -2
  135. package/esm/tests/start-http-development-server.mjs.map +1 -1
  136. package/esm/tests/test-lifecycle-state.mjs.map +1 -1
  137. package/esm/tests/test-locales.mjs +33 -0
  138. package/esm/tests/test-locales.mjs.map +1 -0
  139. package/esm/tests/test-runtime-scope.d.mts +15 -0
  140. package/esm/tests/test-runtime-scope.d.mts.map +1 -0
  141. package/esm/tests/test-runtime-scope.mjs +80 -0
  142. package/esm/tests/test-runtime-scope.mjs.map +1 -0
  143. package/esm/tests/vitest-setup.d.mts +13 -3
  144. package/esm/tests/vitest-setup.d.mts.map +1 -1
  145. package/esm/tests/vitest-setup.mjs +146 -18
  146. package/esm/tests/vitest-setup.mjs.map +1 -1
  147. package/esm/tooling/check/affected-files.d.mts +5 -0
  148. package/esm/tooling/check/affected-files.d.mts.map +1 -0
  149. package/esm/tooling/check/affected-files.mjs +56 -0
  150. package/esm/tooling/check/affected-files.mjs.map +1 -0
  151. package/esm/tooling/check/changed-files.d.mts +2 -0
  152. package/esm/tooling/check/changed-files.d.mts.map +1 -1
  153. package/esm/tooling/check/changed-files.mjs +2 -2
  154. package/esm/tooling/check/changed-files.mjs.map +1 -1
  155. package/esm/tooling/check/conventions-step.d.mts +2 -1
  156. package/esm/tooling/check/conventions-step.d.mts.map +1 -1
  157. package/esm/tooling/check/conventions-step.mjs +48 -6
  158. package/esm/tooling/check/conventions-step.mjs.map +1 -1
  159. package/esm/tooling/check/fixes.d.mts +8 -0
  160. package/esm/tooling/check/fixes.d.mts.map +1 -0
  161. package/esm/tooling/check/fixes.mjs +31 -0
  162. package/esm/tooling/check/fixes.mjs.map +1 -0
  163. package/esm/tooling/check/index.d.mts +12 -0
  164. package/esm/tooling/check/index.mjs +3 -2
  165. package/esm/tooling/check/run-check.d.mts +2 -0
  166. package/esm/tooling/check/run-check.d.mts.map +1 -1
  167. package/esm/tooling/check/run-check.mjs +20 -4
  168. package/esm/tooling/check/run-check.mjs.map +1 -1
  169. package/esm/tooling/check/run-tests.mjs +1 -1
  170. package/esm/tooling/check/source-ast.d.mts +1 -0
  171. package/esm/tooling/check/web-conventions.d.mts +10 -0
  172. package/esm/tooling/check/web-conventions.d.mts.map +1 -0
  173. package/esm/tooling/check/web-conventions.mjs +126 -0
  174. package/esm/tooling/check/web-conventions.mjs.map +1 -0
  175. package/esm/tooling/index.d.mts +7 -2
  176. package/esm/tooling/index.mjs +4 -2
  177. package/esm/tooling/inspect/collect-files.mjs +172 -0
  178. package/esm/tooling/inspect/collect-files.mjs.map +1 -0
  179. package/esm/tooling/inspect/collect-pages.mjs +3 -2
  180. package/esm/tooling/inspect/collect-pages.mjs.map +1 -1
  181. package/esm/tooling/inspect/collect-routes.mjs +1 -1
  182. package/esm/tooling/inspect/collect-routes.mjs.map +1 -1
  183. package/esm/tooling/inspect/explain-file.d.mts +12 -0
  184. package/esm/tooling/inspect/explain-file.d.mts.map +1 -0
  185. package/esm/tooling/inspect/explain-file.mjs +45 -0
  186. package/esm/tooling/inspect/explain-file.mjs.map +1 -0
  187. package/esm/tooling/inspect/index.d.mts +3 -2
  188. package/esm/tooling/inspect/index.mjs +2 -1
  189. package/esm/tooling/inspect/inspect-app.d.mts.map +1 -1
  190. package/esm/tooling/inspect/inspect-app.mjs +9 -3
  191. package/esm/tooling/inspect/inspect-app.mjs.map +1 -1
  192. package/esm/tooling/inspect/inspect-report.schema.mjs +34 -1
  193. package/esm/tooling/inspect/inspect-report.schema.mjs.map +1 -1
  194. package/esm/tooling/inspect/inspect-report.types.d.mts +17 -1
  195. package/esm/tooling/inspect/inspect-report.types.d.mts.map +1 -1
  196. package/esm/tooling/inspect/inspect-report.types.mjs +2 -1
  197. package/esm/tooling/inspect/inspect-report.types.mjs.map +1 -1
  198. package/esm/tooling/mcp/mcp-tools.d.mts.map +1 -1
  199. package/esm/tooling/mcp/mcp-tools.mjs +8 -2
  200. package/esm/tooling/mcp/mcp-tools.mjs.map +1 -1
  201. package/esm/tooling/problem.d.mts +4 -0
  202. package/esm/tooling/problem.d.mts.map +1 -1
  203. package/esm/tooling/problem.mjs +1 -0
  204. package/esm/tooling/problem.mjs.map +1 -1
  205. package/esm/tooling/syntax/ast.d.mts +2 -0
  206. package/esm/tooling/typecheck/run-typecheck.mjs +5 -1
  207. package/esm/tooling/typecheck/run-typecheck.mjs.map +1 -1
  208. package/esm/vite/index.d.mts +3 -1
  209. package/esm/vite/index.mjs +3 -1
  210. package/esm/vite/test-setup-reporter.d.mts +16 -0
  211. package/esm/vite/test-setup-reporter.d.mts.map +1 -0
  212. package/esm/vite/test-setup-reporter.mjs +55 -0
  213. package/esm/vite/test-setup-reporter.mjs.map +1 -0
  214. package/esm/vite/test-workers.d.mts +5 -0
  215. package/esm/vite/test-workers.d.mts.map +1 -0
  216. package/esm/vite/test-workers.mjs +38 -0
  217. package/esm/vite/test-workers.mjs.map +1 -0
  218. package/esm/warlock-config/index.d.mts +2 -1
  219. package/esm/warlock-config/types.d.mts +3 -0
  220. package/esm/warlock-config/types.d.mts.map +1 -1
  221. package/llms-full.txt +763 -9
  222. package/llms.txt +2 -0
  223. package/package.json +15 -14
  224. package/schemas/inspect-report.schema.json +47 -1
  225. package/skills/build-budgets/SKILL.md +51 -0
  226. package/skills/build-restful/SKILL.md +56 -0
  227. package/skills/configure-app/SKILL.md +7 -0
  228. package/skills/create-module/SKILL.md +9 -0
  229. package/skills/generate-web-ui/SKILL.md +71 -0
  230. package/skills/index.md +2 -2
  231. package/skills/run-app/SKILL.md +1 -1
  232. package/skills/send-mail/SKILL.md +25 -0
  233. package/skills/send-response/SKILL.md +50 -0
  234. package/skills/test-browser/SKILL.md +53 -0
  235. package/skills/test-http/SKILL.md +91 -0
  236. package/skills/test-service/SKILL.md +161 -3
  237. package/skills/upgrade-from-5-29/SKILL.md +18 -0
  238. package/skills/use-localization/SKILL.md +21 -0
  239. package/skills/warlock-check/SKILL.md +72 -1
  240. package/skills/warlock-inspect/SKILL.md +45 -2
  241. package/skills/warlock-mcp/SKILL.md +4 -0
  242. package/skills/write-cli-command/SKILL.md +18 -0
  243. package/esm/production/build-app-production.mjs +0 -14
  244. package/esm/production/build-app-production.mjs.map +0 -1
package/esm/index.mjs CHANGED
@@ -28,7 +28,7 @@ import { DEFAULT_CSP_DIRECTIVES, InvalidCspDirectiveError, applyCspHeader, build
28
28
  import { HttpErrorCodes } from "./http/error-codes.mjs";
29
29
  import { isAllowedCsrfOrigin, normalizeOrigin, originOf, ownOrigin, resolveCsrfOriginVerdict } from "./http/csrf-origin-policy.mjs";
30
30
  import { requiresDefaultCsrfGuard, runDefaultCsrfGuard } from "./http/csrf-default-guard.mjs";
31
- import { BadRequestError, ConflictError, CookieJarUnavailableError, ForbiddenError, HttpError, NotAcceptableError, NotAllowedError, ResourceNotFoundError, ServerError, UnAuthorizedError } from "./http/errors/errors.mjs";
31
+ import { AppError, BadRequestError, ConflictError, CookieJarUnavailableError, ForbiddenError, HttpError, NotAcceptableError, NotAllowedError, ResourceNotFoundError, ServerError, UnAuthorizedError } from "./http/errors/errors.mjs";
32
32
  import { deriveTraceId, parseTraceparentTraceId } from "./http/tracing/trace-id.mjs";
33
33
  import { buildTracingContext, dispatchPhase, dispatchRequestEnd, dispatchRequestStart, isTracingEnabled, registerTracingHooks, resetTracingConfigForTests, resolveTracingConfig } from "./http/tracing/tracing-dispatcher.mjs";
34
34
  import { createRequestStore, t } from "./http/middleware/inject-request-context.mjs";
@@ -101,6 +101,7 @@ import { notifyHttpServerRebuilt, onHttpServerRebuilt } from "./http/server-rebu
101
101
  import { escapeHeaderValue } from "./http/escape-header-value.mjs";
102
102
  import { isSafeRedirect, safeRedirect } from "./http/safe-redirect.mjs";
103
103
  import { requestMemo } from "./http/context/request-memo.mjs";
104
+ import { listEndpoint } from "./http/list-endpoint.mjs";
104
105
  import "./http/index.mjs";
105
106
  import { existsExceptCurrentIdRule } from "./validation/database/exists-except-current-id.mjs";
106
107
  import { existsExceptCurrentUserRule } from "./validation/database/exists-except-current-user.mjs";
@@ -203,4 +204,4 @@ import { colors } from "@mongez/copper";
203
204
 
204
205
  export * from "@mongez/localization"
205
206
 
206
- export { $registerUseCase, $unregisterUseCase, APP_ROLES, AUTH_GUARD_MARKER, AccessConnector, AiConnector, Application, BOOT_SIGNAL_ENV_KEY, BOOT_SIGNAL_VERSION, BadRequestError, BadSchemaUseCaseError, BaseConnector, BenchmarkProfiler, BenchmarkSnapshots, CLICommand, CacheConnector, CascadeAdapter, CascadeQueryBuilder, CloudDriver, ConfigSpecialHandlers, ConflictError, ConnectorLifecyclePhase, ConnectorPriority, ConnectorsManager, ConsoleChannel, CookieJarUnavailableError, DEFAULT_CSP_DIRECTIVES, DEFAULT_POSTMAN_BASE_URL, DOSpacesDriver, DatabaseCacheDriver, DatabaseConnector, DatabaseLog, DatabaseLogModel, ERROR_CODES, ERROR_CODES_DOCS_URL, FileValidator, ForbiddenError, HeraldConnector, HttpConnector, HttpError, HttpErrorCodes, Image, ImageVariantsConfigError, InvalidConfigError, InvalidCspDirectiveError, LocalDriver, LocaleDictionaryError, LoggerConnector, MAIL_EVENTS, Mail, MailError, MailerConnector, MemorySecretBackend, MimeTypes, NULLABLE_RESOURCE, NoopChannel, NotAcceptableError, NotAllowedError, NotificationsConnector, OPENAPI_JSON_SCHEMA_DIALECT, POSTMAN_COLLECTION_SCHEMA, Path, PortInUseError, Queue, R2Driver, RegisterResource, RepositoryManager, Request, RequestController, RequestLog, Resource, ResourceFieldBuilder, ResourceNotFoundError, Response, ResponseStatus, Restful, RouteNameTakenError, RouteRegistry, Router, S3Driver, SUPPORTED_RUNTIMES_DOCS_URL, ScopedStorage, SecretStore, SecretStoreError, SeederDependencyCycleError, ServerError, SocketConnector, Storage, StorageCapabilityError, StorageConnector, StorageError, StorageFile, StorageNotInitializedError, UPDATE_MANY_BATCH_SIZE, UPLOADS_DEFAULTS, URL_CREDENTIAL_PARAMS, UnAuthorizedError, UnknownSeederDependencyError, UploadedFile, WarlockConfigManager, WarlockError, addUseCaseHistory, anyMatch, app, appLog, appPath, applyCspHeader, assertConfiguredHttpPortIsFree, assertMailCount, assertMailSent, assertNoReservedConnectorNames, assertNodeOnlyCommand, assertPortIsAvailable, assertSitesRequireWebRole, assertUniqueConnectorNames, assertValidConfigModules, assetsUrl, beginHttpDrain, bootstrap, broadcastSocketEvent, broadcastUseCaseResult, buildCspHeaderValue, buildIdempotencyCacheKey, buildOpenApiDocument, buildRequestLogSnapshot, buildTracingContext, cachePath, captureMail, clearPositionalHandlerSuspects, clearTestMailbox, clock, closeAllMailers, closeMailer, closeServerWithTimeout, colors, command, config, configPath, configSpecialHandlers, connectorsManager, container, createHttpApplication, createRequestStore, createUploadedFileController, decrypt, defaultHttpConfigurations, defineConfig, defineConfigSchema, defineResource, deriveTraceId, describeJavaScriptRuntime, describePositionalHandlerSuspect, detectJavaScriptRuntime, dispatchPhase, dispatchRequestEnd, dispatchRequestStart, displayEnvironmentMode, encrypt, ensureFatalIsVisible, env, environment, errorCodeDocsUrl, errorCodeOf, escapeHeaderValue, evaluateConditionalRequest, exampleFromSchema, existsExceptCurrentIdRule, existsExceptCurrentUserRule, explainErrorCode, fetchLatestVersion, fileExtensionRule, fileRule, fileTypeRule, findMailsBySubject, findMailsTo, fireLifecycleEvent, forgetPositionalHandlerSuspects, formatConfigIssues, generateImageVariants, generateMailId, getDefaultMailConfig, getDevelopmentOpenApiDocument, getDevelopmentPostmanCollection, getExplicitMailMode, getHttpReadyReport, getHttpServer, getLastMail, getLocalized, getMailEventName, getMailMode, getMailboxSize, getMailer, getMailerConfig, getMimeType, getPoolStats, getPublicUrl, getRoles, getSites, getSocketServer, getTestMailbox, getUseCase, getUseCaseHistory, getUseCases, globalEventsCallbacksMap, globalUseCasesEvents, hasRole, hashBody, hashPassword, health, hmacHash, httpConfig, ids, imageRule, increaseUseCaseFailedCalls, increaseUseCaseSuccessCalls, inspectHandlerSignature, ipMatches, isAllowedCsrfOrigin, isAuthGuardMiddleware, isBootSignal, isConfigSchema, isDevelopmentMode, isNewerVersion, isNullableResource, isPortAvailable, isPrivateOrReservedIp, isProductionMode, isSafeRedirect, isTestMode, isTracingEnabled, isUnguardedMutatingRoute, isUnknownTsExtensionError, isUnresolvedConfigImportError, isUnsupportedTypeStrippingError, isValidIdempotencyKey, isWarlockErrorCode, listPositionalHandlerSuspects, loadS3, logResponse, logsPath, looksLikePositionalHandler, mailEvents, markAuthGuard, matchesDerivedRouteName, matchesIfMatch, matchesIfNoneMatch, measure, mergeCspDirectives, middleware, nodeOnlyCommandError, normalizeOrigin, normalizeRequestPath, normalizeRoutePath, notifyHttpServerRebuilt, nullable, onHttpServerRebuilt, openApiToPostmanCollection, originOf, ownOrigin, parseEntityTag, parseEntityTagList, parseLocaleDictionary, parseRoles, parseSites, parseSize, parseTraceparentTraceId, paths, preflightConfiguredHttpPort, promiseAllObject, publicPath, publicUrl, purgeImageVariants, redactQueryCredentials, redactUrlCredentials, registerAppConfig, registerConfiguredConnectors, registerDevelopmentOpenApiProvider, registerGroupedTranslationKeys, registerHttpPlugins, registerLocaleDictionary, registerTracingHooks, remedyLines, renderReact, renderReactMail, reportPositionalHandlerSuspects, reportUnguardedMutatingRoutes, requestContext, requestMemo, requiresDefaultCsrfGuard, resetHttpReadyReport, resetMailConfig, resetMailMode, resetRolesCacheForTests, resetTracingConfigForTests, resetUnguardedRouteReports, resolveCspConfig, resolveCsrfOriginVerdict, resolveImageVariant, resolveMailConfig, resolvePublicRoot, resolveRequestLoggingOptions, resolveTracingConfig, resolveWithinRoot, rootPath, routeNameMethodSuffix, router, runDefaultCsrfGuard, runPipeline, safeFetchToBuffer, safeRedirect, sanitizePath, secrets, seeder, sendBootSignal, sendMail, serializeCspDirectives, servesHttp, setBaseUrl, setClockSource, setConfig, setEnvironment, setHttpReadyReport, setIdsSource, setLogConfigurations, setMailConfigurations, setMailMode, shouldPreflightHttpPort, sleep, sluggable, srcPath, startHttpServer, stopHttpApplication, storage, storageConfig, storageConfigurations, storageDriverContext, storagePath, streamReactResponse, strongEntityTagMatch, t, tempPath, toJson, toSingleLineSseField, trackConnectionsForDrain, uniqueExceptCurrentIdRule, uniqueExceptCurrentUserRule, unregisterLocaleDictionary, unverifiedBuildRuntimeNotice, uploadedFileController, uploadedFileMetadataSchema, uploadsConfig, uploadsPath, uploadsUrl, url, useCase, useComputedModel, useComputedSlug, useCurrentUser, useHashedPassword, useRequest, useRequestStore, validateConfigValue, validateCspConfigAtBoot, validateCspDirectives, verifyMailer, verifyPassword, warlockConfigManager, warlockPath, wasMailSentTo, wasMailSentWithSubject, weakEntityTagMatch, wrapResponseInDataKey };
207
+ export { $registerUseCase, $unregisterUseCase, APP_ROLES, AUTH_GUARD_MARKER, AccessConnector, AiConnector, AppError, Application, BOOT_SIGNAL_ENV_KEY, BOOT_SIGNAL_VERSION, BadRequestError, BadSchemaUseCaseError, BaseConnector, BenchmarkProfiler, BenchmarkSnapshots, CLICommand, CacheConnector, CascadeAdapter, CascadeQueryBuilder, CloudDriver, ConfigSpecialHandlers, ConflictError, ConnectorLifecyclePhase, ConnectorPriority, ConnectorsManager, ConsoleChannel, CookieJarUnavailableError, DEFAULT_CSP_DIRECTIVES, DEFAULT_POSTMAN_BASE_URL, DOSpacesDriver, DatabaseCacheDriver, DatabaseConnector, DatabaseLog, DatabaseLogModel, ERROR_CODES, ERROR_CODES_DOCS_URL, FileValidator, ForbiddenError, HeraldConnector, HttpConnector, HttpError, HttpErrorCodes, Image, ImageVariantsConfigError, InvalidConfigError, InvalidCspDirectiveError, LocalDriver, LocaleDictionaryError, LoggerConnector, MAIL_EVENTS, Mail, MailError, MailerConnector, MemorySecretBackend, MimeTypes, NULLABLE_RESOURCE, NoopChannel, NotAcceptableError, NotAllowedError, NotificationsConnector, OPENAPI_JSON_SCHEMA_DIALECT, POSTMAN_COLLECTION_SCHEMA, Path, PortInUseError, Queue, R2Driver, RegisterResource, RepositoryManager, Request, RequestController, RequestLog, Resource, ResourceFieldBuilder, ResourceNotFoundError, Response, ResponseStatus, Restful, RouteNameTakenError, RouteRegistry, Router, S3Driver, SUPPORTED_RUNTIMES_DOCS_URL, ScopedStorage, SecretStore, SecretStoreError, SeederDependencyCycleError, ServerError, SocketConnector, Storage, StorageCapabilityError, StorageConnector, StorageError, StorageFile, StorageNotInitializedError, UPDATE_MANY_BATCH_SIZE, UPLOADS_DEFAULTS, URL_CREDENTIAL_PARAMS, UnAuthorizedError, UnknownSeederDependencyError, UploadedFile, WarlockConfigManager, WarlockError, addUseCaseHistory, anyMatch, app, appLog, appPath, applyCspHeader, assertConfiguredHttpPortIsFree, assertMailCount, assertMailSent, assertNoReservedConnectorNames, assertNodeOnlyCommand, assertPortIsAvailable, assertSitesRequireWebRole, assertUniqueConnectorNames, assertValidConfigModules, assetsUrl, beginHttpDrain, bootstrap, broadcastSocketEvent, broadcastUseCaseResult, buildCspHeaderValue, buildIdempotencyCacheKey, buildOpenApiDocument, buildRequestLogSnapshot, buildTracingContext, cachePath, captureMail, clearPositionalHandlerSuspects, clearTestMailbox, clock, closeAllMailers, closeMailer, closeServerWithTimeout, colors, command, config, configPath, configSpecialHandlers, connectorsManager, container, createHttpApplication, createRequestStore, createUploadedFileController, decrypt, defaultHttpConfigurations, defineConfig, defineConfigSchema, defineResource, deriveTraceId, describeJavaScriptRuntime, describePositionalHandlerSuspect, detectJavaScriptRuntime, dispatchPhase, dispatchRequestEnd, dispatchRequestStart, displayEnvironmentMode, encrypt, ensureFatalIsVisible, env, environment, errorCodeDocsUrl, errorCodeOf, escapeHeaderValue, evaluateConditionalRequest, exampleFromSchema, existsExceptCurrentIdRule, existsExceptCurrentUserRule, explainErrorCode, fetchLatestVersion, fileExtensionRule, fileRule, fileTypeRule, findMailsBySubject, findMailsTo, fireLifecycleEvent, forgetPositionalHandlerSuspects, formatConfigIssues, generateImageVariants, generateMailId, getDefaultMailConfig, getDevelopmentOpenApiDocument, getDevelopmentPostmanCollection, getExplicitMailMode, getHttpReadyReport, getHttpServer, getLastMail, getLocalized, getMailEventName, getMailMode, getMailboxSize, getMailer, getMailerConfig, getMimeType, getPoolStats, getPublicUrl, getRoles, getSites, getSocketServer, getTestMailbox, getUseCase, getUseCaseHistory, getUseCases, globalEventsCallbacksMap, globalUseCasesEvents, hasRole, hashBody, hashPassword, health, hmacHash, httpConfig, ids, imageRule, increaseUseCaseFailedCalls, increaseUseCaseSuccessCalls, inspectHandlerSignature, ipMatches, isAllowedCsrfOrigin, isAuthGuardMiddleware, isBootSignal, isConfigSchema, isDevelopmentMode, isNewerVersion, isNullableResource, isPortAvailable, isPrivateOrReservedIp, isProductionMode, isSafeRedirect, isTestMode, isTracingEnabled, isUnguardedMutatingRoute, isUnknownTsExtensionError, isUnresolvedConfigImportError, isUnsupportedTypeStrippingError, isValidIdempotencyKey, isWarlockErrorCode, listEndpoint, listPositionalHandlerSuspects, loadS3, logResponse, logsPath, looksLikePositionalHandler, mailEvents, markAuthGuard, matchesDerivedRouteName, matchesIfMatch, matchesIfNoneMatch, measure, mergeCspDirectives, middleware, nodeOnlyCommandError, normalizeOrigin, normalizeRequestPath, normalizeRoutePath, notifyHttpServerRebuilt, nullable, onHttpServerRebuilt, openApiToPostmanCollection, originOf, ownOrigin, parseEntityTag, parseEntityTagList, parseLocaleDictionary, parseRoles, parseSites, parseSize, parseTraceparentTraceId, paths, preflightConfiguredHttpPort, promiseAllObject, publicPath, publicUrl, purgeImageVariants, redactQueryCredentials, redactUrlCredentials, registerAppConfig, registerConfiguredConnectors, registerDevelopmentOpenApiProvider, registerGroupedTranslationKeys, registerHttpPlugins, registerLocaleDictionary, registerTracingHooks, remedyLines, renderReact, renderReactMail, reportPositionalHandlerSuspects, reportUnguardedMutatingRoutes, requestContext, requestMemo, requiresDefaultCsrfGuard, resetHttpReadyReport, resetMailConfig, resetMailMode, resetRolesCacheForTests, resetTracingConfigForTests, resetUnguardedRouteReports, resolveCspConfig, resolveCsrfOriginVerdict, resolveImageVariant, resolveMailConfig, resolvePublicRoot, resolveRequestLoggingOptions, resolveTracingConfig, resolveWithinRoot, rootPath, routeNameMethodSuffix, router, runDefaultCsrfGuard, runPipeline, safeFetchToBuffer, safeRedirect, sanitizePath, secrets, seeder, sendBootSignal, sendMail, serializeCspDirectives, servesHttp, setBaseUrl, setClockSource, setConfig, setEnvironment, setHttpReadyReport, setIdsSource, setLogConfigurations, setMailConfigurations, setMailMode, shouldPreflightHttpPort, sleep, sluggable, srcPath, startHttpServer, stopHttpApplication, storage, storageConfig, storageConfigurations, storageDriverContext, storagePath, streamReactResponse, strongEntityTagMatch, t, tempPath, toJson, toSingleLineSseField, trackConnectionsForDrain, uniqueExceptCurrentIdRule, uniqueExceptCurrentUserRule, unregisterLocaleDictionary, unverifiedBuildRuntimeNotice, uploadedFileController, uploadedFileMetadataSchema, uploadsConfig, uploadsPath, uploadsUrl, url, useCase, useComputedModel, useComputedSlug, useCurrentUser, useHashedPassword, useRequest, useRequestStore, validateConfigValue, validateCspConfigAtBoot, validateCspDirectives, verifyMailer, verifyPassword, warlockConfigManager, warlockPath, wasMailSentTo, wasMailSentWithSubject, weakEntityTagMatch, wrapResponseInDataKey };
@@ -14,12 +14,14 @@ function isAuthSource(value) {
14
14
  */
15
15
  function parseAuthDescriptor(value) {
16
16
  if (typeof value !== "object" || value === null) return;
17
- const { sources, userTypes } = value;
17
+ const { sources, userTypes, required } = value;
18
+ if (required !== void 0 && typeof required !== "boolean") return void 0;
18
19
  if (!Array.isArray(sources) || sources.length === 0 || !sources.every(isAuthSource)) return;
19
20
  if (userTypes !== void 0 && !(Array.isArray(userTypes) && userTypes.every((type) => typeof type === "string"))) return;
20
21
  return {
21
22
  sources: [...sources],
22
- userTypes: Array.isArray(userTypes) ? [...userTypes] : []
23
+ userTypes: Array.isArray(userTypes) ? [...userTypes] : [],
24
+ ...required === void 0 ? {} : { required }
23
25
  };
24
26
  }
25
27
  /**
@@ -36,6 +38,8 @@ function readRouteAuth(middleware, warn) {
36
38
  const sources = [];
37
39
  const userTypes = [];
38
40
  let found = false;
41
+ let required = false;
42
+ let hasRequiredFlag = false;
39
43
  for (const fn of middleware) {
40
44
  if (typeof fn !== "function") continue;
41
45
  const raw = Reflect.get(fn, AUTH_DESCRIPTOR_SYMBOL);
@@ -46,12 +50,15 @@ function readRouteAuth(middleware, warn) {
46
50
  continue;
47
51
  }
48
52
  found = true;
53
+ required ||= descriptor.required !== false;
54
+ hasRequiredFlag ||= descriptor.required !== void 0;
49
55
  for (const source of descriptor.sources) if (!sources.some((existing) => typeof existing === "string" || typeof source === "string" ? existing === source : existing.cookie === source.cookie)) sources.push(source);
50
56
  for (const type of descriptor.userTypes) if (!userTypes.includes(type)) userTypes.push(type);
51
57
  }
52
58
  return found ? {
53
59
  sources,
54
- userTypes
60
+ userTypes,
61
+ ...hasRequiredFlag ? { required } : {}
55
62
  } : void 0;
56
63
  }
57
64
  /**
@@ -64,7 +71,7 @@ var SecuritySchemes = class {
64
71
  this.cookieSchemeNames = /* @__PURE__ */ new Map();
65
72
  }
66
73
  requirementsFor(descriptor) {
67
- return descriptor.sources.map((source) => {
74
+ const requirements = descriptor.sources.map((source) => {
68
75
  if (source === "header") {
69
76
  this.schemes.bearerAuth = {
70
77
  type: "http",
@@ -75,6 +82,7 @@ var SecuritySchemes = class {
75
82
  }
76
83
  return { [this.cookieScheme(source.cookie)]: [] };
77
84
  });
85
+ return descriptor.required === false ? [...requirements, {}] : requirements;
78
86
  }
79
87
  cookieScheme(cookie) {
80
88
  const existing = this.cookieSchemeNames.get(cookie);
@@ -1 +1 @@
1
- {"version":3,"file":"security.mjs","names":[],"sources":["../../../../../../../../packages/core/src/openapi/security.ts"],"sourcesContent":["import type {\n AuthDescriptor,\n OpenApiSecurityRequirement,\n OpenApiSecurityScheme,\n WarningSink,\n} from \"./types\";\n\n/**\n * The marker `authMiddleware()` attaches to the middleware it returns.\n * Core reads it structurally; it never imports the auth package.\n */\nexport const AUTH_DESCRIPTOR_SYMBOL = Symbol.for(\"warlock.auth\");\n\nfunction isAuthSource(value: unknown): value is AuthDescriptor[\"sources\"][number] {\n if (value === \"header\") {\n return true;\n }\n\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as { cookie?: unknown }).cookie === \"string\" &&\n (value as { cookie: string }).cookie.length > 0\n );\n}\n\n/**\n * Validate an auth descriptor. Returns `undefined` for anything that is not\n * `{ sources: (\"header\" | { cookie: string })[], userTypes?: string[] }` with at least one source.\n */\nexport function parseAuthDescriptor(value: unknown): AuthDescriptor | undefined {\n if (typeof value !== \"object\" || value === null) {\n return undefined;\n }\n\n const { sources, userTypes } = value as { sources?: unknown; userTypes?: unknown };\n\n if (!Array.isArray(sources) || sources.length === 0 || !sources.every(isAuthSource)) {\n return undefined;\n }\n\n if (\n userTypes !== undefined &&\n !(Array.isArray(userTypes) && userTypes.every((type) => typeof type === \"string\"))\n ) {\n return undefined;\n }\n\n return { sources: [...sources], userTypes: Array.isArray(userTypes) ? [...userTypes] : [] };\n}\n\n/**\n * Combine the descriptors of every guard on a route (group middleware is already merged into\n * the route): unique sources, union of user types.\n *\n * Middleware without a descriptor is ordinary middleware and is ignored silently. A function\n * that carries a descriptor of the wrong shape is reported through `warn`.\n *\n * @returns `undefined` when the route has no valid auth descriptor.\n */\nexport function readRouteAuth(middleware: unknown, warn: WarningSink): AuthDescriptor | undefined {\n if (!Array.isArray(middleware)) {\n return undefined;\n }\n\n const sources: AuthDescriptor[\"sources\"] = [];\n const userTypes: string[] = [];\n let found = false;\n\n for (const fn of middleware) {\n if (typeof fn !== \"function\") {\n continue;\n }\n\n const raw = Reflect.get(fn, AUTH_DESCRIPTOR_SYMBOL);\n\n if (raw === undefined) {\n continue;\n }\n\n const descriptor = parseAuthDescriptor(raw);\n\n if (!descriptor) {\n warn(\n \"a middleware carries a malformed warlock.auth descriptor; the route is documented without security.\",\n );\n\n continue;\n }\n\n found = true;\n\n for (const source of descriptor.sources) {\n const duplicate = sources.some((existing) =>\n typeof existing === \"string\" || typeof source === \"string\"\n ? existing === source\n : existing.cookie === source.cookie,\n );\n\n if (!duplicate) {\n sources.push(source);\n }\n }\n\n for (const type of descriptor.userTypes) {\n if (!userTypes.includes(type)) {\n userTypes.push(type);\n }\n }\n }\n\n return found ? { sources, userTypes } : undefined;\n}\n\n/**\n * Collects the security schemes the routes actually use and turns descriptors into\n * per-operation requirements. A dual header + cookie guard is an OR: one requirement each.\n */\nexport class SecuritySchemes {\n public readonly schemes: Record<string, OpenApiSecurityScheme> = {};\n\n private readonly cookieSchemeNames = new Map<string, string>();\n\n public requirementsFor(descriptor: AuthDescriptor): OpenApiSecurityRequirement[] {\n return descriptor.sources.map((source) => {\n if (source === \"header\") {\n this.schemes.bearerAuth = { type: \"http\", scheme: \"bearer\", bearerFormat: \"JWT\" };\n\n return { bearerAuth: [] };\n }\n\n const name = this.cookieScheme(source.cookie);\n\n return { [name]: [] };\n });\n }\n\n private cookieScheme(cookie: string): string {\n const existing = this.cookieSchemeNames.get(cookie);\n\n if (existing) {\n return existing;\n }\n\n // The first cookie keeps the conventional name; a second, different cookie gets its own.\n const name =\n this.cookieSchemeNames.size === 0\n ? \"cookieAuth\"\n : `cookieAuth_${cookie.replace(/[^A-Za-z0-9._-]/g, \"\")}`;\n\n this.cookieSchemeNames.set(cookie, name);\n this.schemes[name] = { type: \"apiKey\", in: \"cookie\", name: cookie };\n\n return name;\n }\n\n public hasSchemes(): boolean {\n return Object.keys(this.schemes).length > 0;\n }\n}\n"],"mappings":";;;;;AAWA,MAAa,yBAAyB,OAAO,IAAI,cAAc;AAE/D,SAAS,aAAa,OAA4D;CAChF,IAAI,UAAU,UACZ,OAAO;CAGT,OACE,OAAO,UAAU,YACjB,UAAU,QACV,OAAQ,MAA+B,WAAW,YACjD,MAA6B,OAAO,SAAS;AAElD;;;;;AAMA,SAAgB,oBAAoB,OAA4C;CAC9E,IAAI,OAAO,UAAU,YAAY,UAAU,MACzC;CAGF,MAAM,EAAE,SAAS,cAAc;CAE/B,IAAI,CAAC,MAAM,QAAQ,OAAO,KAAK,QAAQ,WAAW,KAAK,CAAC,QAAQ,MAAM,YAAY,GAChF;CAGF,IACE,cAAc,UACd,EAAE,MAAM,QAAQ,SAAS,KAAK,UAAU,OAAO,SAAS,OAAO,SAAS,QAAQ,IAEhF;CAGF,OAAO;EAAE,SAAS,CAAC,GAAG,OAAO;EAAG,WAAW,MAAM,QAAQ,SAAS,IAAI,CAAC,GAAG,SAAS,IAAI,CAAC;CAAE;AAC5F;;;;;;;;;;AAWA,SAAgB,cAAc,YAAqB,MAA+C;CAChG,IAAI,CAAC,MAAM,QAAQ,UAAU,GAC3B;CAGF,MAAM,UAAqC,CAAC;CAC5C,MAAM,YAAsB,CAAC;CAC7B,IAAI,QAAQ;CAEZ,KAAK,MAAM,MAAM,YAAY;EAC3B,IAAI,OAAO,OAAO,YAChB;EAGF,MAAM,MAAM,QAAQ,IAAI,IAAI,sBAAsB;EAElD,IAAI,QAAQ,QACV;EAGF,MAAM,aAAa,oBAAoB,GAAG;EAE1C,IAAI,CAAC,YAAY;GACf,KACE,qGACF;GAEA;EACF;EAEA,QAAQ;EAER,KAAK,MAAM,UAAU,WAAW,SAO9B,IAAI,CANc,QAAQ,MAAM,aAC9B,OAAO,aAAa,YAAY,OAAO,WAAW,WAC9C,aAAa,SACb,SAAS,WAAW,OAAO,MAGpB,GACX,QAAQ,KAAK,MAAM;EAIvB,KAAK,MAAM,QAAQ,WAAW,WAC5B,IAAI,CAAC,UAAU,SAAS,IAAI,GAC1B,UAAU,KAAK,IAAI;CAGzB;CAEA,OAAO,QAAQ;EAAE;EAAS;CAAU,IAAI;AAC1C;;;;;AAMA,IAAa,kBAAb,MAA6B;;iBACsC,CAAC;2CAE7B,IAAI,IAAoB;;CAE7D,AAAO,gBAAgB,YAA0D;EAC/E,OAAO,WAAW,QAAQ,KAAK,WAAW;GACxC,IAAI,WAAW,UAAU;IACvB,KAAK,QAAQ,aAAa;KAAE,MAAM;KAAQ,QAAQ;KAAU,cAAc;IAAM;IAEhF,OAAO,EAAE,YAAY,CAAC,EAAE;GAC1B;GAIA,OAAO,GAFM,KAAK,aAAa,OAAO,MAEzB,IAAI,CAAC,EAAE;EACtB,CAAC;CACH;CAEA,AAAQ,aAAa,QAAwB;EAC3C,MAAM,WAAW,KAAK,kBAAkB,IAAI,MAAM;EAElD,IAAI,UACF,OAAO;EAIT,MAAM,OACJ,KAAK,kBAAkB,SAAS,IAC5B,eACA,cAAc,OAAO,QAAQ,oBAAoB,EAAE;EAEzD,KAAK,kBAAkB,IAAI,QAAQ,IAAI;EACvC,KAAK,QAAQ,QAAQ;GAAE,MAAM;GAAU,IAAI;GAAU,MAAM;EAAO;EAElE,OAAO;CACT;CAEA,AAAO,aAAsB;EAC3B,OAAO,OAAO,KAAK,KAAK,OAAO,CAAC,CAAC,SAAS;CAC5C;AACF"}
1
+ {"version":3,"file":"security.mjs","names":[],"sources":["../../../../../../../../packages/core/src/openapi/security.ts"],"sourcesContent":["import type {\n AuthDescriptor,\n OpenApiSecurityRequirement,\n OpenApiSecurityScheme,\n WarningSink,\n} from \"./types\";\n\n/**\n * The marker `authMiddleware()` attaches to the middleware it returns.\n * Core reads it structurally; it never imports the auth package.\n */\nexport const AUTH_DESCRIPTOR_SYMBOL = Symbol.for(\"warlock.auth\");\n\nfunction isAuthSource(value: unknown): value is AuthDescriptor[\"sources\"][number] {\n if (value === \"header\") {\n return true;\n }\n\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as { cookie?: unknown }).cookie === \"string\" &&\n (value as { cookie: string }).cookie.length > 0\n );\n}\n\n/**\n * Validate an auth descriptor. Returns `undefined` for anything that is not\n * `{ sources: (\"header\" | { cookie: string })[], userTypes?: string[] }` with at least one source.\n */\nexport function parseAuthDescriptor(value: unknown): AuthDescriptor | undefined {\n if (typeof value !== \"object\" || value === null) {\n return undefined;\n }\n\n const { sources, userTypes, required } = value as {\n sources?: unknown;\n userTypes?: unknown;\n required?: unknown;\n };\n if (required !== undefined && typeof required !== \"boolean\") return undefined;\n\n if (!Array.isArray(sources) || sources.length === 0 || !sources.every(isAuthSource)) {\n return undefined;\n }\n\n if (\n userTypes !== undefined &&\n !(Array.isArray(userTypes) && userTypes.every((type) => typeof type === \"string\"))\n ) {\n return undefined;\n }\n\n return {\n sources: [...sources],\n userTypes: Array.isArray(userTypes) ? [...userTypes] : [],\n ...(required === undefined ? {} : { required }),\n };\n}\n\n/**\n * Combine the descriptors of every guard on a route (group middleware is already merged into\n * the route): unique sources, union of user types.\n *\n * Middleware without a descriptor is ordinary middleware and is ignored silently. A function\n * that carries a descriptor of the wrong shape is reported through `warn`.\n *\n * @returns `undefined` when the route has no valid auth descriptor.\n */\nexport function readRouteAuth(middleware: unknown, warn: WarningSink): AuthDescriptor | undefined {\n if (!Array.isArray(middleware)) {\n return undefined;\n }\n\n const sources: AuthDescriptor[\"sources\"] = [];\n const userTypes: string[] = [];\n let found = false;\n let required = false;\n let hasRequiredFlag = false;\n\n for (const fn of middleware) {\n if (typeof fn !== \"function\") {\n continue;\n }\n\n const raw = Reflect.get(fn, AUTH_DESCRIPTOR_SYMBOL);\n\n if (raw === undefined) {\n continue;\n }\n\n const descriptor = parseAuthDescriptor(raw);\n\n if (!descriptor) {\n warn(\n \"a middleware carries a malformed warlock.auth descriptor; the route is documented without security.\",\n );\n\n continue;\n }\n\n found = true;\n required ||= descriptor.required !== false;\n hasRequiredFlag ||= descriptor.required !== undefined;\n\n for (const source of descriptor.sources) {\n const duplicate = sources.some((existing) =>\n typeof existing === \"string\" || typeof source === \"string\"\n ? existing === source\n : existing.cookie === source.cookie,\n );\n\n if (!duplicate) {\n sources.push(source);\n }\n }\n\n for (const type of descriptor.userTypes) {\n if (!userTypes.includes(type)) {\n userTypes.push(type);\n }\n }\n }\n\n return found ? { sources, userTypes, ...(hasRequiredFlag ? { required } : {}) } : undefined;\n}\n\n/**\n * Collects the security schemes the routes actually use and turns descriptors into\n * per-operation requirements. A dual header + cookie guard is an OR: one requirement each.\n */\nexport class SecuritySchemes {\n public readonly schemes: Record<string, OpenApiSecurityScheme> = {};\n\n private readonly cookieSchemeNames = new Map<string, string>();\n\n public requirementsFor(descriptor: AuthDescriptor): OpenApiSecurityRequirement[] {\n const requirements: OpenApiSecurityRequirement[] = descriptor.sources.map((source) => {\n if (source === \"header\") {\n this.schemes.bearerAuth = { type: \"http\", scheme: \"bearer\", bearerFormat: \"JWT\" };\n\n return { bearerAuth: [] };\n }\n\n const name = this.cookieScheme(source.cookie);\n\n return { [name]: [] };\n });\n return descriptor.required === false ? [...requirements, {}] : requirements;\n }\n\n private cookieScheme(cookie: string): string {\n const existing = this.cookieSchemeNames.get(cookie);\n\n if (existing) {\n return existing;\n }\n\n // The first cookie keeps the conventional name; a second, different cookie gets its own.\n const name =\n this.cookieSchemeNames.size === 0\n ? \"cookieAuth\"\n : `cookieAuth_${cookie.replace(/[^A-Za-z0-9._-]/g, \"\")}`;\n\n this.cookieSchemeNames.set(cookie, name);\n this.schemes[name] = { type: \"apiKey\", in: \"cookie\", name: cookie };\n\n return name;\n }\n\n public hasSchemes(): boolean {\n return Object.keys(this.schemes).length > 0;\n }\n}\n"],"mappings":";;;;;AAWA,MAAa,yBAAyB,OAAO,IAAI,cAAc;AAE/D,SAAS,aAAa,OAA4D;CAChF,IAAI,UAAU,UACZ,OAAO;CAGT,OACE,OAAO,UAAU,YACjB,UAAU,QACV,OAAQ,MAA+B,WAAW,YACjD,MAA6B,OAAO,SAAS;AAElD;;;;;AAMA,SAAgB,oBAAoB,OAA4C;CAC9E,IAAI,OAAO,UAAU,YAAY,UAAU,MACzC;CAGF,MAAM,EAAE,SAAS,WAAW,aAAa;CAKzC,IAAI,aAAa,UAAa,OAAO,aAAa,WAAW,OAAO;CAEpE,IAAI,CAAC,MAAM,QAAQ,OAAO,KAAK,QAAQ,WAAW,KAAK,CAAC,QAAQ,MAAM,YAAY,GAChF;CAGF,IACE,cAAc,UACd,EAAE,MAAM,QAAQ,SAAS,KAAK,UAAU,OAAO,SAAS,OAAO,SAAS,QAAQ,IAEhF;CAGF,OAAO;EACL,SAAS,CAAC,GAAG,OAAO;EACpB,WAAW,MAAM,QAAQ,SAAS,IAAI,CAAC,GAAG,SAAS,IAAI,CAAC;EACxD,GAAI,aAAa,SAAY,CAAC,IAAI,EAAE,SAAS;CAC/C;AACF;;;;;;;;;;AAWA,SAAgB,cAAc,YAAqB,MAA+C;CAChG,IAAI,CAAC,MAAM,QAAQ,UAAU,GAC3B;CAGF,MAAM,UAAqC,CAAC;CAC5C,MAAM,YAAsB,CAAC;CAC7B,IAAI,QAAQ;CACZ,IAAI,WAAW;CACf,IAAI,kBAAkB;CAEtB,KAAK,MAAM,MAAM,YAAY;EAC3B,IAAI,OAAO,OAAO,YAChB;EAGF,MAAM,MAAM,QAAQ,IAAI,IAAI,sBAAsB;EAElD,IAAI,QAAQ,QACV;EAGF,MAAM,aAAa,oBAAoB,GAAG;EAE1C,IAAI,CAAC,YAAY;GACf,KACE,qGACF;GAEA;EACF;EAEA,QAAQ;EACR,aAAa,WAAW,aAAa;EACrC,oBAAoB,WAAW,aAAa;EAE5C,KAAK,MAAM,UAAU,WAAW,SAO9B,IAAI,CANc,QAAQ,MAAM,aAC9B,OAAO,aAAa,YAAY,OAAO,WAAW,WAC9C,aAAa,SACb,SAAS,WAAW,OAAO,MAGpB,GACX,QAAQ,KAAK,MAAM;EAIvB,KAAK,MAAM,QAAQ,WAAW,WAC5B,IAAI,CAAC,UAAU,SAAS,IAAI,GAC1B,UAAU,KAAK,IAAI;CAGzB;CAEA,OAAO,QAAQ;EAAE;EAAS;EAAW,GAAI,kBAAkB,EAAE,SAAS,IAAI,CAAC;CAAG,IAAI;AACpF;;;;;AAMA,IAAa,kBAAb,MAA6B;;iBACsC,CAAC;2CAE7B,IAAI,IAAoB;;CAE7D,AAAO,gBAAgB,YAA0D;EAC/E,MAAM,eAA6C,WAAW,QAAQ,KAAK,WAAW;GACpF,IAAI,WAAW,UAAU;IACvB,KAAK,QAAQ,aAAa;KAAE,MAAM;KAAQ,QAAQ;KAAU,cAAc;IAAM;IAEhF,OAAO,EAAE,YAAY,CAAC,EAAE;GAC1B;GAIA,OAAO,GAFM,KAAK,aAAa,OAAO,MAEzB,IAAI,CAAC,EAAE;EACtB,CAAC;EACD,OAAO,WAAW,aAAa,QAAQ,CAAC,GAAG,cAAc,CAAC,CAAC,IAAI;CACjE;CAEA,AAAQ,aAAa,QAAwB;EAC3C,MAAM,WAAW,KAAK,kBAAkB,IAAI,MAAM;EAElD,IAAI,UACF,OAAO;EAIT,MAAM,OACJ,KAAK,kBAAkB,SAAS,IAC5B,eACA,cAAc,OAAO,QAAQ,oBAAoB,EAAE;EAEzD,KAAK,kBAAkB,IAAI,QAAQ,IAAI;EACvC,KAAK,QAAQ,QAAQ;GAAE,MAAM;GAAU,IAAI;GAAU,MAAM;EAAO;EAElE,OAAO;CACT;CAEA,AAAO,aAAsB;EAC3B,OAAO,OAAO,KAAK,KAAK,OAAO,CAAC,CAAC,SAAS;CAC5C;AACF"}
@@ -112,6 +112,8 @@ export type OpenApiRouteInput = Pick<Route, "method" | "path" | "handler"> & Par
112
112
  * Read structurally and validated; core never imports the auth package.
113
113
  */
114
114
  export type AuthDescriptor = {
115
+ /** Absent on legacy descriptors, which require authentication. */
116
+ required?: boolean;
115
117
  sources: ("header" | {
116
118
  cookie: string;
117
119
  })[];
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.mts","names":[],"sources":["../../../../../../../../packages/core/src/openapi/types.ts"],"mappings":";;;;;YAKY,gBAAgB;YAEhB;EACV;EACA;EACA;EACA;EACA;EACA;EACA,QAAQ;;YAGE;EACV;EACA,SAAS;IAAiB,QAAQ;;;YAGxB;EACV;EACA,UAAU;IAAiB,QAAQ;;;YAGzB,6BAA6B;YAE7B;EACV;EACA;EACA;EACA;EACA,aAAa;EACb,cAAc;EACd,WAAW,eAAe;EAC1B,WAAW;;YAGD;EACN;EAAc;EAAkB;;EAChC;EAAgB;EAAc;;YAExB;EACV,UAAU,eAAe;EACzB,kBAAkB,eAAe;;YAGvB;EACV;EACA;EACA;IAAQ;IAAe;IAAiB;;EACxC;IAAY;;EACZ;IAAS;;EACT,OAAO,eAAe,eAAe;EACrC,aAAa;;;;;;YAOH;EACV;EACA;EACA;EACA;;EAEA;;;;;;YAOU;EACV;IAAQ;IAAe;IAAiB;;;EAExC;;EAEA;;;;;;EAMA,uBAAuB;;EAEvB,qBAAqB;;YAGX;EACV,UAAU;EACV;;;YAIU,oBAAoB,KAAK,wCACnC,QACE,KACE;;;;;YASM;EACV;IAAuB;;EACvB;;YAGU,eAAe"}
1
+ {"version":3,"file":"types.d.mts","names":[],"sources":["../../../../../../../../packages/core/src/openapi/types.ts"],"mappings":";;;;;YAKY,gBAAgB;YAEhB;EACV;EACA;EACA;EACA;EACA;EACA;EACA,QAAQ;;YAGE;EACV;EACA,SAAS;IAAiB,QAAQ;;;YAGxB;EACV;EACA,UAAU;IAAiB,QAAQ;;;YAGzB,6BAA6B;YAE7B;EACV;EACA;EACA;EACA;EACA,aAAa;EACb,cAAc;EACd,WAAW,eAAe;EAC1B,WAAW;;YAGD;EACN;EAAc;EAAkB;;EAChC;EAAgB;EAAc;;YAExB;EACV,UAAU,eAAe;EACzB,kBAAkB,eAAe;;YAGvB;EACV;EACA;EACA;IAAQ;IAAe;IAAiB;;EACxC;IAAY;;EACZ;IAAS;;EACT,OAAO,eAAe,eAAe;EACrC,aAAa;;;;;;YAOH;EACV;EACA;EACA;EACA;;EAEA;;;;;;YAOU;EACV;IAAQ;IAAe;IAAiB;;;EAExC;;EAEA;;;;;;EAMA,uBAAuB;;EAEvB,qBAAqB;;YAGX;EACV,UAAU;EACV;;;YAIU,oBAAoB,KAAK,wCACnC,QACE,KACE;;;;;YASM;;EAEV;EACA;IAAuB;;EACvB;;YAGU,eAAe"}
@@ -0,0 +1,46 @@
1
+ //#region ../../packages/core/src/production/build-budgets.d.ts
2
+ export type PageBudget = {
3
+ js?: string;
4
+ css?: string;
5
+ };
6
+ export type BuildBudgets = {
7
+ page?: PageBudget;
8
+ /** Route-path globs supporting *, ** and ?. Later matching entries win per field. */
9
+ pages?: Record<string, PageBudget>;
10
+ /** Defaults to error when budgets are configured. */
11
+ mode?: "error" | "warn";
12
+ };
13
+ export type BudgetPage = {
14
+ page: string;
15
+ source: string;
16
+ js: number;
17
+ css: number;
18
+ dynamic: {
19
+ js: number;
20
+ css: number;
21
+ };
22
+ files: string[];
23
+ modules: {
24
+ id: string;
25
+ bytes: number;
26
+ }[];
27
+ delta?: {
28
+ js: number;
29
+ css: number;
30
+ };
31
+ };
32
+ export type BudgetViolation = {
33
+ page: string;
34
+ type: "js" | "css";
35
+ budget: number;
36
+ actual: number;
37
+ modules: BudgetPage["modules"];
38
+ };
39
+ export type BuildBudgetReport = {
40
+ version: 1;
41
+ compression: "gzip";
42
+ pages: BudgetPage[];
43
+ violations: BudgetViolation[];
44
+ };
45
+ //#endregion
46
+ //# sourceMappingURL=build-budgets.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-budgets.d.mts","names":[],"sources":["../../../../../../../../packages/core/src/production/build-budgets.ts"],"mappings":";YAGY;EAAe;EAAa;;YAC5B;EACV,OAAO;;EAEP,QAAQ,eAAe;;EAEvB;;YAEU;EACV;EACA;EACA;EACA;EACA;IAAW;IAAY;;EACvB;EACA;IAAW;IAAY;;EACvB;IAAU;IAAY;;;YAEZ;EACV;EACA;EACA;EACA;EACA,SAAS;;YAEC;EACV;EACA;EACA,OAAO;EACP,YAAY"}
@@ -0,0 +1,109 @@
1
+ import path from "node:path";
2
+ import { readFile, writeFile } from "node:fs/promises";
3
+
4
+ //#region ../../packages/core/src/production/build-budgets.ts
5
+ const BUILD_BUDGET_REPORT_FILE = "build-budgets.json";
6
+ function parseBudgetSize(size) {
7
+ const match = /^(\d+(?:\.\d+)?)\s*(b|kb|mb)$/i.exec(size.trim());
8
+ if (!match) throw new Error(`Invalid build budget ${JSON.stringify(size)}: use bytes, kb or mb, e.g. "150kb".`);
9
+ const bytes = Number(match[1]) * ({
10
+ b: 1,
11
+ kb: 1024,
12
+ mb: 1048576
13
+ }[match[2]?.toLowerCase() ?? ""] ?? 0);
14
+ if (!Number.isFinite(bytes)) throw new Error(`Invalid build budget ${JSON.stringify(size)}.`);
15
+ return bytes;
16
+ }
17
+ function routeMatches(pattern, route) {
18
+ if ([
19
+ "[",
20
+ "]",
21
+ "{",
22
+ "}",
23
+ "(",
24
+ ")",
25
+ "!",
26
+ "\\"
27
+ ].some((token) => pattern.includes(token))) throw new Error(`Invalid build budget glob ${JSON.stringify(pattern)}: supported wildcards are *, ** and ?.`);
28
+ let expression = "^";
29
+ for (let i = 0; i < pattern.length; i++) {
30
+ const character = pattern[i];
31
+ if (character === "*" && pattern[i + 1] === "*") {
32
+ i++;
33
+ if (pattern[i + 1] === "/") {
34
+ expression += "(?:.*/)?";
35
+ i++;
36
+ } else expression += ".*";
37
+ } else if (character === "*") expression += "[^/]*";
38
+ else if (character === "?") expression += "[^/]";
39
+ else expression += character?.replace(/[.+^$|]/g, "\\$&") ?? "";
40
+ }
41
+ return new RegExp(`${expression}$`).test(route);
42
+ }
43
+ function evaluateBuildBudgets(pages, budgets, previous) {
44
+ if (budgets?.mode !== void 0 && budgets.mode !== "error" && budgets.mode !== "warn") throw new Error("build.budgets.mode must be \"error\" or \"warn\".");
45
+ for (const budget of [budgets?.page, ...Object.values(budgets?.pages ?? {})]) for (const size of Object.values(budget ?? {})) if (size !== void 0) parseBudgetSize(size);
46
+ for (const pattern of Object.keys(budgets?.pages ?? {})) routeMatches(pattern, "");
47
+ const violations = [];
48
+ return {
49
+ version: 1,
50
+ compression: "gzip",
51
+ pages: pages.map((page) => {
52
+ let limits = { ...budgets?.page };
53
+ for (const [pattern, override] of Object.entries(budgets?.pages ?? {})) if (routeMatches(pattern, page.page)) limits = {
54
+ ...limits,
55
+ ...override
56
+ };
57
+ for (const type of ["js", "css"]) {
58
+ const configured = limits[type];
59
+ if (configured === void 0) continue;
60
+ const budget = parseBudgetSize(configured);
61
+ if (page[type] > budget) violations.push({
62
+ page: page.page,
63
+ type,
64
+ budget,
65
+ actual: page[type],
66
+ modules: page.modules
67
+ });
68
+ }
69
+ const old = previous?.pages.find((entry) => entry.page === page.page && entry.source === page.source);
70
+ return {
71
+ ...page,
72
+ delta: old ? {
73
+ js: page.js - old.js,
74
+ css: page.css - old.css
75
+ } : void 0
76
+ };
77
+ }).sort((a, b) => b.js + b.css - a.js - a.css || a.page.localeCompare(b.page)),
78
+ violations
79
+ };
80
+ }
81
+ async function readBuildBudgetReport(outdir) {
82
+ try {
83
+ const report = JSON.parse(await readFile(path.join(outdir, BUILD_BUDGET_REPORT_FILE), "utf8"));
84
+ if (!report || typeof report !== "object" || report.version !== 1 || report.compression !== "gzip" || !Array.isArray(report.pages)) return void 0;
85
+ return report;
86
+ } catch (error) {
87
+ if (error instanceof SyntaxError || error instanceof Error && "code" in error && error.code === "ENOENT") return void 0;
88
+ throw error;
89
+ }
90
+ }
91
+ const kb = (bytes) => `${(bytes / 1024).toFixed(2)}kb`;
92
+ const delta = (value) => value === void 0 ? "—" : `${value >= 0 ? "+" : ""}${kb(value)}`;
93
+ function formatBuildBudgets(report) {
94
+ return [
95
+ "First-visit client assets (gzip); dynamic imports excluded from budgets",
96
+ "Page | JS | Δ JS | CSS | Δ CSS | Dynamic JS | Dynamic CSS | Largest modules (rendered bytes)",
97
+ ...report.pages.map((page) => `${page.page} | ${kb(page.js)} | ${delta(page.delta?.js)} | ${kb(page.css)} | ${delta(page.delta?.css)} | ${kb(page.dynamic.js)} | ${kb(page.dynamic.css)} | ${page.modules.slice(0, 3).map((module) => `${module.id} (${module.bytes}b)`).join(", ")}`)
98
+ ].join("\n");
99
+ }
100
+ function formatBudgetViolations(report) {
101
+ return report.violations.map((violation) => `Page ${violation.page}: ${violation.type.toUpperCase()} budget ${kb(violation.budget)}, actual ${kb(violation.actual)} (limit ${violation.budget}b, actual ${violation.actual}b). Biggest modules (rendered bytes): ${violation.modules.map((module) => `${module.id} (${module.bytes}b)`).join(", ") || "none"}`).join("\n");
102
+ }
103
+ async function writeBuildBudgetReport(outdir, report) {
104
+ await writeFile(path.join(outdir, BUILD_BUDGET_REPORT_FILE), `${JSON.stringify(report, null, 2)}\n`);
105
+ }
106
+
107
+ //#endregion
108
+ export { BUILD_BUDGET_REPORT_FILE, evaluateBuildBudgets, formatBudgetViolations, formatBuildBudgets, parseBudgetSize, readBuildBudgetReport, writeBuildBudgetReport };
109
+ //# sourceMappingURL=build-budgets.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-budgets.mjs","names":[],"sources":["../../../../../../../../packages/core/src/production/build-budgets.ts"],"sourcesContent":["import { readFile, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nexport type PageBudget = { js?: string; css?: string };\nexport type BuildBudgets = {\n page?: PageBudget;\n /** Route-path globs supporting *, ** and ?. Later matching entries win per field. */\n pages?: Record<string, PageBudget>;\n /** Defaults to error when budgets are configured. */\n mode?: \"error\" | \"warn\";\n};\nexport type BudgetPage = {\n page: string;\n source: string;\n js: number;\n css: number;\n dynamic: { js: number; css: number };\n files: string[];\n modules: { id: string; bytes: number }[];\n delta?: { js: number; css: number };\n};\nexport type BudgetViolation = {\n page: string;\n type: \"js\" | \"css\";\n budget: number;\n actual: number;\n modules: BudgetPage[\"modules\"];\n};\nexport type BuildBudgetReport = {\n version: 1;\n compression: \"gzip\";\n pages: BudgetPage[];\n violations: BudgetViolation[];\n};\nexport const BUILD_BUDGET_REPORT_FILE = \"build-budgets.json\";\n\nexport function parseBudgetSize(size: string): number {\n const match = /^(\\d+(?:\\.\\d+)?)\\s*(b|kb|mb)$/i.exec(size.trim());\n if (!match)\n throw new Error(\n `Invalid build budget ${JSON.stringify(size)}: use bytes, kb or mb, e.g. \"150kb\".`,\n );\n const units: Record<string, number> = { b: 1, kb: 1024, mb: 1024 * 1024 };\n const bytes = Number(match[1]) * (units[match[2]?.toLowerCase() ?? \"\"] ?? 0);\n if (!Number.isFinite(bytes)) throw new Error(`Invalid build budget ${JSON.stringify(size)}.`);\n return bytes;\n}\n\nfunction routeMatches(pattern: string, route: string): boolean {\n if ([\"[\", \"]\", \"{\", \"}\", \"(\", \")\", \"!\", \"\\\\\"].some((token) => pattern.includes(token))) {\n throw new Error(\n `Invalid build budget glob ${JSON.stringify(pattern)}: supported wildcards are *, ** and ?.`,\n );\n }\n let expression = \"^\";\n for (let i = 0; i < pattern.length; i++) {\n const character = pattern[i];\n if (character === \"*\" && pattern[i + 1] === \"*\") {\n i++;\n if (pattern[i + 1] === \"/\") {\n expression += \"(?:.*/)?\";\n i++;\n } else expression += \".*\";\n } else if (character === \"*\") expression += \"[^/]*\";\n else if (character === \"?\") expression += \"[^/]\";\n else expression += character?.replace(/[.+^$|]/g, \"\\\\$&\") ?? \"\";\n }\n return new RegExp(`${expression}$`).test(route);\n}\n\nexport function evaluateBuildBudgets(\n pages: BudgetPage[],\n budgets?: BuildBudgets,\n previous?: BuildBudgetReport,\n): BuildBudgetReport {\n if (budgets?.mode !== undefined && budgets.mode !== \"error\" && budgets.mode !== \"warn\") {\n throw new Error('build.budgets.mode must be \"error\" or \"warn\".');\n }\n // Validate even overrides that do not match any current route.\n for (const budget of [budgets?.page, ...Object.values(budgets?.pages ?? {})]) {\n for (const size of Object.values(budget ?? {})) {\n if (size !== undefined) parseBudgetSize(size);\n }\n }\n for (const pattern of Object.keys(budgets?.pages ?? {})) routeMatches(pattern, \"\");\n const violations: BudgetViolation[] = [];\n const result = pages\n .map((page) => {\n let limits = { ...budgets?.page };\n for (const [pattern, override] of Object.entries(budgets?.pages ?? {})) {\n if (routeMatches(pattern, page.page)) limits = { ...limits, ...override };\n }\n for (const type of [\"js\", \"css\"] as const) {\n const configured = limits[type];\n if (configured === undefined) continue;\n const budget = parseBudgetSize(configured);\n if (page[type] > budget)\n violations.push({\n page: page.page,\n type,\n budget,\n actual: page[type],\n modules: page.modules,\n });\n }\n const old = previous?.pages.find(\n (entry) => entry.page === page.page && entry.source === page.source,\n );\n return {\n ...page,\n delta: old ? { js: page.js - old.js, css: page.css - old.css } : undefined,\n };\n })\n .sort((a, b) => b.js + b.css - a.js - a.css || a.page.localeCompare(b.page));\n return { version: 1, compression: \"gzip\", pages: result, violations };\n}\n\nexport async function readBuildBudgetReport(\n outdir: string,\n): Promise<BuildBudgetReport | undefined> {\n try {\n const report: BuildBudgetReport = JSON.parse(\n await readFile(path.join(outdir, BUILD_BUDGET_REPORT_FILE), \"utf8\"),\n );\n if (\n !report ||\n typeof report !== \"object\" ||\n report.version !== 1 ||\n report.compression !== \"gzip\" ||\n !Array.isArray(report.pages)\n )\n return undefined;\n return report;\n } catch (error) {\n if (\n error instanceof SyntaxError ||\n (error instanceof Error && \"code\" in error && error.code === \"ENOENT\")\n )\n return undefined;\n throw error;\n }\n}\n\nconst kb = (bytes: number) => `${(bytes / 1024).toFixed(2)}kb`;\nconst delta = (value: number | undefined) =>\n value === undefined ? \"—\" : `${value >= 0 ? \"+\" : \"\"}${kb(value)}`;\nexport function formatBuildBudgets(report: BuildBudgetReport): string {\n return [\n \"First-visit client assets (gzip); dynamic imports excluded from budgets\",\n \"Page | JS | Δ JS | CSS | Δ CSS | Dynamic JS | Dynamic CSS | Largest modules (rendered bytes)\",\n ...report.pages.map(\n (page) =>\n `${page.page} | ${kb(page.js)} | ${delta(page.delta?.js)} | ${kb(page.css)} | ${delta(page.delta?.css)} | ${kb(page.dynamic.js)} | ${kb(page.dynamic.css)} | ${page.modules\n .slice(0, 3)\n .map((module) => `${module.id} (${module.bytes}b)`)\n .join(\", \")}`,\n ),\n ].join(\"\\n\");\n}\nexport function formatBudgetViolations(report: BuildBudgetReport): string {\n return report.violations\n .map(\n (violation) =>\n `Page ${violation.page}: ${violation.type.toUpperCase()} budget ${kb(violation.budget)}, actual ${kb(violation.actual)} (limit ${violation.budget}b, actual ${violation.actual}b). Biggest modules (rendered bytes): ${violation.modules.map((module) => `${module.id} (${module.bytes}b)`).join(\", \") || \"none\"}`,\n )\n .join(\"\\n\");\n}\nexport async function writeBuildBudgetReport(\n outdir: string,\n report: BuildBudgetReport,\n): Promise<void> {\n await writeFile(\n path.join(outdir, BUILD_BUDGET_REPORT_FILE),\n `${JSON.stringify(report, null, 2)}\\n`,\n );\n}\n"],"mappings":"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"}
@@ -3,9 +3,10 @@ import "../utils/index.mjs";
3
3
  import { assertNoReservedConnectorNames } from "../connectors/assert-no-reserved-connector-names.mjs";
4
4
  import { assertUniqueConnectorNames } from "../connectors/assert-unique-connector-names.mjs";
5
5
  import { warlockConfigManager } from "../warlock-config/warlock-config.manager.mjs";
6
- import { tsconfigManager } from "../dev-server/tsconfig-manager.mjs";
7
6
  import { isConfigFile, isEventFile, isLocaleFile, isMainFile, isRouteFile, isWorkerFile } from "../dev-server/special-file-patterns.mjs";
7
+ import { tsconfigManager } from "../dev-server/tsconfig-manager.mjs";
8
8
  import { configKeyFromPath } from "../config/config-key-from-path.mjs";
9
+ import { evaluateBuildBudgets, formatBudgetViolations, formatBuildBudgets, readBuildBudgetReport, writeBuildBudgetReport } from "./build-budgets.mjs";
9
10
  import { assertGeneratedImportsAreDeclared } from "./assert-generated-imports.mjs";
10
11
  import { bundleFrameworkDependencies } from "./bundle-framework-dependencies.mjs";
11
12
  import { dedupe, runEmitContributions, runGenerateContributions } from "./build-contributions.mjs";
@@ -124,6 +125,7 @@ var ProductionBuilder = class {
124
125
  assertEsbuildBinaryIsLinked();
125
126
  await this.initializeOptions();
126
127
  const finalOutDir = this.options.outdir;
128
+ const previousBudgetReport = await readBuildBudgetReport(finalOutDir);
127
129
  const tempOutDir = createTempOutputDir(finalOutDir);
128
130
  let staged;
129
131
  try {
@@ -135,6 +137,15 @@ var ProductionBuilder = class {
135
137
  await this.bundle(tempOutDir);
136
138
  staged = await stageDistAsync(tempOutDir, finalOutDir);
137
139
  await runEmitContributions(this.connectors, this.buildContext());
140
+ const clientReport = await readBuildBudgetReport(finalOutDir);
141
+ this.budgetReport = evaluateBuildBudgets(clientReport?.pages ?? [], this.options.budgets, previousBudgetReport);
142
+ if (clientReport) await writeBuildBudgetReport(finalOutDir, this.budgetReport);
143
+ if (this.budgetReport.pages.length > 0) console.log(formatBuildBudgets(this.budgetReport));
144
+ if (this.budgetReport.violations.length > 0) {
145
+ const message = formatBudgetViolations(this.budgetReport);
146
+ if (this.options.budgets?.mode !== "warn") throw new Error(message);
147
+ console.warn(message);
148
+ }
138
149
  await writeDistBuildManifestAsync(finalOutDir);
139
150
  await commitDistAsync(staged);
140
151
  } catch (error) {
@@ -476,6 +487,7 @@ bootstrap();
476
487
  delete esbuildOptions.esmShim;
477
488
  delete esbuildOptions.banner;
478
489
  delete esbuildOptions.routeRegistrationTimeoutMs;
490
+ delete esbuildOptions.budgets;
479
491
  await ensureDirectoryAsync(writeOutDir);
480
492
  const alias = this.buildAliasMapFromTsconfig();
481
493
  const perEdgeExternals = !singleBundle && esbuildOptions.packages === void 0;