@warlock.js/core 4.11.0 → 4.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +200 -0
- package/esm/cli/cli-commands.manager.mjs +28 -5
- package/esm/cli/cli-commands.manager.mjs.map +1 -1
- package/esm/cli/commands/add.command.mjs +5 -3
- package/esm/cli/commands/add.command.mjs.map +1 -1
- package/esm/cli/commands/build.command.mjs +1 -1
- package/esm/cli/commands/build.command.mjs.map +1 -1
- package/esm/cli/commands/create-database.command.mjs +1 -1
- package/esm/cli/commands/create-database.command.mjs.map +1 -1
- package/esm/cli/commands/dev-server.command.mjs +1 -1
- package/esm/cli/commands/dev-server.command.mjs.map +1 -1
- package/esm/cli/commands/doctor/doctor-command.action.mjs.map +1 -1
- package/esm/cli/commands/doctor.command.mjs +1 -1
- package/esm/cli/commands/doctor.command.mjs.map +1 -1
- package/esm/cli/commands/drop-tables.command.mjs +1 -1
- package/esm/cli/commands/drop-tables.command.mjs.map +1 -1
- package/esm/cli/commands/generate/generate.command.mjs +43 -22
- package/esm/cli/commands/generate/generate.command.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/controller.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/migration.generator.mjs +1 -1
- package/esm/cli/commands/generate/generators/migration.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/model.generator.mjs +1 -1
- package/esm/cli/commands/generate/generators/model.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/module.generator.mjs +1 -1
- package/esm/cli/commands/generate/generators/module.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/repository.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/resource.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/service.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/utils/path-resolver.mjs +1 -1
- package/esm/cli/commands/generate/utils/writer.mjs +1 -1
- package/esm/cli/commands/migrate.command.mjs +7 -3
- package/esm/cli/commands/migrate.command.mjs.map +1 -1
- package/esm/cli/commands/routes/routes-command.action.mjs.map +1 -1
- package/esm/cli/commands/routes.command.mjs +1 -1
- package/esm/cli/commands/routes.command.mjs.map +1 -1
- package/esm/cli/commands/seed.command.mjs +1 -1
- package/esm/cli/commands/seed.command.mjs.map +1 -1
- package/esm/cli/commands/start-production.command.mjs +1 -1
- package/esm/cli/commands/start-production.command.mjs.map +1 -1
- package/esm/cli/commands/storage-put.action.mjs.map +1 -1
- package/esm/cli/commands/storage-put.command.mjs +1 -1
- package/esm/cli/commands/storage-put.command.mjs.map +1 -1
- package/esm/cli/commands/typings-generator.command.mjs +2 -2
- package/esm/cli/commands/typings-generator.command.mjs.map +1 -1
- package/esm/cli/commands/update.command.mjs +1 -1
- package/esm/cli/commands/update.command.mjs.map +1 -1
- package/esm/cli/commands-loader.mjs +1 -1
- package/esm/cli/commands-loader.mjs.map +1 -1
- package/esm/cli/parse-cli-args.mjs +86 -9
- package/esm/cli/parse-cli-args.mjs.map +1 -1
- package/esm/{cli → commands}/cli-command.d.mts +1 -1
- package/esm/commands/cli-command.d.mts.map +1 -0
- package/esm/{cli → commands}/cli-command.mjs +1 -1
- package/esm/commands/cli-command.mjs.map +1 -0
- package/esm/{cli → commands}/types.d.mts +1 -1
- package/esm/commands/types.d.mts.map +1 -0
- package/esm/config/load-config-files.mjs +1 -1
- package/esm/config/load-config-files.mjs.map +1 -1
- package/esm/connectors/base-connector.mjs +1 -1
- package/esm/connectors/base-connector.mjs.map +1 -1
- package/esm/connectors/connectors-manager.d.mts.map +1 -1
- package/esm/connectors/connectors-manager.mjs +5 -3
- package/esm/connectors/connectors-manager.mjs.map +1 -1
- package/esm/connectors/http-connector.d.mts.map +1 -1
- package/esm/connectors/http-connector.mjs +0 -2
- package/esm/connectors/http-connector.mjs.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 +54 -2
- package/esm/database/migrate-action.mjs.map +1 -1
- package/esm/database/pending-exit-code.mjs +39 -0
- package/esm/database/pending-exit-code.mjs.map +1 -0
- package/esm/database/resolve-pending-migrations.mjs +49 -0
- package/esm/database/resolve-pending-migrations.mjs.map +1 -0
- package/esm/database/seed-command-action.mjs +1 -1
- package/esm/database/seed-command-action.mjs.map +1 -1
- package/esm/dev-server/dev-logger.mjs +1 -1
- package/esm/dev-server/dev-logger.mjs.map +1 -1
- package/esm/dev-server/development-server.mjs +2 -2
- package/esm/dev-server/file-event-handler.mjs +1 -1
- package/esm/dev-server/file-event-handler.mjs.map +1 -1
- package/esm/dev-server/file-manager.mjs +1 -1
- package/esm/dev-server/file-manager.mjs.map +1 -1
- package/esm/dev-server/file-operations.mjs +1 -1
- package/esm/dev-server/file-operations.mjs.map +1 -1
- package/esm/dev-server/files-orchestrator.mjs +3 -18
- package/esm/dev-server/files-orchestrator.mjs.map +1 -1
- package/esm/dev-server/files-watcher.mjs +1 -1
- package/esm/dev-server/files-watcher.mjs.map +1 -1
- package/esm/dev-server/health-checker/file-health-checker.contract.d.mts +1 -1
- package/esm/dev-server/layer-executor.mjs +1 -1
- package/esm/dev-server/loader/register-loader.mjs +1 -1
- package/esm/dev-server/package-json-manager.mjs +1 -1
- package/esm/dev-server/package-json-manager.mjs.map +1 -1
- package/esm/dev-server/parse-imports.mjs +2 -2
- package/esm/dev-server/parse-imports.mjs.map +1 -1
- package/esm/dev-server/tsconfig-manager.mjs +1 -1
- package/esm/dev-server/tsconfig-manager.mjs.map +1 -1
- package/esm/dev-server/type-generator.mjs +1 -1
- package/esm/dev-server/type-generator.mjs.map +1 -1
- package/esm/dev-server/utils.mjs +1 -1
- package/esm/dev-server/utils.mjs.map +1 -1
- package/esm/generations/add-command.action.mjs +3 -3
- package/esm/generations/add-command.action.mjs.map +1 -1
- package/esm/http/build-cors-options.mjs +32 -0
- package/esm/http/build-cors-options.mjs.map +1 -0
- package/esm/http/middleware/max-body-size.middleware.d.mts +13 -5
- package/esm/http/middleware/max-body-size.middleware.d.mts.map +1 -1
- package/esm/http/middleware/max-body-size.middleware.mjs +13 -5
- package/esm/http/middleware/max-body-size.middleware.mjs.map +1 -1
- package/esm/http/plugins.d.mts.map +1 -1
- package/esm/http/plugins.mjs +2 -9
- package/esm/http/plugins.mjs.map +1 -1
- package/esm/http/server.d.mts.map +1 -1
- package/esm/http/server.mjs +3 -8
- package/esm/http/server.mjs.map +1 -1
- package/esm/image/image.d.mts +3 -2
- package/esm/image/image.d.mts.map +1 -1
- package/esm/image/image.mjs +63 -17
- package/esm/image/image.mjs.map +1 -1
- package/esm/index.d.mts +9 -17
- package/esm/index.mjs +4 -15
- package/esm/mail/mailer-pool.d.mts.map +1 -1
- package/esm/mail/mailer-pool.mjs.map +1 -1
- package/esm/production/production-builder.mjs +51 -3
- package/esm/production/production-builder.mjs.map +1 -1
- package/esm/production/resolve-build-config.mjs +1 -1
- package/esm/production/resolve-build-config.mjs.map +1 -1
- package/esm/react/index.d.mts +4 -0
- package/esm/react/index.d.mts.map +1 -1
- package/esm/react/index.mjs +88 -14
- package/esm/react/index.mjs.map +1 -1
- package/esm/router/router.d.mts +9 -0
- package/esm/router/router.d.mts.map +1 -1
- package/esm/router/router.mjs +58 -5
- package/esm/router/router.mjs.map +1 -1
- package/esm/router/types.d.mts +24 -1
- package/esm/router/types.d.mts.map +1 -1
- package/esm/tests/index.d.mts +2 -2
- package/esm/tests/index.mjs +1 -2
- package/esm/tests/start-http-development-server.d.mts.map +1 -1
- package/esm/tests/start-http-development-server.mjs +38 -15
- package/esm/tests/start-http-development-server.mjs.map +1 -1
- package/esm/tests/test-helpers.d.mts.map +1 -1
- package/esm/tests/test-helpers.mjs +34 -13
- package/esm/tests/test-helpers.mjs.map +1 -1
- package/esm/tests/vitest-setup.d.mts +1 -1
- package/esm/tests/vitest-setup.d.mts.map +1 -1
- package/esm/tests/vitest-setup.mjs +3 -3
- package/esm/tests/vitest-setup.mjs.map +1 -1
- package/esm/utils/framework-vesion.mjs +1 -1
- package/esm/utils/index.d.mts +1 -0
- package/esm/utils/index.mjs +1 -0
- package/esm/utils/normalized-path.d.mts +55 -0
- package/esm/utils/normalized-path.d.mts.map +1 -0
- package/esm/{dev-server/path.mjs → utils/normalized-path.mjs} +15 -2
- package/esm/utils/normalized-path.mjs.map +1 -0
- package/esm/vite/index.d.mts +2 -0
- package/esm/vite/index.mjs +1 -1
- package/esm/vite/lower-stage3-decorators.d.mts +1 -1
- package/esm/vite/lower-stage3-decorators.mjs +1 -1
- package/esm/vite/lower-stage3-decorators.mjs.map +1 -1
- package/esm/warlock-config/index.mjs +1 -1
- package/esm/warlock-config/types.d.mts +34 -1
- package/esm/warlock-config/types.d.mts.map +1 -1
- package/esm/warlock-config/warlock-config.manager.d.mts.map +1 -1
- package/esm/warlock-config/warlock-config.manager.mjs +1 -2
- package/esm/warlock-config/warlock-config.manager.mjs.map +1 -1
- package/llms-full.txt +78 -23
- package/llms.txt +3 -3
- package/package.json +24 -12
- package/skills/lower-stage3-decorators/SKILL.md +4 -2
- package/skills/process-image/SKILL.md +3 -1
- package/skills/run-app/SKILL.md +6 -0
- package/skills/test-http/SKILL.md +29 -10
- package/skills/test-service/SKILL.md +12 -8
- package/skills/use-middleware/SKILL.md +5 -1
- package/skills/write-cli-command/SKILL.md +19 -1
- package/esm/cli/cli-command.d.mts.map +0 -1
- package/esm/cli/cli-command.mjs.map +0 -1
- package/esm/cli/types.d.mts.map +0 -1
- package/esm/dev-server/files-orchestrator.d.mts +0 -89
- package/esm/dev-server/files-orchestrator.d.mts.map +0 -1
- package/esm/dev-server/files-watcher.d.mts +0 -69
- package/esm/dev-server/files-watcher.d.mts.map +0 -1
- package/esm/dev-server/health-checker/checkers/base-health-checker.d.mts +0 -55
- package/esm/dev-server/health-checker/checkers/base-health-checker.d.mts.map +0 -1
- package/esm/dev-server/health-checker/checkers/eslint-health-checker.d.mts +0 -44
- package/esm/dev-server/health-checker/checkers/eslint-health-checker.d.mts.map +0 -1
- package/esm/dev-server/health-checker/checkers/typescript-health-checker.d.mts +0 -60
- package/esm/dev-server/health-checker/checkers/typescript-health-checker.d.mts.map +0 -1
- package/esm/dev-server/health-checker/files-healthcare.manager.d.mts +0 -119
- package/esm/dev-server/health-checker/files-healthcare.manager.d.mts.map +0 -1
- package/esm/dev-server/health-checker/index.mjs +0 -7
- package/esm/dev-server/module-loader.d.mts +0 -63
- package/esm/dev-server/module-loader.d.mts.map +0 -1
- package/esm/dev-server/path.mjs.map +0 -1
- package/esm/tests/test-server-port-channel.d.mts +0 -27
- package/esm/tests/test-server-port-channel.d.mts.map +0 -1
- /package/esm/{cli → commands}/index.d.mts +0 -0
package/llms.txt
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
- [encrypt-data](@warlock.js/core/encrypt-data/SKILL.md): Reversible AES-256-GCM `encrypt` / `decrypt` for secrets you need to read back; one-way HMAC-SHA256 `hmacHash` for deterministic fingerprints (lookup/dedup of encrypted columns). Keys come from `src/config/encryption.ts`. Triggers: `encrypt`, `decrypt`, `hmacHash`, `EncryptionConfigurations`, `APP_ENCRYPTION_KEY`, `APP_HMAC_KEY`; "store an API key reversibly", "fingerprint an encrypted column for lookup", "AES-256-GCM secret", "HMAC-SHA256 dedup key"; typical import `import { encrypt, decrypt, hmacHash } from "@warlock.js/core"`. Skip: password hashing — `@warlock.js/core/hash-password/SKILL.md`; config wiring — `@warlock.js/core/configure-app/SKILL.md`; competing libs Node `crypto` direct, `crypto-js`, `libsodium-wrappers`.
|
|
18
18
|
- [hash-password](@warlock.js/core/hash-password/SKILL.md): One-way bcrypt password hashing — `hashPassword` / `verifyPassword`, plus the declarative `useHashedPassword()` schema transformer that auto-hashes a model's password field on save. Salt rounds come from `src/config/encryption.ts`. Triggers: `hashPassword`, `verifyPassword`, `useHashedPassword`, `password.salt`, `bcryptjs`; "hash a user password", "verify login credentials", "auto-hash on save", "rotate a password"; typical import `import { hashPassword, verifyPassword } from "@warlock.js/core"`. Skip: reversible secrets — `@warlock.js/core/encrypt-data/SKILL.md`; the other transformers — `@warlock.js/core/use-model-transformers/SKILL.md`; config wiring — `@warlock.js/core/configure-app/SKILL.md`; competing libs `bcrypt` native, `argon2`, `scrypt`.
|
|
19
19
|
- [health-checks](@warlock.js/core/health-checks/SKILL.md): Built-in liveness (`/health`) and readiness (`/ready`) endpoints plus graceful HTTP request draining for zero-downtime deploys — the `health` registry (`health.addCheck`/`removeCheck`), the `http.health.*` and `http.gracefulShutdown.*` config, and how readiness ties into `Application.isShuttingDown`. Triggers: `health`, `health.addCheck`, `health.removeCheck`, `HealthCheck`, `/health`, `/ready`, `http.health`, `http.gracefulShutdown`, `forceCloseConnections`, "liveness probe", "readiness probe", "graceful shutdown", "drain in-flight requests", "zero-downtime deploy", "kubernetes health check", "503 until ready"; typical import `import { health } from "@warlock.js/core"`. Skip: the `Application.onShutdown` / `onceBooted` lifecycle hooks — `@warlock.js/core/use-app-context/SKILL.md`; maintenance-mode 503s — `@warlock.js/core/use-middleware/SKILL.md`; connector lifecycle — `@warlock.js/core/add-connector/SKILL.md`; competing libs `@fastify/under-pressure`, `terminus`, hand-rolled `/health` controllers.
|
|
20
|
-
- [lower-stage3-decorators](@warlock.js/core/lower-stage3-decorators/SKILL.md): Vite/Vitest plugin `lowerStage3Decorators()` that lowers TC39 Stage-3 (native) decorators with esbuild before oxc / the SSR rewrite mangles them — so `@RegisterModel`-decorated Cascade models load under Vitest 4 / Vite 8. Triggers: `lowerStage3Decorators`, "SyntaxError: Invalid or unexpected token" on a decorated class, "@(0, __vite_ssr_import__.X)()", "decorator crashes vitest", "models won't load in tests", "Vite 8 oxc decorators"; typical import `import { lowerStage3Decorators } from "@warlock.js/core"` in `vite.config.ts` / `vitest.config.ts`. Skip: writing the tests themselves — `@warlock.js/core/test-service/SKILL.md` / `@warlock.js/core/test-http/SKILL.md`; runtime migrations — `@warlock.js/cascade/write-migration/SKILL.md`.
|
|
20
|
+
- [lower-stage3-decorators](@warlock.js/core/lower-stage3-decorators/SKILL.md): Vite/Vitest plugin `lowerStage3Decorators()` that lowers TC39 Stage-3 (native) decorators with esbuild before oxc / the SSR rewrite mangles them — so `@RegisterModel`-decorated Cascade models load under Vitest 4 / Vite 8. Triggers: `lowerStage3Decorators`, "SyntaxError: Invalid or unexpected token" on a decorated class, "@(0, __vite_ssr_import__.X)()", "decorator crashes vitest", "models won't load in tests", "Vite 8 oxc decorators"; typical import `import { lowerStage3Decorators } from "@warlock.js/core/vite"` in `vite.config.ts` / `vitest.config.ts`. Skip: writing the tests themselves — `@warlock.js/core/test-service/SKILL.md` / `@warlock.js/core/test-http/SKILL.md`; runtime migrations — `@warlock.js/cascade/write-migration/SKILL.md`.
|
|
21
21
|
- [process-image](@warlock.js/core/process-image/SKILL.md): Transform images with the `Image` class — resize, crop, rotate, format, quality, watermark, blur, etc. — using a deferred pipeline that runs only at `save()` / `toBuffer()` / `toBase64()` / `toDataUrl()` time. Requires sharp via `warlock add image`. Triggers: `Image`, `Image.fromFile`, `Image.fromBuffer`, `Image.fromUrl`, `.resize`, `.crop`, `.watermark`, `.toBuffer`, `.toDataUrl`, `.apply`; "resize an image", "generate a thumbnail", "watermark a product photo", "build an image pipeline"; typical import `import { Image } from "@warlock.js/core"`. Skip: multipart upload entry — `@warlock.js/core/upload-file/SKILL.md`; storage persistence — `@warlock.js/core/store-file/SKILL.md`; competing libs `sharp` direct, `jimp`, `imagemagick`, `gm`.
|
|
22
22
|
- [register-route](@warlock.js/core/register-route/SKILL.md): Register HTTP routes via @warlock.js/core's router — single routes, prefix groups, middleware-guarded blocks, and RESTful resource chains. Routes always live in `src/app/<module>/routes.ts`. Triggers: `router.get`, `router.post`, `router.prefix`, `router.group`, `router.route`, `guarded`; "add a route", "wire a controller to a URL", "group routes by prefix", "register a RESTful resource"; typical import `import { router } from "@warlock.js/core"`. Skip: handler shape — `@warlock.js/core/create-controller/SKILL.md`; CRUD chain details — `@warlock.js/core/build-restful/SKILL.md`; middleware authoring — `@warlock.js/core/write-middleware/SKILL.md`; competing libs `express`, `fastify`, `koa`, `@nestjs/common`.
|
|
23
23
|
- [resolve-path](@warlock.js/core/resolve-path/SKILL.md): Path helpers anchored at `process.cwd()` — `rootPath`, `srcPath`, `appPath`, `configPath`, `publicPath`, `storagePath`, `uploadsPath`, `cachePath`, `logsPath`, `tempPath`, `warlockPath`, `sanitizePath`. Optional `uploads.root` config overrides the uploads anchor. Triggers: `appPath`, `configPath`, `uploadsPath`, `storagePath`, `publicPath`, `cachePath`, `logsPath`, `tempPath`, `sanitizePath`, `paths`; "resolve a path inside src/app", "absolute upload destination", "sanitize a user filename", "ship uploads to a mounted volume"; typical import `import { appPath, uploadsPath } from "@warlock.js/core"`. Skip: HTTP URL helpers — `@warlock.js/core/build-url/SKILL.md`; app metadata — `@warlock.js/core/use-app-context/SKILL.md`; storage abstraction — `@warlock.js/core/store-file/SKILL.md`; competing patterns: `path.join(process.cwd(), ...)`, hand-rolled directory constants.
|
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
- [send-mail](@warlock.js/core/send-mail/SKILL.md): Send transactional email — `Mail` fluent builder, `sendMail()` direct call, React Email components. Test mode auto-captures into an in-memory mailbox; dev mode logs. Triggers: `Mail.to`, `sendMail`, `setMailMode`, `mailEvents`, `assertMailSent`, `getTestMailbox`, `wasMailSentTo`, `closeAllMailers`; "send a transactional email", "build a React Email template", "configure SMTP or SES", "assert an email was sent in tests"; typical import `import { Mail, sendMail } from "@warlock.js/core"`. Skip: per-config wiring — `@warlock.js/core/configure-app/SKILL.md`; layered service patterns — `@warlock.js/core/warlock-conventions/SKILL.md`; competing libs `nodemailer` direct, `@sendgrid/mail`, `resend`, `mailgun.js`.
|
|
27
27
|
- [send-response](@warlock.js/core/send-response/SKILL.md): Send HTTP responses via @warlock.js/core's Response helpers — success/error variants, status helpers, redirects, files, streams, and SSE. Picking the right helper carries the HTTP semantic without manual status codes. Triggers: `response.success`, `response.successCreate`, `response.notFound`, `response.forbidden`, `response.badRequest`, `response.sendFile`, `response.stream`, `response.sse`, `response.replay`, `ResourceNotFoundError`, `ForbiddenError`; "return a 201 from a controller", "send a file", "stream Server-Sent Events", "throw HTTP-shaped errors from services"; typical import `import type { RequestHandler, Response } from "@warlock.js/core"`. Skip: controller shape — `@warlock.js/core/create-controller/SKILL.md`; route registration — `@warlock.js/core/register-route/SKILL.md`; competing patterns: hand-rolled status codes via `reply.code(404).send(...)`, raw Fastify reply.
|
|
28
28
|
- [store-file](@warlock.js/core/store-file/SKILL.md): Read/write/delete files via the `storage` singleton — disks, drivers (local/S3/R2/DO Spaces), `storage.use(name)`, `StorageFile` handles, presigned URLs. Triggers: `storage.put`, `storage.get`, `storage.use`, `StorageFile`, `storageConfigurations`, `getPresignedUrl`, `getPresignedUploadUrl`; "save an uploaded file", "switch between local and S3", "generate a presigned URL", "read file metadata"; typical import `import { storage } from "@warlock.js/core"`. Skip: multipart parsing + image chain — `@warlock.js/core/upload-file/SKILL.md`; image transforms — `@warlock.js/core/process-image/SKILL.md`; storage config shape — `@warlock.js/core/configure-app/SKILL.md`; competing libs `@aws-sdk/client-s3`, `multer`, `formidable`.
|
|
29
|
-
- [test-http](@warlock.js/core/test-http/SKILL.md): Integration tests against a real HTTP server — `startHttpTestServer()` boots one shared server in globalSetup, then `testGet` / `testPost` / `expectJson` make typed requests against it. Triggers: `startHttpTestServer`, `startHttpTestServer({ port })`, `stopHttpTestServer`, `testGet`, `testPost`, `testPut`, `testPatch`, `testDelete`, `expectJson`, `getTestServerUrl`, `testRequest`, `PortInUseError`, `assertPortIsAvailable`, `isPortAvailable`; "integration-test a controller", "end-to-end HTTP test", "globalSetup HTTP server", "assert status and body shape", "test server port already in use", "EADDRINUSE while running tests", "run tests while the dev server is up"; typical import `import { testGet, testPost, expectJson } from "@warlock.js/core"`. Skip: pure unit tests — `@warlock.js/core/test-service/SKILL.md`; controller shape — `@warlock.js/core/create-controller/SKILL.md`; competing libs `supertest`, `light-my-request`, `nock`.
|
|
30
|
-
- [test-service](@warlock.js/core/test-service/SKILL.md): Pure unit tests against services, repositories, models, and use-cases — `setupTest({ connectors })` bootstraps each Vitest worker with its own DB/cache connections so you can call your code directly. Triggers: `setupTest`, `src/test-setup.ts`, `tests.connectors`, `Application.setEnvironment`; "unit-test a service", "test a repository query", "vitest setupFiles", "skip connectors for pure-logic tests"; typical import `import { setupTest } from "@warlock.js/core"`. Skip: HTTP integration — `@warlock.js/core/test-http/SKILL.md`; warlock add test scaffold — `@warlock.js/core/write-cli-command/SKILL.md`; competing tooling: jest direct, `supertest`, `nock`.
|
|
29
|
+
- [test-http](@warlock.js/core/test-http/SKILL.md): Integration tests against a real HTTP server — `startHttpTestServer()` boots one shared server in globalSetup, then `testGet` / `testPost` / `expectJson` make typed requests against it. Triggers: `startHttpTestServer`, `startHttpTestServer({ port })`, `stopHttpTestServer`, `testGet`, `testPost`, `testPut`, `testPatch`, `testDelete`, `expectJson`, `getTestServerUrl`, `testRequest`, `PortInUseError`, `assertPortIsAvailable`, `isPortAvailable`; "integration-test a controller", "end-to-end HTTP test", "globalSetup HTTP server", "assert status and body shape", "test server port already in use", "EADDRINUSE while running tests", "run tests while the dev server is up"; typical import `import { testGet, testPost, expectJson } from "@warlock.js/core/tests"`. Skip: pure unit tests — `@warlock.js/core/test-service/SKILL.md`; controller shape — `@warlock.js/core/create-controller/SKILL.md`; competing libs `supertest`, `light-my-request`, `nock`.
|
|
30
|
+
- [test-service](@warlock.js/core/test-service/SKILL.md): Pure unit tests against services, repositories, models, and use-cases — `setupTest({ connectors })` bootstraps each Vitest worker with its own DB/cache connections so you can call your code directly. Triggers: `setupTest`, `src/test-setup.ts`, `tests.connectors`, `Application.setEnvironment`; "unit-test a service", "test a repository query", "vitest setupFiles", "skip connectors for pure-logic tests"; typical import `import { setupTest } from "@warlock.js/core/tests"`. Skip: HTTP integration — `@warlock.js/core/test-http/SKILL.md`; warlock add test scaffold — `@warlock.js/core/write-cli-command/SKILL.md`; competing tooling: jest direct, `supertest`, `nock`.
|
|
31
31
|
- [update-packages](@warlock.js/core/update-packages/SKILL.md): Keep a project current with `warlock update` — bump every `@warlock.js/*` dependency in package.json to its latest published version (range operator preserved), then run the lockfile-detected package manager install. Also covers the `warlock dev` update notice, its `u` update-and-restart keyboard shortcut, and the `devServer.checkForUpdates` toggle. Triggers: `warlock update`, `--no-install`, `--dry-run`, `--check`, `checkForUpdates`, `fetchLatestVersion`, `isNewerVersion`; "update warlock packages", "upgrade the framework", "is there a new warlock version", "update notice in the dev server", "press u to update", "dev server keyboard shortcut", "update check offline", "bump @warlock.js/* to latest"; typical CLI `warlock update`. Skip: dev/build/start runtime — `@warlock.js/core/run-app/SKILL.md`; writing a custom command — `@warlock.js/core/write-cli-command/SKILL.md`; installing a NEW feature package (auth, mail, storage) — that is `warlock add`; releasing/publishing the framework — workspace release tooling, not this command.
|
|
32
32
|
- [upload-file](@warlock.js/core/upload-file/SKILL.md): Handle multipart file uploads — read via `request.file()` or `request.validated()`, validate with `v.file()`, save via `UploadedFile.save()` or the storage layer, transform images inline. Triggers: `UploadedFile`, `request.file`, `v.file`, `.save`, `.saveAs`, `.resize`, `.format`, `.quality`, `.image`, `.mimeType`, `.maxSize`; "accept a file upload", "validate file size and mime", "save to S3 or local disk", "resize an uploaded image on save"; typical import `import type { UploadedFile, RequestHandler } from "@warlock.js/core"`. Skip: storage drivers + presigned URLs — `@warlock.js/core/store-file/SKILL.md`; image-only transforms — `@warlock.js/core/process-image/SKILL.md`; schema rules — `@warlock.js/core/validate-input/SKILL.md`; competing libs `multer`, `formidable`, `busboy`.
|
|
33
33
|
- [use-app-context](@warlock.js/core/use-app-context/SKILL.md): Read app-wide context — the `Application` static class (env, version, uptime, runtime strategy, boot lifecycle) plus the `app` runtime accessor (live Fastify, socket.io, router, database via the DI container). Triggers: `Application.isProduction`, `Application.environment`, `Application.runtimeStrategy`, `Application.uptime`, `Application.version`, `Application.onceBooted`, `Application.whenBooted`, `Application.isBooted`, `Application.onShutdown`, `Application.isShuttingDown`, `app.http`, `app.socket`, `app.database`, `app.router`; "branch on environment", "reach the live Fastify instance", "framework version in health endpoint", "dev vs production runtime check", "run code once the app is fully booted", "after all connectors started", "app booted hook", "run cleanup before shutdown", "graceful shutdown hook"; typical import `import { Application, app } from "@warlock.js/core"`. Skip: path helpers — `@warlock.js/core/resolve-path/SKILL.md`; connector start order — `@warlock.js/core/add-connector/SKILL.md`; competing patterns: bare `process.env.NODE_ENV`, ad-hoc Fastify imports.
|
package/package.json
CHANGED
|
@@ -36,13 +36,13 @@
|
|
|
36
36
|
"@mongez/slug": "^1.0.7",
|
|
37
37
|
"@mongez/supportive-is": "^2.1.3",
|
|
38
38
|
"@mongez/time-wizard": "^1.0.6",
|
|
39
|
-
"@warlock.js/auth": "4.
|
|
40
|
-
"@warlock.js/cache": "4.
|
|
41
|
-
"@warlock.js/cascade": "4.
|
|
42
|
-
"@warlock.js/context": "4.
|
|
43
|
-
"@warlock.js/logger": "4.
|
|
44
|
-
"@warlock.js/seal": "4.
|
|
45
|
-
"@warlock.js/fs": "4.
|
|
39
|
+
"@warlock.js/auth": "4.13.0",
|
|
40
|
+
"@warlock.js/cache": "4.13.0",
|
|
41
|
+
"@warlock.js/cascade": "4.13.0",
|
|
42
|
+
"@warlock.js/context": "4.13.0",
|
|
43
|
+
"@warlock.js/logger": "4.13.0",
|
|
44
|
+
"@warlock.js/seal": "4.13.0",
|
|
45
|
+
"@warlock.js/fs": "4.13.0",
|
|
46
46
|
"chokidar": "^5.0.0",
|
|
47
47
|
"dayjs": "^1.11.19",
|
|
48
48
|
"es-module-lexer": "^2.0.0",
|
|
@@ -68,15 +68,15 @@
|
|
|
68
68
|
"react": "^19.2.3",
|
|
69
69
|
"react-dom": "^19.2.3",
|
|
70
70
|
"@react-email/render": "^2.0.5",
|
|
71
|
-
"@warlock.js/herald": "4.
|
|
72
|
-
"@warlock.js/ai": "4.
|
|
73
|
-
"@warlock.js/access": "4.
|
|
74
|
-
"@warlock.js/notifications": "4.
|
|
71
|
+
"@warlock.js/herald": "4.13.0",
|
|
72
|
+
"@warlock.js/ai": "4.13.0",
|
|
73
|
+
"@warlock.js/access": "4.13.0",
|
|
74
|
+
"@warlock.js/notifications": "4.13.0"
|
|
75
75
|
},
|
|
76
76
|
"bin": {
|
|
77
77
|
"warlock": "bin/warlock.js"
|
|
78
78
|
},
|
|
79
|
-
"version": "4.
|
|
79
|
+
"version": "4.13.0",
|
|
80
80
|
"type": "module",
|
|
81
81
|
"main": "./esm/index.mjs",
|
|
82
82
|
"module": "./esm/index.mjs",
|
|
@@ -94,6 +94,18 @@
|
|
|
94
94
|
"default": "./esm/cli/start.mjs"
|
|
95
95
|
}
|
|
96
96
|
},
|
|
97
|
+
"./tests": {
|
|
98
|
+
"import": {
|
|
99
|
+
"types": "./esm/tests/index.d.mts",
|
|
100
|
+
"default": "./esm/tests/index.mjs"
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
"./vite": {
|
|
104
|
+
"import": {
|
|
105
|
+
"types": "./esm/vite/index.d.mts",
|
|
106
|
+
"default": "./esm/vite/index.mjs"
|
|
107
|
+
}
|
|
108
|
+
},
|
|
97
109
|
"./dev-server/health-checker/workers/eslint-health.worker": {
|
|
98
110
|
"import": {
|
|
99
111
|
"types": "./esm/dev-server/health-checker/workers/eslint-health.worker.d.mts",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: lower-stage3-decorators
|
|
3
|
-
description: 'Vite/Vitest plugin `lowerStage3Decorators()` that lowers TC39 Stage-3 (native) decorators with esbuild before oxc / the SSR rewrite mangles them — so `@RegisterModel`-decorated Cascade models load under Vitest 4 / Vite 8. Triggers: `lowerStage3Decorators`, "SyntaxError: Invalid or unexpected token" on a decorated class, "@(0, __vite_ssr_import__.X)()", "decorator crashes vitest", "models won''t load in tests", "Vite 8 oxc decorators"; typical import `import { lowerStage3Decorators } from "@warlock.js/core"` in `vite.config.ts` / `vitest.config.ts`. Skip: writing the tests themselves — `@warlock.js/core/test-service/SKILL.md` / `@warlock.js/core/test-http/SKILL.md`; runtime migrations — `@warlock.js/cascade/write-migration/SKILL.md`.'
|
|
3
|
+
description: 'Vite/Vitest plugin `lowerStage3Decorators()` that lowers TC39 Stage-3 (native) decorators with esbuild before oxc / the SSR rewrite mangles them — so `@RegisterModel`-decorated Cascade models load under Vitest 4 / Vite 8. Triggers: `lowerStage3Decorators`, "SyntaxError: Invalid or unexpected token" on a decorated class, "@(0, __vite_ssr_import__.X)()", "decorator crashes vitest", "models won''t load in tests", "Vite 8 oxc decorators"; typical import `import { lowerStage3Decorators } from "@warlock.js/core/vite"` in `vite.config.ts` / `vitest.config.ts`. Skip: writing the tests themselves — `@warlock.js/core/test-service/SKILL.md` / `@warlock.js/core/test-http/SKILL.md`; runtime migrations — `@warlock.js/cascade/write-migration/SKILL.md`.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Lower Stage-3 decorators for Vite/Vitest
|
|
@@ -15,12 +15,14 @@ The cause is two transforms that don't understand each other. Vite 8 transpiles
|
|
|
15
15
|
|
|
16
16
|
`lowerStage3Decorators()` fixes it by getting **esbuild** to lower the decorators *first*.
|
|
17
17
|
|
|
18
|
+
⚠ **Changed in 4.13.0 — the import is a subpath now.** The plugin used to be re-exported from the package root; it is not any more, because that put the Vite integration into every application's production module graph. `import { lowerStage3Decorators } from "@warlock.js/core"` now fails with *"has no exported member"* — **add `/vite` to the specifier and nothing else changes.**
|
|
19
|
+
|
|
18
20
|
## Use it
|
|
19
21
|
|
|
20
22
|
Drop it **first** in your `plugins` array:
|
|
21
23
|
|
|
22
24
|
```ts title="vite.config.ts"
|
|
23
|
-
import { lowerStage3Decorators } from "@warlock.js/core";
|
|
25
|
+
import { lowerStage3Decorators } from "@warlock.js/core/vite";
|
|
24
26
|
import mongezVite from "@mongez/vite";
|
|
25
27
|
import { defineConfig } from "vitest/config";
|
|
26
28
|
|
|
@@ -22,7 +22,9 @@ await new Image("./photo.jpg")
|
|
|
22
22
|
.save("./output.webp");
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
That's the full contract. The chain doesn't
|
|
25
|
+
That's the full contract. The chain doesn't run sharp until the output method fires; the
|
|
26
|
+
constructor resolves the sharp module itself, so a missing sharp throws there rather than at the
|
|
27
|
+
final await.
|
|
26
28
|
|
|
27
29
|
## Installation
|
|
28
30
|
|
package/skills/run-app/SKILL.md
CHANGED
|
@@ -165,6 +165,8 @@ export default defineConfig({
|
|
|
165
165
|
outFile: "app.js", // default — bundle filename
|
|
166
166
|
minify: true, // default — esbuild minify
|
|
167
167
|
sourcemap: true, // default — true | false | "inline" | "linked"
|
|
168
|
+
singleBundle: false, // default — one runnable file, deps inlined
|
|
169
|
+
esmShim: true, // default — require/__filename/__dirname for bundled CJS
|
|
168
170
|
},
|
|
169
171
|
});
|
|
170
172
|
```
|
|
@@ -176,6 +178,10 @@ Defaults are sensible for the typical "Node service" deployment. Knobs to actual
|
|
|
176
178
|
- **`minify: false`** — flip to debug a production-only bug. Larger bundle, readable stack traces.
|
|
177
179
|
- **`sourcemap: "inline"`** — embed the source map in the bundle. Useful when your error reporter only captures the bundle and can't fetch a `.map` sidecar.
|
|
178
180
|
- **`sourcemap: false`** — skip source maps entirely. Smaller artifact, but stack traces in production logs lose their file:line precision (and `warlock start` will not enable `--enable-source-maps` since there's nothing to map).
|
|
181
|
+
- **`singleBundle: true`** — inline dependencies so `node dist/app.js` runs with no `node_modules` and no launcher. Sets `packages: "bundle"` + `splitting: false` as **defaults you can override**. ⚠ Native `.node` addons are still emitted alongside — "single bundle" is one JS file *plus* any native addons. Do NOT reach for it as the default: keeping deps external is right when you deploy the folder.
|
|
182
|
+
- **`esmShim: false`** — only if you are certain nothing in the graph is CommonJS. Leave it on.
|
|
183
|
+
|
|
184
|
+
⚠ **The trap this replaced.** Setting `packages: "bundle"` by hand used to produce a clean build whose process died on start with `Error: Dynamic require of "node:assert" is not supported`. The output is ESM; bundled CJS deps call `require(...)` and read `__dirname`, and neither exists in an ES module, so the bundler substitutes a throwing stub. **A green `warlock build` was not evidence the bundle ran.** `esmShim` now injects `createRequire(import.meta.url)` and friends automatically for any ESM build, so both `singleBundle` and a hand-written `packages: "bundle"` work. An existing hand-written `banner` is preserved — the shim is prepended, not substituted.
|
|
179
185
|
|
|
180
186
|
### What it preloads
|
|
181
187
|
|
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: test-http
|
|
3
|
-
description: 'Integration tests against a real HTTP server — `startHttpTestServer()` boots one shared server in globalSetup, then `testGet` / `testPost` / `expectJson` make typed requests against it. Triggers: `startHttpTestServer`, `startHttpTestServer({ port })`, `stopHttpTestServer`, `testGet`, `testPost`, `testPut`, `testPatch`, `testDelete`, `expectJson`, `getTestServerUrl`, `testRequest`, `PortInUseError`, `assertPortIsAvailable`, `isPortAvailable`; "integration-test a controller", "end-to-end HTTP test", "globalSetup HTTP server", "assert status and body shape", "test server port already in use", "EADDRINUSE while running tests", "run tests while the dev server is up"; typical import `import { testGet, testPost, expectJson } from "@warlock.js/core"`. Skip: pure unit tests — `@warlock.js/core/test-service/SKILL.md`; controller shape — `@warlock.js/core/create-controller/SKILL.md`; competing libs `supertest`, `light-my-request`, `nock`.'
|
|
3
|
+
description: 'Integration tests against a real HTTP server — `startHttpTestServer()` boots one shared server in globalSetup, then `testGet` / `testPost` / `expectJson` make typed requests against it. Triggers: `startHttpTestServer`, `startHttpTestServer({ port })`, `stopHttpTestServer`, `testGet`, `testPost`, `testPut`, `testPatch`, `testDelete`, `expectJson`, `getTestServerUrl`, `testRequest`, `PortInUseError`, `assertPortIsAvailable`, `isPortAvailable`; "integration-test a controller", "end-to-end HTTP test", "globalSetup HTTP server", "assert status and body shape", "test server port already in use", "EADDRINUSE while running tests", "run tests while the dev server is up"; typical import `import { testGet, testPost, expectJson } from "@warlock.js/core/tests"`. Skip: pure unit tests — `@warlock.js/core/test-service/SKILL.md`; controller shape — `@warlock.js/core/create-controller/SKILL.md`; competing libs `supertest`, `light-my-request`, `nock`.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Warlock — HTTP integration tests
|
|
7
7
|
|
|
8
8
|
Some tests need the full stack: route matching, middleware chain, validation, controller, response serialization. For those, you boot the real HTTP server once per test run and make real `fetch` calls against it.
|
|
9
9
|
|
|
10
|
-
`startHttpTestServer()` is the bootstrap. `testGet` / `testPost` / `expectJson` are the call helpers. Both ship in `@warlock.js/core`.
|
|
10
|
+
`startHttpTestServer()` is the bootstrap. `testGet` / `testPost` / `expectJson` are the call helpers. Both ship in `@warlock.js/core/tests`.
|
|
11
|
+
|
|
12
|
+
⚠ **Changed in 4.13.0 — the import is a subpath now.** These helpers used to be re-exported from the package root; they are not any more, because that put the test helpers into every application's production module graph. `import { testGet } from "@warlock.js/core"` now fails with *"has no exported member"* — **add `/tests` to the specifier and nothing else changes.**
|
|
11
13
|
|
|
12
14
|
## The shape
|
|
13
15
|
|
|
14
16
|
```ts title="src/app/users/tests/users.controller.test.ts"
|
|
15
17
|
import { describe, expect, it } from "vitest";
|
|
16
|
-
import { expectJson, testGet, testPost } from "@warlock.js/core";
|
|
18
|
+
import { expectJson, testGet, testPost } from "@warlock.js/core/tests";
|
|
17
19
|
|
|
18
20
|
describe("Users API", () => {
|
|
19
21
|
it("GET /users returns the list", async () => {
|
|
@@ -39,7 +41,7 @@ No `beforeAll`, no manual server start — the project's `src/test-global-setup.
|
|
|
39
41
|
## The bootstrap — `startHttpTestServer` / `stopHttpTestServer`
|
|
40
42
|
|
|
41
43
|
```ts
|
|
42
|
-
import { startHttpTestServer, stopHttpTestServer } from "@warlock.js/core";
|
|
44
|
+
import { startHttpTestServer, stopHttpTestServer } from "@warlock.js/core/tests";
|
|
43
45
|
```
|
|
44
46
|
|
|
45
47
|
`startHttpTestServer()` boots a **minimal but real** HTTP server:
|
|
@@ -80,6 +82,23 @@ startHttpTestServer({ port: 2032 }).
|
|
|
80
82
|
|
|
81
83
|
This runs whether or not you passed a port, so a collision never reaches you as a bare `EADDRINUSE` from inside Fastify. The failure is a `PortInUseError` carrying `port` and `host`. The same check is available on its own — `assertPortIsAvailable(port, host)` throws it, `isPortAvailable(port, host)` returns a boolean.
|
|
82
84
|
|
|
85
|
+
### Port `0` is refused
|
|
86
|
+
|
|
87
|
+
`port: 0` — "let the OS pick one" — is not supported, and says so rather than half-working:
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
startHttpTestServer() cannot run on port 0. Pass an explicit port — e.g.
|
|
91
|
+
startHttpTestServer({ port: 3999 }) — or set `http.port` in your config. Test workers
|
|
92
|
+
are separate processes that resolve the server's URL from the port published at
|
|
93
|
+
startup, and an OS-assigned port is not knowable to them.
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The reason is the worker handoff described above: the port the OS assigns is never recorded anywhere the workers can read, so `getTestServerUrl()` would resolve `0` and send every request to `http://host:0`.
|
|
97
|
+
|
|
98
|
+
### If startup fails, nothing is left behind
|
|
99
|
+
|
|
100
|
+
`startHttpTestServer()` owns the connectors it starts. A failure part-way through — a bad config, a connector that won't boot — tears down whatever came up, withdraws the published port, and **rethrows the original error**; a failure during that cleanup is logged but never replaces the cause. `stopHttpTestServer()` withdraws the port and resets its state even when the shutdown itself throws, and still surfaces that failure.
|
|
101
|
+
|
|
83
102
|
## Project wiring — `src/test-global-setup.ts` + `vite.config.ts`
|
|
84
103
|
|
|
85
104
|
```ts title="src/test-global-setup.ts"
|
|
@@ -87,7 +106,7 @@ This runs whether or not you passed a port, so a collision never reaches you as
|
|
|
87
106
|
* Global Test Setup
|
|
88
107
|
* Runs ONCE in the main process before all test workers start.
|
|
89
108
|
*/
|
|
90
|
-
import { startHttpTestServer, stopHttpTestServer } from "@warlock.js/core";
|
|
109
|
+
import { startHttpTestServer, stopHttpTestServer } from "@warlock.js/core/tests";
|
|
91
110
|
|
|
92
111
|
export async function setup() {
|
|
93
112
|
await startHttpTestServer();
|
|
@@ -99,7 +118,7 @@ export async function teardown() {
|
|
|
99
118
|
```
|
|
100
119
|
|
|
101
120
|
```ts title="vite.config.ts"
|
|
102
|
-
import { lowerStage3Decorators } from "@warlock.js/core";
|
|
121
|
+
import { lowerStage3Decorators } from "@warlock.js/core/vite";
|
|
103
122
|
import mongezVite from "@mongez/vite";
|
|
104
123
|
import { defineConfig } from "vitest/config";
|
|
105
124
|
|
|
@@ -124,7 +143,7 @@ Everything is built on native `fetch` — no extra dependency, no special wire f
|
|
|
124
143
|
### URL resolution
|
|
125
144
|
|
|
126
145
|
```ts
|
|
127
|
-
import { getTestServerUrl } from "@warlock.js/core";
|
|
146
|
+
import { getTestServerUrl } from "@warlock.js/core/tests";
|
|
128
147
|
|
|
129
148
|
const url = getTestServerUrl(); // → "http://localhost:2031" (defaults)
|
|
130
149
|
```
|
|
@@ -141,7 +160,7 @@ import {
|
|
|
141
160
|
testPut,
|
|
142
161
|
testPatch,
|
|
143
162
|
testDelete,
|
|
144
|
-
} from "@warlock.js/core";
|
|
163
|
+
} from "@warlock.js/core/tests";
|
|
145
164
|
|
|
146
165
|
await testGet("/products");
|
|
147
166
|
await testGet("/products?published=true", { headers: { "X-Tenant": "abc" } });
|
|
@@ -158,7 +177,7 @@ All accept a relative path (leading `/` optional) and a standard `RequestInit`.
|
|
|
158
177
|
### Parsing + asserting — `expectJson<T>`
|
|
159
178
|
|
|
160
179
|
```ts
|
|
161
|
-
import { expectJson, parseJsonResponse } from "@warlock.js/core";
|
|
180
|
+
import { expectJson, parseJsonResponse } from "@warlock.js/core/tests";
|
|
162
181
|
|
|
163
182
|
// Parse-only
|
|
164
183
|
const body = await parseJsonResponse<MyShape>(response);
|
|
@@ -177,7 +196,7 @@ const body = await expectJson<MyShape>(response, 404); // expects 404 (testi
|
|
|
177
196
|
|
|
178
197
|
```ts
|
|
179
198
|
import { describe, expect, it } from "vitest";
|
|
180
|
-
import { expectJson, testGet, testPost } from "@warlock.js/core";
|
|
199
|
+
import { expectJson, testGet, testPost } from "@warlock.js/core/tests";
|
|
181
200
|
|
|
182
201
|
describe("Products API — happy path", () => {
|
|
183
202
|
it("creates and reads back a product", async () => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: test-service
|
|
3
|
-
description: 'Pure unit tests against services, repositories, models, and use-cases — `setupTest({ connectors })` bootstraps each Vitest worker with its own DB/cache connections so you can call your code directly. Triggers: `setupTest`, `src/test-setup.ts`, `tests.connectors`, `Application.setEnvironment`; "unit-test a service", "test a repository query", "vitest setupFiles", "skip connectors for pure-logic tests"; typical import `import { setupTest } from "@warlock.js/core"`. Skip: HTTP integration — `@warlock.js/core/test-http/SKILL.md`; warlock add test scaffold — `@warlock.js/core/write-cli-command/SKILL.md`; competing tooling: jest direct, `supertest`, `nock`.'
|
|
3
|
+
description: 'Pure unit tests against services, repositories, models, and use-cases — `setupTest({ connectors })` bootstraps each Vitest worker with its own DB/cache connections so you can call your code directly. Triggers: `setupTest`, `src/test-setup.ts`, `tests.connectors`, `Application.setEnvironment`; "unit-test a service", "test a repository query", "vitest setupFiles", "skip connectors for pure-logic tests"; typical import `import { setupTest } from "@warlock.js/core/tests"`. Skip: HTTP integration — `@warlock.js/core/test-http/SKILL.md`; warlock add test scaffold — `@warlock.js/core/write-cli-command/SKILL.md`; competing tooling: jest direct, `supertest`, `nock`.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Warlock — test a service
|
|
@@ -9,6 +9,8 @@ For unit tests, you import the thing under test and call it directly. No HTTP, n
|
|
|
9
9
|
|
|
10
10
|
`setupTest()` is the one-call bootstrap that gives each worker that environment.
|
|
11
11
|
|
|
12
|
+
⚠ **Changed in 4.13.0 — the import is a subpath now.** `setupTest` used to be re-exported from the package root; it is not any more, because that put the test helpers into every application's production module graph. `import { setupTest } from "@warlock.js/core"` now fails with *"has no exported member"* — **add `/tests` to the specifier and nothing else changes.**
|
|
13
|
+
|
|
12
14
|
## The shape
|
|
13
15
|
|
|
14
16
|
```ts title="src/app/users/tests/register-user.service.test.ts"
|
|
@@ -37,7 +39,7 @@ No `beforeAll(setupTest)` in this file — the project's `src/test-setup.ts` (re
|
|
|
37
39
|
## `setupTest({ connectors })` — the worker bootstrap
|
|
38
40
|
|
|
39
41
|
```ts
|
|
40
|
-
import { setupTest } from "@warlock.js/core";
|
|
42
|
+
import { setupTest } from "@warlock.js/core/tests";
|
|
41
43
|
|
|
42
44
|
await setupTest({ connectors: true });
|
|
43
45
|
```
|
|
@@ -85,13 +87,13 @@ The `warlock add test` feature creates both files. The standard wiring:
|
|
|
85
87
|
* Per-Worker Test Setup
|
|
86
88
|
* Runs in EACH Vitest worker thread before tests execute.
|
|
87
89
|
*/
|
|
88
|
-
import { setupTest } from "@warlock.js/core";
|
|
90
|
+
import { setupTest } from "@warlock.js/core/tests";
|
|
89
91
|
|
|
90
92
|
await setupTest({ connectors: true });
|
|
91
93
|
```
|
|
92
94
|
|
|
93
95
|
```ts title="vite.config.ts"
|
|
94
|
-
import { lowerStage3Decorators } from "@warlock.js/core";
|
|
96
|
+
import { lowerStage3Decorators } from "@warlock.js/core/vite";
|
|
95
97
|
import mongezVite from "@mongez/vite";
|
|
96
98
|
import { defineConfig } from "vitest/config";
|
|
97
99
|
|
|
@@ -210,11 +212,11 @@ Vitest runs tests in a single worker file sequentially, so an `afterEach` trunca
|
|
|
210
212
|
|
|
211
213
|
```ts title="src/app/utils/tests/slugify.test.ts"
|
|
212
214
|
import { beforeAll, describe, expect, it } from "vitest";
|
|
213
|
-
import { setupTest } from "@warlock.js/core";
|
|
215
|
+
import { setupTest } from "@warlock.js/core/tests";
|
|
214
216
|
import { slugify } from "../utils/slugify";
|
|
215
217
|
|
|
216
218
|
beforeAll(async () => {
|
|
217
|
-
await setupTest({ connectors: false }); //
|
|
219
|
+
await setupTest({ connectors: false }); // starts no connectors at all
|
|
218
220
|
});
|
|
219
221
|
|
|
220
222
|
describe("slugify", () => {
|
|
@@ -224,7 +226,9 @@ describe("slugify", () => {
|
|
|
224
226
|
});
|
|
225
227
|
```
|
|
226
228
|
|
|
227
|
-
`setupTest` is idempotent per worker (`isSetupComplete` flag) — calling it again with different options after `src/test-setup.ts` already ran is a no-op. To genuinely skip connectors, either set `tests.connectors: false` in config (project-wide) or rely on the default in `src/test-setup.ts` being what you want most of the time.
|
|
229
|
+
`setupTest` is idempotent per worker (`isSetupComplete` flag) — calling it again with different options after `src/test-setup.ts` already ran is a no-op. **That includes a `connectors: false` call: if `src/test-setup.ts` already ran `setupTest()` in this worker, the example above changes nothing.** To genuinely skip connectors, either set `tests.connectors: false` in config (project-wide) or rely on the default in `src/test-setup.ts` being what you want most of the time.
|
|
230
|
+
|
|
231
|
+
⚠ **Config beats the parameter.** If `tests.connectors` is set at all, `setupTest({ connectors })` cannot override it — the config value wins. That is the current contract, not an accident; a per-call override is under discussion for a later release.
|
|
228
232
|
|
|
229
233
|
## Gotchas
|
|
230
234
|
|
|
@@ -232,7 +236,7 @@ describe("slugify", () => {
|
|
|
232
236
|
- **Per-worker connections are separate from the HTTP server's connections.** A row inserted by a service-level test is on the worker's connection; the HTTP test server has its own. They don't see each other unless they're both pointing at the same physical DB and the inserting test has already committed.
|
|
233
237
|
- **`NODE_ENV` is set to `"test"`** by `setupTest`. Code that branches on `Application.isProduction` / `Application.isDevelopment` sees `false` for both. If your tests need production-like config (cookies, CORS), set those values in `src/config/*.ts` explicitly under the test branch — don't rely on the env flag.
|
|
234
238
|
- **No HTTP from this layer.** `setupTest({ connectors: true })` never starts the HTTP connector by design. Don't try to `request.app.http` your way to a fetch test — use the `test-http` skill instead.
|
|
235
|
-
- **Don't import `vitest-setup` from `@warlock.js/core/src/...`.** The public surface is `import { setupTest } from "@warlock.js/core"`. Reaching into source paths breaks when the package layout shifts.
|
|
239
|
+
- **Don't import `vitest-setup` from `@warlock.js/core/src/...`.** The public surface is `import { setupTest } from "@warlock.js/core/tests"`. Reaching into source paths breaks when the package layout shifts.
|
|
236
240
|
- **Test files need the `.test.ts` suffix.** `include: ["src/app/**/*.test.ts"]` is what vitest scans. A file named `service.tests.ts` (plural) silently doesn't run.
|
|
237
241
|
|
|
238
242
|
## See also
|
|
@@ -81,6 +81,8 @@ Server errors (5xx) are not cached — clients can retry past a 5xx. 4xx respons
|
|
|
81
81
|
|
|
82
82
|
`http.bodyLimit` in config is read by Fastify at server-start and applies to every body. `middleware.maxBodySize()` is a per-route middleware on top — it checks `Content-Length` after route match and rejects with 413 before body parsing runs. Use both: global as a safety net, per-route for tight caps on small-payload endpoints.
|
|
83
83
|
|
|
84
|
+
⚠ **Since 4.13.0, configuring nothing gives you Fastify's own 1 MB limit.** Earlier versions defaulted to 200 GB, which **replaced** Fastify's protection rather than adding to it — so an app that set nothing had effectively no cap. **Set `bodyLimit` explicitly if you need more than 1 MB.**
|
|
85
|
+
|
|
84
86
|
```ts
|
|
85
87
|
// src/config/http.ts
|
|
86
88
|
export default { bodyLimit: 10 * 1024 * 1024 }; // 10MB globally
|
|
@@ -111,7 +113,9 @@ export default {
|
|
|
111
113
|
|
|
112
114
|
## `ipFilter` — fail-closed
|
|
113
115
|
|
|
114
|
-
`deny` wins over `allow`. If the IP can't be read (empty / unparseable), the request is rejected with 403. Reads via `request.detectIp()
|
|
116
|
+
`deny` wins over `allow`. If the IP can't be read (empty / unparseable), the request is rejected with 403. Reads via `request.detectIp()`.
|
|
117
|
+
|
|
118
|
+
⚠ **Since 4.13.0 `http.trustProxy` defaults to `false`**, so `request.detectIp()` returns the socket address and **`X-Real-IP` / `X-Forwarded-For` are ignored unless you opt in.** Set `trustProxy: true` **only when you are genuinely behind a proxy that overwrites those headers** — before 4.13.0 the default was `true`, which meant any client could set its own forwarding header and be believed.
|
|
115
119
|
|
|
116
120
|
```ts
|
|
117
121
|
import { middleware } from "@warlock.js/core";
|
|
@@ -156,12 +156,30 @@ The framework ships a fixed set of commands you call but don't author. Knowing t
|
|
|
156
156
|
|
|
157
157
|
| Command | Flags / args | Preloads |
|
|
158
158
|
| -------- | ----------------------------------------------- | ------------------------------ |
|
|
159
|
-
| `warlock migrate` | `--list` (
|
|
159
|
+
| `warlock migrate` | `--list` / `-l` (executed **and** pending), `--pending` (pending only, sets an exit code), `--fresh` / `-f` (drop tables first) | database, logger |
|
|
160
160
|
| `warlock seed` | `--name <pattern>` (run seeds matching the pattern) | full bootstrap (env, configs, app modules) |
|
|
161
161
|
| `warlock create-database <name>` | bare positional `<name>` | database |
|
|
162
162
|
| `warlock drop.tables` | `--force, -f` (skip confirmation prompt) | database, logger |
|
|
163
163
|
| `warlock db.indexes` | builds DB indexes for every registered model | database |
|
|
164
164
|
|
|
165
|
+
**Asking what will run next.** `warlock migrate --list` prints executed migrations and then the pending ones **in execution order**. Do not derive the pending set by differencing `--all` against `--list`: `--all` globs `src/app` only, while `--list` reads the migrations table, which also holds migrations that packages register through `database.migrations` (`@warlock.js/auth` contributes two). The difference under-counts pending, in the direction that says "safe to proceed".
|
|
166
|
+
|
|
167
|
+
`--list` is a report and always exits `0`. `--pending` is a gate, and its exit code is its whole API:
|
|
168
|
+
|
|
169
|
+
| Exit | Meaning |
|
|
170
|
+
| ---- | ------- |
|
|
171
|
+
| `0` | computed, nothing pending |
|
|
172
|
+
| `1` | computed, N pending |
|
|
173
|
+
| `2` | **could not be computed** |
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
warlock migrate --pending && ./deploy.sh
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
`2` is separate from `1` on purpose — a script must be able to tell a backlog from an unknown, because one means *run them* and the other means *stop*. When the migration files cannot be loaded, both commands print `Pending: unavailable — <reason>` beneath a complete executed listing rather than reporting `0`.
|
|
180
|
+
|
|
181
|
+
**If you are writing a command that reports on pending migrations:** register migrations first. `listPendingMigrations()` filters the runner's registry, so a caller that has not loaded anything gets `[]` — which reads as "nothing pending" and is not the same claim.
|
|
182
|
+
|
|
165
183
|
### Scaffolding
|
|
166
184
|
|
|
167
185
|
The `generate.*` family covers every module piece:
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cli-command.d.mts","names":[],"sources":["../../../../../../../core/src/cli/cli-command.ts"],"mappings":";;;cAUa,UAAA;EAqDF,IAAA;EArDE;;;EAIJ,aAAA,GAAgB,gBAAA;EAKA;;;EAAhB,aAAA,GAAgB,gBAAA;EAyDD;;;;EAnDf,gBAAA,GAAmB,gBAAA;EAuHuC;;;EAlH1D,cAAA,GAAiB,iBAAA;EAoNA;;;EA/MjB,kBAAA;EAuNqC;;;EAlNrC,cAAA,EAAgB,wBAAA;EArBhB;;;;;EA4BA,mBAAA;EAZA;;;EAiBA,YAAA;EAAA;;;EAKA,YAAA;EAOL;;;cADO,IAAA,UACP,WAAA;EAoBK;;;EARA,MAAA,CAAO,MAAA,EAAQ,gBAAA;EAyBf;;;EAjBA,WAAA,CAAY,WAAA;EAyBL;;;EAjBP,UAAA,CAAW,YAAA;EAkCX;;;;EAzBA,KAAA,CAAM,KAAA;EA0CN;;;EAlCA,MAAA,CAAO,MAAA,EAAQ,gBAAA;EAmCR;;;;EA1BP,SAAA,CAAU,MAAA,EAAQ,gBAAA;EAwDf;;;EAhDH,OAAA,CAAQ,OAAA,EAAS,gBAAA;EA8FhB;;;EArFD,aAAA,CAAc,YAAA;EAmHR;;;EA3GN,MAAA,CAAO,MAAA,EAAQ,gBAAA;EACf,MAAA,CAAO,IAAA,UAAc,WAAA,WAAsB,OAAA,GAAU,IAAA,CAAK,gBAAA;EA0GrB;AAS9C;;;;;;;;EAT8C,UA5ElC,WAAA,CAAY,MAAA,EAAQ,gBAAA,GAAmB,wBAAA;;;;;;;;;UA8CzC,iBAAA;;;;EAsBD,OAAA,CAAQ,OAAA,EAAS,iBAAA;;;;EAQX,OAAA,CAAQ,IAAA,EAAM,iBAAA,GAAiB,OAAA;AAAA;AAAA,iBAS9B,OAAA,CAAQ,OAAA,EAAS,iBAAA,GAAiB,UAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cli-command.mjs","names":[],"sources":["../../../../../../../core/src/cli/cli-command.ts"],"sourcesContent":["import type {\n CLICommandAction,\n CLICommandOption,\n CLICommandOptions,\n CLICommandPreload,\n CLICommandSource,\n CommandActionData,\n ResolvedCLICommandOption,\n} from \"./types\";\n\nexport class CLICommand {\n /**\n * Command source\n */\n public commandSource?: CLICommandSource;\n\n /**\n * Command action\n */\n public commandAction?: CLICommandAction;\n\n /**\n * Command pre action\n * This will be executed before loading preloaders\n */\n public commandPreAction?: CLICommandAction;\n\n /**\n * Command preload\n */\n public commandPreload?: CLICommandPreload;\n\n /**\n * Command description\n */\n public commandDescription?: string;\n\n /**\n * Command options\n */\n public commandOptions: ResolvedCLICommandOption[] = [];\n\n /**\n * Command relative path\n * Available only for project commands\n * Auto injected by the framework itself\n */\n public commandRelativePath?: string;\n\n /**\n * Determine if the command is persistent\n */\n public isPersistent: boolean = false;\n\n /**\n * Command alias (short name)\n */\n public commandAlias?: string;\n\n /**\n * Constructor\n */\n public constructor(\n public name: string,\n description?: string,\n ) {\n if (description) {\n this.commandDescription = description;\n }\n\n return this;\n }\n\n /**\n * Add command source\n */\n public source(source: CLICommandSource): this {\n this.commandSource = source;\n return this;\n }\n\n /**\n * Set command description\n */\n public description(description: string): this {\n this.commandDescription = description;\n return this;\n }\n\n /**\n * Determine if the command is persistent\n */\n public persistent(isPersistent = true): this {\n this.isPersistent = isPersistent;\n return this;\n }\n\n /**\n * Set command alias (short name)\n * @example .alias(\"m\") for \"migrate\"\n */\n public alias(alias: string): this {\n this.commandAlias = alias;\n return this;\n }\n\n /**\n * Command action\n */\n public action(action: CLICommandAction): this {\n this.commandAction = action;\n return this;\n }\n\n /**\n * Command pre action\n * This will be executed before loading preloaders\n */\n public preAction(action: CLICommandAction): this {\n this.commandPreAction = action;\n return this;\n }\n\n /**\n * Add command options\n */\n public options(options: CLICommandOption[]): this {\n options.map((option) => this.option(option));\n\n return this;\n }\n\n /**\n * Add command relative path\n */\n public $relativePath(relativePath: string) {\n this.commandRelativePath = relativePath;\n return this;\n }\n\n /**\n * Add command option\n */\n public option(option: CLICommandOption): this;\n public option(name: string, description?: string, options?: Omit<CLICommandOption, \"name\">): this;\n public option(\n ...args: [CLICommandOption] | [string, string?, Omit<CLICommandOption, \"name\">?]\n ): this {\n let option: CLICommandOption;\n if (args.length === 1) {\n option = args[0] as CLICommandOption;\n } else {\n option = {\n text: args[0],\n description: args[1],\n ...args[2],\n name: \"\",\n };\n }\n\n this.commandOptions.push(this.parseOption(option));\n\n return this;\n }\n\n /**\n * Parse option name and alias if exists\n *\n * Supports formats:\n * - \"--port, -p\" → name: \"port\", alias: \"p\"\n * - \"-p, --port\" → name: \"port\", alias: \"p\"\n * - \"--port\" → name: \"port\", alias: undefined\n * - \"-p\" → name: \"p\", alias: undefined\n */\n protected parseOption(option: CLICommandOption): ResolvedCLICommandOption {\n const text = option.text.trim();\n\n // Split by comma to check for alias\n const parts = text.split(\",\").map((part) => part.trim());\n\n let name = \"\";\n let alias = \"\";\n\n if (parts.length === 1) {\n // Single option: \"--port\" or \"-p\"\n name = this.extractOptionName(parts[0]);\n } else if (parts.length === 2) {\n // Two options: \"--port, -p\" or \"-p, --port\"\n const first = parts[0];\n const second = parts[1];\n\n // Determine which is the long form (name) and which is short (alias)\n if (first.startsWith(\"--\")) {\n name = this.extractOptionName(first);\n alias = this.extractOptionName(second);\n } else {\n name = this.extractOptionName(second);\n alias = this.extractOptionName(first);\n }\n }\n\n if (alias === \"h\" || name === \"help\") {\n throw new Error(\"Help option is not allowed, it's reserved for displaying command help\");\n }\n\n return {\n ...option,\n name,\n alias,\n };\n }\n\n /**\n * Extract option name from text (removes -- or -)\n *\n * @example\n * extractOptionName(\"--port\") → \"port\"\n * extractOptionName(\"-p\") → \"p\"\n * extractOptionName(\"--port=3000\") → \"port\"\n */\n private extractOptionName(text: string): string {\n // Remove leading dashes\n let name = text.replace(/^-+/, \"\");\n\n // Remove value assignment if exists (e.g., \"--port=3000\" → \"port\")\n const equalIndex = name.indexOf(\"=\");\n if (equalIndex !== -1) {\n name = name.slice(0, equalIndex);\n }\n\n // Remove angle brackets if exists (e.g., \"--port <number>\" → \"port\")\n const spaceIndex = name.indexOf(\" \");\n if (spaceIndex !== -1) {\n name = name.slice(0, spaceIndex);\n }\n\n return name.trim();\n }\n\n /**\n * Command preload\n */\n public preload(options: CLICommandPreload) {\n this.commandPreload = options;\n return this;\n }\n\n /**\n * Execute the command\n */\n public async execute(data: CommandActionData) {\n if (!this.commandAction) {\n throw new Error(`Command \"${this.name}\" has no action defined`);\n }\n\n await this.commandAction(data);\n }\n}\n\nexport function command(options: CLICommandOptions) {\n const commandInstnace = new CLICommand(options.name, options.description);\n\n if (options.preload) {\n commandInstnace.preload(options.preload);\n }\n\n if (options.persistent) {\n commandInstnace.persistent(options.persistent);\n }\n\n if (options.alias) {\n commandInstnace.alias(options.alias);\n }\n\n commandInstnace.action(options.action);\n\n if (options.options) {\n commandInstnace.options(options.options);\n }\n\n if (options.preAction) {\n commandInstnace.preAction(options.preAction);\n }\n\n return commandInstnace;\n}\n"],"mappings":";AAUA,IAAa,aAAb,MAAwB;;;;CAoDtB,AAAO,YACL,AAAO,MACP,aACA;EAFO;wBAvB2C,CAAC;sBAYtB;EAc7B,IAAI,aACF,KAAK,qBAAqB;EAG5B,OAAO;CACT;;;;CAKA,AAAO,OAAO,QAAgC;EAC5C,KAAK,gBAAgB;EACrB,OAAO;CACT;;;;CAKA,AAAO,YAAY,aAA2B;EAC5C,KAAK,qBAAqB;EAC1B,OAAO;CACT;;;;CAKA,AAAO,WAAW,eAAe,MAAY;EAC3C,KAAK,eAAe;EACpB,OAAO;CACT;;;;;CAMA,AAAO,MAAM,OAAqB;EAChC,KAAK,eAAe;EACpB,OAAO;CACT;;;;CAKA,AAAO,OAAO,QAAgC;EAC5C,KAAK,gBAAgB;EACrB,OAAO;CACT;;;;;CAMA,AAAO,UAAU,QAAgC;EAC/C,KAAK,mBAAmB;EACxB,OAAO;CACT;;;;CAKA,AAAO,QAAQ,SAAmC;EAChD,QAAQ,KAAK,WAAW,KAAK,OAAO,MAAM,CAAC;EAE3C,OAAO;CACT;;;;CAKA,AAAO,cAAc,cAAsB;EACzC,KAAK,sBAAsB;EAC3B,OAAO;CACT;CAOA,AAAO,OACL,GAAG,MACG;EACN,IAAI;EACJ,IAAI,KAAK,WAAW,GAClB,SAAS,KAAK;OAEd,SAAS;GACP,MAAM,KAAK;GACX,aAAa,KAAK;GAClB,GAAG,KAAK;GACR,MAAM;EACR;EAGF,KAAK,eAAe,KAAK,KAAK,YAAY,MAAM,CAAC;EAEjD,OAAO;CACT;;;;;;;;;;CAWA,AAAU,YAAY,QAAoD;EAIxE,MAAM,QAHO,OAAO,KAAK,KAGR,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,SAAS,KAAK,KAAK,CAAC;EAEvD,IAAI,OAAO;EACX,IAAI,QAAQ;EAEZ,IAAI,MAAM,WAAW,GAEnB,OAAO,KAAK,kBAAkB,MAAM,EAAE;OACjC,IAAI,MAAM,WAAW,GAAG;GAE7B,MAAM,QAAQ,MAAM;GACpB,MAAM,SAAS,MAAM;GAGrB,IAAI,MAAM,WAAW,IAAI,GAAG;IAC1B,OAAO,KAAK,kBAAkB,KAAK;IACnC,QAAQ,KAAK,kBAAkB,MAAM;GACvC,OAAO;IACL,OAAO,KAAK,kBAAkB,MAAM;IACpC,QAAQ,KAAK,kBAAkB,KAAK;GACtC;EACF;EAEA,IAAI,UAAU,OAAO,SAAS,QAC5B,MAAM,IAAI,MAAM,uEAAuE;EAGzF,OAAO;GACL,GAAG;GACH;GACA;EACF;CACF;;;;;;;;;CAUA,AAAQ,kBAAkB,MAAsB;EAE9C,IAAI,OAAO,KAAK,QAAQ,OAAO,EAAE;EAGjC,MAAM,aAAa,KAAK,QAAQ,GAAG;EACnC,IAAI,eAAe,IACjB,OAAO,KAAK,MAAM,GAAG,UAAU;EAIjC,MAAM,aAAa,KAAK,QAAQ,GAAG;EACnC,IAAI,eAAe,IACjB,OAAO,KAAK,MAAM,GAAG,UAAU;EAGjC,OAAO,KAAK,KAAK;CACnB;;;;CAKA,AAAO,QAAQ,SAA4B;EACzC,KAAK,iBAAiB;EACtB,OAAO;CACT;;;;CAKA,MAAa,QAAQ,MAAyB;EAC5C,IAAI,CAAC,KAAK,eACR,MAAM,IAAI,MAAM,YAAY,KAAK,KAAK,wBAAwB;EAGhE,MAAM,KAAK,cAAc,IAAI;CAC/B;AACF;AAEA,SAAgB,QAAQ,SAA4B;CAClD,MAAM,kBAAkB,IAAI,WAAW,QAAQ,MAAM,QAAQ,WAAW;CAExE,IAAI,QAAQ,SACV,gBAAgB,QAAQ,QAAQ,OAAO;CAGzC,IAAI,QAAQ,YACV,gBAAgB,WAAW,QAAQ,UAAU;CAG/C,IAAI,QAAQ,OACV,gBAAgB,MAAM,QAAQ,KAAK;CAGrC,gBAAgB,OAAO,QAAQ,MAAM;CAErC,IAAI,QAAQ,SACV,gBAAgB,QAAQ,QAAQ,OAAO;CAGzC,IAAI,QAAQ,WACV,gBAAgB,UAAU,QAAQ,SAAS;CAG7C,OAAO;AACT"}
|
package/esm/cli/types.d.mts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.mts","names":[],"sources":["../../../../../../../core/src/cli/types.ts"],"mappings":";;;;;AAgB4B;AAY5B;;;;KAZY,gBAAA;;;;AAwBK;AAsBjB;;;;;;KAlCY,iBAAA;EAuDV;;;;EAlDA,IAAA;EAiFA;;;AAA0B;AAgB5B;EA1FE,OAAA,EAAS,MAAM;AAAA;;;;;;;;;;AAiIP;AAGV;;;;;;;;;KA9GY,iBAAA;EAuHA;;;;;EAjHV,MAAA;EAiHiE;;AAAO;EA5GxE,QAAA;EAkI2B;;;EA7H3B,aAAA;EAsKQ;;;EAjKR,eAAA,GAAkB,eAAA;EA6HlB;;;EAxHA,WAAA;EAyIU;;;;;;;;;EA9HV,GAAA;;;;;EAMA,SAAA;;;;;;;;EASA,UAAA,GAAa,aAAa;AAAA;;;;;;;;;;;;;;KAgBhB,gBAAA;;;;;EAKV,IAAA;;;;;EAMA,IAAA;;;;;EAMA,KAAA;;;;EAKA,WAAA;;;;;EAMA,IAAA;;;;EAKA,YAAA;;;;;EAMA,QAAA;AAAA;AAAA,KAGU,wBAAA,GAA2B,IAAI,CAAC,gBAAA;EAC1C,IAAA;EACA,KAAA;AAAA;;;;;KAOU,gBAAA,IAAoB,IAAA,EAAM,iBAAA,YAA6B,OAAO;;;;;;;;;;;;;;;;;;;;;KAsB9D,iBAAA;;;;;EAKV,IAAA;;;;;EAMA,KAAA;;;;EAKA,WAAA;;;;;EAMA,OAAA,GAAU,iBAAA;;;;;;EAOV,UAAA;;;;;EAMA,SAAA,GAAY,gBAAA;;;;;EAMZ,MAAA,EAAQ,gBAAA;;;;;EAMR,OAAA,GAAU,gBAAA;AAAA"}
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { DependencyGraph } from "./dependency-graph.mjs";
|
|
2
|
-
import { SpecialFilesCollector } from "./special-files-collector.mjs";
|
|
3
|
-
import { FileOperations } from "./file-operations.mjs";
|
|
4
|
-
import { FileManager } from "./file-manager.mjs";
|
|
5
|
-
import { FilesWatcher } from "./files-watcher.mjs";
|
|
6
|
-
import { FileHealthCheckerContract } from "./health-checker/file-health-checker.contract.mjs";
|
|
7
|
-
import { FilesHealthcareManager } from "./health-checker/files-healthcare.manager.mjs";
|
|
8
|
-
import { ModuleLoader } from "./module-loader.mjs";
|
|
9
|
-
|
|
10
|
-
//#region ../core/src/dev-server/files-orchestrator.d.ts
|
|
11
|
-
/**
|
|
12
|
-
* Register a cleanup callback that fires before the current module is
|
|
13
|
-
* unloaded by HMR. Stack-inspects the caller so user code doesn't need to
|
|
14
|
-
* thread the FileManager through.
|
|
15
|
-
*/
|
|
16
|
-
declare function onCleanup(callback: () => any): void;
|
|
17
|
-
/**
|
|
18
|
-
* Top-level coordinator for the dev server's file system.
|
|
19
|
-
*
|
|
20
|
-
* Owns: file discovery, the dependency graph, the manifest, the special-files
|
|
21
|
-
* index, the file watcher, and the lifecycle of the ESM loader hook (which
|
|
22
|
-
* provides cache-busting via `?v=N` version tokens).
|
|
23
|
-
*/
|
|
24
|
-
declare class FilesOrchestrator {
|
|
25
|
-
readonly filesWatcher: FilesWatcher;
|
|
26
|
-
readonly files: Map<string, FileManager>;
|
|
27
|
-
private readonly manifest;
|
|
28
|
-
private readonly dependencyGraph;
|
|
29
|
-
private readonly healthCheckerManager;
|
|
30
|
-
readonly specialFilesCollector: SpecialFilesCollector;
|
|
31
|
-
readonly moduleLoader: ModuleLoader;
|
|
32
|
-
readonly fileOperations: FileOperations;
|
|
33
|
-
private readonly eventHandler;
|
|
34
|
-
/** Main-thread end of the MessageChannel to the loader hook worker. */
|
|
35
|
-
private loaderPort;
|
|
36
|
-
/** Resolve callbacks for pending `flushVersionBumps()` calls. */
|
|
37
|
-
private readonly pendingFlushes;
|
|
38
|
-
isInitialized: boolean;
|
|
39
|
-
constructor();
|
|
40
|
-
add(relativePath: string): Promise<FileManager>;
|
|
41
|
-
load<T>(relativePath: string, type?: string): Promise<T | undefined>;
|
|
42
|
-
getDependencyGraph(): DependencyGraph;
|
|
43
|
-
getInvalidationChain(file: string): string[];
|
|
44
|
-
getFiles(): Map<string, FileManager>;
|
|
45
|
-
getHealthCheckerManager(): FilesHealthcareManager;
|
|
46
|
-
/**
|
|
47
|
-
* Initialise managers and register the ESM loader hook. Must be called
|
|
48
|
-
* before any user `src/` module is dynamically imported.
|
|
49
|
-
*/
|
|
50
|
-
init(): Promise<void>;
|
|
51
|
-
/**
|
|
52
|
-
* Tell the hook worker a file changed. The next `import()` of it will get
|
|
53
|
-
* a fresh `?v=N` URL → Node cache miss → fresh content.
|
|
54
|
-
*/
|
|
55
|
-
bumpVersion(absolutePath: string): void;
|
|
56
|
-
/**
|
|
57
|
-
* Wait until the hook worker has processed every pending bump.
|
|
58
|
-
*
|
|
59
|
-
* `postMessage` is async — without this, a follow-up `import()` may
|
|
60
|
-
* resolve before the worker increments the counter and Node will return
|
|
61
|
-
* the cached old module.
|
|
62
|
-
*/
|
|
63
|
-
flushVersionBumps(): Promise<void>;
|
|
64
|
-
/**
|
|
65
|
-
* Discover files on disk, reconcile against the manifest, build the dep
|
|
66
|
-
* graph, and persist the new manifest. Idempotent.
|
|
67
|
-
*/
|
|
68
|
-
initializeAll(): Promise<void>;
|
|
69
|
-
checkHealth(files: {
|
|
70
|
-
added: string[];
|
|
71
|
-
changed: string[];
|
|
72
|
-
deleted: string[];
|
|
73
|
-
}): Promise<void>;
|
|
74
|
-
startCheckingHealth(healthCheckers?: FileHealthCheckerContract[]): Promise<void>;
|
|
75
|
-
/**
|
|
76
|
-
* Glob the src directory, returning relative paths.
|
|
77
|
-
*/
|
|
78
|
-
getAllFilesFromFilesystem(): Promise<string[]>;
|
|
79
|
-
/**
|
|
80
|
-
* Process every file from scratch — used when no manifest exists.
|
|
81
|
-
*/
|
|
82
|
-
private processFiles;
|
|
83
|
-
private reconcileFiles;
|
|
84
|
-
watchFiles(): Promise<void>;
|
|
85
|
-
}
|
|
86
|
-
declare const filesOrchestrator: FilesOrchestrator;
|
|
87
|
-
//#endregion
|
|
88
|
-
export { FilesOrchestrator, filesOrchestrator, onCleanup };
|
|
89
|
-
//# sourceMappingURL=files-orchestrator.d.mts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"files-orchestrator.d.mts","names":[],"sources":["../../../../../../../core/src/dev-server/files-orchestrator.ts"],"mappings":";;;;;;;;;;;;AA8BA;;;iBAAgB,SAAA,CAAU,QAAmB;AAAA;AAuB7C;;;;;;AAvB6C,cAuBhC,iBAAA;EAAA,SACK,YAAA,EAAY,YAAA;EAAA,SACZ,KAAA,EAAK,GAAA,SAAA,WAAA;EAAA,iBACJ,QAAA;EAAA,iBACA,eAAA;EAAA,iBACA,oBAAA;EAAA,SACD,qBAAA,EAAqB,qBAAA;EAAA,SACrB,YAAA,EAAY,YAAA;EAAA,SACZ,cAAA,EAAgB,cAAA;EAAA,iBACf,YAAA;EA+CiB;EAAA,QA5C1B,UAAA;EA4FoB;EAAA,iBAzFX,cAAA;EAEV,aAAA;;EAkBM,GAAA,CAAI,YAAA,WAAuB,OAAA,CAAQ,WAAA;EAInC,IAAA,IAAQ,YAAA,UAAsB,IAAA,YAAc,OAAA,CAAA,CAAA;EAKlD,kBAAA,IAAsB,eAAA;EAItB,oBAAA,CAAqB,IAAA;EAIrB,QAAA,IAAY,GAAA,SAAY,WAAA;EAIxB,uBAAA,IAA2B,sBAAA;EAvDN;;;;EA+Df,IAAA,IAAI,OAAA;EA5DA;;;;EAyFV,WAAA,CAAY,YAAA;EAtFS;;;;;;;EAiGrB,iBAAA,IAAqB,OAAA;EArEf;;;;EAkFA,aAAA,IAAa,OAAA;EAkBb,WAAA,CAAY,KAAA;IAAS,KAAA;IAAiB,OAAA;IAAmB,OAAA;EAAA,IAAmB,OAAA;EAc5E,mBAAA,CAAoB,cAAA,GAAiB,yBAAA,KAA8B,OAAA;EAzGnD;;;EAmHhB,yBAAA,IAA6B,OAAA;EA3GvB;;;EAAA,QAmHL,YAAA;EAAA,QAcA,cAAA;EAgCD,UAAA,IAAU,OAAA;AAAA;AAAA,cAuBZ,iBAAA,EAAiB,iBAA0B"}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
//#region ../core/src/dev-server/files-watcher.d.ts
|
|
2
|
-
type FileWatcherEvent = "change" | "delete" | "add" | "error" | "addDir" | "unlinkDir";
|
|
3
|
-
type FileChangeCallback = (filePath: string) => void;
|
|
4
|
-
type FileDeleteCallback = (filePath: string) => void;
|
|
5
|
-
type FileAddCallback = (filePath: string) => void;
|
|
6
|
-
type FileErrorCallback = (filePath: string, error: Error) => void;
|
|
7
|
-
type FileAddDirCallback = (filePath: string) => void;
|
|
8
|
-
type FileUnlinkDirCallback = (filePath: string) => void;
|
|
9
|
-
type OnFileEventCallback = FileChangeCallback | FileDeleteCallback | FileAddCallback | FileErrorCallback | FileAddDirCallback | FileUnlinkDirCallback;
|
|
10
|
-
/**
|
|
11
|
-
* Watch configuration options
|
|
12
|
-
*/
|
|
13
|
-
type WatchConfig = {
|
|
14
|
-
/**
|
|
15
|
-
* Glob patterns to include
|
|
16
|
-
*/
|
|
17
|
-
include?: string[];
|
|
18
|
-
/**
|
|
19
|
-
* Glob patterns to exclude
|
|
20
|
-
*/
|
|
21
|
-
exclude?: string[];
|
|
22
|
-
};
|
|
23
|
-
declare class FilesWatcher {
|
|
24
|
-
/**
|
|
25
|
-
* File watcher id
|
|
26
|
-
*/
|
|
27
|
-
private id;
|
|
28
|
-
/**
|
|
29
|
-
* Watch for files changes
|
|
30
|
-
* @param config Optional watch configuration
|
|
31
|
-
*/
|
|
32
|
-
watch(config?: WatchConfig): Promise<void>;
|
|
33
|
-
/**
|
|
34
|
-
* Trigger event immediately (no debouncing here)
|
|
35
|
-
* Debouncing is handled at the orchestrator level for batch processing
|
|
36
|
-
*/
|
|
37
|
-
private triggerEvent;
|
|
38
|
-
/**
|
|
39
|
-
* On file change event
|
|
40
|
-
*/
|
|
41
|
-
onFileChange(callback: FileChangeCallback): import("@mongez/events").EventSubscription;
|
|
42
|
-
/**
|
|
43
|
-
* On file delete event
|
|
44
|
-
*/
|
|
45
|
-
onFileDelete(callback: FileDeleteCallback): import("@mongez/events").EventSubscription;
|
|
46
|
-
/**
|
|
47
|
-
* On file add event
|
|
48
|
-
*/
|
|
49
|
-
onFileAdd(callback: FileAddCallback): import("@mongez/events").EventSubscription;
|
|
50
|
-
/**
|
|
51
|
-
* On file error event
|
|
52
|
-
*/
|
|
53
|
-
onFileError(callback: FileErrorCallback): import("@mongez/events").EventSubscription;
|
|
54
|
-
/**
|
|
55
|
-
* On file add dir event
|
|
56
|
-
*/
|
|
57
|
-
onDirectoryAdd(callback: FileAddDirCallback): import("@mongez/events").EventSubscription;
|
|
58
|
-
/**
|
|
59
|
-
* On file unlink dir event
|
|
60
|
-
*/
|
|
61
|
-
onDirectoryRemove(callback: FileUnlinkDirCallback): import("@mongez/events").EventSubscription;
|
|
62
|
-
/**
|
|
63
|
-
* On file event
|
|
64
|
-
*/
|
|
65
|
-
on(event: FileWatcherEvent, callback: OnFileEventCallback): import("@mongez/events").EventSubscription;
|
|
66
|
-
}
|
|
67
|
-
//#endregion
|
|
68
|
-
export { FilesWatcher };
|
|
69
|
-
//# sourceMappingURL=files-watcher.d.mts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"files-watcher.d.mts","names":[],"sources":["../../../../../../../core/src/dev-server/files-watcher.ts"],"mappings":";KAOK,gBAAA;AAAA,KAEA,kBAAA,IAAsB,QAAgB;AAAA,KACtC,kBAAA,IAAsB,QAAgB;AAAA,KACtC,eAAA,IAAmB,QAAgB;AAAA,KACnC,iBAAA,IAAqB,QAAA,UAAkB,KAAA,EAAO,KAAK;AAAA,KACnD,kBAAA,IAAsB,QAAgB;AAAA,KACtC,qBAAA,IAAyB,QAAgB;AAAA,KACzC,mBAAA,GACD,kBAAA,GACA,kBAAA,GACA,eAAA,GACA,iBAAA,GACA,kBAAA,GACA,qBAAA;;;AAZuC;KAiB/B,WAAA;EAhBW;;;EAoBrB,OAAA;EAnBG;;;EAuBH,OAAO;AAAA;AAAA,cAsBI,YAAA;EA5CS;;;EAAA,QAgDZ,EAAA;EAhDyC;;;AAAK;EAsDzC,KAAA,CAAM,MAAA,GAAS,WAAA,GAAW,OAAA;EArDlB;;;AAAoB;EAApB,QA8Gb,YAAA;EA7GgB;;;EAoHjB,YAAA,CAAa,QAAA,EAAU,kBAAA,4BAAkB,iBAAA;EAnH7C;;;EA0HI,YAAA,CAAa,QAAA,EAAU,kBAAA,4BAAkB,iBAAA;EAxH9C;;;EA+HK,SAAA,CAAU,QAAA,EAAU,eAAA,4BAAe,iBAAA;EA3HxC;;;EAkIK,WAAA,CAAY,QAAA,EAAU,iBAAA,4BAAiB,iBAAA;EAtI5C;;;EA6IK,cAAA,CAAe,QAAA,EAAU,kBAAA,4BAAkB,iBAAA;EAzIhD;;AAAqB;EAgJhB,iBAAA,CAAkB,QAAA,EAAU,qBAAA,4BAAqB,iBAAA;EA3InC;;;EAkJd,EAAA,CAAG,KAAA,EAAO,gBAAA,EAAkB,QAAA,EAAU,mBAAA,4BAAmB,iBAAA;AAAA"}
|