@warlock.js/core 4.12.0 → 4.13.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 (181) hide show
  1. package/CHANGELOG.md +116 -0
  2. package/esm/cli/cli-commands.manager.mjs.map +1 -1
  3. package/esm/cli/commands/add.command.mjs +1 -1
  4. package/esm/cli/commands/add.command.mjs.map +1 -1
  5. package/esm/cli/commands/build.command.mjs +1 -1
  6. package/esm/cli/commands/build.command.mjs.map +1 -1
  7. package/esm/cli/commands/create-database.command.mjs +1 -1
  8. package/esm/cli/commands/create-database.command.mjs.map +1 -1
  9. package/esm/cli/commands/dev-server.command.mjs +1 -1
  10. package/esm/cli/commands/dev-server.command.mjs.map +1 -1
  11. package/esm/cli/commands/doctor/doctor-command.action.mjs.map +1 -1
  12. package/esm/cli/commands/doctor.command.mjs +1 -1
  13. package/esm/cli/commands/doctor.command.mjs.map +1 -1
  14. package/esm/cli/commands/drop-tables.command.mjs +1 -1
  15. package/esm/cli/commands/drop-tables.command.mjs.map +1 -1
  16. package/esm/cli/commands/generate/generate.command.mjs +1 -1
  17. package/esm/cli/commands/generate/generate.command.mjs.map +1 -1
  18. package/esm/cli/commands/generate/generators/controller.generator.mjs.map +1 -1
  19. package/esm/cli/commands/generate/generators/migration.generator.mjs +1 -1
  20. package/esm/cli/commands/generate/generators/migration.generator.mjs.map +1 -1
  21. package/esm/cli/commands/generate/generators/model.generator.mjs +1 -1
  22. package/esm/cli/commands/generate/generators/model.generator.mjs.map +1 -1
  23. package/esm/cli/commands/generate/generators/module.generator.mjs +1 -1
  24. package/esm/cli/commands/generate/generators/module.generator.mjs.map +1 -1
  25. package/esm/cli/commands/generate/generators/repository.generator.mjs.map +1 -1
  26. package/esm/cli/commands/generate/generators/resource.generator.mjs.map +1 -1
  27. package/esm/cli/commands/generate/generators/service.generator.mjs.map +1 -1
  28. package/esm/cli/commands/generate/utils/path-resolver.mjs +1 -1
  29. package/esm/cli/commands/generate/utils/writer.mjs +1 -1
  30. package/esm/cli/commands/migrate.command.mjs +1 -1
  31. package/esm/cli/commands/migrate.command.mjs.map +1 -1
  32. package/esm/cli/commands/routes/routes-command.action.mjs.map +1 -1
  33. package/esm/cli/commands/routes.command.mjs +1 -1
  34. package/esm/cli/commands/routes.command.mjs.map +1 -1
  35. package/esm/cli/commands/seed.command.mjs +1 -1
  36. package/esm/cli/commands/seed.command.mjs.map +1 -1
  37. package/esm/cli/commands/start-production.command.mjs +1 -1
  38. package/esm/cli/commands/start-production.command.mjs.map +1 -1
  39. package/esm/cli/commands/storage-put.action.mjs.map +1 -1
  40. package/esm/cli/commands/storage-put.command.mjs +1 -1
  41. package/esm/cli/commands/storage-put.command.mjs.map +1 -1
  42. package/esm/cli/commands/typings-generator.command.mjs +2 -2
  43. package/esm/cli/commands/typings-generator.command.mjs.map +1 -1
  44. package/esm/cli/commands/update.command.mjs +1 -1
  45. package/esm/cli/commands/update.command.mjs.map +1 -1
  46. package/esm/cli/commands-loader.mjs +1 -1
  47. package/esm/cli/commands-loader.mjs.map +1 -1
  48. package/esm/{cli → commands}/cli-command.d.mts +1 -1
  49. package/esm/commands/cli-command.d.mts.map +1 -0
  50. package/esm/{cli → commands}/cli-command.mjs +1 -1
  51. package/esm/commands/cli-command.mjs.map +1 -0
  52. package/esm/{cli → commands}/types.d.mts +1 -1
  53. package/esm/commands/types.d.mts.map +1 -0
  54. package/esm/config/load-config-files.mjs +1 -1
  55. package/esm/config/load-config-files.mjs.map +1 -1
  56. package/esm/connectors/base-connector.mjs +1 -1
  57. package/esm/connectors/base-connector.mjs.map +1 -1
  58. package/esm/connectors/connectors-manager.d.mts.map +1 -1
  59. package/esm/connectors/connectors-manager.mjs +5 -3
  60. package/esm/connectors/connectors-manager.mjs.map +1 -1
  61. package/esm/connectors/http-connector.d.mts.map +1 -1
  62. package/esm/connectors/http-connector.mjs +0 -2
  63. package/esm/connectors/http-connector.mjs.map +1 -1
  64. package/esm/database/create-database-action.mjs.map +1 -1
  65. package/esm/database/drop-tables-action.mjs.map +1 -1
  66. package/esm/database/migrate-action.mjs +1 -1
  67. package/esm/database/migrate-action.mjs.map +1 -1
  68. package/esm/database/seed-command-action.mjs +1 -1
  69. package/esm/database/seed-command-action.mjs.map +1 -1
  70. package/esm/dev-server/dev-logger.mjs +1 -1
  71. package/esm/dev-server/dev-logger.mjs.map +1 -1
  72. package/esm/dev-server/development-server.mjs +2 -2
  73. package/esm/dev-server/file-event-handler.mjs +1 -1
  74. package/esm/dev-server/file-event-handler.mjs.map +1 -1
  75. package/esm/dev-server/file-manager.mjs +1 -1
  76. package/esm/dev-server/file-manager.mjs.map +1 -1
  77. package/esm/dev-server/file-operations.mjs +1 -1
  78. package/esm/dev-server/file-operations.mjs.map +1 -1
  79. package/esm/dev-server/files-orchestrator.mjs +3 -18
  80. package/esm/dev-server/files-orchestrator.mjs.map +1 -1
  81. package/esm/dev-server/files-watcher.mjs +1 -1
  82. package/esm/dev-server/files-watcher.mjs.map +1 -1
  83. package/esm/dev-server/health-checker/file-health-checker.contract.d.mts +1 -1
  84. package/esm/dev-server/layer-executor.mjs +1 -1
  85. package/esm/dev-server/loader/register-loader.mjs +1 -1
  86. package/esm/dev-server/package-json-manager.mjs +1 -1
  87. package/esm/dev-server/package-json-manager.mjs.map +1 -1
  88. package/esm/dev-server/parse-imports.mjs +2 -2
  89. package/esm/dev-server/parse-imports.mjs.map +1 -1
  90. package/esm/dev-server/tsconfig-manager.mjs +1 -1
  91. package/esm/dev-server/tsconfig-manager.mjs.map +1 -1
  92. package/esm/dev-server/type-generator.mjs +1 -1
  93. package/esm/dev-server/type-generator.mjs.map +1 -1
  94. package/esm/dev-server/utils.mjs +1 -1
  95. package/esm/dev-server/utils.mjs.map +1 -1
  96. package/esm/generations/add-command.action.mjs +3 -3
  97. package/esm/generations/add-command.action.mjs.map +1 -1
  98. package/esm/http/build-cors-options.mjs +32 -0
  99. package/esm/http/build-cors-options.mjs.map +1 -0
  100. package/esm/http/middleware/max-body-size.middleware.d.mts +13 -5
  101. package/esm/http/middleware/max-body-size.middleware.d.mts.map +1 -1
  102. package/esm/http/middleware/max-body-size.middleware.mjs +13 -5
  103. package/esm/http/middleware/max-body-size.middleware.mjs.map +1 -1
  104. package/esm/http/plugins.d.mts.map +1 -1
  105. package/esm/http/plugins.mjs +2 -9
  106. package/esm/http/plugins.mjs.map +1 -1
  107. package/esm/http/server.d.mts.map +1 -1
  108. package/esm/http/server.mjs +3 -8
  109. package/esm/http/server.mjs.map +1 -1
  110. package/esm/index.d.mts +9 -17
  111. package/esm/index.mjs +4 -15
  112. package/esm/production/production-builder.mjs +51 -3
  113. package/esm/production/production-builder.mjs.map +1 -1
  114. package/esm/production/resolve-build-config.mjs +1 -1
  115. package/esm/production/resolve-build-config.mjs.map +1 -1
  116. package/esm/router/router.d.mts +9 -0
  117. package/esm/router/router.d.mts.map +1 -1
  118. package/esm/router/router.mjs +58 -5
  119. package/esm/router/router.mjs.map +1 -1
  120. package/esm/router/types.d.mts +24 -1
  121. package/esm/router/types.d.mts.map +1 -1
  122. package/esm/tests/index.d.mts +2 -2
  123. package/esm/tests/index.mjs +1 -2
  124. package/esm/tests/start-http-development-server.d.mts.map +1 -1
  125. package/esm/tests/start-http-development-server.mjs +38 -15
  126. package/esm/tests/start-http-development-server.mjs.map +1 -1
  127. package/esm/tests/test-helpers.d.mts.map +1 -1
  128. package/esm/tests/test-helpers.mjs +34 -13
  129. package/esm/tests/test-helpers.mjs.map +1 -1
  130. package/esm/tests/vitest-setup.d.mts +1 -1
  131. package/esm/tests/vitest-setup.d.mts.map +1 -1
  132. package/esm/tests/vitest-setup.mjs +3 -3
  133. package/esm/tests/vitest-setup.mjs.map +1 -1
  134. package/esm/utils/framework-vesion.mjs +1 -1
  135. package/esm/utils/index.d.mts +1 -0
  136. package/esm/utils/index.mjs +1 -0
  137. package/esm/utils/normalized-path.d.mts +55 -0
  138. package/esm/utils/normalized-path.d.mts.map +1 -0
  139. package/esm/{dev-server/path.mjs → utils/normalized-path.mjs} +15 -2
  140. package/esm/utils/normalized-path.mjs.map +1 -0
  141. package/esm/vite/index.d.mts +2 -0
  142. package/esm/vite/index.mjs +1 -1
  143. package/esm/vite/lower-stage3-decorators.d.mts +1 -1
  144. package/esm/vite/lower-stage3-decorators.mjs +1 -1
  145. package/esm/vite/lower-stage3-decorators.mjs.map +1 -1
  146. package/esm/warlock-config/index.mjs +1 -1
  147. package/esm/warlock-config/types.d.mts +34 -1
  148. package/esm/warlock-config/types.d.mts.map +1 -1
  149. package/esm/warlock-config/warlock-config.manager.d.mts.map +1 -1
  150. package/esm/warlock-config/warlock-config.manager.mjs +1 -2
  151. package/esm/warlock-config/warlock-config.manager.mjs.map +1 -1
  152. package/llms-full.txt +78 -23
  153. package/llms.txt +3 -3
  154. package/package.json +24 -12
  155. package/skills/lower-stage3-decorators/SKILL.md +4 -2
  156. package/skills/run-app/SKILL.md +6 -0
  157. package/skills/test-http/SKILL.md +29 -10
  158. package/skills/test-service/SKILL.md +12 -8
  159. package/skills/use-middleware/SKILL.md +5 -1
  160. package/esm/cli/cli-command.d.mts.map +0 -1
  161. package/esm/cli/cli-command.mjs.map +0 -1
  162. package/esm/cli/types.d.mts.map +0 -1
  163. package/esm/dev-server/files-orchestrator.d.mts +0 -89
  164. package/esm/dev-server/files-orchestrator.d.mts.map +0 -1
  165. package/esm/dev-server/files-watcher.d.mts +0 -69
  166. package/esm/dev-server/files-watcher.d.mts.map +0 -1
  167. package/esm/dev-server/health-checker/checkers/base-health-checker.d.mts +0 -55
  168. package/esm/dev-server/health-checker/checkers/base-health-checker.d.mts.map +0 -1
  169. package/esm/dev-server/health-checker/checkers/eslint-health-checker.d.mts +0 -44
  170. package/esm/dev-server/health-checker/checkers/eslint-health-checker.d.mts.map +0 -1
  171. package/esm/dev-server/health-checker/checkers/typescript-health-checker.d.mts +0 -60
  172. package/esm/dev-server/health-checker/checkers/typescript-health-checker.d.mts.map +0 -1
  173. package/esm/dev-server/health-checker/files-healthcare.manager.d.mts +0 -119
  174. package/esm/dev-server/health-checker/files-healthcare.manager.d.mts.map +0 -1
  175. package/esm/dev-server/health-checker/index.mjs +0 -7
  176. package/esm/dev-server/module-loader.d.mts +0 -63
  177. package/esm/dev-server/module-loader.d.mts.map +0 -1
  178. package/esm/dev-server/path.mjs.map +0 -1
  179. package/esm/tests/test-server-port-channel.d.mts +0 -27
  180. package/esm/tests/test-server-port-channel.d.mts.map +0 -1
  181. /package/esm/{cli → commands}/index.d.mts +0 -0
