@warlock.js/core 5.2.2 → 5.2.4
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 +13 -0
- package/esm/application/application.d.mts.map +1 -1
- package/esm/application/application.mjs.map +1 -1
- package/esm/benchmark/benchmark-snapshots.d.mts.map +1 -1
- package/esm/benchmark/benchmark.d.mts.map +1 -1
- package/esm/benchmark/channels/console.channel.mjs.map +1 -1
- package/esm/benchmark/channels/noop.channel.d.mts.map +1 -1
- package/esm/benchmark/profiler.d.mts.map +1 -1
- package/esm/benchmark/profiler.mjs.map +1 -1
- package/esm/bootstrap/setup.d.mts.map +1 -1
- package/esm/bootstrap/setup.mjs.map +1 -1
- package/esm/bootstrap.d.mts.map +1 -1
- package/esm/cache/database-cache-driver.d.mts.map +1 -1
- package/esm/cache/database-cache-driver.mjs.map +1 -1
- package/esm/cli/cli-commands.manager.mjs.map +1 -1
- package/esm/cli/cli-commands.utils.mjs.map +1 -1
- package/esm/cli/commands/doctor/boot-for-diagnostics.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/connectors.check.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/handler-signature.check.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/health.check.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/optional-peers.check.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/routes.check.mjs.map +1 -1
- package/esm/cli/commands/generate/generate.command.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/column-dsl-parser.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/migration.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/model.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/templates/stubs.mjs.map +1 -1
- package/esm/cli/commands/generate/utils/name-parser.mjs.map +1 -1
- package/esm/cli/commands/generate/utils/prompt.mjs.map +1 -1
- package/esm/cli/commands/routes/diff-page-routes.mjs.map +1 -1
- package/esm/cli/commands/routes/format-routes-table.mjs.map +1 -1
- package/esm/cli/commands/routes/route-row.mjs.map +1 -1
- package/esm/cli/commands/routes/routes-command.action.mjs.map +1 -1
- package/esm/cli/commands/routes/routes-diff.action.mjs.map +1 -1
- package/esm/cli/commands/storage-put.action.mjs.map +1 -1
- package/esm/cli/commands/typings-generator.command.mjs.map +1 -1
- package/esm/cli/commands-loader.mjs.map +1 -1
- package/esm/cli/start.mjs.map +1 -1
- package/esm/cli/string-similarity.mjs.map +1 -1
- package/esm/commands/cli-command.mjs.map +1 -1
- package/esm/config/config-loader.mjs.map +1 -1
- package/esm/config/config-setter.d.mts.map +1 -1
- package/esm/config/load-config-files.mjs.map +1 -1
- package/esm/connectors/access-connector.d.mts.map +1 -1
- package/esm/connectors/ai-connector.d.mts.map +1 -1
- package/esm/connectors/assert-no-reserved-connector-names.mjs.map +1 -1
- package/esm/connectors/assert-unique-connector-names.mjs.map +1 -1
- package/esm/connectors/base-connector.d.mts.map +1 -1
- package/esm/connectors/base-connector.mjs.map +1 -1
- package/esm/connectors/cache-connector.d.mts.map +1 -1
- package/esm/connectors/connectors-manager.d.mts.map +1 -1
- package/esm/connectors/connectors-manager.mjs.map +1 -1
- package/esm/connectors/database-connector.d.mts.map +1 -1
- package/esm/connectors/describe-server-address.mjs.map +1 -1
- package/esm/connectors/herald-connector.d.mts.map +1 -1
- package/esm/connectors/http-connector.d.mts.map +1 -1
- package/esm/connectors/http-connector.mjs.map +1 -1
- package/esm/connectors/logger-connector.d.mts.map +1 -1
- package/esm/connectors/mail-connector.d.mts.map +1 -1
- package/esm/connectors/notifications-connector.d.mts.map +1 -1
- package/esm/connectors/register-configured-connectors.mjs.map +1 -1
- package/esm/connectors/socket-connector.d.mts.map +1 -1
- package/esm/connectors/socket-connector.mjs.map +1 -1
- package/esm/connectors/storage.connector.d.mts.map +1 -1
- package/esm/container/index.d.mts.map +1 -1
- package/esm/database/create-database-action.mjs.map +1 -1
- package/esm/database/drop-tables-action.mjs.map +1 -1
- package/esm/database/migrate-action.mjs.map +1 -1
- package/esm/database/seed-command-action.mjs.map +1 -1
- package/esm/database/seeds/seed-records-table-migration.mjs.map +1 -1
- package/esm/database/seeds/seeders.manager.mjs.map +1 -1
- package/esm/database/seeds/seeds-table-migration.mjs.map +1 -1
- package/esm/dev-server/check-for-updates.mjs.map +1 -1
- package/esm/dev-server/dependency-graph.d.mts.map +1 -1
- package/esm/dev-server/dependency-graph.mjs.map +1 -1
- package/esm/dev-server/dev-logger.mjs.map +1 -1
- package/esm/dev-server/development-server.mjs.map +1 -1
- 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.map +1 -1
- package/esm/dev-server/file-operations.d.mts.map +1 -1
- package/esm/dev-server/files-orchestrator.mjs.map +1 -1
- package/esm/dev-server/files-watcher.mjs.map +1 -1
- package/esm/dev-server/health-checker/checkers/eslint-health-checker.mjs.map +1 -1
- package/esm/dev-server/health-checker/checkers/typescript-health-checker.mjs.map +1 -1
- package/esm/dev-server/health-checker/file-health-result.d.mts.map +1 -1
- package/esm/dev-server/health-checker/file-health-result.mjs.map +1 -1
- package/esm/dev-server/health-checker/files-healthcare.manager.mjs.map +1 -1
- package/esm/dev-server/layer-executor.mjs.map +1 -1
- package/esm/dev-server/loader/hook-thread.d.mts.map +1 -1
- package/esm/dev-server/loader/own-resolver.mjs.map +1 -1
- package/esm/dev-server/loader/register-loader.mjs.map +1 -1
- package/esm/dev-server/loader/resolve-hook.mjs.map +1 -1
- package/esm/dev-server/loader/source-slug.mjs.map +1 -1
- package/esm/dev-server/loader/transpile-cache.mjs.map +1 -1
- package/esm/dev-server/loader/version-registry.mjs.map +1 -1
- package/esm/dev-server/manifest-manager.d.mts.map +1 -1
- package/esm/dev-server/manifest-manager.mjs.map +1 -1
- package/esm/dev-server/module-loader.mjs.map +1 -1
- package/esm/dev-server/package-json-manager.mjs.map +1 -1
- package/esm/dev-server/parse-imports.mjs.map +1 -1
- package/esm/dev-server/ready-block.mjs.map +1 -1
- package/esm/dev-server/special-files-collector.d.mts.map +1 -1
- package/esm/dev-server/special-files-collector.mjs.map +1 -1
- package/esm/dev-server/tsconfig-manager.mjs.map +1 -1
- package/esm/dev-server/type-generator.mjs.map +1 -1
- package/esm/dev-server/utils.mjs.map +1 -1
- package/esm/encryption/encrypt.mjs.map +1 -1
- package/esm/encryption/hash.mjs.map +1 -1
- package/esm/generations/add-command.action.mjs +28 -11
- package/esm/generations/add-command.action.mjs.map +1 -1
- package/esm/generations/features/shadcn.feature.mjs.map +1 -1
- package/esm/generations/features/shared/migration-timestamp.mjs.map +1 -1
- package/esm/generations/features/tailwind.feature.mjs.map +1 -1
- package/esm/generations/features/web.feature.mjs +8 -7
- package/esm/generations/features/web.feature.mjs.map +1 -1
- package/esm/generations/stubs.mjs +49 -33
- package/esm/generations/stubs.mjs.map +1 -1
- package/esm/http/boot-port-preflight.d.mts.map +1 -1
- package/esm/http/context/request-context.d.mts.map +1 -1
- package/esm/http/context/request-context.mjs.map +1 -1
- package/esm/http/context/request-memo.d.mts.map +1 -1
- package/esm/http/context/request-memo.mjs.map +1 -1
- package/esm/http/createHttpApplication.d.mts.map +1 -1
- package/esm/http/createHttpApplication.mjs.map +1 -1
- package/esm/http/health.d.mts.map +1 -1
- package/esm/http/middleware/cache-response-middleware.mjs.map +1 -1
- package/esm/http/middleware/idempotency.middleware.mjs.map +1 -1
- package/esm/http/middleware/inject-request-context.mjs.map +1 -1
- package/esm/http/middleware/maintenance.middleware.mjs.map +1 -1
- package/esm/http/middleware/utils/idempotency-key.mjs.map +1 -1
- package/esm/http/ready-report.d.mts.map +1 -1
- package/esm/http/request-controller.d.mts.map +1 -1
- package/esm/http/request.d.mts.map +1 -1
- package/esm/http/request.mjs.map +1 -1
- package/esm/http/response.d.mts.map +1 -1
- package/esm/http/response.mjs.map +1 -1
- package/esm/http/server.d.mts.map +1 -1
- package/esm/http/server.mjs.map +1 -1
- package/esm/http/uploaded-file.d.mts.map +1 -1
- package/esm/http/uploaded-file.mjs.map +1 -1
- package/esm/http/uploads-config.d.mts.map +1 -1
- package/esm/image/image.d.mts.map +1 -1
- package/esm/image/image.mjs.map +1 -1
- package/esm/logger/logger.mjs.map +1 -1
- package/esm/mail/config.d.mts.map +1 -1
- package/esm/mail/events.d.mts.map +1 -1
- package/esm/mail/mail.d.mts.map +1 -1
- package/esm/mail/mail.mjs.map +1 -1
- package/esm/mail/mailer-pool.d.mts.map +1 -1
- package/esm/mail/mailer-pool.mjs.map +1 -1
- package/esm/mail/send-mail.mjs.map +1 -1
- package/esm/mail/test-mailbox.d.mts.map +1 -1
- package/esm/mail/test-mailbox.mjs.map +1 -1
- package/esm/production/assert-generated-imports.mjs.map +1 -1
- package/esm/production/build-app-production.mjs.map +1 -1
- package/esm/production/dist-build-manifest.mjs.map +1 -1
- package/esm/production/production-builder.mjs.map +1 -1
- package/esm/production/production-supervisor.mjs.map +1 -1
- package/esm/production/promote-dist.mjs.map +1 -1
- package/esm/production/resolve-build-config.mjs.map +1 -1
- package/esm/repositories/adapters/cascade/cascade-adapter.d.mts.map +1 -1
- package/esm/repositories/adapters/cascade/cascade-adapter.mjs.map +1 -1
- package/esm/repositories/adapters/cascade/cascade-query-builder.d.mts.map +1 -1
- package/esm/repositories/adapters/cascade/cascade-query-builder.mjs.map +1 -1
- package/esm/repositories/adapters/cascade/filter-applicator.mjs.map +1 -1
- package/esm/repositories/repository.manager.d.mts.map +1 -1
- package/esm/repositories/repository.manager.mjs.map +1 -1
- package/esm/resource/register-resource.d.mts.map +1 -1
- package/esm/resource/resource-field-builder.d.mts.map +1 -1
- package/esm/resource/resource-field-builder.mjs.map +1 -1
- package/esm/resource/resource.d.mts.map +1 -1
- package/esm/resource/resource.mjs.map +1 -1
- package/esm/restful/restful.d.mts.map +1 -1
- package/esm/restful/restful.mjs.map +1 -1
- package/esm/router/log-request-lifecycle.mjs.map +1 -1
- package/esm/router/normalize-route-path.d.mts.map +1 -1
- package/esm/router/positional-handler-diagnostics.d.mts.map +1 -1
- package/esm/router/positional-handler-diagnostics.mjs.map +1 -1
- package/esm/router/route-registry.d.mts.map +1 -1
- package/esm/router/route-registry.mjs.map +1 -1
- package/esm/router/router.d.mts.map +1 -1
- package/esm/router/router.mjs.map +1 -1
- package/esm/socket/utils.d.mts.map +1 -1
- package/esm/storage/config.d.mts.map +1 -1
- package/esm/storage/context/storage-driver-context.d.mts.map +1 -1
- package/esm/storage/drivers/cloud-driver.d.mts.map +1 -1
- package/esm/storage/drivers/cloud-driver.mjs.map +1 -1
- package/esm/storage/drivers/do-spaces-driver.d.mts.map +1 -1
- package/esm/storage/drivers/local-driver.d.mts.map +1 -1
- package/esm/storage/drivers/local-driver.mjs.map +1 -1
- package/esm/storage/drivers/r2-driver.d.mts.map +1 -1
- package/esm/storage/scoped-storage.d.mts.map +1 -1
- package/esm/storage/scoped-storage.mjs.map +1 -1
- package/esm/storage/storage-file.d.mts.map +1 -1
- package/esm/storage/storage-file.mjs.map +1 -1
- package/esm/storage/storage.d.mts.map +1 -1
- package/esm/storage/utils/safe-fetch.mjs.map +1 -1
- package/esm/tests/start-http-development-server.d.mts.map +1 -1
- package/esm/tests/test-connectors-selection.mjs.map +1 -1
- package/esm/tests/test-helpers.d.mts.map +1 -1
- package/esm/tests/test-setup-timeout.mjs.map +1 -1
- package/esm/tests/vitest-setup.d.mts.map +1 -1
- package/esm/tests/vitest-setup.mjs.map +1 -1
- package/esm/updater/package-manager.mjs +15 -1
- package/esm/updater/package-manager.mjs.map +1 -1
- package/esm/updater/update-warlock-packages.mjs.map +1 -1
- package/esm/use-cases/use-case-broadcast.d.mts.map +1 -1
- package/esm/use-cases/use-case-events.d.mts.map +1 -1
- package/esm/use-cases/use-case-pipeline.d.mts.map +1 -1
- package/esm/use-cases/use-case.d.mts.map +1 -1
- package/esm/use-cases/use-cases-registry.d.mts.map +1 -1
- package/esm/use-cases/use-cases-registry.mjs.map +1 -1
- package/esm/utils/database-log.d.mts.map +1 -1
- package/esm/utils/database-log.mjs.map +1 -1
- package/esm/utils/environment.d.mts.map +1 -1
- package/esm/utils/framework-vesion.mjs.map +1 -1
- package/esm/utils/get-localized.mjs.map +1 -1
- package/esm/utils/load-environment.mjs.map +1 -1
- package/esm/utils/normalized-path.d.mts.map +1 -1
- package/esm/utils/paths.d.mts.map +1 -1
- package/esm/utils/promise-all-object.d.mts.map +1 -1
- package/esm/utils/promise-all-object.mjs.map +1 -1
- package/esm/utils/sluggable.mjs.map +1 -1
- package/esm/utils/version-compare.mjs.map +1 -1
- package/esm/validation/database/exists-except-current-id.mjs.map +1 -1
- package/esm/validation/database/exists-except-current-user.mjs.map +1 -1
- package/esm/validation/database/unique-except-current-id.mjs.map +1 -1
- package/esm/validation/plugins/localized-plugin.mjs.map +1 -1
- package/esm/validation/validators/file-validator.mjs.map +1 -1
- package/esm/vite/lower-stage3-decorators.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.map +1 -1
- package/llms-full.txt +4 -1
- package/llms.txt +1 -1
- package/package.json +12 -12
- package/skills/README.md +1 -1
- package/skills/write-cli-command/SKILL.md +4 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-cases-registry.mjs","names":[],"sources":["../../../../../../../core/src/use-cases/use-cases-registry.ts"],"sourcesContent":["import { cache } from \"@warlock.js/cache\";\nimport { log } from \"@warlock.js/logger\";\nimport { config } from \"../config\";\nimport { type RegisteredUseCase, type UseCaseConfigurations, type UseCaseResult } from \"./types\";\n\n/**\n * Store registered use cases\n */\nconst useCaseRegister = new Map<string, RegisteredUseCase>();\n\n/**\n * Register a use case\n */\nexport function $registerUseCase<Output, Input>(\n name: string,\n useCase: RegisteredUseCase<Output, Input>,\n) {\n if (useCaseRegister.has(name) && process.env.NODE_ENV !== \"production\") {\n log.warn(\n \"use-cases\",\n \"registering\",\n `Use case \"${name}\" is already registered. Overwriting.`,\n );\n }\n\n useCaseRegister.set(name, useCase);\n}\n\n/**\n * Unregister a use case.\n *\n * Dropping the history namespace is best-effort: `$cleanup` is synchronous, so the\n * async cache removal is fire-and-forget with a `.catch` guard — a rejecting cache\n * driver must never turn cleanup into an unhandled rejection.\n */\nexport function $unregisterUseCase(name: string) {\n useCaseRegister.delete(name);\n\n Promise.resolve(cache.removeNamespace(`use-case:history:${name}`)).catch((error) => {\n log.error(\"use-cases\", name, \"history namespace removal failed\", { error });\n });\n}\n\n/**\n * Get a use case.\n *\n * Returns the `RegisteredUseCase` shape — including the runtime `calls` counters\n * the registry actually stores — so consumers can read call stats without an\n * ad-hoc cast.\n */\nexport function getUseCase<Output, Input>(name: string) {\n return useCaseRegister.get(name) as RegisteredUseCase<Output, Input> | undefined;\n}\n\n/**\n * Get all use cases\n */\nexport function getUseCases() {\n return useCaseRegister;\n}\n\n/**\n * Increase use case success calls\n */\nexport function increaseUseCaseSuccessCalls(name: string): number {\n const useCase = useCaseRegister.get(name);\n if (useCase) {\n useCase.calls.success++;\n useCase.calls.total++;\n\n return useCase.calls.success;\n }\n\n return 0;\n}\n\n/**\n * Increase use case failed calls\n */\nexport function increaseUseCaseFailedCalls(name: string): number {\n const useCase = useCaseRegister.get(name);\n if (useCase) {\n useCase.calls.failed++;\n useCase.calls.total++;\n\n return useCase.calls.failed;\n }\n\n return 0;\n}\n\n/**\n * Resolve the history cache TTL (seconds) from config.\n * `false` means \"use the cache driver's default\", represented as `undefined`.\n */\nfunction resolveHistoryTtl(useCaseConfig?: UseCaseConfigurations): number | undefined {\n const ttlConfig = useCaseConfig?.history?.ttl ?? 3600; // 1 hour default\n\n return ttlConfig === false ? undefined : ttlConfig;\n}\n\n/**\n * Get use case history from cache.\n *\n * Prunes ids whose entries have expired so the list can't accumulate dead refs.\n */\nexport async function getUseCaseHistory(name: string): Promise<UseCaseResult<any>[]> {\n const listKey = `use-case:history:${name}:list`;\n const ids = (await cache.get<string[]>(listKey)) || [];\n\n const entries = await Promise.all(\n ids.map(async (id: string) => ({\n id,\n result: await cache.get<UseCaseResult<any>>(`use-case:history:${name}:${id}`),\n })),\n );\n\n const liveEntries = entries.filter((entry) => Boolean(entry.result));\n\n if (liveEntries.length !== ids.length) {\n const useCaseConfig = config.get<UseCaseConfigurations>(\"use-cases\");\n\n await cache.set(\n listKey,\n liveEntries.map((entry) => entry.id),\n resolveHistoryTtl(useCaseConfig),\n );\n }\n\n return liveEntries.map((entry) => entry.result) as UseCaseResult<any>[];\n}\n\n/**\n * Add use case history to cache, capping the per-use-case list so it can't grow\n * unbounded — oldest entries (and their ids) are evicted past MAX_HISTORY_ENTRIES.\n */\nexport async function addUseCaseHistory(name: string, result: UseCaseResult<any>) {\n const useCaseConfig = config.get<UseCaseConfigurations>(\"use-cases\");\n\n if (useCaseConfig?.history?.enabled === false) return;\n\n const ttl = resolveHistoryTtl(useCaseConfig);\n const maxEntries = useCaseConfig?.history?.maxEntries ?? 100;\n const key = `use-case:history:${name}:${result.id}`;\n const listKey = `use-case:history:${name}:list`;\n\n await cache.set(key, result, ttl);\n\n const list = (await cache.get<string[]>(listKey)) || [];\n\n list.push(result.id);\n\n while (list.length > maxEntries) {\n const evictedId = list.shift();\n\n if (evictedId) {\n await cache.remove(`use-case:history:${name}:${evictedId}`);\n }\n }\n\n await cache.set(listKey, list, ttl);\n}\n"],"mappings":";;;;;;;;;AAQA,MAAM,kCAAkB,IAAI,IAA+B;;;;AAK3D,SAAgB,iBACd,MACA,SACA;CACA,IAAI,gBAAgB,IAAI,IAAI,KAAK,QAAQ,IAAI,aAAa,cACxD,IAAI,KACF,aACA,eACA,aAAa,KAAK,sCACpB;CAGF,gBAAgB,IAAI,MAAM,OAAO;AACnC;;;;;;;;AASA,SAAgB,mBAAmB,MAAc;CAC/C,gBAAgB,OAAO,IAAI;CAE3B,QAAQ,QAAQ,MAAM,gBAAgB,oBAAoB,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"use-cases-registry.mjs","names":[],"sources":["../../../../../../../core/src/use-cases/use-cases-registry.ts"],"sourcesContent":["import { cache } from \"@warlock.js/cache\";\nimport { log } from \"@warlock.js/logger\";\nimport { config } from \"../config\";\nimport { type RegisteredUseCase, type UseCaseConfigurations, type UseCaseResult } from \"./types\";\n\n/**\n * Store registered use cases\n */\nconst useCaseRegister = new Map<string, RegisteredUseCase>();\n\n/**\n * Register a use case\n */\nexport function $registerUseCase<Output, Input>(\n name: string,\n useCase: RegisteredUseCase<Output, Input>,\n) {\n if (useCaseRegister.has(name) && process.env.NODE_ENV !== \"production\") {\n log.warn(\n \"use-cases\",\n \"registering\",\n `Use case \"${name}\" is already registered. Overwriting.`,\n );\n }\n\n useCaseRegister.set(name, useCase);\n}\n\n/**\n * Unregister a use case.\n *\n * Dropping the history namespace is best-effort: `$cleanup` is synchronous, so the\n * async cache removal is fire-and-forget with a `.catch` guard — a rejecting cache\n * driver must never turn cleanup into an unhandled rejection.\n */\nexport function $unregisterUseCase(name: string) {\n useCaseRegister.delete(name);\n\n Promise.resolve(cache.removeNamespace(`use-case:history:${name}`)).catch((error) => {\n log.error(\"use-cases\", name, \"history namespace removal failed\", { error });\n });\n}\n\n/**\n * Get a use case.\n *\n * Returns the `RegisteredUseCase` shape — including the runtime `calls` counters\n * the registry actually stores — so consumers can read call stats without an\n * ad-hoc cast.\n */\nexport function getUseCase<Output, Input>(name: string) {\n return useCaseRegister.get(name) as RegisteredUseCase<Output, Input> | undefined;\n}\n\n/**\n * Get all use cases\n */\nexport function getUseCases() {\n return useCaseRegister;\n}\n\n/**\n * Increase use case success calls\n */\nexport function increaseUseCaseSuccessCalls(name: string): number {\n const useCase = useCaseRegister.get(name);\n if (useCase) {\n useCase.calls.success++;\n useCase.calls.total++;\n\n return useCase.calls.success;\n }\n\n return 0;\n}\n\n/**\n * Increase use case failed calls\n */\nexport function increaseUseCaseFailedCalls(name: string): number {\n const useCase = useCaseRegister.get(name);\n if (useCase) {\n useCase.calls.failed++;\n useCase.calls.total++;\n\n return useCase.calls.failed;\n }\n\n return 0;\n}\n\n/**\n * Resolve the history cache TTL (seconds) from config.\n * `false` means \"use the cache driver's default\", represented as `undefined`.\n */\nfunction resolveHistoryTtl(useCaseConfig?: UseCaseConfigurations): number | undefined {\n const ttlConfig = useCaseConfig?.history?.ttl ?? 3600; // 1 hour default\n\n return ttlConfig === false ? undefined : ttlConfig;\n}\n\n/**\n * Get use case history from cache.\n *\n * Prunes ids whose entries have expired so the list can't accumulate dead refs.\n */\nexport async function getUseCaseHistory(name: string): Promise<UseCaseResult<any>[]> {\n const listKey = `use-case:history:${name}:list`;\n const ids = (await cache.get<string[]>(listKey)) || [];\n\n const entries = await Promise.all(\n ids.map(async (id: string) => ({\n id,\n result: await cache.get<UseCaseResult<any>>(`use-case:history:${name}:${id}`),\n })),\n );\n\n const liveEntries = entries.filter((entry) => Boolean(entry.result));\n\n if (liveEntries.length !== ids.length) {\n const useCaseConfig = config.get<UseCaseConfigurations>(\"use-cases\");\n\n await cache.set(\n listKey,\n liveEntries.map((entry) => entry.id),\n resolveHistoryTtl(useCaseConfig),\n );\n }\n\n return liveEntries.map((entry) => entry.result) as UseCaseResult<any>[];\n}\n\n/**\n * Add use case history to cache, capping the per-use-case list so it can't grow\n * unbounded — oldest entries (and their ids) are evicted past MAX_HISTORY_ENTRIES.\n */\nexport async function addUseCaseHistory(name: string, result: UseCaseResult<any>) {\n const useCaseConfig = config.get<UseCaseConfigurations>(\"use-cases\");\n\n if (useCaseConfig?.history?.enabled === false) return;\n\n const ttl = resolveHistoryTtl(useCaseConfig);\n const maxEntries = useCaseConfig?.history?.maxEntries ?? 100;\n const key = `use-case:history:${name}:${result.id}`;\n const listKey = `use-case:history:${name}:list`;\n\n await cache.set(key, result, ttl);\n\n const list = (await cache.get<string[]>(listKey)) || [];\n\n list.push(result.id);\n\n while (list.length > maxEntries) {\n const evictedId = list.shift();\n\n if (evictedId) {\n await cache.remove(`use-case:history:${name}:${evictedId}`);\n }\n }\n\n await cache.set(listKey, list, ttl);\n}\n"],"mappings":";;;;;;;;;AAQA,MAAM,kCAAkB,IAAI,IAA+B;;;;AAK3D,SAAgB,iBACd,MACA,SACA;CACA,IAAI,gBAAgB,IAAI,IAAI,KAAK,QAAQ,IAAI,aAAa,cACxD,IAAI,KACF,aACA,eACA,aAAa,KAAK,sCACpB;CAGF,gBAAgB,IAAI,MAAM,OAAO;AACnC;;;;;;;;AASA,SAAgB,mBAAmB,MAAc;CAC/C,gBAAgB,OAAO,IAAI;CAE3B,QAAQ,QAAQ,MAAM,gBAAgB,oBAAoB,MAAM,CAAC,CAAC,CAAC,OAAO,UAAU;EAClF,IAAI,MAAM,aAAa,MAAM,oCAAoC,EAAE,MAAM,CAAC;CAC5E,CAAC;AACH;;;;;;;;AASA,SAAgB,WAA0B,MAAc;CACtD,OAAO,gBAAgB,IAAI,IAAI;AACjC;;;;AAKA,SAAgB,cAAc;CAC5B,OAAO;AACT;;;;AAKA,SAAgB,4BAA4B,MAAsB;CAChE,MAAM,UAAU,gBAAgB,IAAI,IAAI;CACxC,IAAI,SAAS;EACX,QAAQ,MAAM;EACd,QAAQ,MAAM;EAEd,OAAO,QAAQ,MAAM;CACvB;CAEA,OAAO;AACT;;;;AAKA,SAAgB,2BAA2B,MAAsB;CAC/D,MAAM,UAAU,gBAAgB,IAAI,IAAI;CACxC,IAAI,SAAS;EACX,QAAQ,MAAM;EACd,QAAQ,MAAM;EAEd,OAAO,QAAQ,MAAM;CACvB;CAEA,OAAO;AACT;;;;;AAMA,SAAS,kBAAkB,eAA2D;CACpF,MAAM,YAAY,eAAe,SAAS,OAAO;CAEjD,OAAO,cAAc,QAAQ,SAAY;AAC3C;;;;;;AAOA,eAAsB,kBAAkB,MAA6C;CACnF,MAAM,UAAU,oBAAoB,KAAK;CACzC,MAAM,MAAO,MAAM,MAAM,IAAc,OAAO,KAAM,CAAC;CASrD,MAAM,eAAc,MAPE,QAAQ,IAC5B,IAAI,IAAI,OAAO,QAAgB;EAC7B;EACA,QAAQ,MAAM,MAAM,IAAwB,oBAAoB,KAAK,GAAG,IAAI;CAC9E,EAAE,CACJ,EAE2B,CAAC,QAAQ,UAAU,QAAQ,MAAM,MAAM,CAAC;CAEnE,IAAI,YAAY,WAAW,IAAI,QAAQ;EACrC,MAAM,gBAAgB,OAAO,IAA2B,WAAW;EAEnE,MAAM,MAAM,IACV,SACA,YAAY,KAAK,UAAU,MAAM,EAAE,GACnC,kBAAkB,aAAa,CACjC;CACF;CAEA,OAAO,YAAY,KAAK,UAAU,MAAM,MAAM;AAChD;;;;;AAMA,eAAsB,kBAAkB,MAAc,QAA4B;CAChF,MAAM,gBAAgB,OAAO,IAA2B,WAAW;CAEnE,IAAI,eAAe,SAAS,YAAY,OAAO;CAE/C,MAAM,MAAM,kBAAkB,aAAa;CAC3C,MAAM,aAAa,eAAe,SAAS,cAAc;CACzD,MAAM,MAAM,oBAAoB,KAAK,GAAG,OAAO;CAC/C,MAAM,UAAU,oBAAoB,KAAK;CAEzC,MAAM,MAAM,IAAI,KAAK,QAAQ,GAAG;CAEhC,MAAM,OAAQ,MAAM,MAAM,IAAc,OAAO,KAAM,CAAC;CAEtD,KAAK,KAAK,OAAO,EAAE;CAEnB,OAAO,KAAK,SAAS,YAAY;EAC/B,MAAM,YAAY,KAAK,MAAM;EAE7B,IAAI,WACF,MAAM,MAAM,OAAO,oBAAoB,KAAK,GAAG,WAAW;CAE9D;CAEA,MAAM,MAAM,IAAI,SAAS,MAAM,GAAG;AACpC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"database-log.d.mts","names":[],"sources":["../../../../../../../core/src/utils/database-log.ts"],"mappings":";;;;;KAKY,kBAAA,GAAqB,sBAAA;;AAAjC;;EAIE,KAAA,UAAe,gBAAgB;AAAA;AAAA,cAGpB,WAAA,SAAoB,UAAA,CAAW,kBAAA,aAA+B,WAAA;EAHzE;;;EAOO,IAAA;EAJI;;;EAAA,IASA,KAAA,
|
|
1
|
+
{"version":3,"file":"database-log.d.mts","names":[],"sources":["../../../../../../../core/src/utils/database-log.ts"],"mappings":";;;;;KAKY,kBAAA,GAAqB,sBAAA;;AAAjC;;EAIE,KAAA,UAAe,gBAAgB;AAAA;AAAA,cAGpB,WAAA,SAAoB,UAAA,CAAW,kBAAA,aAA+B,WAAA;EAHzE;;;EAOO,IAAA;EAJI;;;EAAA,IASA,KAAA,IAAS,UAAA,CAAW,KAAA;EAAA;;;EAOlB,GAAA,CAAI,GAAA,EAAK,WAAA,GAAW,OAAA;AAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"database-log.mjs","names":[],"sources":["../../../../../../../core/src/utils/database-log.ts"],"sourcesContent":["import type { ChildModel, Model } from \"@warlock.js/cascade\";\nimport type { BasicLogConfigurations, LoggingData } from \"@warlock.js/logger\";\nimport { LogChannel, type LogContract, type LogMessage } from \"@warlock.js/logger\";\nimport { DatabaseLogModel } from \"../database/models/database-log\";\n\nexport type DatabaseLogOptions = BasicLogConfigurations & {\n /**\n * Model to use for logging\n */\n model?: typeof DatabaseLogModel;\n};\n\nexport class DatabaseLog extends LogChannel<DatabaseLogOptions> implements LogContract {\n /**\n * {@inheritdoc}\n */\n public name = \"database\";\n\n /**\n * Database model\n */\n public get model(): ChildModel<Model> {\n return this.config(\"model\") ?? DatabaseLogModel;\n }\n\n /**\n * {@inheritdoc}\n */\n public async log(log: LoggingData) {\n const { module, action, message, type: level } = log;\n\n if (!this.model.getDataSource().driver?.isConnected) return;\n\n if (!this.shouldBeLogged(log)) return;\n\n const data: LogMessage = {\n module,\n action,\n content: message,\n level,\n date: new Date().toISOString(),\n };\n\n if (message instanceof Error) {\n data.stack = message.stack;\n data.content = message.message;\n } else {\n data.content = message;\n data.stack = new Error().stack;\n }\n\n try {\n await this.model.create(data);\n } catch (error) {\n console.log(\"Error\", error);\n }\n }\n}\n"],"mappings":";;;;;AAYA,IAAa,cAAb,cAAiC,WAAsD;;;cAIvE;;;;;CAKd,IAAW,QAA2B;EACpC,OAAO,KAAK,OAAO,OAAO,KAAK;CACjC;;;;CAKA,MAAa,IAAI,KAAkB;EACjC,MAAM,EAAE,QAAQ,QAAQ,SAAS,MAAM,UAAU;EAEjD,IAAI,CAAC,KAAK,MAAM,cAAc,
|
|
1
|
+
{"version":3,"file":"database-log.mjs","names":[],"sources":["../../../../../../../core/src/utils/database-log.ts"],"sourcesContent":["import type { ChildModel, Model } from \"@warlock.js/cascade\";\nimport type { BasicLogConfigurations, LoggingData } from \"@warlock.js/logger\";\nimport { LogChannel, type LogContract, type LogMessage } from \"@warlock.js/logger\";\nimport { DatabaseLogModel } from \"../database/models/database-log\";\n\nexport type DatabaseLogOptions = BasicLogConfigurations & {\n /**\n * Model to use for logging\n */\n model?: typeof DatabaseLogModel;\n};\n\nexport class DatabaseLog extends LogChannel<DatabaseLogOptions> implements LogContract {\n /**\n * {@inheritdoc}\n */\n public name = \"database\";\n\n /**\n * Database model\n */\n public get model(): ChildModel<Model> {\n return this.config(\"model\") ?? DatabaseLogModel;\n }\n\n /**\n * {@inheritdoc}\n */\n public async log(log: LoggingData) {\n const { module, action, message, type: level } = log;\n\n if (!this.model.getDataSource().driver?.isConnected) return;\n\n if (!this.shouldBeLogged(log)) return;\n\n const data: LogMessage = {\n module,\n action,\n content: message,\n level,\n date: new Date().toISOString(),\n };\n\n if (message instanceof Error) {\n data.stack = message.stack;\n data.content = message.message;\n } else {\n data.content = message;\n data.stack = new Error().stack;\n }\n\n try {\n await this.model.create(data);\n } catch (error) {\n console.log(\"Error\", error);\n }\n }\n}\n"],"mappings":";;;;;AAYA,IAAa,cAAb,cAAiC,WAAsD;;;cAIvE;;;;;CAKd,IAAW,QAA2B;EACpC,OAAO,KAAK,OAAO,OAAO,KAAK;CACjC;;;;CAKA,MAAa,IAAI,KAAkB;EACjC,MAAM,EAAE,QAAQ,QAAQ,SAAS,MAAM,UAAU;EAEjD,IAAI,CAAC,KAAK,MAAM,cAAc,CAAC,CAAC,QAAQ,aAAa;EAErD,IAAI,CAAC,KAAK,eAAe,GAAG,GAAG;EAE/B,MAAM,OAAmB;GACvB;GACA;GACA,SAAS;GACT;GACA,uBAAM,IAAI,KAAK,EAAC,CAAC,YAAY;EAC/B;EAEA,IAAI,mBAAmB,OAAO;GAC5B,KAAK,QAAQ,QAAQ;GACrB,KAAK,UAAU,QAAQ;EACzB,OAAO;GACL,KAAK,UAAU;GACf,KAAK,yBAAQ,IAAI,MAAM,EAAC,CAAC;EAC3B;EAEA,IAAI;GACF,MAAM,KAAK,MAAM,OAAO,IAAI;EAC9B,SAAS,OAAO;GACd,QAAQ,IAAI,SAAS,KAAK;EAC5B;CACF;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"environment.d.mts","names":[],"sources":["../../../../../../../core/src/utils/environment.ts"],"mappings":";KAAY,WAAA;AAAZ;;;;AAAA,KAMY,eAAA;AAAA,iBAEI,WAAA,
|
|
1
|
+
{"version":3,"file":"environment.d.mts","names":[],"sources":["../../../../../../../core/src/utils/environment.ts"],"mappings":";KAAY,WAAA;AAAZ;;;;AAAA,KAMY,eAAA;AAAA,iBAEI,WAAA,IAAe,WAAW;AAAA,iBAI1B,cAAA,CAAe,GAAgB,EAAX,WAAW"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"framework-vesion.mjs","names":[],"sources":["../../../../../../../core/src/utils/framework-vesion.ts"],"sourcesContent":["import { getJsonFileAsync } from \"@warlock.js/fs\";\r\nimport path from \"node:path\";\r\n\r\n/**\r\n * Cached version string\r\n */\r\nlet cachedVersion: string | null = null;\r\n\r\n/**\r\n * Get the framework version from package.json (cached)\r\n */\r\nexport async function getWarlockVersion(): Promise<string> {\r\n if (cachedVersion) return cachedVersion;\r\n\r\n const frameworkPackageJson = (await getJsonFileAsync(\r\n path.join(import.meta.dirname, \"./../../package.json\"),\r\n )) as { version: string };\r\n\r\n const version = frameworkPackageJson.version.replace(/\\^|\\~/g, \"\");\r\n cachedVersion = version;\r\n return version;\r\n}\r\n\r\nexport function getFrameworkVersion(): string | null {\r\n return cachedVersion;\r\n}\r\n"],"mappings":";;;;;;;AAMA,IAAI,gBAA+B;;;;AAKnC,eAAsB,oBAAqC;CACzD,IAAI,eAAe,OAAO;CAM1B,MAAM,WAAU,MAJoB,iBAClC,KAAK,KAAK,OAAO,KAAK,SAAS,sBAAsB,CACvD,
|
|
1
|
+
{"version":3,"file":"framework-vesion.mjs","names":[],"sources":["../../../../../../../core/src/utils/framework-vesion.ts"],"sourcesContent":["import { getJsonFileAsync } from \"@warlock.js/fs\";\r\nimport path from \"node:path\";\r\n\r\n/**\r\n * Cached version string\r\n */\r\nlet cachedVersion: string | null = null;\r\n\r\n/**\r\n * Get the framework version from package.json (cached)\r\n */\r\nexport async function getWarlockVersion(): Promise<string> {\r\n if (cachedVersion) return cachedVersion;\r\n\r\n const frameworkPackageJson = (await getJsonFileAsync(\r\n path.join(import.meta.dirname, \"./../../package.json\"),\r\n )) as { version: string };\r\n\r\n const version = frameworkPackageJson.version.replace(/\\^|\\~/g, \"\");\r\n cachedVersion = version;\r\n return version;\r\n}\r\n\r\nexport function getFrameworkVersion(): string | null {\r\n return cachedVersion;\r\n}\r\n"],"mappings":";;;;;;;AAMA,IAAI,gBAA+B;;;;AAKnC,eAAsB,oBAAqC;CACzD,IAAI,eAAe,OAAO;CAM1B,MAAM,WAAU,MAJoB,iBAClC,KAAK,KAAK,OAAO,KAAK,SAAS,sBAAsB,CACvD,EAEoC,CAAC,QAAQ,QAAQ,UAAU,EAAE;CACjE,gBAAgB;CAChB,OAAO;AACT;AAEA,SAAgB,sBAAqC;CACnD,OAAO;AACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-localized.mjs","names":[],"sources":["../../../../../../../core/src/utils/get-localized.ts"],"sourcesContent":["import { useRequestStore } from \"../http/context/request-context\";\n\nexport type LocalizedObject = {\n localeCode: string;\n value: any;\n};\n\n/**\n * Get localized value based on the given locale code\n * If the locale code is not given and the function is called within a request context, it will get the current locale code\n */\nexport function getLocalized(values: LocalizedObject[], localeCode?: string, key = \"value\") {\n if (!values) return values;\n\n if (!localeCode) {\n localeCode = useRequestStore().request?.getLocaleCode();\n }\n\n if (Array.isArray(values)) {\n return values.find((value) => value.localeCode === localeCode)?.[key as keyof LocalizedObject];\n }\n\n return values;\n}\n"],"mappings":";;;;;;;AAWA,SAAgB,aAAa,QAA2B,YAAqB,MAAM,SAAS;CAC1F,IAAI,CAAC,QAAQ,OAAO;CAEpB,IAAI,CAAC,YACH,aAAa,gBAAgB,
|
|
1
|
+
{"version":3,"file":"get-localized.mjs","names":[],"sources":["../../../../../../../core/src/utils/get-localized.ts"],"sourcesContent":["import { useRequestStore } from \"../http/context/request-context\";\n\nexport type LocalizedObject = {\n localeCode: string;\n value: any;\n};\n\n/**\n * Get localized value based on the given locale code\n * If the locale code is not given and the function is called within a request context, it will get the current locale code\n */\nexport function getLocalized(values: LocalizedObject[], localeCode?: string, key = \"value\") {\n if (!values) return values;\n\n if (!localeCode) {\n localeCode = useRequestStore().request?.getLocaleCode();\n }\n\n if (Array.isArray(values)) {\n return values.find((value) => value.localeCode === localeCode)?.[key as keyof LocalizedObject];\n }\n\n return values;\n}\n"],"mappings":";;;;;;;AAWA,SAAgB,aAAa,QAA2B,YAAqB,MAAM,SAAS;CAC1F,IAAI,CAAC,QAAQ,OAAO;CAEpB,IAAI,CAAC,YACH,aAAa,gBAAgB,CAAC,CAAC,SAAS,cAAc;CAGxD,IAAI,MAAM,QAAQ,MAAM,GACtB,OAAO,OAAO,MAAM,UAAU,MAAM,eAAe,UAAU,CAAC,GAAG;CAGnE,OAAO;AACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load-environment.mjs","names":[],"sources":["../../../../../../../core/src/utils/load-environment.ts"],"sourcesContent":["import { loadEnv, type EnvLoaderOptions } from \"@mongez/dotenv\";\nimport { existsSync } from \"node:fs\";\nimport path from \"node:path\";\n\n/**\n * Core's env precedence policy, in one place because two callers load env:\n * this module at boot, and the dev server when an `.env` file changes.\n *\n * `@mongez/dotenv` defaults to `precedence: \"file-wins\"`, which is backwards.\n * The `.env` file is a checked-in DEFAULT; a variable already exported into\n * `process.env` is the deliberate, situational override — a second instance on\n * another port, CI pointed at another database, a container's configuration.\n * Under the default, `PORT=6060 warlock dev` produced a server on the `.env`\n * file's 3000 and said nothing. A default that silently beats an explicit\n * instruction is the wrong way round, so core opts into `process-wins`:\n * the file supplies only the keys the environment does not already carry.\n *\n * EMPTY STRING — `FOO=` in the environment counts as SET, and wins, so the\n * file's value is discarded and `env(\"FOO\")` returns `\"\"`. Chosen over\n * \"empty means absent\" because the loader cannot tell a deliberate blanking\n * from an accident, and only one of the two readings is expressible: an\n * operator who wants the file's value can unset the variable, whereas under\n * \"empty means absent\" an operator who wants a blank value has no way to ask\n * for one. It also matches POSIX (an exported empty variable is set), dotenv,\n * dotenv-flow and Vite. Note the consequence: a blank export beats the second\n * argument too, so `env(\"FOO\", \"fallback\")` yields `\"\"`, not the fallback.\n *\n * Keys this loader itself wrote are tracked by the library and are NOT treated\n * as process-provided, so editing `.env` during a dev session still takes\n * effect on reload rather than being pinned by the previous load's own writes.\n */\nexport const environmentLoaderOptions: EnvLoaderOptions = {\n precedence: \"process-wins\",\n};\n\n/**\n * The files `loadEnv()` will look for, in the order it considers them.\n *\n * Mirrors `@mongez/dotenv`'s own resolution — `.env.shared` first, then\n * `.env.<NODE_ENV>` if it exists, else plain `.env` — because the presence\n * check has to ask the same question the loader is about to ask.\n */\nfunction candidateEnvFiles(directory: string): string[] {\n const files = [path.join(directory, \".env\"), path.join(directory, \".env.shared\")];\n\n if (process.env.NODE_ENV) {\n files.push(path.join(directory, `.env.${process.env.NODE_ENV}`));\n }\n\n return files;\n}\n\n/**\n * Whether this process has already loaded its env files.\n *\n * Two callers reach here on a bootstrapping command — the CLI preload phase and\n * `bootstrap()` — and `loadEnv()` defaults to `override: true`, so a second pass\n * re-parses the same files and re-writes `process.env`. Anything set in between\n * (a `warlock.config.ts` module body, a config file, an orchestrator) would be\n * silently clobbered by the later load. Same shape as the defect where a\n * caller's test-server port was overwritten by a re-read of `.env`.\n *\n * A deliberate reload is still possible and still used: the dev server calls\n * `loadEnv()` directly when an `.env` file changes on disk, which is a reload\n * the user asked for rather than an accidental second pass.\n */\nlet environmentLoaded = false;\n\n/**\n * Load `.env` files when there are any, and do nothing when there are not.\n *\n * Loads at most **once per process** — see {@link environmentLoaded}.\n *\n * `@mongez/dotenv`'s `loadEnvFile` **throws** when the file is absent. That was\n * survivable while only `dev` loaded env; now that every command loads it —\n * because `warlock.config.ts` calls `env()` in its module body and had been\n * evaluated against an empty store under every command — an unguarded call\n * would turn \"this project has no .env\" from a non-event into a hard failure\n * of `warlock build`. A project without a `.env` is legitimate: config files\n * carry defaults, and containers inject their own variables.\n *\n * The guard lives here rather than waiting on a fix in `@mongez/dotenv`: core\n * decides its own env policy and must not depend on another package's release\n * schedule to avoid crashing a working build.\n */\nexport async function loadEnvironmentFiles(directory: string = process.cwd()): Promise<void> {\n if (environmentLoaded) {\n return;\n }\n\n const hasEnvFile = candidateEnvFiles(directory).some((file) => existsSync(file));\n\n if (!hasEnvFile) {\n // Not latched: a project may create its `.env` between a command's preload\n // and its bootstrap, and there is nothing to protect from a second override\n // when the first pass loaded nothing.\n return;\n }\n\n environmentLoaded = true;\n\n // `dir` has to be forwarded: `loadEnv()` defaults it to `process.cwd()`, so\n // without this the existence check above asks about `directory` while the\n // load itself reads somewhere else entirely. Identical in production, where\n // `directory` IS `process.cwd()`.\n await loadEnv(undefined, { ...environmentLoaderOptions, dir: directory });\n}\n\n/**\n * Forget that env has been loaded, so the next call loads again.\n *\n * @internal For tests, which run many boots inside one process.\n */\nexport function resetLoadedEnvironment(): void {\n environmentLoaded = false;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,MAAa,2BAA6C,EACxD,YAAY,eACd;;;;;;;;AASA,SAAS,kBAAkB,WAA6B;CACtD,MAAM,QAAQ,CAAC,KAAK,KAAK,WAAW,MAAM,GAAG,KAAK,KAAK,WAAW,aAAa,CAAC;CAEhF,IAAI,QAAQ,IAAI,UACd,MAAM,KAAK,KAAK,KAAK,WAAW,QAAQ,QAAQ,IAAI,UAAU,CAAC;CAGjE,OAAO;AACT;;;;;;;;;;;;;;;AAgBA,IAAI,oBAAoB;;;;;;;;;;;;;;;;;;AAmBxB,eAAsB,qBAAqB,YAAoB,QAAQ,IAAI,GAAkB;CAC3F,IAAI,mBACF;CAKF,IAAI,CAFe,kBAAkB,SAAS,
|
|
1
|
+
{"version":3,"file":"load-environment.mjs","names":[],"sources":["../../../../../../../core/src/utils/load-environment.ts"],"sourcesContent":["import { loadEnv, type EnvLoaderOptions } from \"@mongez/dotenv\";\nimport { existsSync } from \"node:fs\";\nimport path from \"node:path\";\n\n/**\n * Core's env precedence policy, in one place because two callers load env:\n * this module at boot, and the dev server when an `.env` file changes.\n *\n * `@mongez/dotenv` defaults to `precedence: \"file-wins\"`, which is backwards.\n * The `.env` file is a checked-in DEFAULT; a variable already exported into\n * `process.env` is the deliberate, situational override — a second instance on\n * another port, CI pointed at another database, a container's configuration.\n * Under the default, `PORT=6060 warlock dev` produced a server on the `.env`\n * file's 3000 and said nothing. A default that silently beats an explicit\n * instruction is the wrong way round, so core opts into `process-wins`:\n * the file supplies only the keys the environment does not already carry.\n *\n * EMPTY STRING — `FOO=` in the environment counts as SET, and wins, so the\n * file's value is discarded and `env(\"FOO\")` returns `\"\"`. Chosen over\n * \"empty means absent\" because the loader cannot tell a deliberate blanking\n * from an accident, and only one of the two readings is expressible: an\n * operator who wants the file's value can unset the variable, whereas under\n * \"empty means absent\" an operator who wants a blank value has no way to ask\n * for one. It also matches POSIX (an exported empty variable is set), dotenv,\n * dotenv-flow and Vite. Note the consequence: a blank export beats the second\n * argument too, so `env(\"FOO\", \"fallback\")` yields `\"\"`, not the fallback.\n *\n * Keys this loader itself wrote are tracked by the library and are NOT treated\n * as process-provided, so editing `.env` during a dev session still takes\n * effect on reload rather than being pinned by the previous load's own writes.\n */\nexport const environmentLoaderOptions: EnvLoaderOptions = {\n precedence: \"process-wins\",\n};\n\n/**\n * The files `loadEnv()` will look for, in the order it considers them.\n *\n * Mirrors `@mongez/dotenv`'s own resolution — `.env.shared` first, then\n * `.env.<NODE_ENV>` if it exists, else plain `.env` — because the presence\n * check has to ask the same question the loader is about to ask.\n */\nfunction candidateEnvFiles(directory: string): string[] {\n const files = [path.join(directory, \".env\"), path.join(directory, \".env.shared\")];\n\n if (process.env.NODE_ENV) {\n files.push(path.join(directory, `.env.${process.env.NODE_ENV}`));\n }\n\n return files;\n}\n\n/**\n * Whether this process has already loaded its env files.\n *\n * Two callers reach here on a bootstrapping command — the CLI preload phase and\n * `bootstrap()` — and `loadEnv()` defaults to `override: true`, so a second pass\n * re-parses the same files and re-writes `process.env`. Anything set in between\n * (a `warlock.config.ts` module body, a config file, an orchestrator) would be\n * silently clobbered by the later load. Same shape as the defect where a\n * caller's test-server port was overwritten by a re-read of `.env`.\n *\n * A deliberate reload is still possible and still used: the dev server calls\n * `loadEnv()` directly when an `.env` file changes on disk, which is a reload\n * the user asked for rather than an accidental second pass.\n */\nlet environmentLoaded = false;\n\n/**\n * Load `.env` files when there are any, and do nothing when there are not.\n *\n * Loads at most **once per process** — see {@link environmentLoaded}.\n *\n * `@mongez/dotenv`'s `loadEnvFile` **throws** when the file is absent. That was\n * survivable while only `dev` loaded env; now that every command loads it —\n * because `warlock.config.ts` calls `env()` in its module body and had been\n * evaluated against an empty store under every command — an unguarded call\n * would turn \"this project has no .env\" from a non-event into a hard failure\n * of `warlock build`. A project without a `.env` is legitimate: config files\n * carry defaults, and containers inject their own variables.\n *\n * The guard lives here rather than waiting on a fix in `@mongez/dotenv`: core\n * decides its own env policy and must not depend on another package's release\n * schedule to avoid crashing a working build.\n */\nexport async function loadEnvironmentFiles(directory: string = process.cwd()): Promise<void> {\n if (environmentLoaded) {\n return;\n }\n\n const hasEnvFile = candidateEnvFiles(directory).some((file) => existsSync(file));\n\n if (!hasEnvFile) {\n // Not latched: a project may create its `.env` between a command's preload\n // and its bootstrap, and there is nothing to protect from a second override\n // when the first pass loaded nothing.\n return;\n }\n\n environmentLoaded = true;\n\n // `dir` has to be forwarded: `loadEnv()` defaults it to `process.cwd()`, so\n // without this the existence check above asks about `directory` while the\n // load itself reads somewhere else entirely. Identical in production, where\n // `directory` IS `process.cwd()`.\n await loadEnv(undefined, { ...environmentLoaderOptions, dir: directory });\n}\n\n/**\n * Forget that env has been loaded, so the next call loads again.\n *\n * @internal For tests, which run many boots inside one process.\n */\nexport function resetLoadedEnvironment(): void {\n environmentLoaded = false;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,MAAa,2BAA6C,EACxD,YAAY,eACd;;;;;;;;AASA,SAAS,kBAAkB,WAA6B;CACtD,MAAM,QAAQ,CAAC,KAAK,KAAK,WAAW,MAAM,GAAG,KAAK,KAAK,WAAW,aAAa,CAAC;CAEhF,IAAI,QAAQ,IAAI,UACd,MAAM,KAAK,KAAK,KAAK,WAAW,QAAQ,QAAQ,IAAI,UAAU,CAAC;CAGjE,OAAO;AACT;;;;;;;;;;;;;;;AAgBA,IAAI,oBAAoB;;;;;;;;;;;;;;;;;;AAmBxB,eAAsB,qBAAqB,YAAoB,QAAQ,IAAI,GAAkB;CAC3F,IAAI,mBACF;CAKF,IAAI,CAFe,kBAAkB,SAAS,CAAC,CAAC,MAAM,SAAS,WAAW,IAAI,CAEhE,GAIZ;CAGF,oBAAoB;CAMpB,MAAM,QAAQ,QAAW;EAAE,GAAG;EAA0B,KAAK;CAAU,CAAC;AAC1E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"normalized-path.d.mts","names":[],"sources":["../../../../../../../core/src/utils/normalized-path.ts"],"mappings":";;AAeA;;;;;;;;;;;;cAAa,IAAA;EAgCa;;;EAAA,OA5BV,UAAA,CAAW,YAAA;EA0CH;;;EAAA,OAnCR,QAAA,CAAS,YAAA;EAiDT;;;EAAA,OA1CA,oBAAA,CAAqB,YAAA;;;;SAOrB,UAAA,CAAW,YAAA;;;;SAOX,SAAA,CAAU,QAAA;;;;SAOV,IAAA,
|
|
1
|
+
{"version":3,"file":"normalized-path.d.mts","names":[],"sources":["../../../../../../../core/src/utils/normalized-path.ts"],"mappings":";;AAeA;;;;;;;;;;;;cAAa,IAAA;EAgCa;;;EAAA,OA5BV,UAAA,CAAW,YAAA;EA0CH;;;EAAA,OAnCR,QAAA,CAAS,YAAA;EAiDT;;;EAAA,OA1CA,oBAAA,CAAqB,YAAA;;;;SAOrB,UAAA,CAAW,YAAA;;;;SAOX,SAAA,CAAU,QAAA;;;;SAOV,IAAA,IAAQ,KAAA;;;;SAOR,OAAA,CAAQ,QAAA;;;;SAOR,QAAA,CAAS,QAAA,UAAkB,GAAA;;;;SAO3B,OAAA,CAAQ,QAAA;AAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paths.d.mts","names":[],"sources":["../../../../../../../core/src/utils/paths.ts"],"mappings":";;AAMA;;iBAAgB,QAAA,
|
|
1
|
+
{"version":3,"file":"paths.d.mts","names":[],"sources":["../../../../../../../core/src/utils/paths.ts"],"mappings":";;AAMA;;iBAAgB,QAAA,IAAY,KAAK;;AAAA;AAOjC;iBAAgB,OAAA,IAAW,KAAK;;;AAAA;AAShC;;iBAAgB,WAAA,CAAY,YAAiB;;AAAA;AAS7C;;;iBAAgB,WAAA,CAAY,YAAiB;AAAA;AAgB7C;;;;AAhB6C,iBAgB7B,UAAA,CAAW,YAAiB;AAS5C;;;;AAA2C;AAA3C,iBAAgB,SAAA,CAAU,YAAiB;;;;iBAO3B,OAAA,CAAQ,YAAiB;AAOzC;;;AAAA,iBAAgB,QAAA,CAAS,YAAiB;AAAA;AAO1C;;AAP0C,iBAO1B,QAAA,CAAS,YAAiB;AAAA,iBAS1B,YAAA,CAAa,QAAgB;AATH;AAS1C;;;AAT0C,iBAiB1B,WAAA,IAAe,IAAI;AARU;AAQ7C;;AAR6C,iBAe7B,UAAA,IAAc,IAAI;AAAA,cAIrB,KAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"promise-all-object.d.mts","names":[],"sources":["../../../../../../../core/src/utils/promise-all-object.ts"],"mappings":";;AAIA;;;iBAAsB,gBAAA,WAA2B,MAAA,SAAe,OAAA,
|
|
1
|
+
{"version":3,"file":"promise-all-object.d.mts","names":[],"sources":["../../../../../../../core/src/utils/promise-all-object.ts"],"mappings":";;AAIA;;;iBAAsB,gBAAA,WAA2B,MAAA,SAAe,OAAA,QAC9D,QAAA,EAAU,CAAA,GACT,OAAA,eAAsB,CAAA,GAAI,CAAA,CAAE,CAAA,UAAW,OAAA,YAAmB,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"promise-all-object.mjs","names":[],"sources":["../../../../../../../core/src/utils/promise-all-object.ts"],"sourcesContent":["/**\n * Resolve all of the given promises in the object and return the results as an object.\n * This is more convenient than using `Promise.all` and then mapping the results back to the keys.\n */\nexport async function promiseAllObject<T extends Record<string, Promise<any>>>(\n promises: T,\n): Promise<{ [K in keyof T]: T[K] extends Promise<infer U> ? U : never }> {\n const results = await Promise.all(Object.values(promises));\n const keys = Object.keys(promises);\n\n return keys.reduce(\n (result, key, index) => {\n (result as any)[key] = results[index];\n return result;\n },\n {} as { [K in keyof T]: T[K] extends Promise<infer U> ? U : never },\n );\n}\n"],"mappings":";;;;;AAIA,eAAsB,iBACpB,UACwE;CACxE,MAAM,UAAU,MAAM,QAAQ,IAAI,OAAO,OAAO,QAAQ,CAAC;CAGzD,OAFa,OAAO,KAAK,QAEf,
|
|
1
|
+
{"version":3,"file":"promise-all-object.mjs","names":[],"sources":["../../../../../../../core/src/utils/promise-all-object.ts"],"sourcesContent":["/**\n * Resolve all of the given promises in the object and return the results as an object.\n * This is more convenient than using `Promise.all` and then mapping the results back to the keys.\n */\nexport async function promiseAllObject<T extends Record<string, Promise<any>>>(\n promises: T,\n): Promise<{ [K in keyof T]: T[K] extends Promise<infer U> ? U : never }> {\n const results = await Promise.all(Object.values(promises));\n const keys = Object.keys(promises);\n\n return keys.reduce(\n (result, key, index) => {\n (result as any)[key] = results[index];\n return result;\n },\n {} as { [K in keyof T]: T[K] extends Promise<infer U> ? U : never },\n );\n}\n"],"mappings":";;;;;AAIA,eAAsB,iBACpB,UACwE;CACxE,MAAM,UAAU,MAAM,QAAQ,IAAI,OAAO,OAAO,QAAQ,CAAC;CAGzD,OAFa,OAAO,KAAK,QAEf,CAAC,CAAC,QACT,QAAQ,KAAK,UAAU;EACtB,AAAC,OAAe,OAAO,QAAQ;EAC/B,OAAO;CACT,GACA,CAAC,CACH;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sluggable.mjs","names":[],"sources":["../../../../../../../core/src/utils/sluggable.ts"],"sourcesContent":["import slug from \"@mongez/slug\";\nimport type { Model } from \"@warlock.js/cascade\";\n\n/**\n * Used for model castings\n */\nexport function sluggable(generateFrom: string, slugLocaleCode = \"en\") {\n return (model: Model) => {\n let value = model.get(generateFrom);\n\n if (!value) return \"\";\n\n if (Array.isArray(value)) {\n value = (value as any[]).find((value) => value.localeCode === slugLocaleCode)?.value;\n }\n\n const slugFunction = (slug as any).default || slug;\n\n return slugFunction(String(value));\n };\n}\n"],"mappings":";;;;;;AAMA,SAAgB,UAAU,cAAsB,iBAAiB,MAAM;CACrE,QAAQ,UAAiB;EACvB,IAAI,QAAQ,MAAM,IAAI,YAAY;EAElC,IAAI,CAAC,OAAO,OAAO;EAEnB,IAAI,MAAM,QAAQ,KAAK,GACrB,QAAS,MAAgB,MAAM,UAAU,MAAM,eAAe,cAAc,
|
|
1
|
+
{"version":3,"file":"sluggable.mjs","names":[],"sources":["../../../../../../../core/src/utils/sluggable.ts"],"sourcesContent":["import slug from \"@mongez/slug\";\nimport type { Model } from \"@warlock.js/cascade\";\n\n/**\n * Used for model castings\n */\nexport function sluggable(generateFrom: string, slugLocaleCode = \"en\") {\n return (model: Model) => {\n let value = model.get(generateFrom);\n\n if (!value) return \"\";\n\n if (Array.isArray(value)) {\n value = (value as any[]).find((value) => value.localeCode === slugLocaleCode)?.value;\n }\n\n const slugFunction = (slug as any).default || slug;\n\n return slugFunction(String(value));\n };\n}\n"],"mappings":";;;;;;AAMA,SAAgB,UAAU,cAAsB,iBAAiB,MAAM;CACrE,QAAQ,UAAiB;EACvB,IAAI,QAAQ,MAAM,IAAI,YAAY;EAElC,IAAI,CAAC,OAAO,OAAO;EAEnB,IAAI,MAAM,QAAQ,KAAK,GACrB,QAAS,MAAgB,MAAM,UAAU,MAAM,eAAe,cAAc,CAAC,EAAE;EAKjF,QAFsB,KAAa,WAAW,KAE3B,CAAC,OAAO,KAAK,CAAC;CACnC;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version-compare.mjs","names":[],"sources":["../../../../../../../core/src/utils/version-compare.ts"],"sourcesContent":["/**\n * Zero-dependency semver comparison — just enough for the framework's\n * self-update tooling (the dev-server update notice + the `warlock update`\n * command).\n *\n * We deliberately avoid pulling in a semver library: the only question these\n * callers ask is \"is the published version newer than what's installed?\".\n */\n\ntype ParsedVersion = {\n /** Numeric core as `[major, minor, patch]`. */\n core: [number, number, number];\n /** Dot-separated prerelease identifiers; empty for a stable release. */\n prerelease: string[];\n};\n\n/**\n * Parse a semver string into its numeric core + prerelease identifiers.\n * Tolerates a leading `v` and ignores build metadata (`+…`). Returns\n * `undefined` when the core isn't exactly three non-negative integers.\n */\nfunction parseVersion(version: string): ParsedVersion | undefined {\n const cleaned = version.trim().replace(/^v/i, \"\");\n const [coreAndPrerelease] = cleaned.split(\"+\"); // drop build metadata\n const [core, ...prereleaseParts] = coreAndPrerelease.split(\"-\");\n const segments = core.split(\".\");\n\n if (segments.length !== 3) {\n return undefined;\n }\n\n const numbers = segments.map((segment) => Number(segment));\n\n if (numbers.some((value) => !Number.isInteger(value) || value < 0)) {\n return undefined;\n }\n\n const prerelease = prereleaseParts.length > 0 ? prereleaseParts.join(\"-\").split(\".\") : [];\n\n return {\n core: [numbers[0], numbers[1], numbers[2]],\n prerelease,\n };\n}\n\n/**\n * Compare two prerelease identifier lists per semver rules: numeric\n * identifiers compare numerically, alphanumeric ones lexically, a numeric\n * identifier ranks below an alphanumeric one, and a longer list wins when\n * otherwise equal. Returns a negative / zero / positive number.\n */\nfunction comparePrerelease(left: string[], right: string[]): number {\n const length = Math.max(left.length, right.length);\n\n for (let index = 0; index < length; index++) {\n const a = left[index];\n const b = right[index];\n\n if (a === undefined) {\n return -1;\n }\n\n if (b === undefined) {\n return 1;\n }\n\n const aIsNumeric = /^\\d+$/.test(a);\n const bIsNumeric = /^\\d+$/.test(b);\n\n if (aIsNumeric && bIsNumeric) {\n const diff = Number(a) - Number(b);\n\n if (diff !== 0) {\n return diff;\n }\n\n continue;\n }\n\n if (aIsNumeric !== bIsNumeric) {\n return aIsNumeric ? -1 : 1;\n }\n\n if (a !== b) {\n return a < b ? -1 : 1;\n }\n }\n\n return 0;\n}\n\n/**\n * Return `true` when `latest` is a strictly newer semantic version than\n * `current`. Compares the numeric `major.minor.patch` first, then treats a\n * stable release as newer than a prerelease of the same core\n * (`1.2.0` > `1.2.0-beta`). Unparseable input yields `false` — we never nag\n * on a version string we can't understand.\n */\nexport function isNewerVersion(latest: string, current: string): boolean {\n const latestParsed = parseVersion(latest);\n const currentParsed = parseVersion(current);\n\n if (!latestParsed || !currentParsed) {\n return false;\n }\n\n for (let index = 0; index < 3; index++) {\n if (latestParsed.core[index] !== currentParsed.core[index]) {\n return latestParsed.core[index] > currentParsed.core[index];\n }\n }\n\n // Equal numeric core — order by prerelease (a stable release beats a prerelease).\n const latestIsStable = latestParsed.prerelease.length === 0;\n const currentIsStable = currentParsed.prerelease.length === 0;\n\n if (latestIsStable && currentIsStable) {\n return false;\n }\n\n if (latestIsStable !== currentIsStable) {\n return latestIsStable;\n }\n\n return comparePrerelease(latestParsed.prerelease, currentParsed.prerelease) > 0;\n}\n"],"mappings":";;;;;;AAqBA,SAAS,aAAa,SAA4C;CAEhE,MAAM,CAAC,qBADS,QAAQ,KAAK,
|
|
1
|
+
{"version":3,"file":"version-compare.mjs","names":[],"sources":["../../../../../../../core/src/utils/version-compare.ts"],"sourcesContent":["/**\n * Zero-dependency semver comparison — just enough for the framework's\n * self-update tooling (the dev-server update notice + the `warlock update`\n * command).\n *\n * We deliberately avoid pulling in a semver library: the only question these\n * callers ask is \"is the published version newer than what's installed?\".\n */\n\ntype ParsedVersion = {\n /** Numeric core as `[major, minor, patch]`. */\n core: [number, number, number];\n /** Dot-separated prerelease identifiers; empty for a stable release. */\n prerelease: string[];\n};\n\n/**\n * Parse a semver string into its numeric core + prerelease identifiers.\n * Tolerates a leading `v` and ignores build metadata (`+…`). Returns\n * `undefined` when the core isn't exactly three non-negative integers.\n */\nfunction parseVersion(version: string): ParsedVersion | undefined {\n const cleaned = version.trim().replace(/^v/i, \"\");\n const [coreAndPrerelease] = cleaned.split(\"+\"); // drop build metadata\n const [core, ...prereleaseParts] = coreAndPrerelease.split(\"-\");\n const segments = core.split(\".\");\n\n if (segments.length !== 3) {\n return undefined;\n }\n\n const numbers = segments.map((segment) => Number(segment));\n\n if (numbers.some((value) => !Number.isInteger(value) || value < 0)) {\n return undefined;\n }\n\n const prerelease = prereleaseParts.length > 0 ? prereleaseParts.join(\"-\").split(\".\") : [];\n\n return {\n core: [numbers[0], numbers[1], numbers[2]],\n prerelease,\n };\n}\n\n/**\n * Compare two prerelease identifier lists per semver rules: numeric\n * identifiers compare numerically, alphanumeric ones lexically, a numeric\n * identifier ranks below an alphanumeric one, and a longer list wins when\n * otherwise equal. Returns a negative / zero / positive number.\n */\nfunction comparePrerelease(left: string[], right: string[]): number {\n const length = Math.max(left.length, right.length);\n\n for (let index = 0; index < length; index++) {\n const a = left[index];\n const b = right[index];\n\n if (a === undefined) {\n return -1;\n }\n\n if (b === undefined) {\n return 1;\n }\n\n const aIsNumeric = /^\\d+$/.test(a);\n const bIsNumeric = /^\\d+$/.test(b);\n\n if (aIsNumeric && bIsNumeric) {\n const diff = Number(a) - Number(b);\n\n if (diff !== 0) {\n return diff;\n }\n\n continue;\n }\n\n if (aIsNumeric !== bIsNumeric) {\n return aIsNumeric ? -1 : 1;\n }\n\n if (a !== b) {\n return a < b ? -1 : 1;\n }\n }\n\n return 0;\n}\n\n/**\n * Return `true` when `latest` is a strictly newer semantic version than\n * `current`. Compares the numeric `major.minor.patch` first, then treats a\n * stable release as newer than a prerelease of the same core\n * (`1.2.0` > `1.2.0-beta`). Unparseable input yields `false` — we never nag\n * on a version string we can't understand.\n */\nexport function isNewerVersion(latest: string, current: string): boolean {\n const latestParsed = parseVersion(latest);\n const currentParsed = parseVersion(current);\n\n if (!latestParsed || !currentParsed) {\n return false;\n }\n\n for (let index = 0; index < 3; index++) {\n if (latestParsed.core[index] !== currentParsed.core[index]) {\n return latestParsed.core[index] > currentParsed.core[index];\n }\n }\n\n // Equal numeric core — order by prerelease (a stable release beats a prerelease).\n const latestIsStable = latestParsed.prerelease.length === 0;\n const currentIsStable = currentParsed.prerelease.length === 0;\n\n if (latestIsStable && currentIsStable) {\n return false;\n }\n\n if (latestIsStable !== currentIsStable) {\n return latestIsStable;\n }\n\n return comparePrerelease(latestParsed.prerelease, currentParsed.prerelease) > 0;\n}\n"],"mappings":";;;;;;AAqBA,SAAS,aAAa,SAA4C;CAEhE,MAAM,CAAC,qBADS,QAAQ,KAAK,CAAC,CAAC,QAAQ,OAAO,EACZ,CAAC,CAAC,MAAM,GAAG;CAC7C,MAAM,CAAC,MAAM,GAAG,mBAAmB,kBAAkB,MAAM,GAAG;CAC9D,MAAM,WAAW,KAAK,MAAM,GAAG;CAE/B,IAAI,SAAS,WAAW,GACtB;CAGF,MAAM,UAAU,SAAS,KAAK,YAAY,OAAO,OAAO,CAAC;CAEzD,IAAI,QAAQ,MAAM,UAAU,CAAC,OAAO,UAAU,KAAK,KAAK,QAAQ,CAAC,GAC/D;CAGF,MAAM,aAAa,gBAAgB,SAAS,IAAI,gBAAgB,KAAK,GAAG,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC;CAExF,OAAO;EACL,MAAM;GAAC,QAAQ;GAAI,QAAQ;GAAI,QAAQ;EAAE;EACzC;CACF;AACF;;;;;;;AAQA,SAAS,kBAAkB,MAAgB,OAAyB;CAClE,MAAM,SAAS,KAAK,IAAI,KAAK,QAAQ,MAAM,MAAM;CAEjD,KAAK,IAAI,QAAQ,GAAG,QAAQ,QAAQ,SAAS;EAC3C,MAAM,IAAI,KAAK;EACf,MAAM,IAAI,MAAM;EAEhB,IAAI,MAAM,QACR,OAAO;EAGT,IAAI,MAAM,QACR,OAAO;EAGT,MAAM,aAAa,QAAQ,KAAK,CAAC;EACjC,MAAM,aAAa,QAAQ,KAAK,CAAC;EAEjC,IAAI,cAAc,YAAY;GAC5B,MAAM,OAAO,OAAO,CAAC,IAAI,OAAO,CAAC;GAEjC,IAAI,SAAS,GACX,OAAO;GAGT;EACF;EAEA,IAAI,eAAe,YACjB,OAAO,aAAa,KAAK;EAG3B,IAAI,MAAM,GACR,OAAO,IAAI,IAAI,KAAK;CAExB;CAEA,OAAO;AACT;;;;;;;;AASA,SAAgB,eAAe,QAAgB,SAA0B;CACvE,MAAM,eAAe,aAAa,MAAM;CACxC,MAAM,gBAAgB,aAAa,OAAO;CAE1C,IAAI,CAAC,gBAAgB,CAAC,eACpB,OAAO;CAGT,KAAK,IAAI,QAAQ,GAAG,QAAQ,GAAG,SAC7B,IAAI,aAAa,KAAK,WAAW,cAAc,KAAK,QAClD,OAAO,aAAa,KAAK,SAAS,cAAc,KAAK;CAKzD,MAAM,iBAAiB,aAAa,WAAW,WAAW;CAC1D,MAAM,kBAAkB,cAAc,WAAW,WAAW;CAE5D,IAAI,kBAAkB,iBACpB,OAAO;CAGT,IAAI,mBAAmB,iBACrB,OAAO;CAGT,OAAO,kBAAkB,aAAa,YAAY,cAAc,UAAU,IAAI;AAChF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"exists-except-current-id.mjs","names":[],"sources":["../../../../../../../../core/src/validation/database/exists-except-current-id.ts"],"sourcesContent":["import { resolveModelClass } from \"@warlock.js/cascade\";\nimport { invalidRule, VALID_RULE, type SchemaRule } from \"@warlock.js/seal\";\nimport { useRequestStore } from \"../../http\";\nimport type { ExistsExceptCurrentIdRuleOptions } from \"../types\";\n\n/**\n * Exists except current ID rule\n */\nexport const existsExceptCurrentIdRule: SchemaRule<ExistsExceptCurrentIdRuleOptions> = {\n name: \"existsExceptCurrentId\",\n defaultErrorMessage: \"The :input must exist\",\n async validate(value: any, context) {\n const {\n Model,\n query,\n column = context.key,\n exceptCurrentIdColumn = \"id\",\n } = this.context.options;\n\n const { request } = useRequestStore();\n\n const ResolvedModelClass = resolveModelClass(Model);\n\n const dbQuery = ResolvedModelClass.query();\n\n dbQuery.where(column, value);\n dbQuery.where(exceptCurrentIdColumn, \"!=\", request.int(\"id\"));\n\n if (query) {\n await query({\n query: dbQuery,\n value,\n allValues: context.allValues,\n });\n }\n\n const document = await dbQuery.first();\n return document ? VALID_RULE : invalidRule(this, context);\n },\n};\n"],"mappings":";;;;;;;;;AAQA,MAAa,4BAA0E;CACrF,MAAM;CACN,qBAAqB;CACrB,MAAM,SAAS,OAAY,SAAS;EAClC,MAAM,EACJ,OACA,OACA,SAAS,QAAQ,KACjB,wBAAwB,SACtB,KAAK,QAAQ;EAEjB,MAAM,EAAE,YAAY,gBAAgB;EAIpC,MAAM,UAFqB,kBAAkB,KAEZ,
|
|
1
|
+
{"version":3,"file":"exists-except-current-id.mjs","names":[],"sources":["../../../../../../../../core/src/validation/database/exists-except-current-id.ts"],"sourcesContent":["import { resolveModelClass } from \"@warlock.js/cascade\";\nimport { invalidRule, VALID_RULE, type SchemaRule } from \"@warlock.js/seal\";\nimport { useRequestStore } from \"../../http\";\nimport type { ExistsExceptCurrentIdRuleOptions } from \"../types\";\n\n/**\n * Exists except current ID rule\n */\nexport const existsExceptCurrentIdRule: SchemaRule<ExistsExceptCurrentIdRuleOptions> = {\n name: \"existsExceptCurrentId\",\n defaultErrorMessage: \"The :input must exist\",\n async validate(value: any, context) {\n const {\n Model,\n query,\n column = context.key,\n exceptCurrentIdColumn = \"id\",\n } = this.context.options;\n\n const { request } = useRequestStore();\n\n const ResolvedModelClass = resolveModelClass(Model);\n\n const dbQuery = ResolvedModelClass.query();\n\n dbQuery.where(column, value);\n dbQuery.where(exceptCurrentIdColumn, \"!=\", request.int(\"id\"));\n\n if (query) {\n await query({\n query: dbQuery,\n value,\n allValues: context.allValues,\n });\n }\n\n const document = await dbQuery.first();\n return document ? VALID_RULE : invalidRule(this, context);\n },\n};\n"],"mappings":";;;;;;;;;AAQA,MAAa,4BAA0E;CACrF,MAAM;CACN,qBAAqB;CACrB,MAAM,SAAS,OAAY,SAAS;EAClC,MAAM,EACJ,OACA,OACA,SAAS,QAAQ,KACjB,wBAAwB,SACtB,KAAK,QAAQ;EAEjB,MAAM,EAAE,YAAY,gBAAgB;EAIpC,MAAM,UAFqB,kBAAkB,KAEZ,CAAC,CAAC,MAAM;EAEzC,QAAQ,MAAM,QAAQ,KAAK;EAC3B,QAAQ,MAAM,uBAAuB,MAAM,QAAQ,IAAI,IAAI,CAAC;EAE5D,IAAI,OACF,MAAM,MAAM;GACV,OAAO;GACP;GACA,WAAW,QAAQ;EACrB,CAAC;EAIH,OAAO,MADgB,QAAQ,MAAM,IACnB,aAAa,YAAY,MAAM,OAAO;CAC1D;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"exists-except-current-user.mjs","names":[],"sources":["../../../../../../../../core/src/validation/database/exists-except-current-user.ts"],"sourcesContent":["import { resolveModelClass } from \"@warlock.js/cascade\";\nimport { invalidRule, VALID_RULE, type SchemaRule } from \"@warlock.js/seal\";\nimport { useCurrentUser } from \"../../http\";\nimport type { ExistsExceptCurrentUserRuleOptions } from \"../types\";\n\n/**\n * Exists except current user rule\n */\nexport const existsExceptCurrentUserRule: SchemaRule<ExistsExceptCurrentUserRuleOptions> = {\n name: \"existsExceptCurrentUser\",\n defaultErrorMessage: \"The :input must exist\",\n async validate(value: any, context) {\n const {\n Model,\n query,\n column = context.key,\n exceptCurrentUserColumn = \"id\",\n exceptCurrentUserValue = \"id\",\n } = this.context.options;\n\n const user = useCurrentUser();\n\n const ResolvedModelClass = resolveModelClass(Model);\n\n const dbQuery = ResolvedModelClass.query();\n\n dbQuery.where(column, value);\n\n if (user) {\n dbQuery.where(exceptCurrentUserColumn, \"!=\", user.get(exceptCurrentUserValue));\n }\n\n if (query) {\n await query({\n query: dbQuery,\n value,\n allValues: context.allValues,\n });\n }\n\n const document = await dbQuery.first();\n return document ? VALID_RULE : invalidRule(this, context);\n },\n};\n"],"mappings":";;;;;;;;;AAQA,MAAa,8BAA8E;CACzF,MAAM;CACN,qBAAqB;CACrB,MAAM,SAAS,OAAY,SAAS;EAClC,MAAM,EACJ,OACA,OACA,SAAS,QAAQ,KACjB,0BAA0B,MAC1B,yBAAyB,SACvB,KAAK,QAAQ;EAEjB,MAAM,OAAO,eAAe;EAI5B,MAAM,UAFqB,kBAAkB,KAEZ,
|
|
1
|
+
{"version":3,"file":"exists-except-current-user.mjs","names":[],"sources":["../../../../../../../../core/src/validation/database/exists-except-current-user.ts"],"sourcesContent":["import { resolveModelClass } from \"@warlock.js/cascade\";\nimport { invalidRule, VALID_RULE, type SchemaRule } from \"@warlock.js/seal\";\nimport { useCurrentUser } from \"../../http\";\nimport type { ExistsExceptCurrentUserRuleOptions } from \"../types\";\n\n/**\n * Exists except current user rule\n */\nexport const existsExceptCurrentUserRule: SchemaRule<ExistsExceptCurrentUserRuleOptions> = {\n name: \"existsExceptCurrentUser\",\n defaultErrorMessage: \"The :input must exist\",\n async validate(value: any, context) {\n const {\n Model,\n query,\n column = context.key,\n exceptCurrentUserColumn = \"id\",\n exceptCurrentUserValue = \"id\",\n } = this.context.options;\n\n const user = useCurrentUser();\n\n const ResolvedModelClass = resolveModelClass(Model);\n\n const dbQuery = ResolvedModelClass.query();\n\n dbQuery.where(column, value);\n\n if (user) {\n dbQuery.where(exceptCurrentUserColumn, \"!=\", user.get(exceptCurrentUserValue));\n }\n\n if (query) {\n await query({\n query: dbQuery,\n value,\n allValues: context.allValues,\n });\n }\n\n const document = await dbQuery.first();\n return document ? VALID_RULE : invalidRule(this, context);\n },\n};\n"],"mappings":";;;;;;;;;AAQA,MAAa,8BAA8E;CACzF,MAAM;CACN,qBAAqB;CACrB,MAAM,SAAS,OAAY,SAAS;EAClC,MAAM,EACJ,OACA,OACA,SAAS,QAAQ,KACjB,0BAA0B,MAC1B,yBAAyB,SACvB,KAAK,QAAQ;EAEjB,MAAM,OAAO,eAAe;EAI5B,MAAM,UAFqB,kBAAkB,KAEZ,CAAC,CAAC,MAAM;EAEzC,QAAQ,MAAM,QAAQ,KAAK;EAE3B,IAAI,MACF,QAAQ,MAAM,yBAAyB,MAAM,KAAK,IAAI,sBAAsB,CAAC;EAG/E,IAAI,OACF,MAAM,MAAM;GACV,OAAO;GACP;GACA,WAAW,QAAQ;EACrB,CAAC;EAIH,OAAO,MADgB,QAAQ,MAAM,IACnB,aAAa,YAAY,MAAM,OAAO;CAC1D;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unique-except-current-id.mjs","names":[],"sources":["../../../../../../../../core/src/validation/database/unique-except-current-id.ts"],"sourcesContent":["import { resolveModelClass } from \"@warlock.js/cascade\";\nimport { invalidRule, VALID_RULE, type SchemaRule } from \"@warlock.js/seal\";\nimport { useRequestStore } from \"../../http\";\nimport type { UniqueExceptCurrentIdRuleOptions } from \"../types\";\n/**\n * Unique except current ID rule\n */\nexport const uniqueExceptCurrentIdRule: SchemaRule<UniqueExceptCurrentIdRuleOptions> = {\n name: \"uniqueExceptCurrentId\",\n defaultErrorMessage: \"The :input must be unique\",\n async validate(value: any, context) {\n const {\n Model,\n column = context.key,\n exceptCurrentIdColumn = \"id\",\n query,\n } = this.context.options;\n\n const { request } = useRequestStore();\n\n const ResolvedModelClass = resolveModelClass(Model);\n\n const dbQuery = ResolvedModelClass.query();\n\n dbQuery.where(column, value);\n dbQuery.where(exceptCurrentIdColumn, \"!=\", request.input(\"id\"));\n\n if (query) {\n await query({\n query: dbQuery,\n value,\n allValues: context.allValues,\n });\n }\n\n const document = await dbQuery.first();\n return document ? invalidRule(this, context) : VALID_RULE;\n },\n};\n"],"mappings":";;;;;;;;;AAOA,MAAa,4BAA0E;CACrF,MAAM;CACN,qBAAqB;CACrB,MAAM,SAAS,OAAY,SAAS;EAClC,MAAM,EACJ,OACA,SAAS,QAAQ,KACjB,wBAAwB,MACxB,UACE,KAAK,QAAQ;EAEjB,MAAM,EAAE,YAAY,gBAAgB;EAIpC,MAAM,UAFqB,kBAAkB,KAEZ,
|
|
1
|
+
{"version":3,"file":"unique-except-current-id.mjs","names":[],"sources":["../../../../../../../../core/src/validation/database/unique-except-current-id.ts"],"sourcesContent":["import { resolveModelClass } from \"@warlock.js/cascade\";\nimport { invalidRule, VALID_RULE, type SchemaRule } from \"@warlock.js/seal\";\nimport { useRequestStore } from \"../../http\";\nimport type { UniqueExceptCurrentIdRuleOptions } from \"../types\";\n/**\n * Unique except current ID rule\n */\nexport const uniqueExceptCurrentIdRule: SchemaRule<UniqueExceptCurrentIdRuleOptions> = {\n name: \"uniqueExceptCurrentId\",\n defaultErrorMessage: \"The :input must be unique\",\n async validate(value: any, context) {\n const {\n Model,\n column = context.key,\n exceptCurrentIdColumn = \"id\",\n query,\n } = this.context.options;\n\n const { request } = useRequestStore();\n\n const ResolvedModelClass = resolveModelClass(Model);\n\n const dbQuery = ResolvedModelClass.query();\n\n dbQuery.where(column, value);\n dbQuery.where(exceptCurrentIdColumn, \"!=\", request.input(\"id\"));\n\n if (query) {\n await query({\n query: dbQuery,\n value,\n allValues: context.allValues,\n });\n }\n\n const document = await dbQuery.first();\n return document ? invalidRule(this, context) : VALID_RULE;\n },\n};\n"],"mappings":";;;;;;;;;AAOA,MAAa,4BAA0E;CACrF,MAAM;CACN,qBAAqB;CACrB,MAAM,SAAS,OAAY,SAAS;EAClC,MAAM,EACJ,OACA,SAAS,QAAQ,KACjB,wBAAwB,MACxB,UACE,KAAK,QAAQ;EAEjB,MAAM,EAAE,YAAY,gBAAgB;EAIpC,MAAM,UAFqB,kBAAkB,KAEZ,CAAC,CAAC,MAAM;EAEzC,QAAQ,MAAM,QAAQ,KAAK;EAC3B,QAAQ,MAAM,uBAAuB,MAAM,QAAQ,MAAM,IAAI,CAAC;EAE9D,IAAI,OACF,MAAM,MAAM;GACV,OAAO;GACP;GACA,WAAW,QAAQ;EACrB,CAAC;EAIH,OAAO,MADgB,QAAQ,MAAM,IACnB,YAAY,MAAM,OAAO,IAAI;CACjD;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"localized-plugin.mjs","names":[],"sources":["../../../../../../../../core/src/validation/plugins/localized-plugin.ts"],"sourcesContent":["/**\n * Localized Validator Plugin\n *\n * Adds localized() method to Seal v factory\n */\n\nimport type { ArrayValidator, BaseValidator, SealPlugin } from \"@warlock.js/seal\";\nimport { v } from \"@warlock.js/seal\";\n\ntype LocalizedReturn = ArrayValidator & {\n validator: BaseValidator;\n};\n\n/**\n * Localized validation plugin for Seal\n */\nexport const localizedPlugin: SealPlugin = {\n name: \"localized\",\n version: \"1.0.0\",\n description: \"Adds localized validation (v.localized())\",\n install() {\n v.localized = (valueValidator?: BaseValidator, errorMessage?: string): LocalizedReturn =>\n v.array(\n v.object({\n localeCode: v.string().required(),\n value: valueValidator || v.scalar(),\n }),\n errorMessage,\n ) as LocalizedReturn;\n },\n};\n"],"mappings":";;;;;;AAgBA,MAAa,kBAA8B;CACzC,MAAM;CACN,SAAS;CACT,aAAa;CACb,UAAU;EACR,EAAE,aAAa,gBAAgC,iBAC7C,EAAE,MACA,EAAE,OAAO;GACP,YAAY,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"localized-plugin.mjs","names":[],"sources":["../../../../../../../../core/src/validation/plugins/localized-plugin.ts"],"sourcesContent":["/**\n * Localized Validator Plugin\n *\n * Adds localized() method to Seal v factory\n */\n\nimport type { ArrayValidator, BaseValidator, SealPlugin } from \"@warlock.js/seal\";\nimport { v } from \"@warlock.js/seal\";\n\ntype LocalizedReturn = ArrayValidator & {\n validator: BaseValidator;\n};\n\n/**\n * Localized validation plugin for Seal\n */\nexport const localizedPlugin: SealPlugin = {\n name: \"localized\",\n version: \"1.0.0\",\n description: \"Adds localized validation (v.localized())\",\n install() {\n v.localized = (valueValidator?: BaseValidator, errorMessage?: string): LocalizedReturn =>\n v.array(\n v.object({\n localeCode: v.string().required(),\n value: valueValidator || v.scalar(),\n }),\n errorMessage,\n ) as LocalizedReturn;\n },\n};\n"],"mappings":";;;;;;AAgBA,MAAa,kBAA8B;CACzC,MAAM;CACN,SAAS;CACT,aAAa;CACb,UAAU;EACR,EAAE,aAAa,gBAAgC,iBAC7C,EAAE,MACA,EAAE,OAAO;GACP,YAAY,EAAE,OAAO,CAAC,CAAC,SAAS;GAChC,OAAO,kBAAkB,EAAE,OAAO;EACpC,CAAC,GACD,YACF;CACJ;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-validator.mjs","names":[],"sources":["../../../../../../../../core/src/validation/validators/file-validator.ts"],"sourcesContent":["import type { FileSizeOption, JsonSchemaResult, JsonSchemaTarget } from \"@warlock.js/seal\";\nimport {\n BaseValidator,\n maxFileSizeRule,\n maxHeightRule,\n maxWidthRule,\n minFileSizeRule,\n minHeightRule,\n minWidthRule,\n resolveFileSize,\n v,\n} from \"@warlock.js/seal\";\nimport { UploadedFile } from \"../../http\";\nimport { fileExtensionRule, fileRule, fileTypeRule, imageRule } from \"../file\";\n\nexport const uploadedFileMetadataSchema = v.object({\n location: v.string().oneOf([\"local\", \"cloud\"]),\n width: v.int().positive(),\n height: v.int().positive(),\n size: v.int().positive(),\n mimeType: v.string(),\n extension: v.string(),\n name: v.string(),\n});\n\n/**\n * File validator class\n */\nexport class FileValidator extends BaseValidator {\n public constructor(errorMessage?: string) {\n super();\n this.addMutableRule(fileRule, errorMessage);\n }\n\n /**\n * Check if value is a File type\n */\n public matchesType(value: any): boolean {\n return value instanceof UploadedFile;\n }\n\n /** Value must be an image */\n public image(errorMessage?: string): FileValidator {\n return this.addRule(imageRule, errorMessage);\n }\n\n /** Accept specific file extensions */\n public accept(extensions: string | string[], errorMessage?: string): FileValidator {\n return this.addRule(fileExtensionRule, errorMessage, {\n extensions,\n });\n }\n\n /** Allow specific MIME types */\n public mimeType(mimeTypes: string | string[], errorMessage?: string): FileValidator {\n return this.addRule(fileTypeRule, errorMessage, {\n mimeTypes,\n });\n }\n\n /** Allow only pdf files */\n public pdf(errorMessage?: string): FileValidator {\n return this.mimeType(\"application/pdf\", errorMessage);\n }\n\n /** Allow only excel files */\n public excel(errorMessage?: string): FileValidator {\n return this.mimeType(\n [\n \"application/vnd.ms-excel\",\n \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n ],\n errorMessage,\n );\n }\n\n /** Allow only word files */\n public word(errorMessage?: string): FileValidator {\n return this.mimeType(\n [\n \"application/msword\",\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n ],\n errorMessage,\n );\n }\n\n /** Minimum file size */\n public minSize(size: number | FileSizeOption, errorMessage?: string): FileValidator {\n return this.addRule(minFileSizeRule, errorMessage, {\n minSize: resolveFileSize(size),\n });\n }\n\n /** @alias minSize */\n public min(size: number | FileSizeOption, errorMessage?: string): FileValidator {\n return this.minSize(size, errorMessage);\n }\n\n /** Maximum file size */\n public maxSize(size: number | FileSizeOption, errorMessage?: string): FileValidator {\n return this.addRule(maxFileSizeRule, errorMessage, {\n maxSize: resolveFileSize(size),\n });\n }\n\n /** @alias maxSize */\n public max(size: number, errorMessage?: string): FileValidator {\n return this.maxSize(size, errorMessage);\n }\n\n /** Minimum image width */\n public minWidth(width: number, errorMessage?: string): FileValidator {\n return this.addRule(minWidthRule, errorMessage, {\n minWidth: width,\n });\n }\n\n /** Maximum image width */\n public maxWidth(width: number, errorMessage?: string): FileValidator {\n return this.addRule(maxWidthRule, errorMessage, {\n maxWidth: width,\n });\n }\n\n /** Minimum image height */\n public minHeight(height: number, errorMessage?: string): FileValidator {\n return this.addRule(minHeightRule, errorMessage, {\n minHeight: height,\n });\n }\n\n /** Maximum image height */\n public maxHeight(height: number, errorMessage?: string): FileValidator {\n return this.addRule(maxHeightRule, errorMessage, {\n maxHeight: height,\n });\n }\n\n /**\n * Save the file and return it as a string\n */\n public saveTo(relativeDirectory: string): FileValidator {\n return this.addTransformer(async (file: UploadedFile) => {\n const output = await file.save(relativeDirectory);\n\n return output.path;\n });\n }\n\n /**\n * @inheritdoc\n *\n * File uploads are not natively representable in JSON Schema.\n * The output varies by target:\n * - `openapi-3.0` → `{ type: \"string\", format: \"binary\" }` (standard for multipart/form-data uploads)\n * - `draft-2020-12` → `{ type: \"string\", contentEncoding: \"binary\" }`\n * - `draft-07` → `{}` (no standard binary representation — permissive fallback)\n *\n * @example\n * ```ts\n * v.file().toJsonSchema(\"openapi-3.0\")\n * // → { type: \"string\", format: \"binary\" }\n * ```\n */\n public override toJsonSchema(target: JsonSchemaTarget = \"draft-2020-12\"): JsonSchemaResult {\n if (target === \"openapi-3.0\") {\n return { type: \"string\", format: \"binary\" };\n }\n\n if (target === \"draft-2020-12\") {\n return { type: \"string\", contentEncoding: \"binary\" };\n }\n\n // draft-07: no standard binary representation\n return {};\n }\n}\n"],"mappings":";;;;;;;AAeA,MAAa,6BAA6B,EAAE,OAAO;CACjD,UAAU,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"file-validator.mjs","names":[],"sources":["../../../../../../../../core/src/validation/validators/file-validator.ts"],"sourcesContent":["import type { FileSizeOption, JsonSchemaResult, JsonSchemaTarget } from \"@warlock.js/seal\";\nimport {\n BaseValidator,\n maxFileSizeRule,\n maxHeightRule,\n maxWidthRule,\n minFileSizeRule,\n minHeightRule,\n minWidthRule,\n resolveFileSize,\n v,\n} from \"@warlock.js/seal\";\nimport { UploadedFile } from \"../../http\";\nimport { fileExtensionRule, fileRule, fileTypeRule, imageRule } from \"../file\";\n\nexport const uploadedFileMetadataSchema = v.object({\n location: v.string().oneOf([\"local\", \"cloud\"]),\n width: v.int().positive(),\n height: v.int().positive(),\n size: v.int().positive(),\n mimeType: v.string(),\n extension: v.string(),\n name: v.string(),\n});\n\n/**\n * File validator class\n */\nexport class FileValidator extends BaseValidator {\n public constructor(errorMessage?: string) {\n super();\n this.addMutableRule(fileRule, errorMessage);\n }\n\n /**\n * Check if value is a File type\n */\n public matchesType(value: any): boolean {\n return value instanceof UploadedFile;\n }\n\n /** Value must be an image */\n public image(errorMessage?: string): FileValidator {\n return this.addRule(imageRule, errorMessage);\n }\n\n /** Accept specific file extensions */\n public accept(extensions: string | string[], errorMessage?: string): FileValidator {\n return this.addRule(fileExtensionRule, errorMessage, {\n extensions,\n });\n }\n\n /** Allow specific MIME types */\n public mimeType(mimeTypes: string | string[], errorMessage?: string): FileValidator {\n return this.addRule(fileTypeRule, errorMessage, {\n mimeTypes,\n });\n }\n\n /** Allow only pdf files */\n public pdf(errorMessage?: string): FileValidator {\n return this.mimeType(\"application/pdf\", errorMessage);\n }\n\n /** Allow only excel files */\n public excel(errorMessage?: string): FileValidator {\n return this.mimeType(\n [\n \"application/vnd.ms-excel\",\n \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n ],\n errorMessage,\n );\n }\n\n /** Allow only word files */\n public word(errorMessage?: string): FileValidator {\n return this.mimeType(\n [\n \"application/msword\",\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n ],\n errorMessage,\n );\n }\n\n /** Minimum file size */\n public minSize(size: number | FileSizeOption, errorMessage?: string): FileValidator {\n return this.addRule(minFileSizeRule, errorMessage, {\n minSize: resolveFileSize(size),\n });\n }\n\n /** @alias minSize */\n public min(size: number | FileSizeOption, errorMessage?: string): FileValidator {\n return this.minSize(size, errorMessage);\n }\n\n /** Maximum file size */\n public maxSize(size: number | FileSizeOption, errorMessage?: string): FileValidator {\n return this.addRule(maxFileSizeRule, errorMessage, {\n maxSize: resolveFileSize(size),\n });\n }\n\n /** @alias maxSize */\n public max(size: number, errorMessage?: string): FileValidator {\n return this.maxSize(size, errorMessage);\n }\n\n /** Minimum image width */\n public minWidth(width: number, errorMessage?: string): FileValidator {\n return this.addRule(minWidthRule, errorMessage, {\n minWidth: width,\n });\n }\n\n /** Maximum image width */\n public maxWidth(width: number, errorMessage?: string): FileValidator {\n return this.addRule(maxWidthRule, errorMessage, {\n maxWidth: width,\n });\n }\n\n /** Minimum image height */\n public minHeight(height: number, errorMessage?: string): FileValidator {\n return this.addRule(minHeightRule, errorMessage, {\n minHeight: height,\n });\n }\n\n /** Maximum image height */\n public maxHeight(height: number, errorMessage?: string): FileValidator {\n return this.addRule(maxHeightRule, errorMessage, {\n maxHeight: height,\n });\n }\n\n /**\n * Save the file and return it as a string\n */\n public saveTo(relativeDirectory: string): FileValidator {\n return this.addTransformer(async (file: UploadedFile) => {\n const output = await file.save(relativeDirectory);\n\n return output.path;\n });\n }\n\n /**\n * @inheritdoc\n *\n * File uploads are not natively representable in JSON Schema.\n * The output varies by target:\n * - `openapi-3.0` → `{ type: \"string\", format: \"binary\" }` (standard for multipart/form-data uploads)\n * - `draft-2020-12` → `{ type: \"string\", contentEncoding: \"binary\" }`\n * - `draft-07` → `{}` (no standard binary representation — permissive fallback)\n *\n * @example\n * ```ts\n * v.file().toJsonSchema(\"openapi-3.0\")\n * // → { type: \"string\", format: \"binary\" }\n * ```\n */\n public override toJsonSchema(target: JsonSchemaTarget = \"draft-2020-12\"): JsonSchemaResult {\n if (target === \"openapi-3.0\") {\n return { type: \"string\", format: \"binary\" };\n }\n\n if (target === \"draft-2020-12\") {\n return { type: \"string\", contentEncoding: \"binary\" };\n }\n\n // draft-07: no standard binary representation\n return {};\n }\n}\n"],"mappings":";;;;;;;AAeA,MAAa,6BAA6B,EAAE,OAAO;CACjD,UAAU,EAAE,OAAO,CAAC,CAAC,MAAM,CAAC,SAAS,OAAO,CAAC;CAC7C,OAAO,EAAE,IAAI,CAAC,CAAC,SAAS;CACxB,QAAQ,EAAE,IAAI,CAAC,CAAC,SAAS;CACzB,MAAM,EAAE,IAAI,CAAC,CAAC,SAAS;CACvB,UAAU,EAAE,OAAO;CACnB,WAAW,EAAE,OAAO;CACpB,MAAM,EAAE,OAAO;AACjB,CAAC;;;;AAKD,IAAa,gBAAb,cAAmC,cAAc;CAC/C,AAAO,YAAY,cAAuB;EACxC,MAAM;EACN,KAAK,eAAe,UAAU,YAAY;CAC5C;;;;CAKA,AAAO,YAAY,OAAqB;EACtC,OAAO,iBAAiB;CAC1B;;CAGA,AAAO,MAAM,cAAsC;EACjD,OAAO,KAAK,QAAQ,WAAW,YAAY;CAC7C;;CAGA,AAAO,OAAO,YAA+B,cAAsC;EACjF,OAAO,KAAK,QAAQ,mBAAmB,cAAc,EACnD,WACF,CAAC;CACH;;CAGA,AAAO,SAAS,WAA8B,cAAsC;EAClF,OAAO,KAAK,QAAQ,cAAc,cAAc,EAC9C,UACF,CAAC;CACH;;CAGA,AAAO,IAAI,cAAsC;EAC/C,OAAO,KAAK,SAAS,mBAAmB,YAAY;CACtD;;CAGA,AAAO,MAAM,cAAsC;EACjD,OAAO,KAAK,SACV,CACE,4BACA,mEACF,GACA,YACF;CACF;;CAGA,AAAO,KAAK,cAAsC;EAChD,OAAO,KAAK,SACV,CACE,sBACA,yEACF,GACA,YACF;CACF;;CAGA,AAAO,QAAQ,MAA+B,cAAsC;EAClF,OAAO,KAAK,QAAQ,iBAAiB,cAAc,EACjD,SAAS,gBAAgB,IAAI,EAC/B,CAAC;CACH;;CAGA,AAAO,IAAI,MAA+B,cAAsC;EAC9E,OAAO,KAAK,QAAQ,MAAM,YAAY;CACxC;;CAGA,AAAO,QAAQ,MAA+B,cAAsC;EAClF,OAAO,KAAK,QAAQ,iBAAiB,cAAc,EACjD,SAAS,gBAAgB,IAAI,EAC/B,CAAC;CACH;;CAGA,AAAO,IAAI,MAAc,cAAsC;EAC7D,OAAO,KAAK,QAAQ,MAAM,YAAY;CACxC;;CAGA,AAAO,SAAS,OAAe,cAAsC;EACnE,OAAO,KAAK,QAAQ,cAAc,cAAc,EAC9C,UAAU,MACZ,CAAC;CACH;;CAGA,AAAO,SAAS,OAAe,cAAsC;EACnE,OAAO,KAAK,QAAQ,cAAc,cAAc,EAC9C,UAAU,MACZ,CAAC;CACH;;CAGA,AAAO,UAAU,QAAgB,cAAsC;EACrE,OAAO,KAAK,QAAQ,eAAe,cAAc,EAC/C,WAAW,OACb,CAAC;CACH;;CAGA,AAAO,UAAU,QAAgB,cAAsC;EACrE,OAAO,KAAK,QAAQ,eAAe,cAAc,EAC/C,WAAW,OACb,CAAC;CACH;;;;CAKA,AAAO,OAAO,mBAA0C;EACtD,OAAO,KAAK,eAAe,OAAO,SAAuB;GAGvD,QAAO,MAFc,KAAK,KAAK,iBAAiB,EAEnC,CAAC;EAChB,CAAC;CACH;;;;;;;;;;;;;;;;CAiBA,AAAgB,aAAa,SAA2B,iBAAmC;EACzF,IAAI,WAAW,eACb,OAAO;GAAE,MAAM;GAAU,QAAQ;EAAS;EAG5C,IAAI,WAAW,iBACb,OAAO;GAAE,MAAM;GAAU,iBAAiB;EAAS;EAIrD,OAAO,CAAC;CACV;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lower-stage3-decorators.d.mts","names":[],"sources":["../../../../../../../core/src/vite/lower-stage3-decorators.ts"],"mappings":";;AASA;;;;;;UAAiB,uBAAA;EACf,IAAA;EACA,OAAA;EACA,SAAA,CAAU,IAAA,UAAc,EAAA,WAAa,OAAO;IAAG,IAAA;IAAc,GAAA;EAAA;AAAA;AAoC/D;;;;AAAgE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAhE,iBAAgB,qBAAA,
|
|
1
|
+
{"version":3,"file":"lower-stage3-decorators.d.mts","names":[],"sources":["../../../../../../../core/src/vite/lower-stage3-decorators.ts"],"mappings":";;AASA;;;;;;UAAiB,uBAAA;EACf,IAAA;EACA,OAAA;EACA,SAAA,CAAU,IAAA,UAAc,EAAA,WAAa,OAAO;IAAG,IAAA;IAAc,GAAA;EAAA;AAAA;AAoC/D;;;;AAAgE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAhE,iBAAgB,qBAAA,IAAyB,uBAAuB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"warlock-config.manager.d.mts","names":[],"sources":["../../../../../../../core/src/warlock-config/warlock-config.manager.ts"],"mappings":";;;;;AAeA;;;;AAAwD;AAaxD;iBAbgB,yBAAA,CAA0B,KAAc;;;;;;;cAa3C,oBAAA;EAyHR;;;EAAA,QArHK,MAAA;EAyIS;;;EAAA,QApIT,OAAA;EAqIL;;;;;EA9HU,IAAA,
|
|
1
|
+
{"version":3,"file":"warlock-config.manager.d.mts","names":[],"sources":["../../../../../../../core/src/warlock-config/warlock-config.manager.ts"],"mappings":";;;;;AAeA;;;;AAAwD;AAaxD;iBAbgB,yBAAA,CAA0B,KAAc;;;;;;;cAa3C,oBAAA;EAyHR;;;EAAA,QArHK,MAAA;EAyIS;;;EAAA,QApIT,OAAA;EAqIL;;;;;EA9HU,IAAA,IAAQ,OAAA,CAAQ,aAAA;EAPrB;;;;;;EAAA,QAgCM,MAAA;EA6EH;;;;;;;;EAAA,QAtCG,cAAA;EA2DR;;;;;;;;;;;EArBC,GAAA,mBAAsB,aAAA,EAC3B,GAAA,EAAK,GAAA,EACL,YAAA,GAAe,aAAA,CAAc,GAAA,IAC5B,aAAA,CAAc,GAAA;EAsCV;;;;;AAWuB;AAoBhC;;;;EAnDQ,OAAA,mBAA0B,aAAA,EAC9B,GAAA,EAAK,GAAA,EACL,YAAA,GAAe,aAAA,CAAc,GAAA,IAC5B,OAAA,CAAQ,aAAA,CAAc,GAAA;;;;MAQd,QAAA;;;;;;EASJ,MAAA,IAAU,aAAA;;;;EAWJ,MAAA,IAAU,OAAA;AAAA;;;;;;;;;;;;;;cAoBZ,oBAAA,EAAoB,oBAA6B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"warlock-config.manager.mjs","names":[],"sources":["../../../../../../../core/src/warlock-config/warlock-config.manager.ts"],"sourcesContent":["import { fileExistsAsync } from \"@warlock.js/fs\";\r\nimport { get } from \"@mongez/reinforcements\";\r\nimport { readFile, unlink, writeFile } from \"node:fs/promises\";\r\nimport { pathToFileURL } from \"url\";\r\nimport { rootPath } from \"../utils\";\r\nimport { type WarlockConfig } from \"./types\";\r\n\r\n/**\r\n * True when `error` is Node's \"this runtime can't import a `.ts` file\" error.\r\n *\r\n * Node only executes TypeScript natively from v22.18 / v23.6 onward (earlier\r\n * 22.x needs `--experimental-strip-types`). On older runtimes a bare\r\n * `import(\"warlock.config.ts\")` throws `ERR_UNKNOWN_FILE_EXTENSION` — which is\r\n * the signal to fall back to transpiling the config ourselves.\r\n */\r\nexport function isUnknownTsExtensionError(error: unknown): boolean {\r\n return (\r\n (error as { code?: string })?.code === \"ERR_UNKNOWN_FILE_EXTENSION\" ||\r\n /Unknown file extension \"\\.tsx?\"/.test(String((error as Error)?.message))\r\n );\r\n}\r\n\r\n/**\r\n * Warlock Config Manager\r\n *\r\n * Manages lazy loading of the pre-compiled warlock.config.js file\r\n * from the .warlock/cache directory.\r\n */\r\nexport class WarlockConfigManager {\r\n /**\r\n * Cached config instance\r\n */\r\n private config?: WarlockConfig;\r\n\r\n /**\r\n * Loading promise to prevent duplicate loads\r\n */\r\n private loading?: Promise<WarlockConfig | undefined>;\r\n\r\n /**\r\n * Load warlock.config.js (cached after first load)\r\n *\r\n * @returns The resolved Warlock configuration\r\n */\r\n public async load(): Promise<WarlockConfig | undefined> {\r\n // Already loaded\r\n if (this.config) {\r\n return this.config;\r\n }\r\n\r\n // Currently loading (prevent duplicate loads)\r\n if (this.loading) {\r\n return this.loading;\r\n }\r\n\r\n // Start loading\r\n this.loading = this.doLoad();\r\n this.config = await this.loading;\r\n this.loading = undefined;\r\n\r\n return this.config;\r\n }\r\n\r\n /**\r\n * Internal load implementation\r\n *\r\n * The ESM loader hook transpiles `warlock.config.ts` on import — no\r\n * separate compile-to-disk step is needed.\r\n */\r\n private async doLoad(): Promise<WarlockConfig | undefined> {\r\n const configPath = rootPath(\"warlock.config.ts\");\r\n\r\n if (!(await fileExistsAsync(configPath))) {\r\n // Written straight to the console, NOT through `@warlock.js/logger`.\r\n // This runs during CLI bootstrap, before `LoggerConnector.start()` has\r\n // configured a single channel, so a logger call here reaches nobody in\r\n // any application — the warning would be silently dropped exactly when\r\n // the user most needs it.\r\n console.warn(\"warlock.config.ts is missing — run `warlock init` to create it\");\r\n return;\r\n }\r\n\r\n try {\r\n const configModule = await import(pathToFileURL(configPath).href);\r\n return configModule.default;\r\n } catch (error) {\r\n // `dev` registers a TS loader hook, and Node ≥ 22.18 / 23.6 strips types\r\n // natively — in both cases the direct import above works. But `build` /\r\n // `start` register no hook, so on an older Node the import throws\r\n // ERR_UNKNOWN_FILE_EXTENSION. Transpile the config with esbuild ourselves\r\n // so config loading is Node-version-independent rather than relying on an\r\n // experimental runtime feature.\r\n if (isUnknownTsExtensionError(error)) {\r\n return await this.loadViaEsbuild(configPath);\r\n }\r\n\r\n throw new Error(`Failed to load warlock.config.ts: ${error}`);\r\n }\r\n }\r\n\r\n /**\r\n * Fallback config loader for runtimes without native TypeScript support.\r\n *\r\n * Transpiles `warlock.config.ts` with esbuild (already a core dependency),\r\n * writes the result as a sibling `.mjs` — so the config's own imports (bare\r\n * `@warlock.js/*` and any relative paths) resolve exactly as they would from\r\n * the original location — imports it, then removes the temp file.\r\n */\r\n private async loadViaEsbuild(configPath: string): Promise<WarlockConfig | undefined> {\r\n const { transformSync } = await import(\"esbuild\");\r\n const source = await readFile(configPath, \"utf8\");\r\n\r\n const { code } = transformSync(source, {\r\n loader: \"ts\",\r\n format: \"esm\",\r\n target: `node${process.versions.node.split(\".\")[0]}`,\r\n sourcefile: configPath,\r\n });\r\n\r\n // Unique sibling name so concurrent processes never clobber each other.\r\n const compiledPath = configPath.replace(\r\n /warlock\\.config\\.ts$/,\r\n `.warlock.config.${process.pid}.mjs`,\r\n );\r\n\r\n await writeFile(compiledPath, code, \"utf8\");\r\n\r\n try {\r\n const configModule = await import(pathToFileURL(compiledPath).href);\r\n return configModule.default;\r\n } finally {\r\n await unlink(compiledPath).catch(() => {});\r\n }\r\n }\r\n\r\n /**\r\n * Get config value by key (dot notation supported)\r\n *\r\n * @example\r\n * config.get(\"server.port\") // Returns 3000\r\n * config.get(\"cli.commands\") // Returns array of commands\r\n *\r\n * @param key - Config key (supports dot notation), autocompletes for first level only\r\n * @returns The config value\r\n * @throws Error if config is not loaded\r\n */\r\n public get<Key extends keyof WarlockConfig>(\r\n key: Key,\r\n defaultValue?: WarlockConfig[Key],\r\n ): WarlockConfig[Key] {\r\n if (!this.config) {\r\n throw new Error(\"WarlockConfig not loaded. Call load() first or use lazyGet().\");\r\n }\r\n\r\n return get(this.config, key as string, defaultValue);\r\n }\r\n\r\n /**\r\n * Lazy get - loads config if not already loaded\r\n *\r\n * @example\r\n * const port = await config.lazyGet(\"server\");\r\n *\r\n * @param key - Config key (supports dot notation), autocompletes for first level only\r\n * @param defaultValue - Default value if config key is undefined\r\n * @returns The config value\r\n */\r\n async lazyGet<Key extends keyof WarlockConfig>(\r\n key: Key,\r\n defaultValue?: WarlockConfig[Key],\r\n ): Promise<WarlockConfig[Key]> {\r\n await this.load();\r\n return this.get(key, defaultValue);\r\n }\r\n\r\n /**\r\n * Check if config is loaded\r\n */\r\n public get isLoaded(): boolean {\r\n return this.config !== undefined;\r\n }\r\n\r\n /**\r\n * Get the entire config object\r\n *\r\n * @throws Error if config is not loaded\r\n */\r\n public getAll(): WarlockConfig {\r\n if (!this.config) {\r\n throw new Error(\"WarlockConfig not loaded. Call load() first or use lazyGet().\");\r\n }\r\n\r\n return this.config;\r\n }\r\n\r\n /**\r\n * Reload config (useful for HMR/development)\r\n */\r\n public async reload(): Promise<void> {\r\n this.config = undefined;\r\n this.loading = undefined;\r\n await this.load();\r\n }\r\n}\r\n\r\n/**\r\n * Exported singleton instance\r\n *\r\n * @example\r\n * import { warlockConfig } from \"@warlock.js/core\";\r\n *\r\n * // Lazy load and get value\r\n * const port = await warlockConfig.lazyGet(\"server.port\");\r\n *\r\n * // Or load first, then get\r\n * await warlockConfig.load();\r\n * const commands = warlockConfig.get(\"cli.commands\");\r\n */\r\nexport const warlockConfigManager = new WarlockConfigManager();\r\n"],"mappings":";;;;;;;;;;;;;;;;AAeA,SAAgB,0BAA0B,OAAyB;CACjE,OACG,OAA6B,SAAS,gCACvC,kCAAkC,KAAK,OAAQ,OAAiB,OAAO,CAAC;AAE5E;;;;;;;AAQA,IAAa,uBAAb,MAAkC;;;;;;CAgBhC,MAAa,OAA2C;EAEtD,IAAI,KAAK,QACP,OAAO,KAAK;EAId,IAAI,KAAK,SACP,OAAO,KAAK;EAId,KAAK,UAAU,KAAK,OAAO;EAC3B,KAAK,SAAS,MAAM,KAAK;EACzB,KAAK,UAAU;EAEf,OAAO,KAAK;CACd;;;;;;;CAQA,MAAc,SAA6C;EACzD,MAAM,aAAa,SAAS,mBAAmB;EAE/C,IAAI,CAAE,MAAM,gBAAgB,UAAU,GAAI;GAMxC,QAAQ,KAAK,gEAAgE;GAC7E;EACF;EAEA,IAAI;GAEF,QAAO,MADoB,OAAO,cAAc,UAAU,
|
|
1
|
+
{"version":3,"file":"warlock-config.manager.mjs","names":[],"sources":["../../../../../../../core/src/warlock-config/warlock-config.manager.ts"],"sourcesContent":["import { fileExistsAsync } from \"@warlock.js/fs\";\r\nimport { get } from \"@mongez/reinforcements\";\r\nimport { readFile, unlink, writeFile } from \"node:fs/promises\";\r\nimport { pathToFileURL } from \"url\";\r\nimport { rootPath } from \"../utils\";\r\nimport { type WarlockConfig } from \"./types\";\r\n\r\n/**\r\n * True when `error` is Node's \"this runtime can't import a `.ts` file\" error.\r\n *\r\n * Node only executes TypeScript natively from v22.18 / v23.6 onward (earlier\r\n * 22.x needs `--experimental-strip-types`). On older runtimes a bare\r\n * `import(\"warlock.config.ts\")` throws `ERR_UNKNOWN_FILE_EXTENSION` — which is\r\n * the signal to fall back to transpiling the config ourselves.\r\n */\r\nexport function isUnknownTsExtensionError(error: unknown): boolean {\r\n return (\r\n (error as { code?: string })?.code === \"ERR_UNKNOWN_FILE_EXTENSION\" ||\r\n /Unknown file extension \"\\.tsx?\"/.test(String((error as Error)?.message))\r\n );\r\n}\r\n\r\n/**\r\n * Warlock Config Manager\r\n *\r\n * Manages lazy loading of the pre-compiled warlock.config.js file\r\n * from the .warlock/cache directory.\r\n */\r\nexport class WarlockConfigManager {\r\n /**\r\n * Cached config instance\r\n */\r\n private config?: WarlockConfig;\r\n\r\n /**\r\n * Loading promise to prevent duplicate loads\r\n */\r\n private loading?: Promise<WarlockConfig | undefined>;\r\n\r\n /**\r\n * Load warlock.config.js (cached after first load)\r\n *\r\n * @returns The resolved Warlock configuration\r\n */\r\n public async load(): Promise<WarlockConfig | undefined> {\r\n // Already loaded\r\n if (this.config) {\r\n return this.config;\r\n }\r\n\r\n // Currently loading (prevent duplicate loads)\r\n if (this.loading) {\r\n return this.loading;\r\n }\r\n\r\n // Start loading\r\n this.loading = this.doLoad();\r\n this.config = await this.loading;\r\n this.loading = undefined;\r\n\r\n return this.config;\r\n }\r\n\r\n /**\r\n * Internal load implementation\r\n *\r\n * The ESM loader hook transpiles `warlock.config.ts` on import — no\r\n * separate compile-to-disk step is needed.\r\n */\r\n private async doLoad(): Promise<WarlockConfig | undefined> {\r\n const configPath = rootPath(\"warlock.config.ts\");\r\n\r\n if (!(await fileExistsAsync(configPath))) {\r\n // Written straight to the console, NOT through `@warlock.js/logger`.\r\n // This runs during CLI bootstrap, before `LoggerConnector.start()` has\r\n // configured a single channel, so a logger call here reaches nobody in\r\n // any application — the warning would be silently dropped exactly when\r\n // the user most needs it.\r\n console.warn(\"warlock.config.ts is missing — run `warlock init` to create it\");\r\n return;\r\n }\r\n\r\n try {\r\n const configModule = await import(pathToFileURL(configPath).href);\r\n return configModule.default;\r\n } catch (error) {\r\n // `dev` registers a TS loader hook, and Node ≥ 22.18 / 23.6 strips types\r\n // natively — in both cases the direct import above works. But `build` /\r\n // `start` register no hook, so on an older Node the import throws\r\n // ERR_UNKNOWN_FILE_EXTENSION. Transpile the config with esbuild ourselves\r\n // so config loading is Node-version-independent rather than relying on an\r\n // experimental runtime feature.\r\n if (isUnknownTsExtensionError(error)) {\r\n return await this.loadViaEsbuild(configPath);\r\n }\r\n\r\n throw new Error(`Failed to load warlock.config.ts: ${error}`);\r\n }\r\n }\r\n\r\n /**\r\n * Fallback config loader for runtimes without native TypeScript support.\r\n *\r\n * Transpiles `warlock.config.ts` with esbuild (already a core dependency),\r\n * writes the result as a sibling `.mjs` — so the config's own imports (bare\r\n * `@warlock.js/*` and any relative paths) resolve exactly as they would from\r\n * the original location — imports it, then removes the temp file.\r\n */\r\n private async loadViaEsbuild(configPath: string): Promise<WarlockConfig | undefined> {\r\n const { transformSync } = await import(\"esbuild\");\r\n const source = await readFile(configPath, \"utf8\");\r\n\r\n const { code } = transformSync(source, {\r\n loader: \"ts\",\r\n format: \"esm\",\r\n target: `node${process.versions.node.split(\".\")[0]}`,\r\n sourcefile: configPath,\r\n });\r\n\r\n // Unique sibling name so concurrent processes never clobber each other.\r\n const compiledPath = configPath.replace(\r\n /warlock\\.config\\.ts$/,\r\n `.warlock.config.${process.pid}.mjs`,\r\n );\r\n\r\n await writeFile(compiledPath, code, \"utf8\");\r\n\r\n try {\r\n const configModule = await import(pathToFileURL(compiledPath).href);\r\n return configModule.default;\r\n } finally {\r\n await unlink(compiledPath).catch(() => {});\r\n }\r\n }\r\n\r\n /**\r\n * Get config value by key (dot notation supported)\r\n *\r\n * @example\r\n * config.get(\"server.port\") // Returns 3000\r\n * config.get(\"cli.commands\") // Returns array of commands\r\n *\r\n * @param key - Config key (supports dot notation), autocompletes for first level only\r\n * @returns The config value\r\n * @throws Error if config is not loaded\r\n */\r\n public get<Key extends keyof WarlockConfig>(\r\n key: Key,\r\n defaultValue?: WarlockConfig[Key],\r\n ): WarlockConfig[Key] {\r\n if (!this.config) {\r\n throw new Error(\"WarlockConfig not loaded. Call load() first or use lazyGet().\");\r\n }\r\n\r\n return get(this.config, key as string, defaultValue);\r\n }\r\n\r\n /**\r\n * Lazy get - loads config if not already loaded\r\n *\r\n * @example\r\n * const port = await config.lazyGet(\"server\");\r\n *\r\n * @param key - Config key (supports dot notation), autocompletes for first level only\r\n * @param defaultValue - Default value if config key is undefined\r\n * @returns The config value\r\n */\r\n async lazyGet<Key extends keyof WarlockConfig>(\r\n key: Key,\r\n defaultValue?: WarlockConfig[Key],\r\n ): Promise<WarlockConfig[Key]> {\r\n await this.load();\r\n return this.get(key, defaultValue);\r\n }\r\n\r\n /**\r\n * Check if config is loaded\r\n */\r\n public get isLoaded(): boolean {\r\n return this.config !== undefined;\r\n }\r\n\r\n /**\r\n * Get the entire config object\r\n *\r\n * @throws Error if config is not loaded\r\n */\r\n public getAll(): WarlockConfig {\r\n if (!this.config) {\r\n throw new Error(\"WarlockConfig not loaded. Call load() first or use lazyGet().\");\r\n }\r\n\r\n return this.config;\r\n }\r\n\r\n /**\r\n * Reload config (useful for HMR/development)\r\n */\r\n public async reload(): Promise<void> {\r\n this.config = undefined;\r\n this.loading = undefined;\r\n await this.load();\r\n }\r\n}\r\n\r\n/**\r\n * Exported singleton instance\r\n *\r\n * @example\r\n * import { warlockConfig } from \"@warlock.js/core\";\r\n *\r\n * // Lazy load and get value\r\n * const port = await warlockConfig.lazyGet(\"server.port\");\r\n *\r\n * // Or load first, then get\r\n * await warlockConfig.load();\r\n * const commands = warlockConfig.get(\"cli.commands\");\r\n */\r\nexport const warlockConfigManager = new WarlockConfigManager();\r\n"],"mappings":";;;;;;;;;;;;;;;;AAeA,SAAgB,0BAA0B,OAAyB;CACjE,OACG,OAA6B,SAAS,gCACvC,kCAAkC,KAAK,OAAQ,OAAiB,OAAO,CAAC;AAE5E;;;;;;;AAQA,IAAa,uBAAb,MAAkC;;;;;;CAgBhC,MAAa,OAA2C;EAEtD,IAAI,KAAK,QACP,OAAO,KAAK;EAId,IAAI,KAAK,SACP,OAAO,KAAK;EAId,KAAK,UAAU,KAAK,OAAO;EAC3B,KAAK,SAAS,MAAM,KAAK;EACzB,KAAK,UAAU;EAEf,OAAO,KAAK;CACd;;;;;;;CAQA,MAAc,SAA6C;EACzD,MAAM,aAAa,SAAS,mBAAmB;EAE/C,IAAI,CAAE,MAAM,gBAAgB,UAAU,GAAI;GAMxC,QAAQ,KAAK,gEAAgE;GAC7E;EACF;EAEA,IAAI;GAEF,QAAO,MADoB,OAAO,cAAc,UAAU,CAAC,CAAC,MACzC,CAAC;EACtB,SAAS,OAAO;GAOd,IAAI,0BAA0B,KAAK,GACjC,OAAO,MAAM,KAAK,eAAe,UAAU;GAG7C,MAAM,IAAI,MAAM,qCAAqC,OAAO;EAC9D;CACF;;;;;;;;;CAUA,MAAc,eAAe,YAAwD;EACnF,MAAM,EAAE,kBAAkB,MAAM,OAAO;EAGvC,MAAM,EAAE,SAAS,cAAc,MAFV,SAAS,YAAY,MAAM,GAET;GACrC,QAAQ;GACR,QAAQ;GACR,QAAQ,OAAO,QAAQ,SAAS,KAAK,MAAM,GAAG,CAAC,CAAC;GAChD,YAAY;EACd,CAAC;EAGD,MAAM,eAAe,WAAW,QAC9B,wBACA,mBAAmB,QAAQ,IAAI,KACjC;EAEA,MAAM,UAAU,cAAc,MAAM,MAAM;EAE1C,IAAI;GAEF,QAAO,MADoB,OAAO,cAAc,YAAY,CAAC,CAAC,MAC3C,CAAC;EACtB,UAAU;GACR,MAAM,OAAO,YAAY,CAAC,CAAC,YAAY,CAAC,CAAC;EAC3C;CACF;;;;;;;;;;;;CAaA,AAAO,IACL,KACA,cACoB;EACpB,IAAI,CAAC,KAAK,QACR,MAAM,IAAI,MAAM,+DAA+D;EAGjF,OAAO,IAAI,KAAK,QAAQ,KAAe,YAAY;CACrD;;;;;;;;;;;CAYA,MAAM,QACJ,KACA,cAC6B;EAC7B,MAAM,KAAK,KAAK;EAChB,OAAO,KAAK,IAAI,KAAK,YAAY;CACnC;;;;CAKA,IAAW,WAAoB;EAC7B,OAAO,KAAK,WAAW;CACzB;;;;;;CAOA,AAAO,SAAwB;EAC7B,IAAI,CAAC,KAAK,QACR,MAAM,IAAI,MAAM,+DAA+D;EAGjF,OAAO,KAAK;CACd;;;;CAKA,MAAa,SAAwB;EACnC,KAAK,SAAS;EACd,KAAK,UAAU;EACf,MAAM,KAAK,KAAK;CAClB;AACF;;;;;;;;;;;;;;AAeA,MAAa,uBAAuB,IAAI,qBAAqB"}
|
package/llms-full.txt
CHANGED
|
@@ -8624,7 +8624,7 @@ CLI commands, scheduled jobs, queue workers — anything running outside an HTTP
|
|
|
8624
8624
|
|
|
8625
8625
|
---
|
|
8626
8626
|
name: write-cli-command
|
|
8627
|
-
description: 'Author a custom `warlock <my-cmd>` command via the `command()` factory — name, description, action, options, preload, then register in `warlock.config.ts > cli.commands` or drop in `src/app/<module>/commands/`. Triggers: `command`, `CLICommand`, `CLICommandPreload`, `CLICommandOption`, `preload`, `preAction`, `persistent`, `colors`; "write a custom warlock command", "one-off maintenance task", "ship a CLI from a package", "framework built-in commands"; typical import `import { command } from "@warlock.js/core"`. Skip: framework dev/build/start — `@warlock.js/core/run-app/SKILL.md`; warlock.config.ts wiring — `@warlock.js/core/configure-app/SKILL.md`; competing libs `commander`, `yargs`, `oclif`.'
|
|
8627
|
+
description: 'Author a custom `warlock <my-cmd>` command via the `command()` factory — name, description, action, options, preload, then register in `warlock.config.ts > cli.commands` or drop in `src/app/<module>/commands/`. Also covers built-in `warlock add` feature scaffolding, including the Web starter and `index.register.ts`. Triggers: `command`, `CLICommand`, `CLICommandPreload`, `CLICommandOption`, `preload`, `preAction`, `persistent`, `colors`, `warlock add`, `index.register.ts`; "write a custom warlock command", "one-off maintenance task", "ship a CLI from a package", "framework built-in commands"; typical import `import { command } from "@warlock.js/core"`. Skip: framework dev/build/start — `@warlock.js/core/run-app/SKILL.md`; warlock.config.ts wiring — `@warlock.js/core/configure-app/SKILL.md`; competing libs `commander`, `yargs`, `oclif`.'
|
|
8628
8628
|
---
|
|
8629
8629
|
|
|
8630
8630
|
# Warlock — write a CLI command
|
|
@@ -8829,6 +8829,7 @@ warlock generate (alias: g) — interactive picker
|
|
|
8829
8829
|
| Feature | Installs |
|
|
8830
8830
|
| ------------- | --------------------------------------------------------------------------------------------------- |
|
|
8831
8831
|
| `react-email` | `react-email` + `@react-email/components` + `@react-email/render` + `@react-email/tailwind`; drops a `welcome-email.tsx` sample; patches `tsconfig.json` |
|
|
8832
|
+
| `web` | Warlock Web + React stack; scaffolds the application page, localization, contact endpoint, and Web configuration |
|
|
8832
8833
|
| `react` | `react` + `react-dom` + types |
|
|
8833
8834
|
| `image` | `sharp` (for the `Image` class) |
|
|
8834
8835
|
| `mail` | `nodemailer` + types |
|
|
@@ -8846,6 +8847,8 @@ warlock generate (alias: g) — interactive picker
|
|
|
8846
8847
|
|
|
8847
8848
|
Run `warlock add --list` to see what's currently registered. Pass `--packageManager <yarn\|pnpm\|npm>` to override auto-detection (defaults to whichever lockfile is present).
|
|
8848
8849
|
|
|
8850
|
+
The Web starter keeps the page's universal non-component setup in `src/web/index.register.ts`, while `src/web/index.page.tsx` re-exports `register`. Warlock still discovers and runs the hook from the page namespace in both SSR and the browser, but editing the page component remains inside React's Fast Refresh boundary. Do not move the `register()` declaration inline beside the component: React Refresh treats that non-component export as incompatible and reloads the page, losing component state.
|
|
8851
|
+
|
|
8849
8852
|
### Misc
|
|
8850
8853
|
|
|
8851
8854
|
| Command | Purpose |
|
package/llms.txt
CHANGED
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
- [warlock-doctor](@warlock.js/core/warlock-doctor/SKILL.md): Run `warlock doctor` — a read-only diagnostics command that checks routes / config / connectors / optional-peers / health endpoints / release hygiene and prints a pass/warn/fail report, exiting non-zero on any failure. Add your own probe with the `DoctorCheck` contract and `runChecks` / `formatReportLines`. Triggers: `warlock doctor`, `doctorCommand`, `DoctorCheck`, `CheckResult`, `CheckStatus`, `DoctorReport`, `runChecks`, `formatReportLines`, `printReport`, `defaultDoctorChecks`; "diagnose my app", "preflight / preflight check", "is the app healthy", "why are there 0 routes", "pre-release sanity check", "CI smoke check"; run as `pnpm warlock doctor`. Skip: the live `/health` + `/ready` HTTP probes — `@warlock.js/core/health-checks/SKILL.md`; authoring a general CLI command — `@warlock.js/core/write-cli-command/SKILL.md`; releasing the package — `releasing-warlock-monorepo`; competing tools `npm doctor`, `nest info`, hand-rolled preflight scripts.
|
|
43
43
|
- [warlock-routes](@warlock.js/core/warlock-routes/SKILL.md): Run `warlock routes` — a read-only command that lists the registered HTTP routes as a verb-colored table (method / path / name / action / middleware-count / source), a sibling of `warlock doctor`. Filter with `--method` / `--path` / `--name`, or emit normalized rows as JSON with `--json`. Also covers `warlock routes:diff`, which compares live page routes against the last `warlock build`'s route snapshot and exits non-zero on drift. Triggers: `warlock routes`, `routesCommand`, `warlock routes:diff`, `routesDiffCommand`, "list my routes", "show all routes", "route table", "what endpoints does my app expose", "dump routes as JSON", "which routes have middleware", "route map for CI", "did my page routes drift from the last build"; run as `pnpm warlock routes` / `pnpm warlock routes:diff`. Skip: read-only health/preflight checks — `@warlock.js/core/warlock-doctor/SKILL.md`; defining/naming/grouping routes — `@warlock.js/core/register-route/SKILL.md`; authoring a general CLI command — `@warlock.js/core/write-cli-command/SKILL.md`; competing tools `nest`/`express` route listers, `php artisan route:list`.
|
|
44
44
|
- [wire-socket](@warlock.js/core/wire-socket/SKILL.md): Configure Socket.IO via `src/config/socket.ts`, reach the live server through `getSocketServer()` (or `app.socket` post-bootstrap), register `connection` handlers once the late-phase socket connector has booted, emit from controllers/services, use rooms and namespaces. Triggers: `app.socket`, `getSocketServer`, `SocketOptions`, `socket.io` `Server`, `socket.join`, `socket.to`, `io.of`, `io.use`; "add realtime chat", "emit socket events from a service", "use rooms and namespaces", "per-socket JWT auth". Skip: connector lifecycle — `@warlock.js/core/add-connector/SKILL.md`; app context accessors — `@warlock.js/core/use-app-context/SKILL.md`; competing libs `ws`, `socket.io` direct without Warlock connector, `uWebSockets.js`.
|
|
45
|
-
- [write-cli-command](@warlock.js/core/write-cli-command/SKILL.md): Author a custom `warlock <my-cmd>` command via the `command()` factory — name, description, action, options, preload, then register in `warlock.config.ts > cli.commands` or drop in `src/app/<module>/commands/`. Triggers: `command`, `CLICommand`, `CLICommandPreload`, `CLICommandOption`, `preload`, `preAction`, `persistent`, `colors`; "write a custom warlock command", "one-off maintenance task", "ship a CLI from a package", "framework built-in commands"; typical import `import { command } from "@warlock.js/core"`. Skip: framework dev/build/start — `@warlock.js/core/run-app/SKILL.md`; warlock.config.ts wiring — `@warlock.js/core/configure-app/SKILL.md`; competing libs `commander`, `yargs`, `oclif`.
|
|
45
|
+
- [write-cli-command](@warlock.js/core/write-cli-command/SKILL.md): Author a custom `warlock <my-cmd>` command via the `command()` factory — name, description, action, options, preload, then register in `warlock.config.ts > cli.commands` or drop in `src/app/<module>/commands/`. Also covers built-in `warlock add` feature scaffolding, including the Web starter and `index.register.ts`. Triggers: `command`, `CLICommand`, `CLICommandPreload`, `CLICommandOption`, `preload`, `preAction`, `persistent`, `colors`, `warlock add`, `index.register.ts`; "write a custom warlock command", "one-off maintenance task", "ship a CLI from a package", "framework built-in commands"; typical import `import { command } from "@warlock.js/core"`. Skip: framework dev/build/start — `@warlock.js/core/run-app/SKILL.md`; warlock.config.ts wiring — `@warlock.js/core/configure-app/SKILL.md`; competing libs `commander`, `yargs`, `oclif`.
|
|
46
46
|
- [write-middleware](@warlock.js/core/write-middleware/SKILL.md): Author HTTP middleware for @warlock.js/core — the `(request, response)` signature, short-circuit by returning a response, enrich the request with extra fields, register per-route, per-group, or app-wide. Triggers: `Middleware`, `MiddlewareResponse`, `router.group`, `guarded`, `request.detectIp`, `authMiddleware`; "write a custom middleware", "short-circuit a request", "enrich the request with extra fields", "per-route vs per-group middleware"; typical import `import type { Middleware } from "@warlock.js/core"`. Skip: built-in middleware catalog — `@warlock.js/core/use-middleware/SKILL.md`; route attachment — `@warlock.js/core/register-route/SKILL.md`; response helpers — `@warlock.js/core/send-response/SKILL.md`; competing patterns: `express` `(req, res, next)` middleware, Fastify `preHandler` hooks.
|
|
47
47
|
- [write-seeder](@warlock.js/core/write-seeder/SKILL.md): Author a seed file under `src/app/<module>/seeds/<name>.ts` using the `seeder()` factory — `name`, `dependsOn`, `once`, `order`, `batchSize`, `run({ track, now, batchSize })`. Auto-discovered by `warlock seed`; tracked in a `seeds` table; per-record refs in `seed_records` so `warlock seed --drop` can undo a seed. Triggers: `seeder`, `Seeder`, `SeedResult`, `SeedContext`, `SeedClock`, `track`, `now`, `batchSize`, `SeedersManager`, `warlock seed`, `--fresh`, `--drop`, `--list`, `--path`; "seed default roles", "undo a seed", "one-time data migration", "auto-discovered seeds", "order seeds by dependency", "deterministic seed timestamps", "inject a seed clock"; typical import `import { seeder } from "@warlock.js/core"`. Skip: module folder layout — `@warlock.js/core/create-module/SKILL.md`; repository CRUD — `@warlock.js/core/use-repository/SKILL.md`; CLI flags — `@warlock.js/core/write-cli-command/SKILL.md`; competing patterns: hand-rolled `node scripts/seed.js`, `typeorm-seeding`.
|
|
48
48
|
- [write-use-case](@warlock.js/core/write-use-case/SKILL.md): Author `useCase()` pipelines for business logic — guards, schema, before/after middleware, retry, benchmark, broadcast, lifecycle callbacks; transport-agnostic and observable by default. Input is inferred from the `schema`. Triggers: `useCase`, `UseCaseContext`, `UseCaseResult`, `retry`, `benchmark`, `broadcast`, `description`, `globalUseCasesEvents`, `UseCaseBroadcastChannel`; "encapsulate a business operation", "share logic between HTTP and CLI", "add guards and lifecycle hooks", "broadcast a use case result", "transport-agnostic pipeline"; typical import `import { useCase } from "@warlock.js/core"`. Skip: thin handler shape — `@warlock.js/core/create-controller/SKILL.md`; schema details — `@warlock.js/core/validate-input/SKILL.md`; the standalone retry util — `@warlock.js/core/retry-operation/SKILL.md`; competing libs `@nestjs/cqrs`, `inversify`, hand-rolled service classes.
|
package/package.json
CHANGED
|
@@ -25,13 +25,13 @@
|
|
|
25
25
|
"@mongez/slug": "^1.0.7",
|
|
26
26
|
"@mongez/supportive-is": "^2.1.4",
|
|
27
27
|
"@mongez/time-wizard": "^1.0.6",
|
|
28
|
-
"@warlock.js/auth": "5.2.
|
|
29
|
-
"@warlock.js/cache": "5.2.
|
|
30
|
-
"@warlock.js/cascade": "5.2.
|
|
31
|
-
"@warlock.js/context": "5.2.
|
|
32
|
-
"@warlock.js/logger": "5.2.
|
|
33
|
-
"@warlock.js/seal": "5.2.
|
|
34
|
-
"@warlock.js/fs": "5.2.
|
|
28
|
+
"@warlock.js/auth": "5.2.4",
|
|
29
|
+
"@warlock.js/cache": "5.2.4",
|
|
30
|
+
"@warlock.js/cascade": "5.2.4",
|
|
31
|
+
"@warlock.js/context": "5.2.4",
|
|
32
|
+
"@warlock.js/logger": "5.2.4",
|
|
33
|
+
"@warlock.js/seal": "5.2.4",
|
|
34
|
+
"@warlock.js/fs": "5.2.4",
|
|
35
35
|
"chokidar": "^5.0.0",
|
|
36
36
|
"dayjs": "^1.11.19",
|
|
37
37
|
"es-module-lexer": "^2.0.0",
|
|
@@ -57,10 +57,10 @@
|
|
|
57
57
|
"react": "^19.2.3",
|
|
58
58
|
"react-dom": "^19.2.3",
|
|
59
59
|
"@react-email/render": "^2.0.5",
|
|
60
|
-
"@warlock.js/herald": "5.2.
|
|
61
|
-
"@warlock.js/ai": "5.2.
|
|
62
|
-
"@warlock.js/access": "5.2.
|
|
63
|
-
"@warlock.js/notifications": "5.2.
|
|
60
|
+
"@warlock.js/herald": "5.2.4",
|
|
61
|
+
"@warlock.js/ai": "5.2.4",
|
|
62
|
+
"@warlock.js/access": "5.2.4",
|
|
63
|
+
"@warlock.js/notifications": "5.2.4"
|
|
64
64
|
},
|
|
65
65
|
"peerDependenciesMeta": {
|
|
66
66
|
"sharp": {
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
],
|
|
124
124
|
"author": "hassanzohdy",
|
|
125
125
|
"license": "MIT",
|
|
126
|
-
"version": "5.2.
|
|
126
|
+
"version": "5.2.4",
|
|
127
127
|
"type": "module",
|
|
128
128
|
"main": "./esm/index.mjs",
|
|
129
129
|
"module": "./esm/index.mjs",
|
package/skills/README.md
CHANGED
|
@@ -45,7 +45,7 @@ Each folder holds one `SKILL.md` (an agent-facing how-to for a single task). The
|
|
|
45
45
|
|
|
46
46
|
- [warlock-doctor](warlock-doctor/SKILL.md) — `warlock doctor`: read-only diagnostics (routes/config/connectors/optional-peers/health/release-hygiene) with a pass/warn/fail report and non-zero exit on failure.
|
|
47
47
|
- [warlock-routes](warlock-routes/SKILL.md) — `warlock routes`: list the registered HTTP routes as a verb-colored table (method/path/name/action/middleware/source); filter by method/path/name or emit JSON. Read-only, no connectors.
|
|
48
|
-
- [write-cli-command](write-cli-command/SKILL.md) — author a custom `warlock <cmd>` via the `command()` factory
|
|
48
|
+
- [write-cli-command](write-cli-command/SKILL.md) — author a custom `warlock <cmd>` via the `command()` factory, or inspect built-in `warlock add` feature scaffolding such as the Web starter.
|
|
49
49
|
- [run-app](run-app/SKILL.md) — `warlock dev` / `warlock build` / `warlock start` operational commands.
|
|
50
50
|
- [update-packages](update-packages/SKILL.md) — bump every `@warlock.js/*` dependency with `warlock update`.
|
|
51
51
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: write-cli-command
|
|
3
|
-
description: 'Author a custom `warlock <my-cmd>` command via the `command()` factory — name, description, action, options, preload, then register in `warlock.config.ts > cli.commands` or drop in `src/app/<module>/commands/`. Triggers: `command`, `CLICommand`, `CLICommandPreload`, `CLICommandOption`, `preload`, `preAction`, `persistent`, `colors`; "write a custom warlock command", "one-off maintenance task", "ship a CLI from a package", "framework built-in commands"; typical import `import { command } from "@warlock.js/core"`. Skip: framework dev/build/start — `@warlock.js/core/run-app/SKILL.md`; warlock.config.ts wiring — `@warlock.js/core/configure-app/SKILL.md`; competing libs `commander`, `yargs`, `oclif`.'
|
|
3
|
+
description: 'Author a custom `warlock <my-cmd>` command via the `command()` factory — name, description, action, options, preload, then register in `warlock.config.ts > cli.commands` or drop in `src/app/<module>/commands/`. Also covers built-in `warlock add` feature scaffolding, including the Web starter and `index.register.ts`. Triggers: `command`, `CLICommand`, `CLICommandPreload`, `CLICommandOption`, `preload`, `preAction`, `persistent`, `colors`, `warlock add`, `index.register.ts`; "write a custom warlock command", "one-off maintenance task", "ship a CLI from a package", "framework built-in commands"; typical import `import { command } from "@warlock.js/core"`. Skip: framework dev/build/start — `@warlock.js/core/run-app/SKILL.md`; warlock.config.ts wiring — `@warlock.js/core/configure-app/SKILL.md`; competing libs `commander`, `yargs`, `oclif`.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Warlock — write a CLI command
|
|
@@ -205,6 +205,7 @@ warlock generate (alias: g) — interactive picker
|
|
|
205
205
|
| Feature | Installs |
|
|
206
206
|
| ------------- | --------------------------------------------------------------------------------------------------- |
|
|
207
207
|
| `react-email` | `react-email` + `@react-email/components` + `@react-email/render` + `@react-email/tailwind`; drops a `welcome-email.tsx` sample; patches `tsconfig.json` |
|
|
208
|
+
| `web` | Warlock Web + React stack; scaffolds the application page, localization, contact endpoint, and Web configuration |
|
|
208
209
|
| `react` | `react` + `react-dom` + types |
|
|
209
210
|
| `image` | `sharp` (for the `Image` class) |
|
|
210
211
|
| `mail` | `nodemailer` + types |
|
|
@@ -222,6 +223,8 @@ warlock generate (alias: g) — interactive picker
|
|
|
222
223
|
|
|
223
224
|
Run `warlock add --list` to see what's currently registered. Pass `--packageManager <yarn\|pnpm\|npm>` to override auto-detection (defaults to whichever lockfile is present).
|
|
224
225
|
|
|
226
|
+
The Web starter keeps the page's universal non-component setup in `src/web/index.register.ts`, while `src/web/index.page.tsx` re-exports `register`. Warlock still discovers and runs the hook from the page namespace in both SSR and the browser, but editing the page component remains inside React's Fast Refresh boundary. Do not move the `register()` declaration inline beside the component: React Refresh treats that non-component export as incompatible and reloads the page, losing component state.
|
|
227
|
+
|
|
225
228
|
### Misc
|
|
226
229
|
|
|
227
230
|
| Command | Purpose |
|