@warlock.js/core 5.20.0 → 5.22.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.
- package/CHANGELOG.md +1130 -1086
- package/esm/benchmark/benchmark.mjs +34 -25
- package/esm/benchmark/benchmark.mjs.map +1 -1
- package/esm/benchmark/profiler.d.mts.map +1 -1
- package/esm/benchmark/profiler.mjs +8 -3
- package/esm/benchmark/profiler.mjs.map +1 -1
- package/esm/bootstrap.d.mts.map +1 -1
- package/esm/bootstrap.mjs +3 -0
- package/esm/bootstrap.mjs.map +1 -1
- package/esm/cache/database-cache-driver.d.mts +2 -2
- package/esm/cache/database-cache-driver.d.mts.map +1 -1
- package/esm/cache/database-cache-driver.mjs +3 -2
- package/esm/cache/database-cache-driver.mjs.map +1 -1
- package/esm/cli/cli-commands.manager.mjs +16 -14
- package/esm/cli/cli-commands.manager.mjs.map +1 -1
- package/esm/cli/commands/add.command.mjs +1 -1
- package/esm/cli/commands/add.command.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/optional-peers.check.mjs +3 -2
- package/esm/cli/commands/doctor/checks/optional-peers.check.mjs.map +1 -1
- package/esm/cli/commands/generate/generate.command.mjs +20 -0
- package/esm/cli/commands/generate/generate.command.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/column-dsl-parser.mjs +4 -2
- package/esm/cli/commands/generate/generators/column-dsl-parser.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/controller.generator.mjs +1 -1
- package/esm/cli/commands/generate/generators/controller.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/migration.generator.mjs +22 -6
- package/esm/cli/commands/generate/generators/migration.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/model.generator.mjs +22 -7
- package/esm/cli/commands/generate/generators/model.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/module.generator.mjs +8 -6
- package/esm/cli/commands/generate/generators/module.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/repository.generator.mjs +1 -1
- package/esm/cli/commands/generate/generators/repository.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/resource.generator.mjs +1 -1
- package/esm/cli/commands/generate/generators/resource.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/service.generator.mjs +1 -1
- package/esm/cli/commands/generate/generators/service.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/templates/stubs.mjs +7 -7
- package/esm/cli/commands/generate/templates/stubs.mjs.map +1 -1
- package/esm/cli/commands/generate/utils/writer.mjs +25 -1
- package/esm/cli/commands/generate/utils/writer.mjs.map +1 -1
- package/esm/cli/commands/routes/routes-command.action.mjs +5 -1
- package/esm/cli/commands/routes/routes-command.action.mjs.map +1 -1
- package/esm/cli/commands/routes/with-console-on-stderr.mjs +24 -0
- package/esm/cli/commands/routes/with-console-on-stderr.mjs.map +1 -0
- package/esm/cli/commands/routes.command.mjs +3 -4
- package/esm/cli/commands/routes.command.mjs.map +1 -1
- package/esm/cli/commands/start-production.command.mjs +5 -5
- package/esm/cli/commands/start-production.command.mjs.map +1 -1
- package/esm/cli/commands/storage-put.action.mjs +2 -1
- package/esm/cli/commands/storage-put.action.mjs.map +1 -1
- package/esm/cli/commands/update.command.mjs +1 -0
- package/esm/cli/commands/update.command.mjs.map +1 -1
- package/esm/cli/parse-cli-args.mjs +13 -1
- package/esm/cli/parse-cli-args.mjs.map +1 -1
- package/esm/commands/cli-command.d.mts.map +1 -1
- package/esm/commands/cli-command.mjs +4 -2
- package/esm/commands/cli-command.mjs.map +1 -1
- package/esm/commands/index.mjs +3 -0
- package/esm/config/config-key-from-path.mjs +20 -0
- package/esm/config/config-key-from-path.mjs.map +1 -0
- package/esm/config/config-loader.d.mts.map +1 -1
- package/esm/config/config-loader.mjs +2 -3
- package/esm/config/config-loader.mjs.map +1 -1
- package/esm/connectors/connectors-manager.d.mts +4 -0
- package/esm/connectors/connectors-manager.d.mts.map +1 -1
- package/esm/connectors/connectors-manager.mjs +30 -2
- package/esm/connectors/connectors-manager.mjs.map +1 -1
- package/esm/connectors/socket-connector.d.mts +29 -0
- package/esm/connectors/socket-connector.d.mts.map +1 -1
- package/esm/connectors/socket-connector.mjs +73 -9
- package/esm/connectors/socket-connector.mjs.map +1 -1
- package/esm/connectors/types.d.mts +1 -1
- package/esm/connectors/types.mjs.map +1 -1
- package/esm/database/models/database-log/database-log.d.mts +47 -24
- package/esm/database/models/database-log/database-log.d.mts.map +1 -1
- package/esm/database/models/database-log/database-log.mjs +36 -4
- package/esm/database/models/database-log/database-log.mjs.map +1 -1
- package/esm/database/seed-command-action.mjs +10 -2
- package/esm/database/seed-command-action.mjs.map +1 -1
- package/esm/dev-server/development-server.mjs +11 -4
- package/esm/dev-server/development-server.mjs.map +1 -1
- package/esm/dev-server/file-event-handler.mjs +26 -6
- package/esm/dev-server/file-event-handler.mjs.map +1 -1
- package/esm/dev-server/file-manager.d.mts.map +1 -1
- package/esm/dev-server/file-manager.mjs +2 -2
- package/esm/dev-server/file-manager.mjs.map +1 -1
- package/esm/dev-server/files-orchestrator.mjs +3 -1
- package/esm/dev-server/files-orchestrator.mjs.map +1 -1
- package/esm/dev-server/files-watcher.mjs +64 -5
- package/esm/dev-server/files-watcher.mjs.map +1 -1
- package/esm/dev-server/health-checker/files-healthcare.manager.mjs +31 -9
- package/esm/dev-server/health-checker/files-healthcare.manager.mjs.map +1 -1
- package/esm/dev-server/health-checker/workers/eslint-health.worker.mjs +11 -7
- package/esm/dev-server/health-checker/workers/eslint-health.worker.mjs.map +1 -1
- package/esm/dev-server/loader/load-hook.mjs +1 -1
- package/esm/dev-server/loader/register-loader.mjs +1 -1
- package/esm/dev-server/manifest-manager.d.mts.map +1 -1
- package/esm/dev-server/manifest-manager.mjs +12 -6
- package/esm/dev-server/manifest-manager.mjs.map +1 -1
- package/esm/dev-server/parse-imports.mjs +9 -0
- package/esm/dev-server/parse-imports.mjs.map +1 -1
- package/esm/dev-server/register-dev-shortcuts.mjs +2 -1
- package/esm/dev-server/register-dev-shortcuts.mjs.map +1 -1
- package/esm/dev-server/shortcuts.mjs +2 -2
- package/esm/dev-server/shortcuts.mjs.map +1 -1
- package/esm/dev-server/special-file-patterns.mjs +18 -0
- package/esm/dev-server/special-file-patterns.mjs.map +1 -0
- package/esm/dev-server/special-files-collector.d.mts.map +1 -1
- package/esm/dev-server/special-files-collector.mjs +2 -5
- package/esm/dev-server/special-files-collector.mjs.map +1 -1
- package/esm/dev-server/start-development-server.mjs +1 -2
- package/esm/dev-server/start-development-server.mjs.map +1 -1
- package/esm/dev-server/supervisor.mjs +23 -3
- package/esm/dev-server/supervisor.mjs.map +1 -1
- package/esm/dev-server/tsconfig-manager.mjs +22 -12
- package/esm/dev-server/tsconfig-manager.mjs.map +1 -1
- package/esm/dev-server/type-generator.mjs +10 -9
- package/esm/dev-server/type-generator.mjs.map +1 -1
- package/esm/encryption/encrypt.d.mts.map +1 -1
- package/esm/encryption/encrypt.mjs +16 -5
- package/esm/encryption/encrypt.mjs.map +1 -1
- package/esm/encryption/hash.d.mts.map +1 -1
- package/esm/encryption/hash.mjs +2 -0
- package/esm/encryption/hash.mjs.map +1 -1
- package/esm/encryption/password.d.mts.map +1 -1
- package/esm/encryption/password.mjs +3 -1
- package/esm/encryption/password.mjs.map +1 -1
- package/esm/generations/features/index.mjs +1 -1
- package/esm/generations/features/mysql.feature.mjs +9 -2
- package/esm/generations/features/mysql.feature.mjs.map +1 -1
- package/esm/generations/features/test.feature.mjs +2 -1
- package/esm/generations/features/test.feature.mjs.map +1 -1
- package/esm/generations/features/web.feature.mjs +4 -2
- package/esm/generations/features/web.feature.mjs.map +1 -1
- package/esm/http/csrf-origin-policy.d.mts.map +1 -1
- package/esm/http/csrf-origin-policy.mjs +5 -2
- package/esm/http/csrf-origin-policy.mjs.map +1 -1
- package/esm/http/flush-pending-cookies.mjs +52 -0
- package/esm/http/flush-pending-cookies.mjs.map +1 -0
- package/esm/http/middleware/cache-response-middleware.d.mts.map +1 -1
- package/esm/http/middleware/cache-response-middleware.mjs +4 -2
- package/esm/http/middleware/cache-response-middleware.mjs.map +1 -1
- package/esm/http/middleware/inject-request-context.d.mts.map +1 -1
- package/esm/http/middleware/inject-request-context.mjs +14 -3
- package/esm/http/middleware/inject-request-context.mjs.map +1 -1
- package/esm/http/middleware/utils/cidr-match.d.mts +1 -1
- package/esm/http/middleware/utils/cidr-match.d.mts.map +1 -1
- package/esm/http/middleware/utils/cidr-match.mjs +11 -2
- package/esm/http/middleware/utils/cidr-match.mjs.map +1 -1
- package/esm/http/middleware/utils/idempotency-key.mjs +1 -1
- package/esm/http/middleware/utils/idempotency-key.mjs.map +1 -1
- package/esm/http/plugins.d.mts.map +1 -1
- package/esm/http/plugins.mjs +10 -1
- package/esm/http/plugins.mjs.map +1 -1
- package/esm/http/request.d.mts +12 -1
- package/esm/http/request.d.mts.map +1 -1
- package/esm/http/request.mjs +35 -7
- package/esm/http/request.mjs.map +1 -1
- package/esm/http/response.d.mts +16 -0
- package/esm/http/response.d.mts.map +1 -1
- package/esm/http/response.mjs +75 -37
- package/esm/http/response.mjs.map +1 -1
- package/esm/http/types.d.mts +24 -0
- package/esm/http/types.d.mts.map +1 -1
- package/esm/http/uploads/uploaded-file.controller.mjs +1 -1
- package/esm/http/uploads-config.mjs +2 -1
- package/esm/http/uploads-config.mjs.map +1 -1
- package/esm/http/uploads-types.d.mts +3 -3
- package/esm/image/image.d.mts +1 -1
- package/esm/image/image.d.mts.map +1 -1
- package/esm/image/image.mjs +25 -15
- package/esm/image/image.mjs.map +1 -1
- package/esm/index.d.mts +4 -3
- package/esm/index.mjs +5 -3
- package/esm/logger/logger.mjs +6 -1
- package/esm/logger/logger.mjs.map +1 -1
- package/esm/mail/config.d.mts.map +1 -1
- package/esm/mail/config.mjs +31 -21
- package/esm/mail/config.mjs.map +1 -1
- package/esm/mail/react-mail.mjs +6 -3
- package/esm/mail/react-mail.mjs.map +1 -1
- package/esm/mail/send-mail.d.mts.map +1 -1
- package/esm/mail/send-mail.mjs +40 -19
- package/esm/mail/send-mail.mjs.map +1 -1
- package/esm/production/build-contributions.mjs +3 -0
- package/esm/production/build-contributions.mjs.map +1 -1
- package/esm/production/esbuild-plugins.mjs +3 -2
- package/esm/production/esbuild-plugins.mjs.map +1 -1
- package/esm/production/production-builder.mjs +32 -36
- package/esm/production/production-builder.mjs.map +1 -1
- package/esm/production/production-supervisor.mjs +12 -5
- package/esm/production/production-supervisor.mjs.map +1 -1
- package/esm/repositories/adapters/cascade/cascade-adapter.d.mts +3 -1
- package/esm/repositories/adapters/cascade/cascade-adapter.d.mts.map +1 -1
- package/esm/repositories/adapters/cascade/cascade-adapter.mjs +17 -5
- package/esm/repositories/adapters/cascade/cascade-adapter.mjs.map +1 -1
- package/esm/repositories/adapters/cascade/cascade-query-builder.d.mts +3 -3
- package/esm/repositories/adapters/cascade/cascade-query-builder.mjs.map +1 -1
- package/esm/repositories/adapters/cascade/filter-applicator.mjs +88 -61
- package/esm/repositories/adapters/cascade/filter-applicator.mjs.map +1 -1
- package/esm/repositories/adapters/cascade/index.d.mts +1 -1
- package/esm/repositories/adapters/cascade/index.mjs +1 -1
- package/esm/repositories/contracts/query-builder.contract.d.mts +3 -3
- package/esm/repositories/contracts/types.d.mts +5 -0
- package/esm/repositories/contracts/types.d.mts.map +1 -1
- package/esm/repositories/index.d.mts +1 -1
- package/esm/repositories/index.mjs +1 -1
- package/esm/repositories/repository.manager.d.mts +65 -3
- package/esm/repositories/repository.manager.d.mts.map +1 -1
- package/esm/repositories/repository.manager.mjs +146 -57
- package/esm/repositories/repository.manager.mjs.map +1 -1
- package/esm/resource/resource-field-builder.d.mts.map +1 -1
- package/esm/resource/resource-field-builder.mjs +1 -0
- package/esm/resource/resource-field-builder.mjs.map +1 -1
- package/esm/resource/resource.d.mts +10 -3
- package/esm/resource/resource.d.mts.map +1 -1
- package/esm/resource/resource.mjs +19 -5
- package/esm/resource/resource.mjs.map +1 -1
- package/esm/restful/restful.d.mts +12 -5
- package/esm/restful/restful.d.mts.map +1 -1
- package/esm/restful/restful.mjs +47 -16
- package/esm/restful/restful.mjs.map +1 -1
- package/esm/router/build-route-rate-limit.mjs +22 -0
- package/esm/router/build-route-rate-limit.mjs.map +1 -0
- package/esm/router/dev-dispatch.mjs +60 -0
- package/esm/router/dev-dispatch.mjs.map +1 -0
- package/esm/router/route-builder.mjs +4 -1
- package/esm/router/route-builder.mjs.map +1 -1
- package/esm/router/route-registry.d.mts.map +1 -1
- package/esm/router/route-registry.mjs +2 -2
- package/esm/router/route-registry.mjs.map +1 -1
- package/esm/router/router.d.mts +1 -1
- package/esm/router/router.d.mts.map +1 -1
- package/esm/router/router.mjs +57 -39
- package/esm/router/router.mjs.map +1 -1
- package/esm/socket/types.d.mts +9 -2
- package/esm/socket/types.d.mts.map +1 -1
- package/esm/storage/drivers/cloud-driver.d.mts +17 -0
- package/esm/storage/drivers/cloud-driver.d.mts.map +1 -1
- package/esm/storage/drivers/cloud-driver.mjs +53 -17
- package/esm/storage/drivers/cloud-driver.mjs.map +1 -1
- package/esm/storage/drivers/local-driver.d.mts +7 -5
- package/esm/storage/drivers/local-driver.d.mts.map +1 -1
- package/esm/storage/drivers/local-driver.mjs +36 -31
- package/esm/storage/drivers/local-driver.mjs.map +1 -1
- package/esm/storage/index.d.mts +2 -1
- package/esm/storage/index.mjs +2 -1
- package/esm/storage/scoped-storage.d.mts +13 -1
- package/esm/storage/scoped-storage.d.mts.map +1 -1
- package/esm/storage/scoped-storage.mjs +18 -8
- package/esm/storage/scoped-storage.mjs.map +1 -1
- package/esm/storage/storage.d.mts +0 -5
- package/esm/storage/storage.d.mts.map +1 -1
- package/esm/storage/storage.mjs +7 -16
- package/esm/storage/storage.mjs.map +1 -1
- package/esm/storage/types.d.mts +22 -4
- package/esm/storage/types.d.mts.map +1 -1
- package/esm/storage/utils/classify-ip-address.d.mts +13 -0
- package/esm/storage/utils/classify-ip-address.d.mts.map +1 -0
- package/esm/storage/utils/classify-ip-address.mjs +98 -0
- package/esm/storage/utils/classify-ip-address.mjs.map +1 -0
- package/esm/storage/utils/safe-fetch.d.mts +3 -12
- package/esm/storage/utils/safe-fetch.d.mts.map +1 -1
- package/esm/storage/utils/safe-fetch.mjs +3 -48
- package/esm/storage/utils/safe-fetch.mjs.map +1 -1
- package/esm/updater/update-warlock-packages.mjs +11 -1
- package/esm/updater/update-warlock-packages.mjs.map +1 -1
- package/esm/use-cases/use-case-broadcast.mjs +7 -1
- package/esm/use-cases/use-case-broadcast.mjs.map +1 -1
- package/esm/use-cases/use-cases-registry.mjs +21 -2
- package/esm/use-cases/use-cases-registry.mjs.map +1 -1
- package/esm/utils/database-log.mjs +0 -3
- package/esm/utils/database-log.mjs.map +1 -1
- package/esm/utils/queue.d.mts +6 -4
- package/esm/utils/queue.d.mts.map +1 -1
- package/esm/utils/queue.mjs +20 -9
- package/esm/utils/queue.mjs.map +1 -1
- package/esm/validation/database/exists-except-current-id.mjs +6 -2
- package/esm/validation/database/exists-except-current-id.mjs.map +1 -1
- package/esm/validation/database/unique-except-current-id.mjs +6 -2
- package/esm/validation/database/unique-except-current-id.mjs.map +1 -1
- package/esm/validation/init.mjs +6 -2
- package/esm/validation/init.mjs.map +1 -1
- package/esm/validation/validateAll.mjs +3 -2
- package/esm/validation/validateAll.mjs.map +1 -1
- package/esm/warlock-config/types.d.mts +14 -1
- package/esm/warlock-config/types.d.mts.map +1 -1
- package/esm/warlock-config/warlock-config.manager.d.mts.map +1 -1
- package/esm/warlock-config/warlock-config.manager.mjs +3 -1
- package/esm/warlock-config/warlock-config.manager.mjs.map +1 -1
- package/esm/warlock-config/warn-deprecated-config.mjs +25 -0
- package/esm/warlock-config/warn-deprecated-config.mjs.map +1 -0
- package/package.json +16 -16
- package/esm/_virtual/_rolldown/runtime.mjs +0 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.mjs","names":[],"sources":["../../../../../../../core/src/connectors/types.ts"],"sourcesContent":["import type { BuildOptions } from \"esbuild\";\nimport type { NamedApiRoute } from \"../router/types\";\nimport type { ResolvedBuildConfig } from \"../production/resolve-build-config\";\n\n/**\n * Connector Interface\n * All service connectors (Database, HTTP, Cache, etc.) must implement this interface\n */\nexport interface Connector {\n /**\n * Unique name of the connector\n */\n readonly name: ConnectorName;\n\n /**\n * Priority for initialization order\n * Lower numbers initialize first (e.g., Database: 1, HTTP: 3)\n */\n readonly priority: number;\n\n /**\n * Phase the connector boots in, relative to user code.\n * See `ConnectorLifecyclePhase` for semantics.\n */\n readonly lifecyclePhase: ConnectorLifecyclePhase;\n\n /**\n * Whether the connector is currently active/connected\n */\n isActive(): boolean;\n\n /**\n * Boot the connector\n * Called during server startup before starting the connector\n */\n boot(): void | Promise<void>;\n\n /**\n * Initialize the connector\n * Called during server startup\n */\n start(): Promise<void>;\n\n /**\n * Restart the connector\n * Called when watched files change\n */\n restart(): Promise<void>;\n\n /**\n * Gracefully shutdown the connector\n * Called during server shutdown\n */\n shutdown(): Promise<void>;\n\n /**\n * Determine if this connector needs restart based on changed files\n * @param changedFiles Array of relative file paths that changed\n * @returns true if connector should restart\n */\n shouldRestart(changedFiles: string[]): boolean;\n\n /**\n * What this connector contributes to `warlock build`.\n *\n * Read STATICALLY from `warlock.config.ts > connectors` — `warlock build`\n * never calls `boot()`/`start()`, so nothing here may assume a running\n * connector. The same `connectors` key drives both halves of a connector's\n * life — build-time contribution and runtime boot; see\n * {@link ConnectorBuildContribution}.\n */\n readonly build?: ConnectorBuildContribution;\n}\n\n/**\n * What a build contribution is handed. Everything it needs to write files\n * and shape the bundle, and nothing that would let it reach the runtime.\n */\nexport type ConnectorBuildContext = {\n /** Absolute path to `.warlock/production` — deleted after the build */\n productionDir: string;\n /** Absolute path to the application root (where `package.json` lives) */\n appRoot: string;\n /** The build config after framework defaults and user overrides */\n options: ResolvedBuildConfig;\n /** Fresh registration-only snapshot, present only when the Web connector is configured. */\n readonly namedApiRoutes?: readonly NamedApiRoute[];\n};\n\n/**\n * The esbuild options a contributor may patch.\n *\n * Deliberately a narrow `Pick`, not `Partial<BuildOptions>`: `entryPoints`,\n * `tsconfig` and `tsconfigRaw` stay forbidden here for the same reason they\n * are forbidden in the user's `build` block — they re-compile every file in\n * the bundle, workspace packages included (see `ForbiddenBuildOptionError`).\n * A contributor reaching them would reintroduce the failure the user-level\n * guard exists to prevent.\n */\nexport type ConnectorEsbuildPatch = Pick<\n BuildOptions,\n \"jsx\" | \"jsxImportSource\" | \"define\" | \"external\" | \"loader\"\n>;\n\n/**\n * What `generate` may hand back to the builder.\n */\nexport type ConnectorBuildGenerateResult = {\n /**\n * Lines appended verbatim to section 4 of the generated entry, AFTER the\n * `./routes` import — page registration must see the routes app code\n * collected, mirroring dev's ordering.\n *\n * @example ['await import(\"./pages\");']\n */\n entryImports?: string[];\n /** Merged into the esbuild call: defaults < this patch < user `build` */\n esbuild?: ConnectorEsbuildPatch;\n};\n\n/**\n * A connector's build-time half: two hooks, both awaited, drained in\n * `connectors` array order. A hook that throws fails the build.\n *\n * **This type is closed on purpose**: exactly these two optional function properties and ZERO data-bearing\n * fields. There is deliberately no property where a Vite/babel plugin\n * instance, an alias map or a compiled pipeline could live, so\n * `build: { plugins: [...] }` is a type error rather than something a\n * reviewer has to catch. Anything heavy must be constructed INSIDE a hook,\n * after that hook dynamically imports it — that is what keeps a connector's\n * static import graph out of the runtime closure. The drain mirrors this at\n * read time by rejecting unknown enumerable keys (`assertClosedContribution`):\n * the type narrows, the runtime asserts.\n */\nexport type ConnectorBuildContribution = {\n /**\n * Write files into `ctx.productionDir` and/or patch the bundle.\n *\n * Runs after the generated-imports check and before esbuild bundles.\n */\n generate?(\n context: ConnectorBuildContext,\n ): Promise<ConnectorBuildGenerateResult | void> | ConnectorBuildGenerateResult | void;\n\n /**\n * Produce artifacts esbuild does not — e.g. a client bundle.\n *\n * Runs after esbuild and before `.warlock/production` is removed.\n */\n emit?(context: ConnectorBuildContext): Promise<void> | void;\n};\n\nexport type ConnectorName =\n | \"logger\"\n | \"mailer\"\n | \"http\"\n | \"database\"\n | \"cache\"\n | \"storage\"\n | \"herald\"\n | \"socket\"\n | \"notifications\"\n | \"access\"\n | \"ai\"\n | (string & {});\n\n/**\n * When a connector boots relative to user code (main, routes, events).\n *\n * - `Early`: before user code is imported. For services user code\n * needs at import time (database, cache, logger, storage, mailer).\n * - `Late`: after user code is imported. For services that bind to\n * things user code registered (http scans the router; socket\n * depends on http's instance).\n */\nexport enum ConnectorLifecyclePhase {\n Early = \"early\",\n Late = \"late\",\n}\n\n/**\n * Connector priority constants\n */\nexport enum ConnectorPriority {\n LOGGER = 0,\n MAILER = 1,\n DATABASE = 2,\n COMMUNICATOR = 3,\n CACHE = 4,\n HTTP = 5,\n STORAGE = 6,\n SOCKET = 7,\n NOTIFICATIONS = 8,\n ACCESS = 9,\n AI = 10,\n}\n"],"mappings":";;;;;;;;;;AA+KA,IAAY,0BAAL;CACL;CACA;;AACF;;;;AAKA,IAAY,oBAAL;CACL;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;;AACF"}
|
|
1
|
+
{"version":3,"file":"types.mjs","names":[],"sources":["../../../../../../../core/src/connectors/types.ts"],"sourcesContent":["import type { BuildOptions } from \"esbuild\";\nimport type { NamedApiRoute } from \"../router/types\";\nimport type { ResolvedBuildConfig } from \"../production/resolve-build-config\";\n\n/**\n * Connector Interface\n * All service connectors (Database, HTTP, Cache, etc.) must implement this interface\n */\nexport interface Connector {\n /**\n * Unique name of the connector\n */\n readonly name: ConnectorName;\n\n /**\n * Priority for initialization order\n * Lower numbers initialize first (e.g., Database: 1, HTTP: 3)\n */\n readonly priority: number;\n\n /**\n * Phase the connector boots in, relative to user code.\n * See `ConnectorLifecyclePhase` for semantics.\n */\n readonly lifecyclePhase: ConnectorLifecyclePhase;\n\n /**\n * Whether the connector is currently active/connected\n */\n isActive(): boolean;\n\n /**\n * Boot the connector\n * Called during server startup before starting the connector\n */\n boot(): void | Promise<void>;\n\n /**\n * Initialize the connector\n * Called during server startup\n */\n start(): Promise<void>;\n\n /**\n * Restart the connector\n * Called when watched files change\n */\n restart(): Promise<void>;\n\n /**\n * Gracefully shutdown the connector\n * Called during server shutdown\n */\n shutdown(): Promise<void>;\n\n /**\n * Determine if this connector needs restart based on changed files\n * @param changedFiles Array of relative file paths that changed\n * @returns true if connector should restart\n */\n shouldRestart(changedFiles: string[]): boolean;\n\n /**\n * What this connector contributes to `warlock build`.\n *\n * Read STATICALLY from `warlock.config.ts > connectors` — `warlock build`\n * never calls `boot()`/`start()`, so nothing here may assume a running\n * connector. The same `connectors` key drives both halves of a connector's\n * life — build-time contribution and runtime boot; see\n * {@link ConnectorBuildContribution}.\n */\n readonly build?: ConnectorBuildContribution;\n}\n\n/**\n * What a build contribution is handed. Everything it needs to write files\n * and shape the bundle, and nothing that would let it reach the runtime.\n */\nexport type ConnectorBuildContext = {\n /** Absolute path to `.warlock/production` — deleted after the build */\n productionDir: string;\n /** Absolute path to the application root (where `package.json` lives) */\n appRoot: string;\n /** The build config after framework defaults and user overrides */\n options: ResolvedBuildConfig;\n /** Fresh registration-only snapshot, present only when the Web connector is configured. */\n readonly namedApiRoutes?: readonly NamedApiRoute[];\n};\n\n/**\n * The esbuild options a contributor may patch.\n *\n * Deliberately a narrow `Pick`, not `Partial<BuildOptions>`: `entryPoints`,\n * `tsconfig` and `tsconfigRaw` stay forbidden here for the same reason they\n * are forbidden in the user's `build` block — they re-compile every file in\n * the bundle, workspace packages included (see `ForbiddenBuildOptionError`).\n * A contributor reaching them would reintroduce the failure the user-level\n * guard exists to prevent.\n */\nexport type ConnectorEsbuildPatch = Pick<\n BuildOptions,\n \"jsx\" | \"jsxImportSource\" | \"define\" | \"external\" | \"loader\" | \"plugins\"\n>;\n\n/**\n * What `generate` may hand back to the builder.\n */\nexport type ConnectorBuildGenerateResult = {\n /**\n * Lines appended verbatim to section 4 of the generated entry, AFTER the\n * `./routes` import — page registration must see the routes app code\n * collected, mirroring dev's ordering.\n *\n * @example ['await import(\"./pages\");']\n */\n entryImports?: string[];\n /** Merged into the esbuild call: defaults < this patch < user `build` */\n esbuild?: ConnectorEsbuildPatch;\n};\n\n/**\n * A connector's build-time half: two hooks, both awaited, drained in\n * `connectors` array order. A hook that throws fails the build.\n *\n * **This type is closed on purpose**: exactly these two optional function properties and ZERO data-bearing\n * fields. There is deliberately no property where a Vite/babel plugin\n * instance, an alias map or a compiled pipeline could live, so\n * `build: { plugins: [...] }` is a type error rather than something a\n * reviewer has to catch. Anything heavy must be constructed INSIDE a hook,\n * after that hook dynamically imports it — that is what keeps a connector's\n * static import graph out of the runtime closure. The drain mirrors this at\n * read time by rejecting unknown enumerable keys (`assertClosedContribution`):\n * the type narrows, the runtime asserts.\n */\nexport type ConnectorBuildContribution = {\n /**\n * Write files into `ctx.productionDir` and/or patch the bundle.\n *\n * Runs after the generated-imports check and before esbuild bundles.\n */\n generate?(\n context: ConnectorBuildContext,\n ): Promise<ConnectorBuildGenerateResult | void> | ConnectorBuildGenerateResult | void;\n\n /**\n * Produce artifacts esbuild does not — e.g. a client bundle.\n *\n * Runs after esbuild and before `.warlock/production` is removed.\n */\n emit?(context: ConnectorBuildContext): Promise<void> | void;\n};\n\nexport type ConnectorName =\n | \"logger\"\n | \"mailer\"\n | \"http\"\n | \"database\"\n | \"cache\"\n | \"storage\"\n | \"herald\"\n | \"socket\"\n | \"notifications\"\n | \"access\"\n | \"ai\"\n | (string & {});\n\n/**\n * When a connector boots relative to user code (main, routes, events).\n *\n * - `Early`: before user code is imported. For services user code\n * needs at import time (database, cache, logger, storage, mailer).\n * - `Late`: after user code is imported. For services that bind to\n * things user code registered (http scans the router; socket\n * depends on http's instance).\n */\nexport enum ConnectorLifecyclePhase {\n Early = \"early\",\n Late = \"late\",\n}\n\n/**\n * Connector priority constants\n */\nexport enum ConnectorPriority {\n LOGGER = 0,\n MAILER = 1,\n DATABASE = 2,\n COMMUNICATOR = 3,\n CACHE = 4,\n HTTP = 5,\n STORAGE = 6,\n SOCKET = 7,\n NOTIFICATIONS = 8,\n ACCESS = 9,\n AI = 10,\n}\n"],"mappings":";;;;;;;;;;AA+KA,IAAY,0BAAL;CACL;CACA;;AACF;;;;AAKA,IAAY,oBAAL;CACL;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;;AACF"}
|
|
@@ -5,27 +5,30 @@ import { Model } from "@warlock.js/cascade";
|
|
|
5
5
|
declare const schema: import("@warlock.js/seal").ObjectValidator<{
|
|
6
6
|
module: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
|
|
7
7
|
action: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
8
|
+
content: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
|
|
9
|
+
stack: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string> & {
|
|
10
|
+
isOptional: true;
|
|
11
|
+
};
|
|
12
12
|
level: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
|
|
13
|
+
date: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
|
|
13
14
|
}> & import("@warlock.js/seal").StandardSchemaV1<import("@warlock.js/seal").InferInputObjectShape<{
|
|
14
15
|
module: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
|
|
15
16
|
action: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
17
|
+
content: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
|
|
18
|
+
stack: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string> & {
|
|
19
|
+
isOptional: true;
|
|
20
|
+
};
|
|
20
21
|
level: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
|
|
22
|
+
date: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
|
|
21
23
|
}>, import("@warlock.js/seal").InferInputObjectShape<{
|
|
22
24
|
module: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
|
|
23
25
|
action: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
26
|
+
content: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
|
|
27
|
+
stack: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string> & {
|
|
28
|
+
isOptional: true;
|
|
29
|
+
};
|
|
28
30
|
level: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
|
|
31
|
+
date: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
|
|
29
32
|
}>>;
|
|
30
33
|
type LogSchema = Infer.Output<typeof schema>;
|
|
31
34
|
declare class DatabaseLogModel extends Model<LogSchema> {
|
|
@@ -39,28 +42,48 @@ declare class DatabaseLogModel extends Model<LogSchema> {
|
|
|
39
42
|
static schema: import("@warlock.js/seal").ObjectValidator<{
|
|
40
43
|
module: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
|
|
41
44
|
action: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
45
|
+
content: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
|
|
46
|
+
stack: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string> & {
|
|
47
|
+
isOptional: true;
|
|
48
|
+
};
|
|
46
49
|
level: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
|
|
50
|
+
date: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
|
|
47
51
|
}> & import("@warlock.js/seal").StandardSchemaV1<import("@warlock.js/seal").InferInputObjectShape<{
|
|
48
52
|
module: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
|
|
49
53
|
action: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
+
content: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
|
|
55
|
+
stack: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string> & {
|
|
56
|
+
isOptional: true;
|
|
57
|
+
};
|
|
54
58
|
level: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
|
|
59
|
+
date: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
|
|
55
60
|
}>, import("@warlock.js/seal").InferInputObjectShape<{
|
|
56
61
|
module: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
|
|
57
62
|
action: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
63
|
+
content: import("@warlock.js/seal").AnyValidator & import("@warlock.js/seal").StandardSchemaV1<any, any>;
|
|
64
|
+
stack: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string> & {
|
|
65
|
+
isOptional: true;
|
|
66
|
+
};
|
|
62
67
|
level: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
|
|
68
|
+
date: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
|
|
63
69
|
}>>;
|
|
70
|
+
/**
|
|
71
|
+
* Legacy rows stored `message`/`trace`; map them to `content`/`stack`
|
|
72
|
+
* when the new fields are absent.
|
|
73
|
+
*/
|
|
74
|
+
private static readonly legacyFields;
|
|
75
|
+
/**
|
|
76
|
+
* Log content, falling back to the legacy `message` field
|
|
77
|
+
*/
|
|
78
|
+
get content(): unknown;
|
|
79
|
+
/**
|
|
80
|
+
* Error stack, falling back to the legacy `trace` field
|
|
81
|
+
*/
|
|
82
|
+
get stack(): string | undefined;
|
|
83
|
+
/**
|
|
84
|
+
* {@inheritdoc}
|
|
85
|
+
*/
|
|
86
|
+
get(field: string, defaultValue?: any): any;
|
|
64
87
|
}
|
|
65
88
|
//#endregion
|
|
66
89
|
export { DatabaseLogModel };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"database-log.d.mts","names":[],"sources":["../../../../../../../../../core/src/database/models/database-log/database-log.ts"],"mappings":";;;;cAGM,MAAA,6BAAM,eAAA
|
|
1
|
+
{"version":3,"file":"database-log.d.mts","names":[],"sources":["../../../../../../../../../core/src/database/models/database-log/database-log.ts"],"mappings":";;;;cAGM,MAAA,6BAAM,eAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;KASP,SAAA,GAAY,KAAA,CAAM,MAAM,QAAQ,MAAA;AAAA,cAExB,gBAAA,SAAyB,KAAK,CAAC,SAAA;;;;SAI5B,KAAA;;;;SAKA,MAAA,6BAAM,eAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAMI,YAAA;EAjBZ;;;EAAA,IAyBD,OAAA;EAzBU;;;EAAA,IAgCV,KAAA;EA9BA;;;EAqCJ,GAAA,CAAI,KAAA,UAAe,YAAA;AAAA"}
|
|
@@ -5,17 +5,49 @@ import { Model } from "@warlock.js/cascade";
|
|
|
5
5
|
const schema = v.object({
|
|
6
6
|
module: v.string(),
|
|
7
7
|
action: v.string(),
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
level: v.string()
|
|
8
|
+
content: v.any(),
|
|
9
|
+
stack: v.string().optional(),
|
|
10
|
+
level: v.string(),
|
|
11
|
+
date: v.string()
|
|
11
12
|
});
|
|
12
|
-
var DatabaseLogModel = class extends Model {
|
|
13
|
+
var DatabaseLogModel = class DatabaseLogModel extends Model {
|
|
13
14
|
static {
|
|
14
15
|
this.table = "logs";
|
|
15
16
|
}
|
|
16
17
|
static {
|
|
17
18
|
this.schema = schema;
|
|
18
19
|
}
|
|
20
|
+
static {
|
|
21
|
+
this.legacyFields = {
|
|
22
|
+
content: "message",
|
|
23
|
+
stack: "trace"
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Log content, falling back to the legacy `message` field
|
|
28
|
+
*/
|
|
29
|
+
get content() {
|
|
30
|
+
return this.get("content");
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Error stack, falling back to the legacy `trace` field
|
|
34
|
+
*/
|
|
35
|
+
get stack() {
|
|
36
|
+
return this.get("stack");
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* {@inheritdoc}
|
|
40
|
+
*/
|
|
41
|
+
get(field, defaultValue) {
|
|
42
|
+
const value = super.get(field, void 0);
|
|
43
|
+
if (value !== void 0 && value !== null) return value;
|
|
44
|
+
const legacy = DatabaseLogModel.legacyFields[field];
|
|
45
|
+
if (legacy) {
|
|
46
|
+
const legacyValue = super.get(legacy, void 0);
|
|
47
|
+
if (legacyValue !== void 0 && legacyValue !== null) return legacyValue;
|
|
48
|
+
}
|
|
49
|
+
return value ?? defaultValue;
|
|
50
|
+
}
|
|
19
51
|
};
|
|
20
52
|
|
|
21
53
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"database-log.mjs","names":[],"sources":["../../../../../../../../../core/src/database/models/database-log/database-log.ts"],"sourcesContent":["import { Model } from \"@warlock.js/cascade\";\r\nimport { type Infer, v } from \"@warlock.js/seal\";\r\n\r\nconst schema = v.object({\r\n module: v.string(),\r\n action: v.string(),\r\n
|
|
1
|
+
{"version":3,"file":"database-log.mjs","names":[],"sources":["../../../../../../../../../core/src/database/models/database-log/database-log.ts"],"sourcesContent":["import { Model } from \"@warlock.js/cascade\";\r\nimport { type Infer, v } from \"@warlock.js/seal\";\r\n\r\nconst schema = v.object({\r\n module: v.string(),\r\n action: v.string(),\r\n content: v.any(),\r\n stack: v.string().optional(),\r\n level: v.string(),\r\n date: v.string(),\r\n});\r\n\r\ntype LogSchema = Infer.Output<typeof schema>;\r\n\r\nexport class DatabaseLogModel extends Model<LogSchema> {\r\n /**\r\n * Table name\r\n */\r\n public static table = \"logs\";\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public static schema = schema;\r\n\r\n /**\r\n * Legacy rows stored `message`/`trace`; map them to `content`/`stack`\r\n * when the new fields are absent.\r\n */\r\n private static readonly legacyFields: Record<string, string> = {\r\n content: \"message\",\r\n stack: \"trace\",\r\n };\r\n\r\n /**\r\n * Log content, falling back to the legacy `message` field\r\n */\r\n public get content(): unknown {\r\n return this.get(\"content\");\r\n }\r\n\r\n /**\r\n * Error stack, falling back to the legacy `trace` field\r\n */\r\n public get stack(): string | undefined {\r\n return this.get(\"stack\");\r\n }\r\n\r\n /**\r\n * {@inheritdoc}\r\n */\r\n public get(field: string, defaultValue?: any): any {\r\n const value = super.get(field, undefined as any);\r\n\r\n if (value !== undefined && value !== null) return value;\r\n\r\n const legacy = DatabaseLogModel.legacyFields[field];\r\n\r\n if (legacy) {\r\n const legacyValue = super.get(legacy, undefined as any);\r\n\r\n if (legacyValue !== undefined && legacyValue !== null) return legacyValue;\r\n }\r\n\r\n return value ?? defaultValue;\r\n }\r\n}\r\n"],"mappings":";;;;AAGA,MAAM,SAAS,EAAE,OAAO;CACtB,QAAQ,EAAE,OAAO;CACjB,QAAQ,EAAE,OAAO;CACjB,SAAS,EAAE,IAAI;CACf,OAAO,EAAE,OAAO,CAAC,CAAC,SAAS;CAC3B,OAAO,EAAE,OAAO;CAChB,MAAM,EAAE,OAAO;AACjB,CAAC;AAID,IAAa,mBAAb,MAAa,yBAAyB,MAAiB;;eAI/B;;;gBAKC;;;sBAMwC;GAC7D,SAAS;GACT,OAAO;EACT;;;;;CAKA,IAAW,UAAmB;EAC5B,OAAO,KAAK,IAAI,SAAS;CAC3B;;;;CAKA,IAAW,QAA4B;EACrC,OAAO,KAAK,IAAI,OAAO;CACzB;;;;CAKA,AAAO,IAAI,OAAe,cAAyB;EACjD,MAAM,QAAQ,MAAM,IAAI,OAAO,MAAgB;EAE/C,IAAI,UAAU,UAAa,UAAU,MAAM,OAAO;EAElD,MAAM,SAAS,iBAAiB,aAAa;EAE7C,IAAI,QAAQ;GACV,MAAM,cAAc,MAAM,IAAI,QAAQ,MAAgB;GAEtD,IAAI,gBAAgB,UAAa,gBAAgB,MAAM,OAAO;EAChE;EAEA,OAAO,SAAS;CAClB;AACF"}
|
|
@@ -9,9 +9,17 @@ import { dataSourceRegistry, transaction } from "@warlock.js/cascade";
|
|
|
9
9
|
import { colors } from "@mongez/copper";
|
|
10
10
|
|
|
11
11
|
//#region ../core/src/database/seed-command-action.ts
|
|
12
|
+
/**
|
|
13
|
+
* Truncate every table except cascade's migrations tracking table — wiping it
|
|
14
|
+
* would make the next `warlock migrate` re-run every migration.
|
|
15
|
+
*/
|
|
12
16
|
async function clearAllTables(datasource) {
|
|
17
|
+
const migrationsTable = datasource.migrations?.table ?? "_migrations";
|
|
13
18
|
const tables = await datasource.driver.blueprint.listTables();
|
|
14
|
-
for (const table of tables)
|
|
19
|
+
for (const table of tables) {
|
|
20
|
+
if (table === migrationsTable) continue;
|
|
21
|
+
await datasource.driver.truncateTable(table, { cascade: true });
|
|
22
|
+
}
|
|
15
23
|
}
|
|
16
24
|
/**
|
|
17
25
|
* Undo seeded data by deleting the records a seeder tracked via `track`.
|
|
@@ -68,7 +76,6 @@ async function seedCommandAction(options, overrides) {
|
|
|
68
76
|
console.log(`🗑️ Dropped ${colors.redBright(deleted)} tracked record(s) for ${scope}; matching seeds-log entries reset.`);
|
|
69
77
|
return;
|
|
70
78
|
}
|
|
71
|
-
if (fresh) await clearAllTables(datasource);
|
|
72
79
|
if (list) {
|
|
73
80
|
const seedFiles = await listSeedsFiles();
|
|
74
81
|
if (seedFiles.length === 0) {
|
|
@@ -88,6 +95,7 @@ async function seedCommandAction(options, overrides) {
|
|
|
88
95
|
console.log(`Total Seeds: ${colors.blueBright(seeds.length)}, enabled: ${colors.greenBright(seeds.filter((seed) => seed.enabled !== false).length)}, disabled: ${colors.redBright(seeds.filter((seed) => seed.enabled === false).length)}`);
|
|
89
96
|
return;
|
|
90
97
|
}
|
|
98
|
+
if (fresh) await clearAllTables(datasource);
|
|
91
99
|
const seeds = path ? [await loadSeedFile(Path.toAbsolute(path))] : await listSeedsFiles();
|
|
92
100
|
const seedersManager = new SeedersManager({ clock: overrides?.clock });
|
|
93
101
|
seedersManager.register(...seeds);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"seed-command-action.mjs","names":["runInTransaction"],"sources":["../../../../../../../core/src/database/seed-command-action.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport {\r\n DataSource,\r\n dataSourceRegistry,\r\n transaction as runInTransaction,\r\n} from \"@warlock.js/cascade\";\r\nimport type { CommandActionData } from \"../commands/types\";\r\nimport { filesOrchestrator } from \"../dev-server/files-orchestrator\";\r\nimport { Path } from \"../utils/normalized-path\";\r\nimport { getFilesFromDirectory } from \"../dev-server/utils\";\r\nimport { srcPath } from \"../utils\";\r\nimport type { Seeder } from \"./seeds/seeder\";\r\nimport { SeedersManager } from \"./seeds/seeders.manager\";\r\nimport type { SeedClock, SeedRecordRef } from \"./seeds/types\";\r\nimport { seedRecordsTableName, seedsTableName } from \"./seeds/utils\";\r\n\r\n/**\r\n * Programmatic-only overrides for {@link seedCommandAction}.\r\n *\r\n * Not derived from CLI flags (the CLI passes only {@link CommandActionData}),\r\n * so a back-fill script or a test can inject behaviour the command line can't\r\n * express.\r\n */\r\nexport type SeedCommandOverrides = {\r\n /**\r\n * Clock the run reads for the `now` handed to each seeder and for the\r\n * seeds-log metadata timestamps. Defaults to `() => new Date()`. Forwarded to\r\n * {@link SeedersManager}'s `clock` option.\r\n */\r\n clock?: SeedClock;\r\n};\r\n\r\
|
|
1
|
+
{"version":3,"file":"seed-command-action.mjs","names":["runInTransaction"],"sources":["../../../../../../../core/src/database/seed-command-action.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport {\r\n DataSource,\r\n dataSourceRegistry,\r\n transaction as runInTransaction,\r\n} from \"@warlock.js/cascade\";\r\nimport type { CommandActionData } from \"../commands/types\";\r\nimport { filesOrchestrator } from \"../dev-server/files-orchestrator\";\r\nimport { Path } from \"../utils/normalized-path\";\r\nimport { getFilesFromDirectory } from \"../dev-server/utils\";\r\nimport { srcPath } from \"../utils\";\r\nimport type { Seeder } from \"./seeds/seeder\";\r\nimport { SeedersManager } from \"./seeds/seeders.manager\";\r\nimport type { SeedClock, SeedRecordRef } from \"./seeds/types\";\r\nimport { seedRecordsTableName, seedsTableName } from \"./seeds/utils\";\r\n\r\n/**\r\n * Programmatic-only overrides for {@link seedCommandAction}.\r\n *\r\n * Not derived from CLI flags (the CLI passes only {@link CommandActionData}),\r\n * so a back-fill script or a test can inject behaviour the command line can't\r\n * express.\r\n */\r\nexport type SeedCommandOverrides = {\r\n /**\r\n * Clock the run reads for the `now` handed to each seeder and for the\r\n * seeds-log metadata timestamps. Defaults to `() => new Date()`. Forwarded to\r\n * {@link SeedersManager}'s `clock` option.\r\n */\r\n clock?: SeedClock;\r\n};\r\n\r\n/**\r\n * Truncate every table except cascade's migrations tracking table — wiping it\r\n * would make the next `warlock migrate` re-run every migration.\r\n */\r\nexport async function clearAllTables(datasource: DataSource) {\r\n const migrationsTable = datasource.migrations?.table ?? \"_migrations\";\r\n const tables = await datasource.driver.blueprint.listTables();\r\n\r\n for (const table of tables) {\r\n if (table === migrationsTable) continue;\r\n\r\n await datasource.driver.truncateTable(table, { cascade: true });\r\n }\r\n}\r\n\r\ntype SeedRecordRow = SeedRecordRef & { id: number };\r\n\r\n/**\r\n * Undo seeded data by deleting the records a seeder tracked via `track`.\r\n *\r\n * Reads `seed_records` (all seeders, or a single `seederName`), then deletes\r\n * the referenced records in REVERSE seed-order and, within each seed, in\r\n * reverse insertion-order. Insertion id is strictly increasing across the\r\n * whole run, so deleting by descending `id` satisfies both at once. The whole\r\n * operation runs in a single transaction, and the matching seeds-log rows are\r\n * reset afterwards so `once: true` seeds re-run on the next `warlock seed`.\r\n *\r\n * @returns the number of tracked records deleted.\r\n */\r\nexport async function dropSeedRecords(\r\n datasource: DataSource,\r\n seederName?: string,\r\n): Promise<number> {\r\n const driver = datasource.driver;\r\n\r\n if (!(await driver.blueprint.tableExists(seedRecordsTableName))) {\r\n return 0;\r\n }\r\n\r\n const query = driver.queryBuilder<SeedRecordRow>(seedRecordsTableName);\r\n\r\n if (seederName) {\r\n query.where(\"seeder\", seederName);\r\n }\r\n\r\n const rows = await query.get();\r\n\r\n if (rows.length === 0) {\r\n return 0;\r\n }\r\n\r\n // Descending insertion id == reverse seed-order + reverse within-seed order.\r\n const ordered = [...rows].sort((a, b) => b.id - a.id);\r\n\r\n await runInTransaction(async () => {\r\n for (const row of ordered) {\r\n await driver.deleteMany(row.table, { id: row.recordId });\r\n }\r\n\r\n // Clear the tracked refs we just acted on.\r\n if (seederName) {\r\n await driver.deleteMany(seedRecordsTableName, { seeder: seederName });\r\n } else {\r\n await driver.deleteMany(seedRecordsTableName, {});\r\n }\r\n\r\n // Reset the seeds-log rows so `once: true` seeds re-run.\r\n const seederNames = seederName ? [seederName] : [...new Set(ordered.map((row) => row.seeder))];\r\n\r\n for (const name of seederNames) {\r\n await driver.deleteMany(seedsTableName, { name });\r\n }\r\n });\r\n\r\n return ordered.length;\r\n}\r\n\r\n/**\r\n * Run database seeds.\r\n *\r\n * @example\r\n * ```ts\r\n * import { seedCommandAction } from \"@warlock.js/core\";\r\n *\r\n * await seedCommandAction({\r\n * command: \"seed\",\r\n * options: { fresh: true, list: false }\r\n * });\r\n * ```\r\n *\r\n * @param overrides Programmatic-only overrides (e.g. an injected `clock`); the\r\n * CLI never passes this, so the command-line signature stays unchanged.\r\n */\r\nexport async function seedCommandAction(\r\n options: CommandActionData,\r\n overrides?: SeedCommandOverrides,\r\n) {\r\n const { path, fresh, transaction, list, drop } = options.options;\r\n\r\n const datasource = dataSourceRegistry.get();\r\n\r\n if (drop) {\r\n // `--drop` (boolean) undoes every tracked record; `--drop=<name>` scopes to\r\n // a single seeder.\r\n const seederName = typeof drop === \"string\" ? drop : undefined;\r\n\r\n const deleted = await dropSeedRecords(datasource, seederName);\r\n\r\n const scope = seederName ? colors.cyan(seederName) : \"all seeders\";\r\n console.log(\r\n `🗑️ Dropped ${colors.redBright(deleted)} tracked record(s) for ${scope}; matching seeds-log entries reset.`,\r\n );\r\n return;\r\n }\r\n\r\n if (list) {\r\n const seedFiles = await listSeedsFiles();\r\n\r\n if (seedFiles.length === 0) {\r\n console.log(\"No seeds found.\");\r\n return;\r\n }\r\n\r\n const seedersManager = new SeedersManager();\r\n\r\n seedersManager.register(...seedFiles);\r\n\r\n const seeds = seedersManager.sort().seeders.map((seed) => {\r\n return {\r\n name: seed.name,\r\n order: seed.order,\r\n enabled: seed.enabled,\r\n };\r\n });\r\n\r\n console.table(seeds);\r\n\r\n console.log(\r\n `Total Seeds: ${colors.blueBright(seeds.length)}, enabled: ${colors.greenBright(seeds.filter((seed) => seed.enabled !== false).length)}, disabled: ${colors.redBright(seeds.filter((seed) => seed.enabled === false).length)}`,\r\n );\r\n return;\r\n }\r\n\r\n if (fresh) {\r\n await clearAllTables(datasource);\r\n }\r\n\r\n const seeds = path\r\n ? [await loadSeedFile(Path.toAbsolute(path as string))]\r\n : await listSeedsFiles();\r\n\r\n const seedersManager = new SeedersManager({ clock: overrides?.clock });\r\n\r\n seedersManager.register(...seeds);\r\n\r\n await seedersManager.run(transaction as boolean);\r\n}\r\n\r\nasync function listSeedsFiles() {\r\n const seedsFiles = await getFilesFromDirectory(srcPath(\"app\"), \"*/seeds/*.ts\");\r\n\r\n const seeds = [];\r\n\r\n for (const seedFile of seedsFiles) {\r\n const seed = await loadSeedFile(seedFile);\r\n seeds.push(seed);\r\n }\r\n\r\n return seeds;\r\n}\r\n\r\nasync function loadSeedFile(absPath: string): Promise<Seeder> {\r\n const relativePath = Path.toRelative(absPath);\r\n const seedImport = await filesOrchestrator.load<{ default: Seeder }>(relativePath);\r\n\r\n if (!seedImport || !seedImport.default) {\r\n throw new Error(`Seeder file ${relativePath} does not export a default seeder.`);\r\n }\r\n\r\n return seedImport.default;\r\n}\r\n"],"mappings":"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"}
|
|
@@ -28,11 +28,14 @@ import { fileExistsAsync, unlinkAsync } from "@warlock.js/fs";
|
|
|
28
28
|
var DevelopmentServer = class {
|
|
29
29
|
constructor(options = {}) {
|
|
30
30
|
this.running = false;
|
|
31
|
+
this.connectorsStarted = false;
|
|
32
|
+
this.reloadQueue = Promise.resolve();
|
|
31
33
|
this.options = options;
|
|
32
34
|
devLogSection("Starting Development Server...");
|
|
33
35
|
}
|
|
34
36
|
async start() {
|
|
35
37
|
try {
|
|
38
|
+
this.connectorsStarted = true;
|
|
36
39
|
const startedAt = performance.now();
|
|
37
40
|
if (this.options.fresh && await fileExistsAsync(MANIFEST_PATH)) {
|
|
38
41
|
await unlinkAsync(MANIFEST_PATH);
|
|
@@ -61,6 +64,7 @@ var DevelopmentServer = class {
|
|
|
61
64
|
bootDurationMs: duration
|
|
62
65
|
});
|
|
63
66
|
printReadyBlock(duration);
|
|
67
|
+
process.send?.("warlock:booted");
|
|
64
68
|
const devServerConfig = await warlockConfigManager.get("devServer");
|
|
65
69
|
const generateTypings = this.options.generateTypings ?? devServerConfig?.generateTypings ?? true;
|
|
66
70
|
const healthCheckers = this.options.healthCheckers ?? devServerConfig?.healthCheckers ?? true;
|
|
@@ -81,14 +85,16 @@ var DevelopmentServer = class {
|
|
|
81
85
|
for (const file of filesOrchestrator.files.values()) if (file.type && discoveryTypes.includes(file.type)) await filesOrchestrator.moduleLoader.loadModule(file, file.type);
|
|
82
86
|
}
|
|
83
87
|
setupEventListeners() {
|
|
84
|
-
events.on("dev-server:batch-complete", (batch) =>
|
|
88
|
+
events.on("dev-server:batch-complete", (batch) => {
|
|
89
|
+
this.reloadQueue = this.reloadQueue.then(() => this.handleBatchComplete(batch)).catch((error) => devServeLog(colors.redBright(`Batch reload failed: ${error}`)));
|
|
90
|
+
});
|
|
85
91
|
}
|
|
86
92
|
async handleBatchComplete(batch) {
|
|
87
93
|
if (!this.running || !this.layerExecutor) return;
|
|
88
94
|
const restartTrigger = this.findRestartTrigger(batch);
|
|
89
95
|
if (restartTrigger) await this.restartForConfigChange(restartTrigger);
|
|
90
96
|
if (batch.added.length + batch.changed.length + batch.deleted.length === 0) return;
|
|
91
|
-
const codeFiles = [...batch.added, ...batch.changed].filter((p) => !isEnvPath(p) && p !== "warlock.config.ts");
|
|
97
|
+
const codeFiles = [...batch.added, ...batch.changed].filter((p) => !isEnvPath(p) && p !== "warlock.config.ts" && !batch.restartTriggers?.includes(p));
|
|
92
98
|
try {
|
|
93
99
|
await this.layerExecutor.executeBatchReload(codeFiles, filesOrchestrator.getFiles(), batch.deleted, batch.changed, batch.timings);
|
|
94
100
|
await publishCurrentRouteTypes(process.cwd(), router.getNamedApiRoutes());
|
|
@@ -108,7 +114,7 @@ var DevelopmentServer = class {
|
|
|
108
114
|
*/
|
|
109
115
|
findRestartTrigger(batch) {
|
|
110
116
|
if (batch.changed.includes("warlock.config.ts")) return "warlock.config.ts";
|
|
111
|
-
return [...batch.added, ...batch.changed].find(isEnvPath);
|
|
117
|
+
return [...batch.added, ...batch.changed].find(isEnvPath) ?? batch.restartTriggers?.[0];
|
|
112
118
|
}
|
|
113
119
|
/**
|
|
114
120
|
* Restart so a changed boot-time file actually takes effect.
|
|
@@ -129,9 +135,10 @@ var DevelopmentServer = class {
|
|
|
129
135
|
}
|
|
130
136
|
async shutdown() {
|
|
131
137
|
devServerShortcuts.release();
|
|
132
|
-
if (!this.running) return;
|
|
138
|
+
if (!this.running && !this.connectorsStarted) return;
|
|
133
139
|
devServeLog(colors.redBright("Shutting down Development Server..."));
|
|
134
140
|
this.running = false;
|
|
141
|
+
this.connectorsStarted = false;
|
|
135
142
|
await connectorsManager.shutdown();
|
|
136
143
|
devServeLog(colors.greenBright("Development Server stopped"));
|
|
137
144
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"development-server.mjs","names":[],"sources":["../../../../../../../core/src/dev-server/development-server.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport events from \"@mongez/events\";\r\nimport { fileExistsAsync, unlinkAsync } from \"@warlock.js/fs\";\r\nimport { Application } from \"../application\";\r\nimport { connectorsManager } from \"../connectors/connectors-manager\";\r\nimport { ConnectorLifecyclePhase } from \"../connectors/types\";\r\nimport { router } from \"../router/router\";\r\nimport { warlockConfigManager } from \"../warlock-config\";\r\nimport { BootPreconditionError } from \"./boot-precondition-error\";\r\nimport { devLogInfo, devLogSection, devLogWarn, devServeLog } from \"./dev-logger\";\r\nimport { filesOrchestrator } from \"./files-orchestrator\";\r\nimport { MANIFEST_PATH } from \"./flags\";\r\nimport type { IncomingReloadTimings } from \"./layer-executor\";\r\nimport { LayerExecutor } from \"./layer-executor\";\r\nimport { printReadyBlock } from \"./ready-block\";\r\nimport { restartDevServer } from \"./restart-dev-server\";\r\nimport { publishCurrentRouteTypes } from \"./route-types-publisher\";\r\nimport { devServerShortcuts } from \"./shortcuts\";\r\nimport type { StartDevServerOptions } from \"./start-development-server\";\r\nimport { typeGenerator } from \"./type-generator\";\r\n\r\ntype Batch = {\r\n added: string[];\r\n changed: string[];\r\n deleted: string[];\r\n timings?: IncomingReloadTimings;\r\n};\r\n\r\n/**\r\n * Top-level coordinator for `warlock dev`. Wires the file orchestrator, the\r\n * connectors, and the layer executor together, and listens for the watcher's\r\n * batched events to drive HMR.\r\n */\r\nexport class DevelopmentServer {\r\n private layerExecutor?: LayerExecutor;\r\n private running = false;\r\n private readonly options: StartDevServerOptions;\r\n\r\n public constructor(options: StartDevServerOptions = {}) {\r\n this.options = options;\r\n devLogSection(\"Starting Development Server...\");\r\n }\r\n\r\n public async start(): Promise<void> {\r\n try {\r\n const startedAt = performance.now();\r\n\r\n // --fresh deletes the manifest so reconciliation re-parses every file\r\n // from disk. Transpile caching is owned by the loader hook (in-memory).\r\n if (this.options.fresh && (await fileExistsAsync(MANIFEST_PATH))) {\r\n await unlinkAsync(MANIFEST_PATH);\r\n devServeLog(colors.cyanBright(\"Cleared manifest (--fresh)\"));\r\n }\r\n\r\n await filesOrchestrator.init();\r\n await filesOrchestrator.initializeAll();\r\n await filesOrchestrator.watchFiles();\r\n\r\n filesOrchestrator.specialFilesCollector.collect(filesOrchestrator.getFiles());\r\n\r\n this.setupEventListeners();\r\n\r\n // Decorator-driven registries (models, etc.) must be populated before\r\n // routes/services can resolve symbols by name.\r\n await this.autoDiscoverFiles();\r\n\r\n await filesOrchestrator.moduleLoader.loadAll();\r\n\r\n // A rejecting validator (Application.onValidateBoot) must abort boot\r\n // before late-phase connectors bind a port — this is what makes the\r\n // hook actually prevent serving instead of merely being defined.\r\n //\r\n // Wrapped and re-thrown as a BootPreconditionError: a rejected\r\n // validator is, by definition, a precondition whose cause cannot\r\n // change because the worker tried again — the supervisor must not\r\n // restart into the same rejection. See boot-precondition-error.ts.\r\n try {\r\n await Application.runStartupValidators();\r\n } catch (error) {\r\n throw new BootPreconditionError((error as Error).message, { cause: error });\r\n }\r\n\r\n // Late-phase connectors (http, socket) bind after app code has\r\n // registered routes/listeners.\r\n await connectorsManager.startPhase(ConnectorLifecyclePhase.Late);\r\n\r\n // Web installs its page table from its late connector. Generate only\r\n // after that installation so `href()` and the declaration table agree.\r\n await publishCurrentRouteTypes(process.cwd(), router.getNamedApiRoutes());\r\n\r\n this.layerExecutor = new LayerExecutor(\r\n filesOrchestrator.getDependencyGraph(),\r\n filesOrchestrator.specialFilesCollector,\r\n filesOrchestrator.moduleLoader,\r\n (absolutePath) => filesOrchestrator.bumpVersion(absolutePath),\r\n () => filesOrchestrator.flushVersionBumps(),\r\n );\r\n\r\n this.running = true;\r\n\r\n const duration = performance.now() - startedAt;\r\n\r\n // App modules are loaded and both connector phases are active — signal a\r\n // complete boot so `Application.onceBooted(...)` listeners fire.\r\n Application.markBooted({\r\n environment: Application.environment,\r\n runtimeStrategy: Application.runtimeStrategy,\r\n bootDurationMs: duration,\r\n });\r\n\r\n // ONE block, AFTER the boot it summarises, replacing the scattered\r\n // \"N route(s) registered\" / \"Server ready at …\" / \"ready in …\" lines that\r\n // used to arrive in completion order. Warnings logged during boot sit\r\n // above it and stay there — see `ready-block.ts`.\r\n printReadyBlock(duration);\r\n\r\n // Precedence: explicit CLI option > devServer.* config > default.\r\n const devServerConfig = await warlockConfigManager.get(\"devServer\");\r\n const generateTypings =\r\n this.options.generateTypings ?? devServerConfig?.generateTypings ?? true;\r\n const healthCheckers = this.options.healthCheckers ?? devServerConfig?.healthCheckers ?? true;\r\n\r\n if (generateTypings) await typeGenerator.executeGenerateAllCommand();\r\n\r\n if (healthCheckers) {\r\n filesOrchestrator.startCheckingHealth(healthCheckers === true ? undefined : healthCheckers);\r\n }\r\n } catch (error) {\r\n devServeLog(colors.redBright(`Failed to start Development Server: ${error}`));\r\n await this.shutdown();\r\n throw error;\r\n }\r\n }\r\n\r\n /**\r\n * Eagerly import files whose decorators populate global registries so any\r\n * symbol-by-name resolution later in boot finds them.\r\n */\r\n private async autoDiscoverFiles(): Promise<void> {\r\n const discoveryTypes = [\"model\"] as const;\r\n for (const file of filesOrchestrator.files.values()) {\r\n if (file.type && (discoveryTypes as readonly string[]).includes(file.type)) {\r\n await filesOrchestrator.moduleLoader.loadModule(file, file.type);\r\n }\r\n }\r\n }\r\n\r\n private setupEventListeners(): void {\r\n events.on(\"dev-server:batch-complete\", (batch: Batch) => this.handleBatchComplete(batch));\r\n }\r\n\r\n private async handleBatchComplete(batch: Batch): Promise<void> {\r\n if (!this.running || !this.layerExecutor) return;\r\n\r\n // warlock.config.ts holds settings read at boot (CLI commands, build\r\n // options, watch patterns, scheduled jobs) and .env feeds every config\r\n // that reads it. Neither can be hot-reloaded without leaving running\r\n // services configured with stale values, so the only honest response is\r\n // a full restart.\r\n const restartTrigger = this.findRestartTrigger(batch);\r\n\r\n if (restartTrigger) {\r\n // On success this never comes back — the process exits and the\r\n // supervisor replaces it. Reaching the next line means the restart was\r\n // declined by config or wasn't possible, so we fall through and still\r\n // hot-reload whatever ordinary code shared the batch.\r\n await this.restartForConfigChange(restartTrigger);\r\n }\r\n\r\n // No-op saves (an editor that fsyncs without a content write) are already\r\n // filtered upstream by the file hash in FileEventHandler, so batch.changed\r\n // only contains genuinely-changed paths by the time it reaches here.\r\n\r\n const total = batch.added.length + batch.changed.length + batch.deleted.length;\r\n if (total === 0) return;\r\n\r\n // Boot-time files still ride along in `changed` when a restart was\r\n // declined (`restartOnConfigChange: false`) or wasn't possible — they are\r\n // never hot-reloaded, so keep them out of the reload set either way.\r\n const codeFiles = [...batch.added, ...batch.changed].filter(\r\n (p) => !isEnvPath(p) && p !== \"warlock.config.ts\",\r\n );\r\n\r\n try {\r\n await this.layerExecutor.executeBatchReload(\r\n codeFiles,\r\n filesOrchestrator.getFiles(),\r\n batch.deleted,\r\n batch.changed,\r\n batch.timings,\r\n );\r\n\r\n // `executeBatchReload` includes route re-imports and affected connector\r\n // restarts. Publishing earlier could freeze a partial page/API view.\r\n // This also refreshes deletion-only batches after their cleanup/restart.\r\n await publishCurrentRouteTypes(process.cwd(), router.getNamedApiRoutes());\r\n\r\n typeGenerator.executeTypingsGenerator([...batch.added, ...batch.changed, ...batch.deleted]);\r\n\r\n filesOrchestrator.checkHealth(batch);\r\n } catch (error) {\r\n devServeLog(colors.redBright(`Failed to execute batch reload: ${error}`));\r\n }\r\n }\r\n\r\n /**\r\n * The boot-time file in this batch that can only be applied by restarting,\r\n * or `undefined` when the batch is ordinary application code.\r\n */\r\n private findRestartTrigger(batch: Batch): string | undefined {\r\n if (batch.changed.includes(\"warlock.config.ts\")) {\r\n return \"warlock.config.ts\";\r\n }\r\n\r\n return [...batch.added, ...batch.changed].find(isEnvPath);\r\n }\r\n\r\n /**\r\n * Restart so a changed boot-time file actually takes effect.\r\n *\r\n * Opt out with `devServer.restartOnConfigChange: false` and the old\r\n * behaviour returns: a warning telling you to restart yourself. If the\r\n * restart can't be performed at all (no supervisor, a shutdown that threw),\r\n * `restartDevServer` reports it and we fall back to the same warning rather\r\n * than leaving the server running against config it isn't using.\r\n */\r\n private async restartForConfigChange(file: string): Promise<void> {\r\n const devServerConfig = await warlockConfigManager.get(\"devServer\");\r\n\r\n if (devServerConfig?.restartOnConfigChange === false) {\r\n devLogWarn(`${file} changed — restart the dev server to apply.`);\r\n return;\r\n }\r\n\r\n devLogInfo(`${file} changed — restarting to apply.`);\r\n\r\n const restarted = await restartDevServer(this);\r\n\r\n if (!restarted) {\r\n devLogWarn(`${file} changed — restart the dev server to apply.`);\r\n }\r\n }\r\n\r\n public async shutdown(): Promise<void> {\r\n // Always hand the terminal back, even on a repeat/no-op shutdown — a\r\n // process that exits while stdin is still in raw mode leaves the user's\r\n // shell without line editing or Ctrl+C.\r\n devServerShortcuts.release();\r\n\r\n if (!this.running) return;\r\n devServeLog(colors.redBright(\"Shutting down Development Server...\"));\r\n this.running = false;\r\n await connectorsManager.shutdown();\r\n devServeLog(colors.greenBright(\"Development Server stopped\"));\r\n }\r\n\r\n public isRunning(): boolean {\r\n return this.running;\r\n }\r\n}\r\n\r\nfunction isEnvPath(path: string): boolean {\r\n const basename = path.split(\"/\").pop() ?? path;\r\n return basename === \".env\" || basename.startsWith(\".env.\");\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,IAAa,oBAAb,MAA+B;CAK7B,AAAO,YAAY,UAAiC,CAAC,GAAG;iBAHtC;EAIhB,KAAK,UAAU;EACf,cAAc,gCAAgC;CAChD;CAEA,MAAa,QAAuB;EAClC,IAAI;GACF,MAAM,YAAY,YAAY,IAAI;GAIlC,IAAI,KAAK,QAAQ,SAAU,MAAM,gBAAgB,aAAa,GAAI;IAChE,MAAM,YAAY,aAAa;IAC/B,YAAY,OAAO,WAAW,4BAA4B,CAAC;GAC7D;GAEA,MAAM,kBAAkB,KAAK;GAC7B,MAAM,kBAAkB,cAAc;GACtC,MAAM,kBAAkB,WAAW;GAEnC,kBAAkB,sBAAsB,QAAQ,kBAAkB,SAAS,CAAC;GAE5E,KAAK,oBAAoB;GAIzB,MAAM,KAAK,kBAAkB;GAE7B,MAAM,kBAAkB,aAAa,QAAQ;GAU7C,IAAI;IACF,MAAM,YAAY,qBAAqB;GACzC,SAAS,OAAO;IACd,MAAM,IAAI,sBAAuB,MAAgB,SAAS,EAAE,OAAO,MAAM,CAAC;GAC5E;GAIA,MAAM,kBAAkB,iBAAuC;GAI/D,MAAM,yBAAyB,QAAQ,IAAI,GAAG,OAAO,kBAAkB,CAAC;GAExE,KAAK,gBAAgB,IAAI,cACvB,kBAAkB,mBAAmB,GACrC,kBAAkB,uBAClB,kBAAkB,eACjB,iBAAiB,kBAAkB,YAAY,YAAY,SACtD,kBAAkB,kBAAkB,CAC5C;GAEA,KAAK,UAAU;GAEf,MAAM,WAAW,YAAY,IAAI,IAAI;GAIrC,YAAY,WAAW;IACrB,aAAa,YAAY;IACzB,iBAAiB,YAAY;IAC7B,gBAAgB;GAClB,CAAC;GAMD,gBAAgB,QAAQ;GAGxB,MAAM,kBAAkB,MAAM,qBAAqB,IAAI,WAAW;GAClE,MAAM,kBACJ,KAAK,QAAQ,mBAAmB,iBAAiB,mBAAmB;GACtE,MAAM,iBAAiB,KAAK,QAAQ,kBAAkB,iBAAiB,kBAAkB;GAEzF,IAAI,iBAAiB,MAAM,cAAc,0BAA0B;GAEnE,IAAI,gBACF,kBAAkB,oBAAoB,mBAAmB,OAAO,SAAY,cAAc;EAE9F,SAAS,OAAO;GACd,YAAY,OAAO,UAAU,uCAAuC,OAAO,CAAC;GAC5E,MAAM,KAAK,SAAS;GACpB,MAAM;EACR;CACF;;;;;CAMA,MAAc,oBAAmC;EAC/C,MAAM,iBAAiB,CAAC,OAAO;EAC/B,KAAK,MAAM,QAAQ,kBAAkB,MAAM,OAAO,GAChD,IAAI,KAAK,QAAS,eAAqC,SAAS,KAAK,IAAI,GACvE,MAAM,kBAAkB,aAAa,WAAW,MAAM,KAAK,IAAI;CAGrE;CAEA,AAAQ,sBAA4B;EAClC,OAAO,GAAG,8BAA8B,UAAiB,KAAK,oBAAoB,KAAK,CAAC;CAC1F;CAEA,MAAc,oBAAoB,OAA6B;EAC7D,IAAI,CAAC,KAAK,WAAW,CAAC,KAAK,eAAe;EAO1C,MAAM,iBAAiB,KAAK,mBAAmB,KAAK;EAEpD,IAAI,gBAKF,MAAM,KAAK,uBAAuB,cAAc;EAQlD,IADc,MAAM,MAAM,SAAS,MAAM,QAAQ,SAAS,MAAM,QAAQ,WAC1D,GAAG;EAKjB,MAAM,YAAY,CAAC,GAAG,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,CAAC,QAClD,MAAM,CAAC,UAAU,CAAC,KAAK,MAAM,mBAChC;EAEA,IAAI;GACF,MAAM,KAAK,cAAc,mBACvB,WACA,kBAAkB,SAAS,GAC3B,MAAM,SACN,MAAM,SACN,MAAM,OACR;GAKA,MAAM,yBAAyB,QAAQ,IAAI,GAAG,OAAO,kBAAkB,CAAC;GAExE,cAAc,wBAAwB;IAAC,GAAG,MAAM;IAAO,GAAG,MAAM;IAAS,GAAG,MAAM;GAAO,CAAC;GAE1F,kBAAkB,YAAY,KAAK;EACrC,SAAS,OAAO;GACd,YAAY,OAAO,UAAU,mCAAmC,OAAO,CAAC;EAC1E;CACF;;;;;CAMA,AAAQ,mBAAmB,OAAkC;EAC3D,IAAI,MAAM,QAAQ,SAAS,mBAAmB,GAC5C,OAAO;EAGT,OAAO,CAAC,GAAG,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,CAAC,KAAK,SAAS;CAC1D;;;;;;;;;;CAWA,MAAc,uBAAuB,MAA6B;EAGhE,KAAI,MAF0B,qBAAqB,IAAI,WAAW,EAE/C,EAAE,0BAA0B,OAAO;GACpD,WAAW,GAAG,KAAK,4CAA4C;GAC/D;EACF;EAEA,WAAW,GAAG,KAAK,gCAAgC;EAInD,IAAI,CAAC,MAFmB,iBAAiB,IAAI,GAG3C,WAAW,GAAG,KAAK,4CAA4C;CAEnE;CAEA,MAAa,WAA0B;EAIrC,mBAAmB,QAAQ;EAE3B,IAAI,CAAC,KAAK,SAAS;EACnB,YAAY,OAAO,UAAU,qCAAqC,CAAC;EACnE,KAAK,UAAU;EACf,MAAM,kBAAkB,SAAS;EACjC,YAAY,OAAO,YAAY,4BAA4B,CAAC;CAC9D;CAEA,AAAO,YAAqB;EAC1B,OAAO,KAAK;CACd;AACF;AAEA,SAAS,UAAU,MAAuB;CACxC,MAAM,WAAW,KAAK,MAAM,GAAG,CAAC,CAAC,IAAI,KAAK;CAC1C,OAAO,aAAa,UAAU,SAAS,WAAW,OAAO;AAC3D"}
|
|
1
|
+
{"version":3,"file":"development-server.mjs","names":[],"sources":["../../../../../../../core/src/dev-server/development-server.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport events from \"@mongez/events\";\r\nimport { fileExistsAsync, unlinkAsync } from \"@warlock.js/fs\";\r\nimport { Application } from \"../application\";\r\nimport { connectorsManager } from \"../connectors/connectors-manager\";\r\nimport { ConnectorLifecyclePhase } from \"../connectors/types\";\r\nimport { router } from \"../router/router\";\r\nimport { warlockConfigManager } from \"../warlock-config\";\r\nimport { BootPreconditionError } from \"./boot-precondition-error\";\r\nimport { devLogInfo, devLogSection, devLogWarn, devServeLog } from \"./dev-logger\";\r\nimport { filesOrchestrator } from \"./files-orchestrator\";\r\nimport { MANIFEST_PATH } from \"./flags\";\r\nimport type { IncomingReloadTimings } from \"./layer-executor\";\r\nimport { LayerExecutor } from \"./layer-executor\";\r\nimport { printReadyBlock } from \"./ready-block\";\r\nimport { restartDevServer } from \"./restart-dev-server\";\r\nimport { publishCurrentRouteTypes } from \"./route-types-publisher\";\r\nimport { devServerShortcuts } from \"./shortcuts\";\r\nimport type { StartDevServerOptions } from \"./start-development-server\";\r\nimport { typeGenerator } from \"./type-generator\";\r\n\r\ntype Batch = {\r\n added: string[];\r\n changed: string[];\r\n deleted: string[];\r\n /** Non-code `watch.include` files that changed; only a restart applies them. */\r\n restartTriggers?: string[];\r\n timings?: IncomingReloadTimings;\r\n};\r\n\r\n/**\r\n * Top-level coordinator for `warlock dev`. Wires the file orchestrator, the\r\n * connectors, and the layer executor together, and listens for the watcher's\r\n * batched events to drive HMR.\r\n */\r\nexport class DevelopmentServer {\r\n private layerExecutor?: LayerExecutor;\r\n private running = false;\r\n\r\n /** Set once boot begins, so a failed boot still closes connectors that already started. */\r\n private connectorsStarted = false;\r\n private readonly options: StartDevServerOptions;\r\n\r\n public constructor(options: StartDevServerOptions = {}) {\r\n this.options = options;\r\n devLogSection(\"Starting Development Server...\");\r\n }\r\n\r\n public async start(): Promise<void> {\r\n try {\r\n this.connectorsStarted = true;\r\n const startedAt = performance.now();\r\n\r\n // --fresh deletes the manifest so reconciliation re-parses every file\r\n // from disk. Transpile caching is owned by the loader hook (in-memory).\r\n if (this.options.fresh && (await fileExistsAsync(MANIFEST_PATH))) {\r\n await unlinkAsync(MANIFEST_PATH);\r\n devServeLog(colors.cyanBright(\"Cleared manifest (--fresh)\"));\r\n }\r\n\r\n await filesOrchestrator.init();\r\n await filesOrchestrator.initializeAll();\r\n await filesOrchestrator.watchFiles();\r\n\r\n filesOrchestrator.specialFilesCollector.collect(filesOrchestrator.getFiles());\r\n\r\n this.setupEventListeners();\r\n\r\n // Decorator-driven registries (models, etc.) must be populated before\r\n // routes/services can resolve symbols by name.\r\n await this.autoDiscoverFiles();\r\n\r\n await filesOrchestrator.moduleLoader.loadAll();\r\n\r\n // A rejecting validator (Application.onValidateBoot) must abort boot\r\n // before late-phase connectors bind a port — this is what makes the\r\n // hook actually prevent serving instead of merely being defined.\r\n //\r\n // Wrapped and re-thrown as a BootPreconditionError: a rejected\r\n // validator is, by definition, a precondition whose cause cannot\r\n // change because the worker tried again — the supervisor must not\r\n // restart into the same rejection. See boot-precondition-error.ts.\r\n try {\r\n await Application.runStartupValidators();\r\n } catch (error) {\r\n throw new BootPreconditionError((error as Error).message, { cause: error });\r\n }\r\n\r\n // Late-phase connectors (http, socket) bind after app code has\r\n // registered routes/listeners.\r\n await connectorsManager.startPhase(ConnectorLifecyclePhase.Late);\r\n\r\n // Web installs its page table from its late connector. Generate only\r\n // after that installation so `href()` and the declaration table agree.\r\n await publishCurrentRouteTypes(process.cwd(), router.getNamedApiRoutes());\r\n\r\n this.layerExecutor = new LayerExecutor(\r\n filesOrchestrator.getDependencyGraph(),\r\n filesOrchestrator.specialFilesCollector,\r\n filesOrchestrator.moduleLoader,\r\n (absolutePath) => filesOrchestrator.bumpVersion(absolutePath),\r\n () => filesOrchestrator.flushVersionBumps(),\r\n );\r\n\r\n this.running = true;\r\n\r\n const duration = performance.now() - startedAt;\r\n\r\n // App modules are loaded and both connector phases are active — signal a\r\n // complete boot so `Application.onceBooted(...)` listeners fire.\r\n Application.markBooted({\r\n environment: Application.environment,\r\n runtimeStrategy: Application.runtimeStrategy,\r\n bootDurationMs: duration,\r\n });\r\n\r\n // ONE block, AFTER the boot it summarises, replacing the scattered\r\n // \"N route(s) registered\" / \"Server ready at …\" / \"ready in …\" lines that\r\n // used to arrive in completion order. Warnings logged during boot sit\r\n // above it and stay there — see `ready-block.ts`.\r\n printReadyBlock(duration);\r\n\r\n // Tell the supervisor (when there is one) that boot completed, so its\r\n // healthy-uptime window counts from here rather than from spawn.\r\n process.send?.(\"warlock:booted\");\r\n\r\n // Precedence: explicit CLI option > devServer.* config > default.\r\n const devServerConfig = await warlockConfigManager.get(\"devServer\");\r\n const generateTypings =\r\n this.options.generateTypings ?? devServerConfig?.generateTypings ?? true;\r\n const healthCheckers = this.options.healthCheckers ?? devServerConfig?.healthCheckers ?? true;\r\n\r\n if (generateTypings) await typeGenerator.executeGenerateAllCommand();\r\n\r\n if (healthCheckers) {\r\n filesOrchestrator.startCheckingHealth(healthCheckers === true ? undefined : healthCheckers);\r\n }\r\n } catch (error) {\r\n devServeLog(colors.redBright(`Failed to start Development Server: ${error}`));\r\n await this.shutdown();\r\n throw error;\r\n }\r\n }\r\n\r\n /**\r\n * Eagerly import files whose decorators populate global registries so any\r\n * symbol-by-name resolution later in boot finds them.\r\n */\r\n private async autoDiscoverFiles(): Promise<void> {\r\n const discoveryTypes = [\"model\"] as const;\r\n for (const file of filesOrchestrator.files.values()) {\r\n if (file.type && (discoveryTypes as readonly string[]).includes(file.type)) {\r\n await filesOrchestrator.moduleLoader.loadModule(file, file.type);\r\n }\r\n }\r\n }\r\n\r\n /** Tail of the batch-reload chain (see `setupEventListeners`). */\r\n private reloadQueue: Promise<void> = Promise.resolve();\r\n\r\n private setupEventListeners(): void {\r\n // Reloads run one at a time: a second batch waits for the first to finish\r\n // so route re-registration and connector restarts never interleave.\r\n events.on(\"dev-server:batch-complete\", (batch: Batch) => {\r\n this.reloadQueue = this.reloadQueue\r\n .then(() => this.handleBatchComplete(batch))\r\n .catch((error) => devServeLog(colors.redBright(`Batch reload failed: ${error}`)));\r\n });\r\n }\r\n\r\n private async handleBatchComplete(batch: Batch): Promise<void> {\r\n if (!this.running || !this.layerExecutor) return;\r\n\r\n // warlock.config.ts holds settings read at boot (CLI commands, build\r\n // options, watch patterns, scheduled jobs) and .env feeds every config\r\n // that reads it. Neither can be hot-reloaded without leaving running\r\n // services configured with stale values, so the only honest response is\r\n // a full restart.\r\n const restartTrigger = this.findRestartTrigger(batch);\r\n\r\n if (restartTrigger) {\r\n // On success this never comes back — the process exits and the\r\n // supervisor replaces it. Reaching the next line means the restart was\r\n // declined by config or wasn't possible, so we fall through and still\r\n // hot-reload whatever ordinary code shared the batch.\r\n await this.restartForConfigChange(restartTrigger);\r\n }\r\n\r\n // No-op saves (an editor that fsyncs without a content write) are already\r\n // filtered upstream by the file hash in FileEventHandler, so batch.changed\r\n // only contains genuinely-changed paths by the time it reaches here.\r\n\r\n const total = batch.added.length + batch.changed.length + batch.deleted.length;\r\n if (total === 0) return;\r\n\r\n // Boot-time files still ride along in `changed` when a restart was\r\n // declined (`restartOnConfigChange: false`) or wasn't possible — they are\r\n // never hot-reloaded, so keep them out of the reload set either way.\r\n const codeFiles = [...batch.added, ...batch.changed].filter(\r\n (p) => !isEnvPath(p) && p !== \"warlock.config.ts\" && !batch.restartTriggers?.includes(p),\r\n );\r\n\r\n try {\r\n await this.layerExecutor.executeBatchReload(\r\n codeFiles,\r\n filesOrchestrator.getFiles(),\r\n batch.deleted,\r\n batch.changed,\r\n batch.timings,\r\n );\r\n\r\n // `executeBatchReload` includes route re-imports and affected connector\r\n // restarts. Publishing earlier could freeze a partial page/API view.\r\n // This also refreshes deletion-only batches after their cleanup/restart.\r\n await publishCurrentRouteTypes(process.cwd(), router.getNamedApiRoutes());\r\n\r\n typeGenerator.executeTypingsGenerator([...batch.added, ...batch.changed, ...batch.deleted]);\r\n\r\n filesOrchestrator.checkHealth(batch);\r\n } catch (error) {\r\n devServeLog(colors.redBright(`Failed to execute batch reload: ${error}`));\r\n }\r\n }\r\n\r\n /**\r\n * The boot-time file in this batch that can only be applied by restarting,\r\n * or `undefined` when the batch is ordinary application code.\r\n */\r\n private findRestartTrigger(batch: Batch): string | undefined {\r\n if (batch.changed.includes(\"warlock.config.ts\")) {\r\n return \"warlock.config.ts\";\r\n }\r\n\r\n return [...batch.added, ...batch.changed].find(isEnvPath) ?? batch.restartTriggers?.[0];\r\n }\r\n\r\n /**\r\n * Restart so a changed boot-time file actually takes effect.\r\n *\r\n * Opt out with `devServer.restartOnConfigChange: false` and the old\r\n * behaviour returns: a warning telling you to restart yourself. If the\r\n * restart can't be performed at all (no supervisor, a shutdown that threw),\r\n * `restartDevServer` reports it and we fall back to the same warning rather\r\n * than leaving the server running against config it isn't using.\r\n */\r\n private async restartForConfigChange(file: string): Promise<void> {\r\n const devServerConfig = await warlockConfigManager.get(\"devServer\");\r\n\r\n if (devServerConfig?.restartOnConfigChange === false) {\r\n devLogWarn(`${file} changed — restart the dev server to apply.`);\r\n return;\r\n }\r\n\r\n devLogInfo(`${file} changed — restarting to apply.`);\r\n\r\n const restarted = await restartDevServer(this);\r\n\r\n if (!restarted) {\r\n devLogWarn(`${file} changed — restart the dev server to apply.`);\r\n }\r\n }\r\n\r\n public async shutdown(): Promise<void> {\r\n // Always hand the terminal back, even on a repeat/no-op shutdown — a\r\n // process that exits while stdin is still in raw mode leaves the user's\r\n // shell without line editing or Ctrl+C.\r\n devServerShortcuts.release();\r\n\r\n if (!this.running && !this.connectorsStarted) return;\r\n devServeLog(colors.redBright(\"Shutting down Development Server...\"));\r\n this.running = false;\r\n this.connectorsStarted = false;\r\n await connectorsManager.shutdown();\r\n devServeLog(colors.greenBright(\"Development Server stopped\"));\r\n }\r\n\r\n public isRunning(): boolean {\r\n return this.running;\r\n }\r\n}\r\n\r\nfunction isEnvPath(path: string): boolean {\r\n const basename = path.split(\"/\").pop() ?? path;\r\n return basename === \".env\" || basename.startsWith(\".env.\");\r\n}\r\n"],"mappings":"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"}
|
|
@@ -2,6 +2,7 @@ import { Path } from "../utils/normalized-path.mjs";
|
|
|
2
2
|
import { devLogSuccess } from "./dev-logger.mjs";
|
|
3
3
|
import { isTimingsEnabled } from "./flags.mjs";
|
|
4
4
|
import { clearFileExistsCache } from "./parse-imports.mjs";
|
|
5
|
+
import { isCodeFile } from "./special-file-patterns.mjs";
|
|
5
6
|
import events from "@mongez/events";
|
|
6
7
|
|
|
7
8
|
//#region ../core/src/dev-server/file-event-handler.ts
|
|
@@ -23,6 +24,15 @@ var FileEventHandler = class {
|
|
|
23
24
|
this.pendingChanges = /* @__PURE__ */ new Set();
|
|
24
25
|
this.pendingAdds = /* @__PURE__ */ new Set();
|
|
25
26
|
this.pendingDeletes = /* @__PURE__ */ new Set();
|
|
27
|
+
this.batchQueue = Promise.resolve();
|
|
28
|
+
this.includeMatcher = () => false;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Non-code files in the include set (`.sql` fixtures, JSON config) can't be
|
|
32
|
+
* hot-reloaded, so they are reported as restart triggers instead.
|
|
33
|
+
*/
|
|
34
|
+
setIncludeMatcher(matcher) {
|
|
35
|
+
this.includeMatcher = matcher;
|
|
26
36
|
}
|
|
27
37
|
handleFileChange(absolutePath, settleMs) {
|
|
28
38
|
this.recordTimingStart(settleMs);
|
|
@@ -53,7 +63,9 @@ var FileEventHandler = class {
|
|
|
53
63
|
clearTimeout(this.maxWaitTimer);
|
|
54
64
|
this.maxWaitTimer = void 0;
|
|
55
65
|
}
|
|
56
|
-
this.processBatch()
|
|
66
|
+
this.batchQueue = this.batchQueue.then(() => this.processBatch()).catch((error) => {
|
|
67
|
+
console.error("[dev-server] batch processing failed", error);
|
|
68
|
+
});
|
|
57
69
|
}
|
|
58
70
|
/**
|
|
59
71
|
* Mark the debounce-wait phase start on the first event of a batch, and
|
|
@@ -78,10 +90,17 @@ var FileEventHandler = class {
|
|
|
78
90
|
this.batchStartedAt = void 0;
|
|
79
91
|
this.watcherSettleMs = void 0;
|
|
80
92
|
if (changes.length === 0 && adds.length === 0 && deletes.length === 0) return;
|
|
81
|
-
const
|
|
82
|
-
const
|
|
83
|
-
const
|
|
84
|
-
const
|
|
93
|
+
const isIncludedData = (p) => !isCodeFile(p) && this.includeMatcher(p);
|
|
94
|
+
const isExternal = (p) => isExternalPath(p) || isIncludedData(p);
|
|
95
|
+
const externalChanges = changes.filter(isExternal);
|
|
96
|
+
const externalAdds = adds.filter(isExternal);
|
|
97
|
+
const codeChanges = changes.filter((p) => !isExternal(p));
|
|
98
|
+
const codeAdds = adds.filter((p) => !isExternal(p));
|
|
99
|
+
const restartTriggers = [
|
|
100
|
+
...changes,
|
|
101
|
+
...adds,
|
|
102
|
+
...deletes
|
|
103
|
+
].filter(isIncludedData);
|
|
85
104
|
if (codeAdds.length + codeChanges.length > 1) {
|
|
86
105
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
87
106
|
clearFileExistsCache();
|
|
@@ -95,7 +114,8 @@ var FileEventHandler = class {
|
|
|
95
114
|
events.trigger("dev-server:batch-complete", {
|
|
96
115
|
added: [...externalAdds, ...addedCodePaths],
|
|
97
116
|
changed: [...externalChanges, ...changedCodePaths],
|
|
98
|
-
deleted: [...deletes, ...vanished],
|
|
117
|
+
deleted: [...deletes.filter((p) => !isIncludedData(p)), ...vanished],
|
|
118
|
+
restartTriggers,
|
|
99
119
|
timings: isTimingsEnabled() ? {
|
|
100
120
|
watcherSettleMs: watcherSettleMs ?? 0,
|
|
101
121
|
debounceWaitMs: debounceWaitMs ?? 0
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-event-handler.mjs","names":[],"sources":["../../../../../../../core/src/dev-server/file-event-handler.ts"],"sourcesContent":["import events from \"@mongez/events\";\nimport type { DependencyGraph } from \"./dependency-graph\";\nimport { devLogSuccess } from \"./dev-logger\";\nimport type { FileManager } from \"./file-manager\";\nimport type { FileOperations } from \"./file-operations\";\nimport { FILE_PROCESSING_BATCH_SIZE, isTimingsEnabled } from \"./flags\";\nimport type { ManifestManager } from \"./manifest-manager\";\nimport { clearFileExistsCache } from \"./parse-imports\";\nimport { Path } from \"../utils/normalized-path\";\n\n/** Lets an isolated editor save reach HMR without the former fixed 50ms delay. */\nconst ISOLATED_SAVE_QUIET_WINDOW_MS = 12;\n\n/** Bounds a sustained formatter or checkout stream so it cannot postpone HMR forever. */\nconst BATCH_MAX_WAIT_MS = 60;\n\n/**\n * Receives raw watcher events and processes them in a single debounced batch.\n * Order within a batch: adds → changes → deletes, so changes can reference\n * newly-added files and deletes fire last.\n */\nexport class FileEventHandler {\n private pendingChanges = new Set<string>();\n private pendingAdds = new Set<string>();\n private pendingDeletes = new Set<string>();\n\n /** When this batch's first watcher event arrived — the debounce-wait phase start. */\n private batchStartedAt?: number;\n\n /** Slowest watcher-settle duration seen so far this batch, when `devServer.timings` is on. */\n private watcherSettleMs?: number;\n\n private debounceTimer?: ReturnType<typeof setTimeout>;\n private maxWaitTimer?: ReturnType<typeof setTimeout>;\n\n constructor(\n private readonly fileOperations: FileOperations,\n private readonly manifest: ManifestManager,\n private readonly dependencyGraph: DependencyGraph,\n private readonly files: Map<string, FileManager>,\n ) {}\n\n public handleFileChange(absolutePath: string, settleMs?: number): void {\n this.recordTimingStart(settleMs);\n this.pendingChanges.add(Path.toRelative(absolutePath));\n this.schedulePendingEvents();\n }\n\n public handleFileAdd(absolutePath: string, settleMs?: number): void {\n this.recordTimingStart(settleMs);\n this.pendingAdds.add(Path.toRelative(absolutePath));\n this.schedulePendingEvents();\n }\n\n public handleFileDelete(absolutePath: string, settleMs?: number): void {\n this.recordTimingStart(settleMs);\n this.pendingDeletes.add(Path.toRelative(absolutePath));\n this.schedulePendingEvents();\n }\n\n private schedulePendingEvents(): void {\n if (this.debounceTimer !== undefined) {\n clearTimeout(this.debounceTimer);\n }\n\n this.debounceTimer = setTimeout(() => this.flushPendingEvents(), ISOLATED_SAVE_QUIET_WINDOW_MS);\n\n if (this.maxWaitTimer === undefined) {\n this.maxWaitTimer = setTimeout(() => this.flushPendingEvents(), BATCH_MAX_WAIT_MS);\n }\n }\n\n private flushPendingEvents(): void {\n if (this.debounceTimer !== undefined) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = undefined;\n }\n\n if (this.maxWaitTimer !== undefined) {\n clearTimeout(this.maxWaitTimer);\n this.maxWaitTimer = undefined;\n }\n\n void this.processBatch();\n }\n\n /**\n * Mark the debounce-wait phase start on the first event of a batch, and\n * track the slowest watcher-settle duration seen this batch. `settleMs`\n * only arrives when `devServer.timings` is on (see `FilesWatcher`), so\n * this is a no-op past the cheap `performance.now()` call when it's off.\n */\n private recordTimingStart(settleMs?: number): void {\n if (this.batchStartedAt === undefined) {\n this.batchStartedAt = performance.now();\n }\n\n if (settleMs === undefined) return;\n\n this.watcherSettleMs =\n this.watcherSettleMs === undefined ? settleMs : Math.max(this.watcherSettleMs, settleMs);\n }\n\n private async processBatch(): Promise<void> {\n const changes = Array.from(this.pendingChanges);\n const adds = Array.from(this.pendingAdds);\n const deletes = Array.from(this.pendingDeletes);\n\n this.pendingChanges.clear();\n this.pendingAdds.clear();\n this.pendingDeletes.clear();\n\n const debounceWaitMs =\n this.batchStartedAt !== undefined ? performance.now() - this.batchStartedAt : undefined;\n const watcherSettleMs = this.watcherSettleMs;\n this.batchStartedAt = undefined;\n this.watcherSettleMs = undefined;\n\n if (changes.length === 0 && adds.length === 0 && deletes.length === 0) return;\n\n // Both .env files and warlock.config.ts live outside src/ — they should\n // never enter the dep graph, only ride along in the batch event so the\n // dev server can react (config reload / restart warning).\n const externalChanges = changes.filter(isExternalPath);\n const externalAdds = adds.filter(isExternalPath);\n const codeChanges = changes.filter((p) => !isExternalPath(p));\n const codeAdds = adds.filter((p) => !isExternalPath(p));\n\n // Multi-file batches can race the filesystem on Windows.\n if (codeAdds.length + codeChanges.length > 1) {\n await new Promise((resolve) => setTimeout(resolve, 500));\n clearFileExistsCache();\n }\n\n const { added: addedCodePaths, vanished } = await this.processBatchAdds(codeAdds);\n const changedCodePaths = await this.processBatchChanges(codeChanges);\n await this.processBatchDeletes(deletes);\n\n this.fileOperations.updateFileDependents();\n this.fileOperations.syncFilesToManifest();\n await this.manifest.save();\n\n // Emit only the code paths that genuinely changed (hash differs). A no-op\n // save — an editor that fsyncs without writing — reports no change and is\n // dropped here, where we still know the pre-change hash. (Doing this\n // downstream is impossible: the source has already been overwritten, so a\n // content compare always looks unchanged — which is exactly why emptying a\n // file used to silently skip HMR.) External paths (.env / warlock.config.ts)\n // ride along untouched so the dev server can still react to them. Paths\n // that vanished between the add event and their read/stat (a rename or\n // move racing the filesystem) are folded into `deleted` instead of\n // `added` — they were already unwound as deletions in processBatchAdds.\n events.trigger(\"dev-server:batch-complete\", {\n added: [...externalAdds, ...addedCodePaths],\n changed: [...externalChanges, ...changedCodePaths],\n deleted: [...deletes, ...vanished],\n timings: isTimingsEnabled()\n ? { watcherSettleMs: watcherSettleMs ?? 0, debounceWaitMs: debounceWaitMs ?? 0 }\n : undefined,\n });\n }\n\n /**\n * Reprocess each changed file and return only the paths that genuinely\n * changed. `updateFile` returns false when the on-disk hash matches the\n * last-processed hash (e.g. an editor that fsyncs on save without writing),\n * so those no-ops are kept out of the reload batch. Emptying a file changes\n * its hash, so it is correctly reported as changed.\n */\n private async processBatchChanges(relativePaths: string[]): Promise<string[]> {\n const changed: string[] = [];\n await runInBatches(relativePaths, FILE_PROCESSING_BATCH_SIZE, async (path) => {\n if (await this.fileOperations.updateFile(path)) {\n changed.push(path);\n }\n });\n return changed;\n }\n\n /**\n * Process pending add events. Returns the paths that genuinely became\n * available (`added`) separately from paths that no longer existed by the\n * time they were read/stat'd (`vanished`) — the latter is a rename or move\n * racing the filesystem, not a failure, so it is unwound as a deletion\n * instead of being logged as an error.\n */\n private async processBatchAdds(\n relativePaths: string[],\n ): Promise<{ added: string[]; vanished: string[] }> {\n const added: string[] = [];\n const vanished: string[] = [];\n\n await runInBatches(relativePaths, FILE_PROCESSING_BATCH_SIZE, async (path) => {\n try {\n const fileManager = await this.fileOperations.addFile(path);\n\n if (fileManager.state === \"deleted\") {\n await this.fileOperations.deleteFile(path);\n vanished.push(path);\n return;\n }\n\n added.push(path);\n devLogSuccess(`Added file: ${path}`);\n } catch (error) {\n console.error(`Failed to add file ${path}:`, error);\n }\n });\n\n return { added, vanished };\n }\n\n private async processBatchDeletes(relativePaths: string[]): Promise<void> {\n for (const relativePath of relativePaths) {\n await this.fileOperations.deleteFile(relativePath);\n devLogSuccess(`Deleted file: ${relativePath}`);\n }\n }\n}\n\nfunction isEnvFile(path: string): boolean {\n const basename = path.split(\"/\").pop() || path;\n return basename === \".env\" || basename.startsWith(\".env.\");\n}\n\n/** Paths watched but never added to the dependency graph. */\nfunction isExternalPath(path: string): boolean {\n return isEnvFile(path) || path === \"warlock.config.ts\";\n}\n\nasync function runInBatches<T>(\n items: T[],\n size: number,\n fn: (item: T) => Promise<unknown>,\n): Promise<void> {\n if (items.length === 0) return;\n for (let i = 0; i < items.length; i += size) {\n await Promise.all(items.slice(i, i + size).map(fn));\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"file-event-handler.mjs","names":[],"sources":["../../../../../../../core/src/dev-server/file-event-handler.ts"],"sourcesContent":["import events from \"@mongez/events\";\nimport type { DependencyGraph } from \"./dependency-graph\";\nimport { devLogSuccess } from \"./dev-logger\";\nimport type { FileManager } from \"./file-manager\";\nimport type { FileOperations } from \"./file-operations\";\nimport { FILE_PROCESSING_BATCH_SIZE, isTimingsEnabled } from \"./flags\";\nimport type { ManifestManager } from \"./manifest-manager\";\nimport { clearFileExistsCache } from \"./parse-imports\";\nimport { isCodeFile } from \"./special-file-patterns\";\nimport { Path } from \"../utils/normalized-path\";\n\n/** Lets an isolated editor save reach HMR without the former fixed 50ms delay. */\nconst ISOLATED_SAVE_QUIET_WINDOW_MS = 12;\n\n/** Bounds a sustained formatter or checkout stream so it cannot postpone HMR forever. */\nconst BATCH_MAX_WAIT_MS = 60;\n\n/**\n * Receives raw watcher events and processes them in a single debounced batch.\n * Order within a batch: adds → changes → deletes, so changes can reference\n * newly-added files and deletes fire last.\n */\nexport class FileEventHandler {\n private pendingChanges = new Set<string>();\n private pendingAdds = new Set<string>();\n private pendingDeletes = new Set<string>();\n\n /** When this batch's first watcher event arrived — the debounce-wait phase start. */\n private batchStartedAt?: number;\n\n /** Slowest watcher-settle duration seen so far this batch, when `devServer.timings` is on. */\n private watcherSettleMs?: number;\n\n /** Tail of the batch-processing chain; keeps batches strictly sequential. */\n private batchQueue: Promise<void> = Promise.resolve();\n\n private debounceTimer?: ReturnType<typeof setTimeout>;\n private maxWaitTimer?: ReturnType<typeof setTimeout>;\n\n constructor(\n private readonly fileOperations: FileOperations,\n private readonly manifest: ManifestManager,\n private readonly dependencyGraph: DependencyGraph,\n private readonly files: Map<string, FileManager>,\n ) {}\n\n /** Matches paths the user listed in `devServer.watch.include`. */\n private includeMatcher: (path: string) => boolean = () => false;\n\n /**\n * Non-code files in the include set (`.sql` fixtures, JSON config) can't be\n * hot-reloaded, so they are reported as restart triggers instead.\n */\n public setIncludeMatcher(matcher: (path: string) => boolean): void {\n this.includeMatcher = matcher;\n }\n\n public handleFileChange(absolutePath: string, settleMs?: number): void {\n this.recordTimingStart(settleMs);\n this.pendingChanges.add(Path.toRelative(absolutePath));\n this.schedulePendingEvents();\n }\n\n public handleFileAdd(absolutePath: string, settleMs?: number): void {\n this.recordTimingStart(settleMs);\n this.pendingAdds.add(Path.toRelative(absolutePath));\n this.schedulePendingEvents();\n }\n\n public handleFileDelete(absolutePath: string, settleMs?: number): void {\n this.recordTimingStart(settleMs);\n this.pendingDeletes.add(Path.toRelative(absolutePath));\n this.schedulePendingEvents();\n }\n\n private schedulePendingEvents(): void {\n if (this.debounceTimer !== undefined) {\n clearTimeout(this.debounceTimer);\n }\n\n this.debounceTimer = setTimeout(() => this.flushPendingEvents(), ISOLATED_SAVE_QUIET_WINDOW_MS);\n\n if (this.maxWaitTimer === undefined) {\n this.maxWaitTimer = setTimeout(() => this.flushPendingEvents(), BATCH_MAX_WAIT_MS);\n }\n }\n\n private flushPendingEvents(): void {\n if (this.debounceTimer !== undefined) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = undefined;\n }\n\n if (this.maxWaitTimer !== undefined) {\n clearTimeout(this.maxWaitTimer);\n this.maxWaitTimer = undefined;\n }\n\n // Serialize: a batch arriving while the previous one is in flight queues\n // behind it (pending sets keep coalescing meanwhile) instead of racing it.\n this.batchQueue = this.batchQueue\n .then(() => this.processBatch())\n .catch((error) => {\n console.error(\"[dev-server] batch processing failed\", error);\n });\n }\n\n /**\n * Mark the debounce-wait phase start on the first event of a batch, and\n * track the slowest watcher-settle duration seen this batch. `settleMs`\n * only arrives when `devServer.timings` is on (see `FilesWatcher`), so\n * this is a no-op past the cheap `performance.now()` call when it's off.\n */\n private recordTimingStart(settleMs?: number): void {\n if (this.batchStartedAt === undefined) {\n this.batchStartedAt = performance.now();\n }\n\n if (settleMs === undefined) return;\n\n this.watcherSettleMs =\n this.watcherSettleMs === undefined ? settleMs : Math.max(this.watcherSettleMs, settleMs);\n }\n\n private async processBatch(): Promise<void> {\n const changes = Array.from(this.pendingChanges);\n const adds = Array.from(this.pendingAdds);\n const deletes = Array.from(this.pendingDeletes);\n\n this.pendingChanges.clear();\n this.pendingAdds.clear();\n this.pendingDeletes.clear();\n\n const debounceWaitMs =\n this.batchStartedAt !== undefined ? performance.now() - this.batchStartedAt : undefined;\n const watcherSettleMs = this.watcherSettleMs;\n this.batchStartedAt = undefined;\n this.watcherSettleMs = undefined;\n\n if (changes.length === 0 && adds.length === 0 && deletes.length === 0) return;\n\n // Both .env files and warlock.config.ts live outside src/ — they should\n // never enter the dep graph, only ride along in the batch event so the\n // dev server can react (config reload / restart warning).\n const isIncludedData = (p: string) => !isCodeFile(p) && this.includeMatcher(p);\n const isExternal = (p: string) => isExternalPath(p) || isIncludedData(p);\n const externalChanges = changes.filter(isExternal);\n const externalAdds = adds.filter(isExternal);\n const codeChanges = changes.filter((p) => !isExternal(p));\n const codeAdds = adds.filter((p) => !isExternal(p));\n const restartTriggers = [...changes, ...adds, ...deletes].filter(isIncludedData);\n\n // Multi-file batches can race the filesystem on Windows.\n if (codeAdds.length + codeChanges.length > 1) {\n await new Promise((resolve) => setTimeout(resolve, 500));\n clearFileExistsCache();\n }\n\n const { added: addedCodePaths, vanished } = await this.processBatchAdds(codeAdds);\n const changedCodePaths = await this.processBatchChanges(codeChanges);\n await this.processBatchDeletes(deletes);\n\n this.fileOperations.updateFileDependents();\n this.fileOperations.syncFilesToManifest();\n await this.manifest.save();\n\n // Emit only the code paths that genuinely changed (hash differs). A no-op\n // save — an editor that fsyncs without writing — reports no change and is\n // dropped here, where we still know the pre-change hash. (Doing this\n // downstream is impossible: the source has already been overwritten, so a\n // content compare always looks unchanged — which is exactly why emptying a\n // file used to silently skip HMR.) External paths (.env / warlock.config.ts)\n // ride along untouched so the dev server can still react to them. Paths\n // that vanished between the add event and their read/stat (a rename or\n // move racing the filesystem) are folded into `deleted` instead of\n // `added` — they were already unwound as deletions in processBatchAdds.\n events.trigger(\"dev-server:batch-complete\", {\n added: [...externalAdds, ...addedCodePaths],\n changed: [...externalChanges, ...changedCodePaths],\n deleted: [...deletes.filter((p) => !isIncludedData(p)), ...vanished],\n restartTriggers,\n timings: isTimingsEnabled()\n ? { watcherSettleMs: watcherSettleMs ?? 0, debounceWaitMs: debounceWaitMs ?? 0 }\n : undefined,\n });\n }\n\n /**\n * Reprocess each changed file and return only the paths that genuinely\n * changed. `updateFile` returns false when the on-disk hash matches the\n * last-processed hash (e.g. an editor that fsyncs on save without writing),\n * so those no-ops are kept out of the reload batch. Emptying a file changes\n * its hash, so it is correctly reported as changed.\n */\n private async processBatchChanges(relativePaths: string[]): Promise<string[]> {\n const changed: string[] = [];\n await runInBatches(relativePaths, FILE_PROCESSING_BATCH_SIZE, async (path) => {\n if (await this.fileOperations.updateFile(path)) {\n changed.push(path);\n }\n });\n return changed;\n }\n\n /**\n * Process pending add events. Returns the paths that genuinely became\n * available (`added`) separately from paths that no longer existed by the\n * time they were read/stat'd (`vanished`) — the latter is a rename or move\n * racing the filesystem, not a failure, so it is unwound as a deletion\n * instead of being logged as an error.\n */\n private async processBatchAdds(\n relativePaths: string[],\n ): Promise<{ added: string[]; vanished: string[] }> {\n const added: string[] = [];\n const vanished: string[] = [];\n\n await runInBatches(relativePaths, FILE_PROCESSING_BATCH_SIZE, async (path) => {\n try {\n const fileManager = await this.fileOperations.addFile(path);\n\n if (fileManager.state === \"deleted\") {\n await this.fileOperations.deleteFile(path);\n vanished.push(path);\n return;\n }\n\n added.push(path);\n devLogSuccess(`Added file: ${path}`);\n } catch (error) {\n console.error(`Failed to add file ${path}:`, error);\n }\n });\n\n return { added, vanished };\n }\n\n private async processBatchDeletes(relativePaths: string[]): Promise<void> {\n for (const relativePath of relativePaths) {\n await this.fileOperations.deleteFile(relativePath);\n devLogSuccess(`Deleted file: ${relativePath}`);\n }\n }\n}\n\nfunction isEnvFile(path: string): boolean {\n const basename = path.split(\"/\").pop() || path;\n return basename === \".env\" || basename.startsWith(\".env.\");\n}\n\n/** Paths watched but never added to the dependency graph. */\nfunction isExternalPath(path: string): boolean {\n return isEnvFile(path) || path === \"warlock.config.ts\";\n}\n\nasync function runInBatches<T>(\n items: T[],\n size: number,\n fn: (item: T) => Promise<unknown>,\n): Promise<void> {\n if (items.length === 0) return;\n for (let i = 0; i < items.length; i += size) {\n await Promise.all(items.slice(i, i + size).map(fn));\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-manager.d.mts","names":[],"sources":["../../../../../../../core/src/dev-server/file-manager.ts"],"mappings":";;;;KASY,eAAA;;AAAZ;;;;AAA2B;AAyB3B;;cAAa,WAAA;EAAA,SAgCO,YAAA;EACT,KAAA,EAAO,GAAA,SAAY,WAAA;EACnB,cAAA,EAAgB,cAAA;EAjClB,YAAA;EACA,YAAA;EACA,IAAA;EACA,MAAA;EAiBM;EAdN,YAAA,EAAY,GAAA;EAsBL;;;;EAhBP,oBAAA,EAAoB,GAAA;EAwBkB;EArBtC,SAAA,EAAS,GAAA;EAmCiB;EAhC1B,UAAA,EAAU,GAAA;EAEV,IAAA,EAAM,QAAA;EAwDsD;EArD5D,OAAA,EAAS,eAAA;EAoIuC;EAjIhD,cAAA;EAEA,KAAA,EAAO,SAAA;cAGI,YAAA,UACT,KAAA,EAAO,GAAA,SAAY,WAAA,GACnB,cAAA,EAAgB,cAAA;EAGlB,UAAA,CAAW,OAAA,EAAS,eAAA,GAAkB,eAAA;EAMtC,YAAA;EAXW;;;;EAmBL,IAAA,CAAK,YAAA,GAAe,OAAA,CAAQ,YAAA,IAAgB,OAAA;EAjBhC;;;;;;;;;;;;;;;EA2CZ,OAAA;IAAU;EAAA;IAAmB,KAAA;EAAA,IAAyB,OAAA;EAhD5D;;;;;;EAAA,UA+FS,qBAAA,IAAyB,OAAA;EA1FhB;;;;;EAAA,UA0HT,gBAAA,CAAiB,YAAA,EAAc,OAAA,CAAQ,YAAA,IAAgB,OAAA;EAAA,UA6B7D,cAAA;
|
|
1
|
+
{"version":3,"file":"file-manager.d.mts","names":[],"sources":["../../../../../../../core/src/dev-server/file-manager.ts"],"mappings":";;;;KASY,eAAA;;AAAZ;;;;AAA2B;AAyB3B;;cAAa,WAAA;EAAA,SAgCO,YAAA;EACT,KAAA,EAAO,GAAA,SAAY,WAAA;EACnB,cAAA,EAAgB,cAAA;EAjClB,YAAA;EACA,YAAA;EACA,IAAA;EACA,MAAA;EAiBM;EAdN,YAAA,EAAY,GAAA;EAsBL;;;;EAhBP,oBAAA,EAAoB,GAAA;EAwBkB;EArBtC,SAAA,EAAS,GAAA;EAmCiB;EAhC1B,UAAA,EAAU,GAAA;EAEV,IAAA,EAAM,QAAA;EAwDsD;EArD5D,OAAA,EAAS,eAAA;EAoIuC;EAjIhD,cAAA;EAEA,KAAA,EAAO,SAAA;cAGI,YAAA,UACT,KAAA,EAAO,GAAA,SAAY,WAAA,GACnB,cAAA,EAAgB,cAAA;EAGlB,UAAA,CAAW,OAAA,EAAS,eAAA,GAAkB,eAAA;EAMtC,YAAA;EAXW;;;;EAmBL,IAAA,CAAK,YAAA,GAAe,OAAA,CAAQ,YAAA,IAAgB,OAAA;EAjBhC;;;;;;;;;;;;;;;EA2CZ,OAAA;IAAU;EAAA;IAAmB,KAAA;EAAA,IAAyB,OAAA;EAhD5D;;;;;;EAAA,UA+FS,qBAAA,IAAyB,OAAA;EA1FhB;;;;;EAAA,UA0HT,gBAAA,CAAiB,YAAA,EAAc,OAAA,CAAQ,YAAA,IAAgB,OAAA;EAAA,UA6B7D,cAAA;EAuBH,UAAA,IAAc,YAAA;AAAA"}
|
|
@@ -151,13 +151,13 @@ var FileManager = class {
|
|
|
151
151
|
}
|
|
152
152
|
detectFileType() {
|
|
153
153
|
const path = this.relativePath;
|
|
154
|
-
if (
|
|
154
|
+
if (/(^|\/)main\.tsx?$/.test(path)) this.type = "main";
|
|
155
|
+
else if (/\.model\.tsx?$/.test(path)) this.type = "model";
|
|
155
156
|
else if (path.startsWith("src/config/")) this.type = "config";
|
|
156
157
|
else if (path.endsWith("routes.ts") || path.endsWith("routes.tsx")) this.type = "route";
|
|
157
158
|
else if (path.includes("/events/")) this.type = "event";
|
|
158
159
|
else if (path.includes("controller")) this.type = "controller";
|
|
159
160
|
else if (path.includes("service")) this.type = "service";
|
|
160
|
-
else if (path.endsWith(".model.ts")) this.type = "model";
|
|
161
161
|
else this.type = "other";
|
|
162
162
|
}
|
|
163
163
|
toManifest() {
|