package/esm/index.mjs CHANGED
@@ -9,6 +9,7 @@ import { DatabaseLog } from "./utils/database-log.mjs";
9
9
  import { environment, setEnvironment } from "./utils/environment.mjs";
10
10
  import { requestContext, useCurrentUser, useRequest, useRequestStore } from "./http/context/request-context.mjs";
11
11
  import { getLocalized } from "./utils/get-localized.mjs";
12
+ import { Path } from "./utils/normalized-path.mjs";
12
13
  import { fetchLatestVersion } from "./utils/npm-registry.mjs";
13
14
  import { appPath, cachePath, configPath, logsPath, paths, publicPath, rootPath, sanitizePath, srcPath, storagePath, tempPath, uploadsPath, warlockPath } from "./utils/paths.mjs";
14
15
  import { promiseAllObject } from "./utils/promise-all-object.mjs";
@@ -82,7 +83,7 @@ import { bootstrap } from "./bootstrap.mjs";
82
83
  import { displayEnvironmentMode } from "./bootstrap/setup.mjs";
83
84
  import { DatabaseCacheDriver } from "./cache/database-cache-driver.mjs";
84
85
  import "./cache/index.mjs";
85
- import { CLICommand, command } from "./cli/cli-command.mjs";
86
+ import { CLICommand, command } from "./commands/cli-command.mjs";
86
87
  import { ConnectorLifecyclePhase, ConnectorPriority } from "./connectors/types.mjs";
87
88
  import { BaseConnector } from "./connectors/base-connector.mjs";
88
89
  import { AccessConnector } from "./connectors/access-connector.mjs";
@@ -113,12 +114,6 @@ import { seeder } from "./database/seeds/seeder.mjs";
113
114
  import { SeederDependencyCycleError, UnknownSeederDependencyError } from "./database/seeds/seeder.errors.mjs";
114
115
  import { useComputedModel, useComputedSlug, useHashedPassword } from "./database/utils.mjs";
115
116
  import "./database/index.mjs";
116
- import { WarlockConfigManager, isUnknownTsExtensionError, warlockConfigManager } from "./warlock-config/warlock-config.manager.mjs";
117
- import { BaseHealthChecker } from "./dev-server/health-checker/checkers/base-health-checker.mjs";
118
- import { EslintHealthChecker } from "./dev-server/health-checker/checkers/eslint-health-checker.mjs";
119
- import { TypescriptHealthChecker } from "./dev-server/health-checker/checkers/typescript-health-checker.mjs";
120
- import { FilesOrchestrator, filesOrchestrator, onCleanup } from "./dev-server/files-orchestrator.mjs";
121
- import "./dev-server/health-checker/index.mjs";
122
117
  import { decrypt, encrypt } from "./encryption/encrypt.mjs";
123
118
  import { hmacHash } from "./encryption/hash.mjs";
124
119
  import { hashPassword, verifyPassword } from "./encryption/password.mjs";
@@ -136,11 +131,6 @@ import { Restful } from "./restful/restful.mjs";
136
131
  import "./restful/index.mjs";
137
132
  import { getSocketServer } from "./socket/utils.mjs";
138
133
  import "./socket/index.mjs";
139
- import { TEST_SERVER_PORT_ENV_KEY, publishTestServerPort, withdrawTestServerPort } from "./tests/test-server-port-channel.mjs";
140
- import { isTestServerRunning, startHttpTestServer, stopHttpTestServer } from "./tests/start-http-development-server.mjs";
141
- import { expectJson, getTestServerUrl, parseJsonResponse, testDelete, testGet, testPatch, testPost, testPut, testRequest } from "./tests/test-helpers.mjs";
142
- import { setupTest } from "./tests/vitest-setup.mjs";
143
- import "./tests/index.mjs";
144
134
  import { broadcastUseCaseResult } from "./use-cases/use-case-broadcast.mjs";
145
135
  import { fireLifecycleEvent, globalEventsCallbacksMap, globalUseCasesEvents } from "./use-cases/use-case-events.mjs";
146
136
  import { BadSchemaUseCaseError } from "./use-cases/use-case.errors.mjs";
@@ -149,13 +139,12 @@ import { $registerUseCase, $unregisterUseCase, addUseCaseHistory, getUseCase, ge
149
139
  import { useCase } from "./use-cases/use-case.mjs";
150
140
  import "./use-cases/index.mjs";
151
141
  import "./validation/index.mjs";
152
- import { lowerStage3Decorators } from "./vite/lower-stage3-decorators.mjs";
153
- import "./vite/index.mjs";
154
142
  import { defineConfig } from "./warlock-config/define-config.mjs";
143
+ import { WarlockConfigManager, isUnknownTsExtensionError, warlockConfigManager } from "./warlock-config/warlock-config.manager.mjs";
155
144
  import "./warlock-config/index.mjs";
156
145
  import { env } from "@mongez/dotenv";
157
146
  import { colors } from "@mongez/copper";
158
147
 
159
148
  export * from "@mongez/localization"
160
149
 
161
- export { $registerUseCase, $unregisterUseCase, AccessConnector, AiConnector, Application, BOOT_SIGNAL_ENV_KEY, BOOT_SIGNAL_VERSION, BadRequestError, BadSchemaUseCaseError, BaseConnector, BaseHealthChecker, BenchmarkProfiler, BenchmarkSnapshots, CLICommand, CacheConnector, CascadeAdapter, CascadeQueryBuilder, CloudDriver, ConfigSpecialHandlers, ConflictError, ConnectorLifecyclePhase, ConnectorPriority, ConnectorsManager, ConsoleChannel, DOSpacesDriver, DatabaseCacheDriver, DatabaseConnector, DatabaseLog, DatabaseLogModel, EslintHealthChecker, FileValidator, FilesOrchestrator, ForbiddenError, HeraldConnector, HttpConnector, HttpError, HttpErrorCodes, Image, LocalDriver, LoggerConnector, MAIL_EVENTS, Mail, MailError, MailerConnector, MimeTypes, NoopChannel, NotAcceptableError, NotAllowedError, NotificationsConnector, PortInUseError, Queue, R2Driver, RegisterResource, RepositoryManager, Request, RequestController, RequestLog, Resource, ResourceFieldBuilder, ResourceNotFoundError, Response, ResponseStatus, Restful, Router, S3Driver, ScopedStorage, SeederDependencyCycleError, ServerError, SocketConnector, Storage, StorageConnector, StorageError, StorageFile, TEST_SERVER_PORT_ENV_KEY, TypescriptHealthChecker, UPLOADS_DEFAULTS, UnAuthorizedError, UnknownSeederDependencyError, UploadedFile, WarlockConfigManager, addUseCaseHistory, anyMatch, app, appLog, appPath, assertMailCount, assertMailSent, assertPortIsAvailable, assetsUrl, bootstrap, broadcastUseCaseResult, buildIdempotencyCacheKey, cachePath, captureMail, clearTestMailbox, closeAllMailers, closeMailer, closeServerWithTimeout, colors, command, config, configPath, configSpecialHandlers, connectorsManager, container, createHttpApplication, createRequestStore, decrypt, defaultHttpConfigurations, defineConfig, defineResource, displayEnvironmentMode, encrypt, env, environment, existsExceptCurrentIdRule, existsExceptCurrentUserRule, expectJson, fetchLatestVersion, fileExtensionRule, fileRule, fileTypeRule, filesOrchestrator, findMailsBySubject, findMailsTo, fireLifecycleEvent, fromRequest, generateMailId, getDefaultMailConfig, getHttpServer, getLastMail, getLocalized, getMailEventName, getMailMode, getMailboxSize, getMailer, getMailerConfig, getMimeType, getPoolStats, getSocketServer, getTestMailbox, getTestServerUrl, getUseCase, getUseCaseHistory, getUseCases, globalEventsCallbacksMap, globalUseCasesEvents, hashBody, hashPassword, health, hmacHash, httpConfig, imageRule, increaseUseCaseFailedCalls, increaseUseCaseSuccessCalls, ipMatches, isBootSignal, isDevelopmentMode, isNewerVersion, isPortAvailable, isPrivateOrReservedIp, isProductionMode, isTestMode, isTestServerRunning, isUnknownTsExtensionError, isValidIdempotencyKey, loadS3, logResponse, logsPath, lowerStage3Decorators, mailEvents, measure, middleware, onCleanup, parseJsonResponse, parseSize, paths, promiseAllObject, publicPath, publicUrl, publishTestServerPort, registerAppConfig, registerHttpPlugins, renderReact, renderReactMail, requestContext, resetMailConfig, resolveMailConfig, resolveWithinRoot, rootPath, router, runPipeline, safeFetchToBuffer, sanitizePath, seeder, sendBootSignal, sendMail, setBaseUrl, setConfig, setEnvironment, setLogConfigurations, setMailConfigurations, setMailMode, setupTest, sleep, sluggable, srcPath, startHttpServer, startHttpTestServer, stopHttpApplication, stopHttpTestServer, storage, storageConfig, storageConfigurations, storageDriverContext, storagePath, t, tempPath, testDelete, testGet, testPatch, testPost, testPut, testRequest, toJson, uniqueExceptCurrentIdRule, uniqueExceptCurrentUserRule, uploadedFileMetadataSchema, uploadsConfig, uploadsPath, uploadsUrl, url, useCase, useComputedModel, useComputedSlug, useCurrentUser, useHashedPassword, useRequest, useRequestStore, verifyMailer, verifyPassword, warlockConfigManager, warlockPath, wasMailSentTo, wasMailSentWithSubject, withdrawTestServerPort, wrapResponseInDataKey };
150
+ export { $registerUseCase, $unregisterUseCase, 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, DOSpacesDriver, DatabaseCacheDriver, DatabaseConnector, DatabaseLog, DatabaseLogModel, FileValidator, ForbiddenError, HeraldConnector, HttpConnector, HttpError, HttpErrorCodes, Image, LocalDriver, LoggerConnector, MAIL_EVENTS, Mail, MailError, MailerConnector, MimeTypes, NoopChannel, NotAcceptableError, NotAllowedError, NotificationsConnector, Path, PortInUseError, Queue, R2Driver, RegisterResource, RepositoryManager, Request, RequestController, RequestLog, Resource, ResourceFieldBuilder, ResourceNotFoundError, Response, ResponseStatus, Restful, Router, S3Driver, ScopedStorage, SeederDependencyCycleError, ServerError, SocketConnector, Storage, StorageConnector, StorageError, StorageFile, UPLOADS_DEFAULTS, UnAuthorizedError, UnknownSeederDependencyError, UploadedFile, WarlockConfigManager, addUseCaseHistory, anyMatch, app, appLog, appPath, assertMailCount, assertMailSent, assertPortIsAvailable, assetsUrl, bootstrap, broadcastUseCaseResult, buildIdempotencyCacheKey, cachePath, captureMail, clearTestMailbox, closeAllMailers, closeMailer, closeServerWithTimeout, colors, command, config, configPath, configSpecialHandlers, connectorsManager, container, createHttpApplication, createRequestStore, decrypt, defaultHttpConfigurations, defineConfig, defineResource, displayEnvironmentMode, encrypt, env, environment, existsExceptCurrentIdRule, existsExceptCurrentUserRule, fetchLatestVersion, fileExtensionRule, fileRule, fileTypeRule, findMailsBySubject, findMailsTo, fireLifecycleEvent, fromRequest, generateMailId, getDefaultMailConfig, getHttpServer, getLastMail, getLocalized, getMailEventName, getMailMode, getMailboxSize, getMailer, getMailerConfig, getMimeType, getPoolStats, getSocketServer, getTestMailbox, getUseCase, getUseCaseHistory, getUseCases, globalEventsCallbacksMap, globalUseCasesEvents, hashBody, hashPassword, health, hmacHash, httpConfig, imageRule, increaseUseCaseFailedCalls, increaseUseCaseSuccessCalls, ipMatches, isBootSignal, isDevelopmentMode, isNewerVersion, isPortAvailable, isPrivateOrReservedIp, isProductionMode, isTestMode, isUnknownTsExtensionError, isValidIdempotencyKey, loadS3, logResponse, logsPath, mailEvents, measure, middleware, parseSize, paths, promiseAllObject, publicPath, publicUrl, registerAppConfig, registerHttpPlugins, renderReact, renderReactMail, requestContext, resetMailConfig, resolveMailConfig, resolveWithinRoot, rootPath, router, runPipeline, safeFetchToBuffer, sanitizePath, seeder, sendBootSignal, sendMail, setBaseUrl, setConfig, setEnvironment, setLogConfigurations, setMailConfigurations, setMailMode, sleep, sluggable, srcPath, startHttpServer, stopHttpApplication, storage, storageConfig, storageConfigurations, storageDriverContext, storagePath, t, tempPath, toJson, uniqueExceptCurrentIdRule, uniqueExceptCurrentUserRule, uploadedFileMetadataSchema, uploadsConfig, uploadsPath, uploadsUrl, url, useCase, useComputedModel, useComputedSlug, useCurrentUser, useHashedPassword, useRequest, useRequestStore, verifyMailer, verifyPassword, warlockConfigManager, warlockPath, wasMailSentTo, wasMailSentWithSubject, wrapResponseInDataKey };
@@ -13,6 +13,44 @@ import esbuild from "esbuild";
13
13
 
14
14
  //#region ../core/src/production/production-builder.ts
15
15
  /**
16
+ * Recreates `require`, `__filename` and `__dirname` at the top of an ESM bundle.
17
+ *
18
+ * Bundled CommonJS dependencies call `require("node:assert")` and read
19
+ * `__dirname` to locate their own assets. Neither exists in an ES module, so
20
+ * esbuild substitutes a stub that throws — **after the build has reported
21
+ * success**. `Dynamic require of "node:assert" is not supported` is that stub
22
+ * firing, and it is the defect this shim removes.
23
+ *
24
+ * `__dirname` matters as much as `require`: without it a dependency resolving
25
+ * an asset path silently gets `undefined` and fails later, further from the
26
+ * cause.
27
+ *
28
+ * Written as one line because esbuild inserts a banner verbatim above the
29
+ * bundle and a multi-line string here would shift every mapped line.
30
+ */
31
+ const ESM_INTEROP_SHIM = "import{createRequire as __wlCreateRequire}from'node:module';import{fileURLToPath as __wlFileURLToPath}from'node:url';import{dirname as __wlDirname}from'node:path';const require=__wlCreateRequire(import.meta.url);const __filename=__wlFileURLToPath(import.meta.url);const __dirname=__wlDirname(__filename);";
32
+ /**
33
+ * Combine the framework's banner with the user's, keeping both.
34
+ *
35
+ * esbuild's `banner` is an object keyed by output type, and every merge in the
36
+ * path to it — the config merge and the options spread — REPLACES that object
37
+ * rather than merging it. So whichever is applied second silently deletes the
38
+ * other. This is the only place the two can coexist.
39
+ *
40
+ * The shim goes first: it defines `require`, which a user banner may use.
41
+ *
42
+ * @param userBanner banner from `warlock.config.ts`, if any
43
+ * @param shim the interop shim, or undefined when it does not apply
44
+ */
45
+ function mergeBanner(userBanner, shim) {
46
+ if (!shim) return userBanner;
47
+ const userJs = userBanner?.js;
48
+ return {
49
+ ...userBanner,
50
+ js: userJs ? `${shim}${userJs}` : shim
51
+ };
52
+ }
53
+ /**
16
54
  * Production Builder
17
55
  * Generates production-ready files and bundles them for deployment
18
56
  * Build options are loaded from warlock.config.ts
@@ -244,17 +282,26 @@ bootstrap();
244
282
  const outDir = this.options.outdir;
245
283
  const outFileName = this.options.outFile;
246
284
  const entryName = path.basename(outFileName, path.extname(outFileName));
285
+ const singleBundle = this.options.singleBundle === true;
286
+ const esmShim = this.options.esmShim !== false;
287
+ const userBanner = this.options.banner;
247
288
  delete this.options.outFile;
248
289
  delete this.options.entryPath;
290
+ delete this.options.singleBundle;
291
+ delete this.options.esmShim;
292
+ delete this.options.banner;
249
293
  await ensureDirectoryAsync(outDir);
250
294
  const alias = this.buildAliasMapFromTsconfig();
295
+ const isEsm = (this.options.format ?? "esm") === "esm";
296
+ const banner = mergeBanner(userBanner, esmShim && isEsm ? ESM_INTEROP_SHIM : void 0);
297
+ if (singleBundle) console.log(colors.magenta(" Single-bundle mode — one JS file" + (esmShim && isEsm ? " with the ESM interop shim" : "") + ". Native .node addons are still emitted alongside it."));
251
298
  try {
252
299
  await esbuild.build({
253
300
  platform: "node",
254
301
  entryPoints: [entryPoint],
255
302
  bundle: true,
256
- splitting: true,
257
- packages: "external",
303
+ splitting: !singleBundle,
304
+ packages: singleBundle ? "bundle" : "external",
258
305
  minify: this.options.minify,
259
306
  sourcemap: this.options.sourcemap === true ? "linked" : this.options.sourcemap,
260
307
  format: "esm",
@@ -263,7 +310,8 @@ bootstrap();
263
310
  entryNames: entryName,
264
311
  alias,
265
312
  plugins: [nativeNodeModulesPlugin],
266
- ...this.options
313
+ ...this.options,
314
+ ...banner ? { banner } : {}
267
315
  });
268
316
  } catch (e) {
269
317
  console.log(e);
@@ -1 +1 @@
1
- {"version":3,"file":"production-builder.mjs","names":[],"sources":["../../../../../../../core/src/production/production-builder.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport {\r\n ensureDirectoryAsync,\r\n fileExistsAsync,\r\n getFileAsync,\r\n getJsonFileAsync,\r\n putFileAsync,\r\n removeDirectoryAsync,\r\n} from \"@warlock.js/fs\";\r\nimport esbuild from \"esbuild\";\r\nimport glob from \"fast-glob\";\r\nimport path from \"path\";\r\nimport { tsconfigManager } from \"../dev-server/tsconfig-manager\";\r\nimport { appPath, rootPath, warlockPath } from \"../utils\";\r\nimport { assertGeneratedImportsAreDeclared } from \"./assert-generated-imports\";\r\nimport { nativeNodeModulesPlugin } from \"./esbuild-plugins\";\r\nimport { resolveBuildConfig, type ResolvedBuildConfig } from \"./resolve-build-config\";\r\nimport { toCamelCase, toKebabCase } from \"@mongez/reinforcements\";\r\n\r\n/**\r\n * Production Builder\r\n * Generates production-ready files and bundles them for deployment\r\n * Build options are loaded from warlock.config.ts\r\n */\r\nexport class ProductionBuilder {\r\n private options!: ResolvedBuildConfig;\r\n private readonly productionDir = warlockPath(\"production\");\r\n\r\n /**\r\n * Main build entry point\r\n */\r\n public async build(): Promise<void> {\r\n console.log(colors.cyan(\"Building for production...\\n\"));\r\n\r\n // Step 1: Initialize options from config\r\n await this.initializeOptions();\r\n\r\n // Step 2: Generate combined files\r\n await this.generateCombinedFiles();\r\n\r\n // Step 3: Generate entry point\r\n await this.generateEntryPoint();\r\n\r\n // Step 4: Refuse to bundle code the app could not resolve at runtime\r\n await this.assertGeneratedImports();\r\n\r\n // Step 5: Bundle with esbuild\r\n await this.bundle();\r\n\r\n // Step 6: Remove production folder\r\n await removeDirectoryAsync(this.productionDir);\r\n\r\n console.log(colors.green(\"Build complete!\"));\r\n console.log(`Start production server by running ${colors.cyan(\"warlock start\")}`);\r\n }\r\n\r\n /**\r\n * Initialize options from warlock.config.ts\r\n */\r\n private async initializeOptions(): Promise<void> {\r\n this.options = resolveBuildConfig();\r\n\r\n // Ensure production directory exists\r\n await ensureDirectoryAsync(this.productionDir);\r\n }\r\n\r\n /**\r\n * Track which special files were generated\r\n */\r\n private generatedFiles = {\r\n locales: false,\r\n events: false,\r\n main: false,\r\n routes: false,\r\n };\r\n\r\n /**\r\n * Generate all combined files\r\n */\r\n private async generateCombinedFiles(): Promise<void> {\r\n console.log(colors.yellow(\" Generating production files...\"));\r\n\r\n // Generate bootstrap.ts file\r\n await this.generateBootstrap();\r\n\r\n // Generate config loader\r\n await this.generateConfigLoader();\r\n\r\n // Generate special files and track which ones have content\r\n const [locales, events, main, routes] = await Promise.all([\r\n this.generateLocales(),\r\n this.generateEvents(),\r\n this.generateMain(),\r\n this.generateRoutes(),\r\n ]);\r\n\r\n this.generatedFiles = { locales, events, main, routes };\r\n }\r\n\r\n /**\r\n * Generate bootstrap.ts - ensures bootstrap() runs first and sets production environment\r\n */\r\n private async generateBootstrap(): Promise<void> {\r\n let content = `import { bootstrap, Application } from \"@warlock.js/core\";\r\n\r\n// Set production environment\r\nApplication.setRuntimeStrategy(\"production\");\r\nApplication.setEnvironment(\"production\");\r\n\r\n// Bootstrap the application\r\nbootstrap();\r\n`;\r\n if (await fileExistsAsync(appPath(\"bootstrap.ts\"))) {\r\n content += \"import './../../src/app/bootstrap';\\n\";\r\n }\r\n\r\n await putFileAsync(path.join(this.productionDir, \"bootstrap.ts\"), content);\r\n }\r\n\r\n /**\r\n * Fail the build when generated code imports a package the app does not\r\n * declare.\r\n *\r\n * The bundler is configured `packages: \"external\"`, so every bare specifier\r\n * written above survives verbatim into an artifact that resolves against the\r\n * **consuming app's** node_modules. A framework-internal dependency imported\r\n * there resolves by accident under npm/yarn hoisting and dies under pnpm —\r\n * which is how a production app shipped that could not boot. Rewriting the\r\n * offending import fixes today's bundle; this check is what stops the next\r\n * change to the generator from quietly reintroducing it.\r\n */\r\n private async assertGeneratedImports(): Promise<void> {\r\n const generatedPaths = await glob(\"**/*.{ts,tsx,js,mjs}\", {\r\n cwd: this.productionDir,\r\n absolute: false,\r\n });\r\n\r\n const [files, appPackage] = await Promise.all([\r\n Promise.all(\r\n generatedPaths.map(async (file) => ({\r\n file,\r\n content: await getFileAsync(path.join(this.productionDir, file)),\r\n })),\r\n ),\r\n getJsonFileAsync<{ dependencies?: Record<string, string> }>(rootPath(\"package.json\")),\r\n ]);\r\n\r\n assertGeneratedImportsAreDeclared(files, Object.keys(appPackage.dependencies ?? {}));\r\n }\r\n\r\n /**\r\n * Glob for module files matching a pattern\r\n * Returns relative paths from .warlock/production/ to src/app/\r\n */\r\n private async globModule(fileName: string): Promise<string[]> {\r\n const pattern = `**/${fileName}.{ts,tsx}`;\r\n const appDirectory = appPath();\r\n\r\n const files = await glob(pattern, {\r\n cwd: appDirectory,\r\n absolute: false,\r\n });\r\n\r\n // Convert to relative paths from .warlock/production/ to src/app/\r\n // e.g., \"users/main\" -> \"../../src/app/users/main\"\r\n return files.map((file) => \"../../src/app/\" + file.replace(/\\.(ts|tsx)$/, \"\"));\r\n }\r\n\r\n /**\r\n * Glob for files in a specific directory pattern\r\n * Returns relative paths from .warlock/production/ to src/app/\r\n */\r\n private async globModuleDirectory(directory: string): Promise<string[]> {\r\n const pattern = `**/${directory}/*.{ts,tsx}`;\r\n const appDirectory = appPath();\r\n\r\n const files = await glob(pattern, {\r\n cwd: appDirectory,\r\n absolute: false,\r\n });\r\n\r\n return files.map((file) => \"../../src/app/\" + file.replace(/\\.(ts|tsx)$/, \"\"));\r\n }\r\n\r\n /**\r\n * Generate config-loader.ts\r\n */\r\n private async generateConfigLoader(): Promise<void> {\r\n const configDirectory = path.join(process.cwd(), \"src/config\");\r\n\r\n const files = await glob(\"*.{ts,tsx}\", {\r\n cwd: configDirectory,\r\n absolute: false,\r\n });\r\n\r\n const configNames = files.map((f) => f.replace(/\\.(ts|tsx)$/, \"\"));\r\n\r\n // Only `@warlock.js/core` — the one package the app itself declares. See\r\n // `assertGeneratedImportsAreDeclared`: `@mongez/config` is core's own\r\n // dependency, so a bare import of it resolved by luck under npm/yarn\r\n // hoisting and died under pnpm's strict layout.\r\n const imports: string[] = [\r\n 'import { setConfig, configSpecialHandlers } from \"@warlock.js/core\";',\r\n ];\r\n const configImports: string[] = [];\r\n const configSetCalls: string[] = [];\r\n const executors: string[] = [];\r\n\r\n for (const configName of configNames) {\r\n const properConfigName = toCamelCase(configName);\r\n const varName = `${properConfigName}Config`;\r\n configImports.push(`import ${varName} from \"../../src/config/${configName}\";`);\r\n configSetCalls.push(`setConfig(\"${properConfigName}\", ${varName});`);\r\n executors.push(`await configSpecialHandlers.execute(\"${properConfigName}\", ${varName});`);\r\n }\r\n\r\n let content = [\r\n ...imports,\r\n \"\",\r\n \"// Config imports\",\r\n ...configImports,\r\n \"\",\r\n \"// Register configs\",\r\n ...configSetCalls,\r\n \"\",\r\n \"// Special handlers\",\r\n ...executors,\r\n \"\",\r\n ].join(\"\\n\");\r\n\r\n if (await fileExistsAsync(appPath(\"prestart.ts\"))) {\r\n content += \"import './../../src/app/prestart';\\n\";\r\n }\r\n\r\n await putFileAsync(path.join(this.productionDir, \"config-loader.ts\"), content);\r\n }\r\n\r\n /**\r\n * Generate locales.ts (only if there are locale files)\r\n * @returns true if file was generated with content\r\n */\r\n private async generateLocales(): Promise<boolean> {\r\n const files = await this.globModule(\"utils/locales\");\r\n if (files.length === 0) return false;\r\n await this.generateImportsFile(files, \"locales.ts\");\r\n return true;\r\n }\r\n\r\n /**\r\n * Generate events.ts (only if there are event files)\r\n * @returns true if file was generated with content\r\n */\r\n private async generateEvents(): Promise<boolean> {\r\n const files = await this.globModuleDirectory(\"events\");\r\n if (files.length === 0) return false;\r\n await this.generateImportsFile(files, \"events.ts\");\r\n return true;\r\n }\r\n\r\n /**\r\n * Generate main.ts (only if there are main files)\r\n * @returns true if file was generated with content\r\n */\r\n private async generateMain(): Promise<boolean> {\r\n const files = await this.globModule(\"main\");\r\n if (files.length === 0) return false;\r\n await this.generateImportsFile(files, \"main.ts\");\r\n return true;\r\n }\r\n\r\n /**\r\n * Generate routes.ts (only if there are route files)\r\n * @returns true if file was generated with content\r\n */\r\n private async generateRoutes(): Promise<boolean> {\r\n const files = await this.globModule(\"routes\");\r\n if (files.length === 0) return false;\r\n await this.generateImportsFile(files, \"routes.ts\");\r\n return true;\r\n }\r\n\r\n /**\r\n * Generate a file with imports from all given files\r\n */\r\n private async generateImportsFile(importPaths: string[], outputFile: string): Promise<void> {\r\n const imports = importPaths.map((importPath) => `import \"${importPath}\";`);\r\n const content = imports.join(\"\\n\") + \"\\n\";\r\n await putFileAsync(path.join(this.productionDir, outputFile), content);\r\n }\r\n\r\n /**\r\n * Generate the main entry point (app.ts)\r\n */\r\n private async generateEntryPoint(): Promise<void> {\r\n console.log(colors.yellow(\" Generating entry point...\"));\r\n\r\n // Build imports based on which files were generated\r\n const imports: string[] = [\r\n \"// 1. Bootstrap (loads .env, initializes framework)\",\r\n 'import \"./bootstrap\";',\r\n \"\",\r\n \"// 2. Load configs\",\r\n 'import \"./config-loader\";',\r\n \"\",\r\n \"// 3. Start early-phase connectors (database, cache, logger, ...)\",\r\n \"// so data sources, cache, etc. are ready before app code runs\",\r\n 'import { Application, connectorsManager, ConnectorLifecyclePhase } from \"@warlock.js/core\";',\r\n \"await connectorsManager.startPhase(ConnectorLifecyclePhase.Early);\",\r\n ];\r\n\r\n // App code uses dynamic `await import(...)` so each module's side\r\n // effects fire at THIS point in execution. Static imports would be\r\n // hoisted to module-instantiation time (before the early-phase await\r\n // resolves), which is exactly the bug this split is meant to fix.\r\n // Requires `splitting: true` in esbuild (set below in bundle()).\r\n imports.push(\"\", \"// 4. Load app code (events, locales, main, routes)\");\r\n\r\n if (this.generatedFiles.events) {\r\n imports.push('await import(\"./events\");');\r\n }\r\n if (this.generatedFiles.locales) {\r\n imports.push('await import(\"./locales\");');\r\n }\r\n if (this.generatedFiles.main) {\r\n imports.push('await import(\"./main\");');\r\n }\r\n if (this.generatedFiles.routes) {\r\n imports.push('await import(\"./routes\");');\r\n }\r\n\r\n // Start late-phase connectors after app code registers routes/listeners\r\n imports.push(\r\n \"\",\r\n \"// 5. Start late-phase connectors (http, socket) — routes and\",\r\n \"// listeners registered by app code are now ready to bind\",\r\n \"await connectorsManager.startPhase(ConnectorLifecyclePhase.Late);\",\r\n \"\",\r\n \"// 6. Signal a complete boot so `Application.onceBooted(...)` listeners fire\",\r\n \"Application.markBooted({ environment: Application.environment, runtimeStrategy: Application.runtimeStrategy });\",\r\n \"connectorsManager.shutdownOnProcessKill();\",\r\n );\r\n\r\n const content = imports.join(\"\\n\") + \"\\n\";\r\n await putFileAsync(path.join(this.productionDir, \"app.ts\"), content);\r\n }\r\n\r\n /**\r\n * Bundle with esbuild\r\n */\r\n private async bundle(): Promise<void> {\r\n console.log(colors.magenta(\" Bundling with esbuild...\"));\r\n\r\n const entryPoint = path.join(this.productionDir, \"app.ts\");\r\n const outDir = this.options.outdir!;\r\n const outFileName = this.options.outFile!;\r\n // Strip extension so entryNames produces \"<base>.js\" via esbuild\r\n const entryName = path.basename(outFileName, path.extname(outFileName));\r\n\r\n delete this.options.outFile;\r\n delete this.options.entryPath;\r\n\r\n await ensureDirectoryAsync(outDir);\r\n\r\n const alias = this.buildAliasMapFromTsconfig();\r\n\r\n\r\n try {\r\n \r\n await esbuild.build({\r\n platform: \"node\",\r\n entryPoints: [entryPoint],\r\n bundle: true,\r\n // Required so dynamic `await import(\"./main\")` in the generated\r\n // app.ts produces separate chunks loaded at the runtime call site,\r\n // instead of inlining the modules at instantiation time (which\r\n // would defeat the early/late phase split).\r\n splitting: true,\r\n packages: \"external\",\r\n minify: this.options!.minify,\r\n sourcemap: this.options!.sourcemap === true ? \"linked\" : this.options!.sourcemap,\r\n format: \"esm\",\r\n // Targeting a concrete Node version (not \"esnext\") so esbuild\r\n // transpiles TC39 stage 3 decorators into helpers — Node does not\r\n // implement them natively yet.\r\n target: [\"node22\"],\r\n outdir: outDir,\r\n entryNames: entryName,\r\n alias,\r\n plugins: [nativeNodeModulesPlugin],\r\n ...(this.options as any),\r\n });\r\n } catch (e) {\r\n console.log(e);\r\n throw e;\r\n }\r\n }\r\n\r\n /**\r\n * Build an alias map from tsconfig `paths` so esbuild resolves local\r\n * source aliases (e.g. `@warlock.js/cascade`) to their on-disk source\r\n * folders during bundling. Without this, `packages: \"external\"` would\r\n * leave those bare specifiers as raw imports and Node would fail to\r\n * resolve them at runtime (they aren't installed in node_modules).\r\n *\r\n * Only exact (non-wildcard) aliases are included — esbuild's `alias`\r\n * option doesn't support glob-style mappings.\r\n */\r\n private buildAliasMapFromTsconfig(): Record<string, string> {\r\n tsconfigManager.init();\r\n\r\n const alias: Record<string, string> = {};\r\n const baseUrl = path.resolve(process.cwd(), tsconfigManager.baseUrl);\r\n\r\n for (const [from, to] of Object.entries(tsconfigManager.aliases)) {\r\n if (from.endsWith(\"/*\") || !Array.isArray(to) || to.length === 0) {\r\n continue;\r\n }\r\n\r\n alias[from] = path.resolve(baseUrl, to[0]);\r\n }\r\n\r\n return alias;\r\n }\r\n}\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"production-builder.mjs","names":[],"sources":["../../../../../../../core/src/production/production-builder.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport {\r\n ensureDirectoryAsync,\r\n fileExistsAsync,\r\n getFileAsync,\r\n getJsonFileAsync,\r\n putFileAsync,\r\n removeDirectoryAsync,\r\n} from \"@warlock.js/fs\";\r\nimport esbuild from \"esbuild\";\r\nimport glob from \"fast-glob\";\r\nimport path from \"path\";\r\nimport { tsconfigManager } from \"../dev-server/tsconfig-manager\";\r\nimport { appPath, rootPath, warlockPath } from \"../utils\";\r\nimport { assertGeneratedImportsAreDeclared } from \"./assert-generated-imports\";\r\nimport { nativeNodeModulesPlugin } from \"./esbuild-plugins\";\r\nimport { resolveBuildConfig, type ResolvedBuildConfig } from \"./resolve-build-config\";\r\nimport { toCamelCase, toKebabCase } from \"@mongez/reinforcements\";\r\n\r\n/**\r\n * Recreates `require`, `__filename` and `__dirname` at the top of an ESM bundle.\r\n *\r\n * Bundled CommonJS dependencies call `require(\"node:assert\")` and read\r\n * `__dirname` to locate their own assets. Neither exists in an ES module, so\r\n * esbuild substitutes a stub that throws — **after the build has reported\r\n * success**. `Dynamic require of \"node:assert\" is not supported` is that stub\r\n * firing, and it is the defect this shim removes.\r\n *\r\n * `__dirname` matters as much as `require`: without it a dependency resolving\r\n * an asset path silently gets `undefined` and fails later, further from the\r\n * cause.\r\n *\r\n * Written as one line because esbuild inserts a banner verbatim above the\r\n * bundle and a multi-line string here would shift every mapped line.\r\n */\r\nconst ESM_INTEROP_SHIM =\r\n \"import{createRequire as __wlCreateRequire}from'node:module';\" +\r\n \"import{fileURLToPath as __wlFileURLToPath}from'node:url';\" +\r\n \"import{dirname as __wlDirname}from'node:path';\" +\r\n \"const require=__wlCreateRequire(import.meta.url);\" +\r\n \"const __filename=__wlFileURLToPath(import.meta.url);\" +\r\n \"const __dirname=__wlDirname(__filename);\";\r\n\r\n/**\r\n * Combine the framework's banner with the user's, keeping both.\r\n *\r\n * esbuild's `banner` is an object keyed by output type, and every merge in the\r\n * path to it — the config merge and the options spread — REPLACES that object\r\n * rather than merging it. So whichever is applied second silently deletes the\r\n * other. This is the only place the two can coexist.\r\n *\r\n * The shim goes first: it defines `require`, which a user banner may use.\r\n *\r\n * @param userBanner banner from `warlock.config.ts`, if any\r\n * @param shim the interop shim, or undefined when it does not apply\r\n */\r\nfunction mergeBanner(\r\n userBanner: Record<string, string> | undefined,\r\n shim: string | undefined,\r\n): Record<string, string> | undefined {\r\n if (!shim) return userBanner;\r\n\r\n const userJs = userBanner?.js;\r\n\r\n return {\r\n ...userBanner,\r\n js: userJs ? `${shim}${userJs}` : shim,\r\n };\r\n}\r\n\r\n/**\r\n * Production Builder\r\n * Generates production-ready files and bundles them for deployment\r\n * Build options are loaded from warlock.config.ts\r\n */\r\nexport class ProductionBuilder {\r\n private options!: ResolvedBuildConfig;\r\n private readonly productionDir = warlockPath(\"production\");\r\n\r\n /**\r\n * Main build entry point\r\n */\r\n public async build(): Promise<void> {\r\n console.log(colors.cyan(\"Building for production...\\n\"));\r\n\r\n // Step 1: Initialize options from config\r\n await this.initializeOptions();\r\n\r\n // Step 2: Generate combined files\r\n await this.generateCombinedFiles();\r\n\r\n // Step 3: Generate entry point\r\n await this.generateEntryPoint();\r\n\r\n // Step 4: Refuse to bundle code the app could not resolve at runtime\r\n await this.assertGeneratedImports();\r\n\r\n // Step 5: Bundle with esbuild\r\n await this.bundle();\r\n\r\n // Step 6: Remove production folder\r\n await removeDirectoryAsync(this.productionDir);\r\n\r\n console.log(colors.green(\"Build complete!\"));\r\n console.log(`Start production server by running ${colors.cyan(\"warlock start\")}`);\r\n }\r\n\r\n /**\r\n * Initialize options from warlock.config.ts\r\n */\r\n private async initializeOptions(): Promise<void> {\r\n this.options = resolveBuildConfig();\r\n\r\n // Ensure production directory exists\r\n await ensureDirectoryAsync(this.productionDir);\r\n }\r\n\r\n /**\r\n * Track which special files were generated\r\n */\r\n private generatedFiles = {\r\n locales: false,\r\n events: false,\r\n main: false,\r\n routes: false,\r\n };\r\n\r\n /**\r\n * Generate all combined files\r\n */\r\n private async generateCombinedFiles(): Promise<void> {\r\n console.log(colors.yellow(\" Generating production files...\"));\r\n\r\n // Generate bootstrap.ts file\r\n await this.generateBootstrap();\r\n\r\n // Generate config loader\r\n await this.generateConfigLoader();\r\n\r\n // Generate special files and track which ones have content\r\n const [locales, events, main, routes] = await Promise.all([\r\n this.generateLocales(),\r\n this.generateEvents(),\r\n this.generateMain(),\r\n this.generateRoutes(),\r\n ]);\r\n\r\n this.generatedFiles = { locales, events, main, routes };\r\n }\r\n\r\n /**\r\n * Generate bootstrap.ts - ensures bootstrap() runs first and sets production environment\r\n */\r\n private async generateBootstrap(): Promise<void> {\r\n let content = `import { bootstrap, Application } from \"@warlock.js/core\";\r\n\r\n// Set production environment\r\nApplication.setRuntimeStrategy(\"production\");\r\nApplication.setEnvironment(\"production\");\r\n\r\n// Bootstrap the application\r\nbootstrap();\r\n`;\r\n if (await fileExistsAsync(appPath(\"bootstrap.ts\"))) {\r\n content += \"import './../../src/app/bootstrap';\\n\";\r\n }\r\n\r\n await putFileAsync(path.join(this.productionDir, \"bootstrap.ts\"), content);\r\n }\r\n\r\n /**\r\n * Fail the build when generated code imports a package the app does not\r\n * declare.\r\n *\r\n * The bundler is configured `packages: \"external\"`, so every bare specifier\r\n * written above survives verbatim into an artifact that resolves against the\r\n * **consuming app's** node_modules. A framework-internal dependency imported\r\n * there resolves by accident under npm/yarn hoisting and dies under pnpm —\r\n * which is how a production app shipped that could not boot. Rewriting the\r\n * offending import fixes today's bundle; this check is what stops the next\r\n * change to the generator from quietly reintroducing it.\r\n */\r\n private async assertGeneratedImports(): Promise<void> {\r\n const generatedPaths = await glob(\"**/*.{ts,tsx,js,mjs}\", {\r\n cwd: this.productionDir,\r\n absolute: false,\r\n });\r\n\r\n const [files, appPackage] = await Promise.all([\r\n Promise.all(\r\n generatedPaths.map(async (file) => ({\r\n file,\r\n content: await getFileAsync(path.join(this.productionDir, file)),\r\n })),\r\n ),\r\n getJsonFileAsync<{ dependencies?: Record<string, string> }>(rootPath(\"package.json\")),\r\n ]);\r\n\r\n assertGeneratedImportsAreDeclared(files, Object.keys(appPackage.dependencies ?? {}));\r\n }\r\n\r\n /**\r\n * Glob for module files matching a pattern\r\n * Returns relative paths from .warlock/production/ to src/app/\r\n */\r\n private async globModule(fileName: string): Promise<string[]> {\r\n const pattern = `**/${fileName}.{ts,tsx}`;\r\n const appDirectory = appPath();\r\n\r\n const files = await glob(pattern, {\r\n cwd: appDirectory,\r\n absolute: false,\r\n });\r\n\r\n // Convert to relative paths from .warlock/production/ to src/app/\r\n // e.g., \"users/main\" -> \"../../src/app/users/main\"\r\n return files.map((file) => \"../../src/app/\" + file.replace(/\\.(ts|tsx)$/, \"\"));\r\n }\r\n\r\n /**\r\n * Glob for files in a specific directory pattern\r\n * Returns relative paths from .warlock/production/ to src/app/\r\n */\r\n private async globModuleDirectory(directory: string): Promise<string[]> {\r\n const pattern = `**/${directory}/*.{ts,tsx}`;\r\n const appDirectory = appPath();\r\n\r\n const files = await glob(pattern, {\r\n cwd: appDirectory,\r\n absolute: false,\r\n });\r\n\r\n return files.map((file) => \"../../src/app/\" + file.replace(/\\.(ts|tsx)$/, \"\"));\r\n }\r\n\r\n /**\r\n * Generate config-loader.ts\r\n */\r\n private async generateConfigLoader(): Promise<void> {\r\n const configDirectory = path.join(process.cwd(), \"src/config\");\r\n\r\n const files = await glob(\"*.{ts,tsx}\", {\r\n cwd: configDirectory,\r\n absolute: false,\r\n });\r\n\r\n const configNames = files.map((f) => f.replace(/\\.(ts|tsx)$/, \"\"));\r\n\r\n // Only `@warlock.js/core` — the one package the app itself declares. See\r\n // `assertGeneratedImportsAreDeclared`: `@mongez/config` is core's own\r\n // dependency, so a bare import of it resolved by luck under npm/yarn\r\n // hoisting and died under pnpm's strict layout.\r\n const imports: string[] = [\r\n 'import { setConfig, configSpecialHandlers } from \"@warlock.js/core\";',\r\n ];\r\n const configImports: string[] = [];\r\n const configSetCalls: string[] = [];\r\n const executors: string[] = [];\r\n\r\n for (const configName of configNames) {\r\n const properConfigName = toCamelCase(configName);\r\n const varName = `${properConfigName}Config`;\r\n configImports.push(`import ${varName} from \"../../src/config/${configName}\";`);\r\n configSetCalls.push(`setConfig(\"${properConfigName}\", ${varName});`);\r\n executors.push(`await configSpecialHandlers.execute(\"${properConfigName}\", ${varName});`);\r\n }\r\n\r\n let content = [\r\n ...imports,\r\n \"\",\r\n \"// Config imports\",\r\n ...configImports,\r\n \"\",\r\n \"// Register configs\",\r\n ...configSetCalls,\r\n \"\",\r\n \"// Special handlers\",\r\n ...executors,\r\n \"\",\r\n ].join(\"\\n\");\r\n\r\n if (await fileExistsAsync(appPath(\"prestart.ts\"))) {\r\n content += \"import './../../src/app/prestart';\\n\";\r\n }\r\n\r\n await putFileAsync(path.join(this.productionDir, \"config-loader.ts\"), content);\r\n }\r\n\r\n /**\r\n * Generate locales.ts (only if there are locale files)\r\n * @returns true if file was generated with content\r\n */\r\n private async generateLocales(): Promise<boolean> {\r\n const files = await this.globModule(\"utils/locales\");\r\n if (files.length === 0) return false;\r\n await this.generateImportsFile(files, \"locales.ts\");\r\n return true;\r\n }\r\n\r\n /**\r\n * Generate events.ts (only if there are event files)\r\n * @returns true if file was generated with content\r\n */\r\n private async generateEvents(): Promise<boolean> {\r\n const files = await this.globModuleDirectory(\"events\");\r\n if (files.length === 0) return false;\r\n await this.generateImportsFile(files, \"events.ts\");\r\n return true;\r\n }\r\n\r\n /**\r\n * Generate main.ts (only if there are main files)\r\n * @returns true if file was generated with content\r\n */\r\n private async generateMain(): Promise<boolean> {\r\n const files = await this.globModule(\"main\");\r\n if (files.length === 0) return false;\r\n await this.generateImportsFile(files, \"main.ts\");\r\n return true;\r\n }\r\n\r\n /**\r\n * Generate routes.ts (only if there are route files)\r\n * @returns true if file was generated with content\r\n */\r\n private async generateRoutes(): Promise<boolean> {\r\n const files = await this.globModule(\"routes\");\r\n if (files.length === 0) return false;\r\n await this.generateImportsFile(files, \"routes.ts\");\r\n return true;\r\n }\r\n\r\n /**\r\n * Generate a file with imports from all given files\r\n */\r\n private async generateImportsFile(importPaths: string[], outputFile: string): Promise<void> {\r\n const imports = importPaths.map((importPath) => `import \"${importPath}\";`);\r\n const content = imports.join(\"\\n\") + \"\\n\";\r\n await putFileAsync(path.join(this.productionDir, outputFile), content);\r\n }\r\n\r\n /**\r\n * Generate the main entry point (app.ts)\r\n */\r\n private async generateEntryPoint(): Promise<void> {\r\n console.log(colors.yellow(\" Generating entry point...\"));\r\n\r\n // Build imports based on which files were generated\r\n const imports: string[] = [\r\n \"// 1. Bootstrap (loads .env, initializes framework)\",\r\n 'import \"./bootstrap\";',\r\n \"\",\r\n \"// 2. Load configs\",\r\n 'import \"./config-loader\";',\r\n \"\",\r\n \"// 3. Start early-phase connectors (database, cache, logger, ...)\",\r\n \"// so data sources, cache, etc. are ready before app code runs\",\r\n 'import { Application, connectorsManager, ConnectorLifecyclePhase } from \"@warlock.js/core\";',\r\n \"await connectorsManager.startPhase(ConnectorLifecyclePhase.Early);\",\r\n ];\r\n\r\n // App code uses dynamic `await import(...)` so each module's side\r\n // effects fire at THIS point in execution. Static imports would be\r\n // hoisted to module-instantiation time (before the early-phase await\r\n // resolves), which is exactly the bug this split is meant to fix.\r\n //\r\n // The guarantee comes from the imports being DYNAMIC — esbuild wraps a\r\n // dynamically-imported module in its `__esm(() => {…})` helper and runs\r\n // it at the await, not at instantiation. It does NOT come from\r\n // `splitting: true`, which only decides one-file-vs-chunks. This\r\n // previously said splitting was required; it is not, and `singleBundle`\r\n // (splitting: false) preserves the ordering.\r\n imports.push(\"\", \"// 4. Load app code (events, locales, main, routes)\");\r\n\r\n if (this.generatedFiles.events) {\r\n imports.push('await import(\"./events\");');\r\n }\r\n if (this.generatedFiles.locales) {\r\n imports.push('await import(\"./locales\");');\r\n }\r\n if (this.generatedFiles.main) {\r\n imports.push('await import(\"./main\");');\r\n }\r\n if (this.generatedFiles.routes) {\r\n imports.push('await import(\"./routes\");');\r\n }\r\n\r\n // Start late-phase connectors after app code registers routes/listeners\r\n imports.push(\r\n \"\",\r\n \"// 5. Start late-phase connectors (http, socket) — routes and\",\r\n \"// listeners registered by app code are now ready to bind\",\r\n \"await connectorsManager.startPhase(ConnectorLifecyclePhase.Late);\",\r\n \"\",\r\n \"// 6. Signal a complete boot so `Application.onceBooted(...)` listeners fire\",\r\n \"Application.markBooted({ environment: Application.environment, runtimeStrategy: Application.runtimeStrategy });\",\r\n \"connectorsManager.shutdownOnProcessKill();\",\r\n );\r\n\r\n const content = imports.join(\"\\n\") + \"\\n\";\r\n await putFileAsync(path.join(this.productionDir, \"app.ts\"), content);\r\n }\r\n\r\n /**\r\n * Bundle with esbuild\r\n */\r\n private async bundle(): Promise<void> {\r\n console.log(colors.magenta(\" Bundling with esbuild...\"));\r\n\r\n const entryPoint = path.join(this.productionDir, \"app.ts\");\r\n const outDir = this.options.outdir!;\r\n const outFileName = this.options.outFile!;\r\n // Strip extension so entryNames produces \"<base>.js\" via esbuild\r\n const entryName = path.basename(outFileName, path.extname(outFileName));\r\n\r\n // `singleBundle` and `esmShim` are ours, not esbuild's — it throws on\r\n // unknown keys, and the user options are spread into its call verbatim.\r\n const singleBundle = this.options.singleBundle === true;\r\n const esmShim = this.options.esmShim !== false;\r\n // Read before deleting: the merged banner is applied AFTER the user\r\n // spread, so a user banner must not be lost to it or clobber the shim.\r\n const userBanner = this.options.banner;\r\n\r\n delete this.options.outFile;\r\n delete this.options.entryPath;\r\n delete this.options.singleBundle;\r\n delete this.options.esmShim;\r\n delete this.options.banner;\r\n\r\n await ensureDirectoryAsync(outDir);\r\n\r\n const alias = this.buildAliasMapFromTsconfig();\r\n\r\n // The user can override `format`, so gate the shim on the EFFECTIVE\r\n // format rather than on the default below. A CJS build already has\r\n // `require` and friends; injecting them there would be a redefinition.\r\n const isEsm = (this.options.format ?? \"esm\") === \"esm\";\r\n const banner = mergeBanner(userBanner, esmShim && isEsm ? ESM_INTEROP_SHIM : undefined);\r\n\r\n if (singleBundle) {\r\n console.log(\r\n colors.magenta(\r\n \" Single-bundle mode — one JS file\" +\r\n (esmShim && isEsm ? \" with the ESM interop shim\" : \"\") +\r\n \". Native .node addons are still emitted alongside it.\",\r\n ),\r\n );\r\n }\r\n\r\n\r\n try {\r\n \r\n await esbuild.build({\r\n platform: \"node\",\r\n entryPoints: [entryPoint],\r\n bundle: true,\r\n // Both are DEFAULTS the user can override — they sit before the\r\n // `...this.options` spread deliberately. `singleBundle` moves them,\r\n // an explicit `splitting`/`packages` in warlock.config.ts beats both.\r\n //\r\n // Phase ordering does not depend on `splitting`: it comes from the\r\n // generated app.ts using dynamic `await import(...)` (see\r\n // generateAppEntry above), which esbuild defers to the call site in\r\n // either mode. Splitting only decides one file vs chunks.\r\n splitting: !singleBundle,\r\n packages: singleBundle ? \"bundle\" : \"external\",\r\n minify: this.options!.minify,\r\n sourcemap: this.options!.sourcemap === true ? \"linked\" : this.options!.sourcemap,\r\n format: \"esm\",\r\n // Targeting a concrete Node version (not \"esnext\") so esbuild\r\n // transpiles TC39 stage 3 decorators into helpers — Node does not\r\n // implement them natively yet.\r\n target: [\"node22\"],\r\n outdir: outDir,\r\n entryNames: entryName,\r\n alias,\r\n plugins: [nativeNodeModulesPlugin],\r\n ...(this.options as any),\r\n // AFTER the spread, and intentionally so: `banner` is an object, and\r\n // both the config merge and this spread REPLACE it wholesale rather\r\n // than merging. Left as a default above, any user banner would delete\r\n // the shim; left to the spread, the shim would delete theirs. The\r\n // merge is the only form that keeps both.\r\n ...(banner ? { banner } : {}),\r\n });\r\n } catch (e) {\r\n console.log(e);\r\n throw e;\r\n }\r\n }\r\n\r\n /**\r\n * Build an alias map from tsconfig `paths` so esbuild resolves local\r\n * source aliases (e.g. `@warlock.js/cascade`) to their on-disk source\r\n * folders during bundling. Without this, `packages: \"external\"` would\r\n * leave those bare specifiers as raw imports and Node would fail to\r\n * resolve them at runtime (they aren't installed in node_modules).\r\n *\r\n * Only exact (non-wildcard) aliases are included — esbuild's `alias`\r\n * option doesn't support glob-style mappings.\r\n */\r\n private buildAliasMapFromTsconfig(): Record<string, string> {\r\n tsconfigManager.init();\r\n\r\n const alias: Record<string, string> = {};\r\n const baseUrl = path.resolve(process.cwd(), tsconfigManager.baseUrl);\r\n\r\n for (const [from, to] of Object.entries(tsconfigManager.aliases)) {\r\n if (from.endsWith(\"/*\") || !Array.isArray(to) || to.length === 0) {\r\n continue;\r\n }\r\n\r\n alias[from] = path.resolve(baseUrl, to[0]);\r\n }\r\n\r\n return alias;\r\n }\r\n}\r\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
- import { warlockConfigManager } from "../warlock-config/warlock-config.manager.mjs";
2
1
  import { defaultWarlockConfigurations } from "../warlock-config/default-configurations.mjs";
3
2
  import { normalizeBuildConfig } from "../warlock-config/normalize-build-config.mjs";
3
+ import { warlockConfigManager } from "../warlock-config/warlock-config.manager.mjs";
4
4
  import path from "path";
5
5
 
6
6
  //#region ../core/src/production/resolve-build-config.ts
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-build-config.mjs","names":[],"sources":["../../../../../../../core/src/production/resolve-build-config.ts"],"sourcesContent":["import path from \"path\";\nimport { defaultWarlockConfigurations } from \"../warlock-config/default-configurations\";\nimport { normalizeBuildConfig } from \"../warlock-config/normalize-build-config\";\nimport type { WarlockConfig } from \"../warlock-config/types\";\nimport { warlockConfigManager } from \"../warlock-config/warlock-config.manager\";\n\nexport type ResolvedBuildConfig = Required<\n Omit<NonNullable<WarlockConfig[\"build\"]>, \"outDirectory\">\n> & {\n /** Absolute path to the bundled entry file (`{outdir}/{outFile}`) */\n entryPath: string;\n};\n\n/**\n * Resolve the build config with framework defaults applied.\n *\n * Both the production builder and `warlock start` call this so they\n * agree on where the bundle lives — previously each had its own local\n * fallbacks and they drifted (`.warlock/production` vs `dist`),\n * letting `build` and `start` look at different paths.\n */\nexport function resolveBuildConfig(): ResolvedBuildConfig {\n // Normalised here rather than only in `defineConfig` because a project may\n // export a plain config object that never went through it.\n const userBuild = normalizeBuildConfig(warlockConfigManager.get(\"build\") ?? {});\n const defaults = defaultWarlockConfigurations.build!;\n const merged = { ...defaults, ...userBuild } as ResolvedBuildConfig;\n\n return {\n ...merged,\n entryPath: path.resolve(merged.outdir, merged.outFile),\n };\n}\n"],"mappings":";;;;;;;;;;;;;;AAqBA,SAAgB,qBAA0C;CAGxD,MAAM,YAAY,qBAAqB,qBAAqB,IAAI,OAAO,KAAK,CAAC,CAAC;CAE9E,MAAM,SAAS;EAAE,GADA,6BAA6B;EAChB,GAAG;CAAU;CAE3C,OAAO;EACL,GAAG;EACH,WAAW,KAAK,QAAQ,OAAO,QAAQ,OAAO,OAAO;CACvD;AACF"}
1
+ {"version":3,"file":"resolve-build-config.mjs","names":[],"sources":["../../../../../../../core/src/production/resolve-build-config.ts"],"sourcesContent":["import path from \"path\";\nimport { defaultWarlockConfigurations } from \"../warlock-config/default-configurations\";\nimport { normalizeBuildConfig } from \"../warlock-config/normalize-build-config\";\nimport type { WarlockConfig } from \"../warlock-config/types\";\nimport { warlockConfigManager } from \"../warlock-config/warlock-config.manager\";\n\n/**\n * Keys the builder deletes before handing the options to esbuild — ours\n * (`singleBundle`, `esmShim`), which esbuild would reject as unknown, and\n * `banner`, which is re-merged and re-applied after the user spread.\n *\n * They are optional here for a concrete reason: `Required<…>` makes `delete`\n * a type error (TS2790). Naming exactly these three says which keys are\n * removable, rather than casting at each `delete` — a cast there would also\n * hide a genuine mistake.\n *\n * `outFile` and `entryPath` stay REQUIRED even though the builder deletes\n * them too. They are always present after resolution and callers depend on\n * that; widening them to `string | undefined` pushed the lie downstream into\n * `start-production.command.ts`, which then could not trust its own config.\n */\ntype BuilderStrippedKeys = \"singleBundle\" | \"esmShim\" | \"banner\";\n\nexport type ResolvedBuildConfig = Required<\n Omit<NonNullable<WarlockConfig[\"build\"]>, \"outDirectory\" | BuilderStrippedKeys>\n> &\n Partial<Pick<NonNullable<WarlockConfig[\"build\"]>, BuilderStrippedKeys>> & {\n /** Absolute path to the bundled entry file (`{outdir}/{outFile}`) */\n entryPath: string;\n };\n\n/**\n * Resolve the build config with framework defaults applied.\n *\n * Both the production builder and `warlock start` call this so they\n * agree on where the bundle lives — previously each had its own local\n * fallbacks and they drifted (`.warlock/production` vs `dist`),\n * letting `build` and `start` look at different paths.\n */\nexport function resolveBuildConfig(): ResolvedBuildConfig {\n // Normalised here rather than only in `defineConfig` because a project may\n // export a plain config object that never went through it.\n const userBuild = normalizeBuildConfig(warlockConfigManager.get(\"build\") ?? {});\n const defaults = defaultWarlockConfigurations.build!;\n const merged = { ...defaults, ...userBuild } as ResolvedBuildConfig;\n\n return {\n ...merged,\n entryPath: path.resolve(merged.outdir, merged.outFile),\n };\n}\n"],"mappings":";;;;;;;;;;;;;;AAuCA,SAAgB,qBAA0C;CAGxD,MAAM,YAAY,qBAAqB,qBAAqB,IAAI,OAAO,KAAK,CAAC,CAAC;CAE9E,MAAM,SAAS;EAAE,GADA,6BAA6B;EAChB,GAAG;CAAU;CAE3C,OAAO;EACL,GAAG;EACH,WAAW,KAAK,QAAQ,OAAO,QAAQ,OAAO,OAAO;CACvD;AACF"}
@@ -10,6 +10,15 @@ declare class Router {
10
10
  * Routes list
11
11
  */
12
12
  private routes;
13
+ /**
14
+ * Bumped on every mutation of {@link routes}.
15
+ *
16
+ * The dev dispatcher caches a route registry and needs to know when it is
17
+ * stale. Comparing `routes.length` would miss an HMR reload that replaces a
18
+ * route without changing the count — the registry would then keep matching
19
+ * paths that no longer exist. A version counter cannot miss that.
20
+ */
21
+ private routesVersion;
13
22
  /**
14
23
  * Router Instance
15
24
  */
@@ -1 +1 @@
1
- {"version":3,"file":"router.d.mts","names":[],"sources":["../../../../../../../core/src/router/router.ts"],"mappings":"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"}
1
+ {"version":3,"file":"router.d.mts","names":[],"sources":["../../../../../../../core/src/router/router.ts"],"mappings":"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"}
@@ -11,6 +11,35 @@ import fastifyStatic from "@fastify/static";
11
11
  import concatRoute from "@mongez/concat-route";
12
12
 
13
13
  //#region ../core/src/router/router.ts
14
+ /**
15
+ * The pre-handler phases the dev server forwards from a route's `serverOptions`.
16
+ *
17
+ * These are the admission-control phases and they share the `(request, reply)`
18
+ * signature. `preParsing` is deliberately absent — it must return the payload
19
+ * stream, so forwarding it generically would corrupt the body rather than
20
+ * guard it. `bodyLimit` cannot be forwarded at all; Fastify reads it at
21
+ * registration time and no hook can bound a body already being parsed.
22
+ */
23
+ const FORWARDED_ROUTE_PHASES = [
24
+ "onRequest",
25
+ "preValidation",
26
+ "preHandler"
27
+ ];
28
+ function toHookList(hook) {
29
+ if (!hook) return [];
30
+ return Array.isArray(hook) ? hook : [hook];
31
+ }
32
+ /**
33
+ * Run a matched route's hooks for one phase.
34
+ *
35
+ * Returning the reply is how an async Fastify hook signals that it has answered
36
+ * the request; the chain must stop there, so the value is propagated rather
37
+ * than discarded.
38
+ */
39
+ async function runRouteHooks(route, phase, request, reply) {
40
+ const hooks = toHookList(route.serverOptions?.[phase]);
41
+ for (const hook of hooks) if (await hook(request, reply) === reply) return reply;
42
+ }
14
43
  var Router = class Router {
15
44
  /**
16
45
  * Get router instance
@@ -21,6 +50,7 @@ var Router = class Router {
21
50
  }
22
51
  constructor() {
23
52
  this.routes = [];
53
+ this.routesVersion = 0;
24
54
  this.staticDirectories = [];
25
55
  this.eventListeners = {};
26
56
  this.stacks = {
@@ -145,6 +175,7 @@ var Router = class Router {
145
175
  else routeData.name += `.${routeData.method.toLowerCase()}`;
146
176
  }
147
177
  this.routes.push(routeData);
178
+ this.routesVersion++;
148
179
  return this;
149
180
  }
150
181
  /**
@@ -351,6 +382,7 @@ var Router = class Router {
351
382
  */
352
383
  removeRoutesBySourceFile(sourceFile) {
353
384
  this.routes = this.routes.filter((route) => route.sourceFile !== sourceFile);
385
+ this.routesVersion++;
354
386
  }
355
387
  /**
356
388
  * Manage validation system for the given resource
@@ -438,21 +470,42 @@ var Router = class Router {
438
470
  */
439
471
  scanDevServer(server) {
440
472
  this.eventListeners.beforeScan?.forEach((callback) => callback(this, server));
441
- const wildcardHandler = async (fastifyRequest, fastifyReply) => {
442
- const routeRegistry = new RouteRegistry();
473
+ let routeRegistry;
474
+ let registryVersion = -1;
475
+ const resolveRouteRegistry = () => {
476
+ if (routeRegistry && registryVersion === this.routesVersion) return routeRegistry;
477
+ routeRegistry = new RouteRegistry();
443
478
  routeRegistry.register(this.routes);
444
- const match = routeRegistry.find(fastifyRequest.method, fastifyRequest.url);
479
+ registryVersion = this.routesVersion;
480
+ return routeRegistry;
481
+ };
482
+ server.addHook("onRequest", async (fastifyRequest, fastifyReply) => {
483
+ const match = resolveRouteRegistry().find(fastifyRequest.method, fastifyRequest.url);
484
+ if (!match) return;
485
+ fastifyRequest.matchedDevRoute = match;
486
+ fastifyRequest.params = match.params;
487
+ return runRouteHooks(match.route, "onRequest", fastifyRequest, fastifyReply);
488
+ });
489
+ for (const phase of FORWARDED_ROUTE_PHASES.filter((name) => name !== "onRequest")) server.addHook(phase, async (fastifyRequest, fastifyReply) => {
490
+ const match = fastifyRequest.matchedDevRoute;
491
+ if (!match) return;
492
+ return runRouteHooks(match.route, phase, fastifyRequest, fastifyReply);
493
+ });
494
+ const wildcardHandler = async (fastifyRequest, fastifyReply) => {
495
+ const match = fastifyRequest.matchedDevRoute;
445
496
  if (!match) return fastifyReply.code(404).send({
446
497
  error: "Route not found",
447
498
  path: fastifyRequest.url,
448
499
  method: fastifyRequest.method
449
500
  });
450
- fastifyRequest.params = match.params;
451
501
  try {
452
502
  const { output, response } = await this.handleRoute(match.route)(fastifyRequest, fastifyReply);
453
503
  return output || response.baseResponse;
454
504
  } catch (error) {
455
- console.log(error);
505
+ log.error("router", "dev-dispatch", error, {
506
+ method: fastifyRequest.method,
507
+ url: fastifyRequest.url
508
+ });
456
509
  throw error;
457
510
  }
458
511
  };