@warlock.js/core 5.3.0 → 5.3.2
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 +34 -0
- package/esm/cli/cli-commands.manager.mjs +2 -0
- package/esm/cli/cli-commands.manager.mjs.map +1 -1
- package/esm/cli/cli-commands.utils.mjs.map +1 -1
- package/esm/connectors/http-connector.d.mts.map +1 -1
- package/esm/connectors/http-connector.mjs +26 -4
- 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.map +1 -1
- package/esm/database/seed-command-action.mjs.map +1 -1
- package/esm/database/seeds/seeders.manager.mjs.map +1 -1
- package/esm/dev-server/boot-precondition-error.mjs +35 -0
- package/esm/dev-server/boot-precondition-error.mjs.map +1 -0
- package/esm/dev-server/development-server.mjs +6 -1
- package/esm/dev-server/development-server.mjs.map +1 -1
- package/esm/dev-server/files-orchestrator.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/start-development-server.mjs +3 -1
- package/esm/dev-server/start-development-server.mjs.map +1 -1
- package/esm/dev-server/supervisor.mjs +15 -0
- package/esm/dev-server/supervisor.mjs.map +1 -1
- package/esm/generations/add-command.action.mjs.map +1 -1
- package/esm/generations/features/access.feature.mjs.map +1 -1
- package/esm/generations/features/ai-anthropic.feature.mjs.map +1 -1
- package/esm/generations/features/ai-bedrock.feature.mjs.map +1 -1
- package/esm/generations/features/ai-google.feature.mjs.map +1 -1
- package/esm/generations/features/ai-ollama.feature.mjs.map +1 -1
- package/esm/generations/features/ai-openai.feature.mjs.map +1 -1
- package/esm/generations/features/ai-panoptic.feature.mjs.map +1 -1
- package/esm/generations/features/ai-tools.feature.mjs.map +1 -1
- package/esm/generations/features/ai-workspace.feature.mjs.map +1 -1
- package/esm/generations/features/ai.feature.mjs.map +1 -1
- package/esm/generations/features/herald.feature.mjs.map +1 -1
- package/esm/generations/features/image.feature.mjs.map +1 -1
- package/esm/generations/features/index.mjs.map +1 -1
- package/esm/generations/features/mail.feature.mjs.map +1 -1
- package/esm/generations/features/mongodb.feature.mjs.map +1 -1
- package/esm/generations/features/mysql.feature.mjs.map +1 -1
- package/esm/generations/features/notifications.feature.mjs.map +1 -1
- package/esm/generations/features/postgres.feature.mjs.map +1 -1
- package/esm/generations/features/react-email.feature.mjs.map +1 -1
- package/esm/generations/features/react.feature.mjs.map +1 -1
- package/esm/generations/features/redis.feature.mjs.map +1 -1
- package/esm/generations/features/s3.feature.mjs.map +1 -1
- package/esm/generations/features/scheduler.feature.mjs.map +1 -1
- package/esm/generations/features/ses.feature.mjs.map +1 -1
- package/esm/generations/features/shadcn.feature.mjs.map +1 -1
- package/esm/generations/features/socket.feature.mjs.map +1 -1
- package/esm/generations/features/tailwind.feature.mjs.map +1 -1
- package/esm/generations/features/test.feature.mjs.map +1 -1
- package/esm/generations/features/types.mjs.map +1 -1
- package/esm/generations/features/web.feature.mjs.map +1 -1
- package/esm/http/boot-port-preflight.d.mts.map +1 -1
- package/esm/http/boot-port-preflight.mjs +17 -3
- package/esm/http/boot-port-preflight.mjs.map +1 -1
- package/esm/http/resolve-bind-port.mjs +51 -0
- package/esm/http/resolve-bind-port.mjs.map +1 -0
- package/esm/tests/test-helpers.d.mts.map +1 -1
- package/esm/tests/test-helpers.mjs +4 -1
- package/esm/tests/test-helpers.mjs.map +1 -1
- package/esm/utils/detect-environment-overrides.mjs +67 -0
- package/esm/utils/detect-environment-overrides.mjs.map +1 -0
- package/esm/utils/load-environment.mjs +24 -0
- package/esm/utils/load-environment.mjs.map +1 -1
- package/esm/utils/report-environment-overrides.mjs +30 -0
- package/esm/utils/report-environment-overrides.mjs.map +1 -0
- package/esm/validation/types.d.mts +4 -1
- package/esm/validation/types.d.mts.map +1 -1
- package/llms-full.txt +10 -2
- package/package.json +12 -12
- package/skills/run-app/SKILL.md +10 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"supervisor.mjs","names":[],"sources":["../../../../../../../core/src/dev-server/supervisor.ts"],"sourcesContent":["import { spawn, type ChildProcess } from \"node:child_process\";\nimport { devLogError, devLogWarn } from \"./dev-logger\";\n\n/**\n * Set on the worker process so it knows it was launched by a supervisor —\n * and so `warlock dev` inside the worker runs the server instead of\n * supervising a third process.\n */\nexport const WORKER_ENV_FLAG = \"WARLOCK_DEV_WORKER\";\n\n/**\n * Exit code a worker uses to ask for a fresh process. Chosen as EX_TEMPFAIL,\n * whose conventional meaning (\"temporary failure, retry\") is close enough that\n * a stray supervisor-less exit reads sensibly in a shell.\n */\nexport const RESTART_EXIT_CODE = 75;\n\n/** Signals the supervisor must hand to the worker rather than act on itself. */\nconst FORWARDED_SIGNALS: NodeJS.Signals[] = [\"SIGTERM\", \"SIGHUP\"];\n\n/**\n * How long a worker must survive before an abnormal exit counts as a crash\n * worth recovering from rather than a failed boot worth reporting once.\n */\nconst HEALTHY_UPTIME_MS = 5_000;\n\n/** Crashes allowed inside {@link CRASH_WINDOW_MS} before we stop restarting. */\nconst CRASH_RESTART_LIMIT = 3;\n\n/** The rolling window flapping is measured over. */\nconst CRASH_WINDOW_MS = 60_000;\n\n/** Whether this process is the worker (i.e. a supervisor already exists). */\nexport function isDevWorker(): boolean {\n return process.env[WORKER_ENV_FLAG] === \"1\";\n}\n\n/**\n * Run `warlock dev` as a supervised pair: a thin parent that owns the terminal\n * and a disposable child that is the actual dev server.\n *\n * The child asks to be replaced by exiting {@link RESTART_EXIT_CODE}; the\n * supervisor spawns a fresh one in its place. Because the parent is never\n * itself replaced, the process tree stays exactly two deep no matter how many\n * restarts happen — the previous design re-exec'd itself and left one idle\n * waiter behind per restart.\n *\n * The supervisor deliberately loads no config, no connectors, and no\n * application code, so a worker that wedges or dies can always be replaced by\n * a process that is known-good.\n *\n * Never resolves: the returned promise is a deliberate dead end so callers can\n * `return` it to stop everything downstream (notably the command's preloaders,\n * which must only ever run in the worker). The process ends with the last\n * worker, not with this promise.\n */\nexport function superviseDevServer(now: () => number = Date.now): Promise<never> {\n let worker: ChildProcess | undefined;\n let shuttingDown = false;\n\n /** Timestamps of recent crash-restarts, used to detect flapping. */\n let crashes: number[] = [];\n\n const spawnWorker = (): void => {\n const spawnedAt = now();\n\n const spawnedWorker = spawn(process.execPath, [...process.execArgv, ...process.argv.slice(1)], {\n cwd: process.cwd(),\n env: { ...process.env, [WORKER_ENV_FLAG]: \"1\" },\n stdio: \"inherit\",\n windowsHide: false,\n });\n worker = spawnedWorker;\n\n spawnedWorker.on(\"error\", (error) => {\n if (worker !== spawnedWorker) return;\n\n shuttingDown = true;\n devLogError(`Could not start the development server: ${error.message}`);\n process.exit(1);\n });\n\n spawnedWorker.on(\"exit\", (code, signal) => {\n // A replaced worker can finish emitting lifecycle events after its\n // successor is already healthy. It must not spend that successor's crash\n // budget, spawn another worker, or print a terminal flapping diagnosis.\n if (worker !== spawnedWorker) return;\n\n worker = undefined;\n releaseTerminal();\n\n // `return` after every exit deliberately: `process.exit` is typed\n // `never`, but treating it as control flow means anything that stubs it\n // out turns \"we are shutting down\" into \"spawn another worker\".\n if (shuttingDown) {\n process.exit(signal ? 1 : (code ?? 0));\n return;\n }\n\n if (code === RESTART_EXIT_CODE) {\n spawnWorker();\n return;\n }\n\n const crashed = signal !== null || (code ?? 0) !== 0;\n\n if (crashed && shouldRecoverFromCrash(now() - spawnedAt)) {\n devLogWarn(\n `Development server ${signal ? `was killed by ${signal}` : `exited with code ${code}`} — restarting.`,\n );\n spawnWorker();\n return;\n }\n\n // The worker owns its own error reporting; the supervisor only mirrors\n // the outcome so `npm run dev` and CI see the code they expect.\n process.exit(signal ? 1 : (code ?? 0));\n });\n };\n\n /**\n * Whether a worker that died on its own should be replaced.\n *\n * The rule is minimum healthy uptime, not a bare retry count. A worker that\n * dies almost immediately failed to *boot* — a broken config, a port already\n * taken — and it has already printed why; respawning would just reprint the\n * same error and bury it. A worker that ran fine for a while and then died\n * (OOM, a native crash, a dead loader thread) is the case worth recovering,\n * because the state that killed it is usually not reproduced on a fresh boot.\n *\n * Flapping is still capped: enough crashes inside one window and we stop and\n * say so, rather than restarting forever behind the developer's back.\n */\n function shouldRecoverFromCrash(uptimeMs: number): boolean {\n if (uptimeMs < HEALTHY_UPTIME_MS) {\n return false;\n }\n\n const at = now();\n\n crashes = [...crashes.filter((time) => at - time < CRASH_WINDOW_MS), at];\n\n if (crashes.length > CRASH_RESTART_LIMIT) {\n devLogError(\n `Development server crashed ${crashes.length} times in ` +\n `${Math.round(CRASH_WINDOW_MS / 1000)}s — not restarting again. ` +\n `Fix the cause and run \\`warlock dev\\` when you're ready.`,\n );\n return false;\n }\n\n return true;\n }\n\n // SIGINT reaches the worker directly from the terminal (same process group),\n // so the supervisor must NOT act on it — exiting here would orphan a server\n // that is still draining. It only notes that the next exit is final.\n process.on(\"SIGINT\", () => {\n shuttingDown = true;\n });\n\n for (const signal of FORWARDED_SIGNALS) {\n process.on(signal, () => {\n shuttingDown = true;\n worker?.kill(signal);\n });\n }\n\n spawnWorker();\n\n return new Promise<never>(() => {\n // Intentionally never settles — see the doc comment.\n });\n}\n\n/**\n * Put the shared TTY back into a sane state after a worker exits.\n *\n * The worker takes stdin into raw mode for its keyboard shortcuts and releases\n * it on shutdown — but a worker that was killed outright never got the chance,\n * and the mode belongs to the terminal, not the process that set it. Without\n * this, the next worker (or the user's shell) inherits a stdin with no line\n * editing and no Ctrl+C.\n */\nfunction releaseTerminal(): void {\n try {\n if (process.stdin.isTTY) {\n process.stdin.setRawMode(false);\n }\n } catch {\n // Nothing to recover if the terminal is already gone.\n }\n}\n"],"mappings":";;;;;;;;;AAQA,MAAa,kBAAkB;;;;;;AAO/B,MAAa,oBAAoB;;
|
|
1
|
+
{"version":3,"file":"supervisor.mjs","names":[],"sources":["../../../../../../../core/src/dev-server/supervisor.ts"],"sourcesContent":["import { spawn, type ChildProcess } from \"node:child_process\";\nimport { devLogError, devLogWarn } from \"./dev-logger\";\n\n/**\n * Set on the worker process so it knows it was launched by a supervisor —\n * and so `warlock dev` inside the worker runs the server instead of\n * supervising a third process.\n */\nexport const WORKER_ENV_FLAG = \"WARLOCK_DEV_WORKER\";\n\n/**\n * Exit code a worker uses to ask for a fresh process. Chosen as EX_TEMPFAIL,\n * whose conventional meaning (\"temporary failure, retry\") is close enough that\n * a stray supervisor-less exit reads sensibly in a shell.\n */\nexport const RESTART_EXIT_CODE = 75;\n\n/**\n * Exit code a worker uses when it hit a startup precondition whose cause\n * cannot change by trying again — a busy port, a rejected boot validator, an\n * unusable configured value. Chosen as EX_CONFIG, whose conventional meaning\n * (\"configuration error\") matches the class. The supervisor treats this as\n * terminal: no restart, no crash-budget spend, and no extra banner — the\n * worker has already printed a good message, and the whole point of this\n * exit code is that the supervisor does not scroll it away with a\n * \"restarting\" line and then reprint the same failure a moment later.\n */\nexport const BOOT_PRECONDITION_EXIT_CODE = 78;\n\n/** Signals the supervisor must hand to the worker rather than act on itself. */\nconst FORWARDED_SIGNALS: NodeJS.Signals[] = [\"SIGTERM\", \"SIGHUP\"];\n\n/**\n * How long a worker must survive before an abnormal exit counts as a crash\n * worth recovering from rather than a failed boot worth reporting once.\n */\nconst HEALTHY_UPTIME_MS = 5_000;\n\n/** Crashes allowed inside {@link CRASH_WINDOW_MS} before we stop restarting. */\nconst CRASH_RESTART_LIMIT = 3;\n\n/** The rolling window flapping is measured over. */\nconst CRASH_WINDOW_MS = 60_000;\n\n/** Whether this process is the worker (i.e. a supervisor already exists). */\nexport function isDevWorker(): boolean {\n return process.env[WORKER_ENV_FLAG] === \"1\";\n}\n\n/**\n * Run `warlock dev` as a supervised pair: a thin parent that owns the terminal\n * and a disposable child that is the actual dev server.\n *\n * The child asks to be replaced by exiting {@link RESTART_EXIT_CODE}; the\n * supervisor spawns a fresh one in its place. Because the parent is never\n * itself replaced, the process tree stays exactly two deep no matter how many\n * restarts happen — the previous design re-exec'd itself and left one idle\n * waiter behind per restart.\n *\n * The supervisor deliberately loads no config, no connectors, and no\n * application code, so a worker that wedges or dies can always be replaced by\n * a process that is known-good.\n *\n * Never resolves: the returned promise is a deliberate dead end so callers can\n * `return` it to stop everything downstream (notably the command's preloaders,\n * which must only ever run in the worker). The process ends with the last\n * worker, not with this promise.\n */\nexport function superviseDevServer(now: () => number = Date.now): Promise<never> {\n let worker: ChildProcess | undefined;\n let shuttingDown = false;\n\n /** Timestamps of recent crash-restarts, used to detect flapping. */\n let crashes: number[] = [];\n\n const spawnWorker = (): void => {\n const spawnedAt = now();\n\n const spawnedWorker = spawn(process.execPath, [...process.execArgv, ...process.argv.slice(1)], {\n cwd: process.cwd(),\n env: { ...process.env, [WORKER_ENV_FLAG]: \"1\" },\n stdio: \"inherit\",\n windowsHide: false,\n });\n worker = spawnedWorker;\n\n spawnedWorker.on(\"error\", (error) => {\n if (worker !== spawnedWorker) return;\n\n shuttingDown = true;\n devLogError(`Could not start the development server: ${error.message}`);\n process.exit(1);\n });\n\n spawnedWorker.on(\"exit\", (code, signal) => {\n // A replaced worker can finish emitting lifecycle events after its\n // successor is already healthy. It must not spend that successor's crash\n // budget, spawn another worker, or print a terminal flapping diagnosis.\n if (worker !== spawnedWorker) return;\n\n worker = undefined;\n releaseTerminal();\n\n // `return` after every exit deliberately: `process.exit` is typed\n // `never`, but treating it as control flow means anything that stubs it\n // out turns \"we are shutting down\" into \"spawn another worker\".\n if (shuttingDown) {\n process.exit(signal ? 1 : (code ?? 0));\n return;\n }\n\n if (code === RESTART_EXIT_CODE) {\n spawnWorker();\n return;\n }\n\n // A boot precondition failure cannot be fixed by trying again — skip\n // the crash-recovery path entirely (no uptime check, no crash-budget\n // spend, no \"restarting\" banner) and mirror the exit code exactly, the\n // same way a deliberate shutdown does just below.\n if (code === BOOT_PRECONDITION_EXIT_CODE) {\n process.exit(code);\n return;\n }\n\n const crashed = signal !== null || (code ?? 0) !== 0;\n\n if (crashed && shouldRecoverFromCrash(now() - spawnedAt)) {\n devLogWarn(\n `Development server ${signal ? `was killed by ${signal}` : `exited with code ${code}`} — restarting.`,\n );\n spawnWorker();\n return;\n }\n\n // The worker owns its own error reporting; the supervisor only mirrors\n // the outcome so `npm run dev` and CI see the code they expect.\n process.exit(signal ? 1 : (code ?? 0));\n });\n };\n\n /**\n * Whether a worker that died on its own should be replaced.\n *\n * The rule is minimum healthy uptime, not a bare retry count. A worker that\n * dies almost immediately failed to *boot* — a broken config, a port already\n * taken — and it has already printed why; respawning would just reprint the\n * same error and bury it. A worker that ran fine for a while and then died\n * (OOM, a native crash, a dead loader thread) is the case worth recovering,\n * because the state that killed it is usually not reproduced on a fresh boot.\n *\n * Flapping is still capped: enough crashes inside one window and we stop and\n * say so, rather than restarting forever behind the developer's back.\n */\n function shouldRecoverFromCrash(uptimeMs: number): boolean {\n if (uptimeMs < HEALTHY_UPTIME_MS) {\n return false;\n }\n\n const at = now();\n\n crashes = [...crashes.filter((time) => at - time < CRASH_WINDOW_MS), at];\n\n if (crashes.length > CRASH_RESTART_LIMIT) {\n devLogError(\n `Development server crashed ${crashes.length} times in ` +\n `${Math.round(CRASH_WINDOW_MS / 1000)}s — not restarting again. ` +\n `Fix the cause and run \\`warlock dev\\` when you're ready.`,\n );\n return false;\n }\n\n return true;\n }\n\n // SIGINT reaches the worker directly from the terminal (same process group),\n // so the supervisor must NOT act on it — exiting here would orphan a server\n // that is still draining. It only notes that the next exit is final.\n process.on(\"SIGINT\", () => {\n shuttingDown = true;\n });\n\n for (const signal of FORWARDED_SIGNALS) {\n process.on(signal, () => {\n shuttingDown = true;\n worker?.kill(signal);\n });\n }\n\n spawnWorker();\n\n return new Promise<never>(() => {\n // Intentionally never settles — see the doc comment.\n });\n}\n\n/**\n * Put the shared TTY back into a sane state after a worker exits.\n *\n * The worker takes stdin into raw mode for its keyboard shortcuts and releases\n * it on shutdown — but a worker that was killed outright never got the chance,\n * and the mode belongs to the terminal, not the process that set it. Without\n * this, the next worker (or the user's shell) inherits a stdin with no line\n * editing and no Ctrl+C.\n */\nfunction releaseTerminal(): void {\n try {\n if (process.stdin.isTTY) {\n process.stdin.setRawMode(false);\n }\n } catch {\n // Nothing to recover if the terminal is already gone.\n }\n}\n"],"mappings":";;;;;;;;;AAQA,MAAa,kBAAkB;;;;;;AAO/B,MAAa,oBAAoB;;;;;;;;;;;AAYjC,MAAa,8BAA8B;;AAG3C,MAAM,oBAAsC,CAAC,WAAW,QAAQ;;;;;AAMhE,MAAM,oBAAoB;;AAG1B,MAAM,sBAAsB;;AAG5B,MAAM,kBAAkB;;AAGxB,SAAgB,cAAuB;CACrC,OAAO,QAAQ,IAAI,qBAAqB;AAC1C;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,mBAAmB,MAAoB,KAAK,KAAqB;CAC/E,IAAI;CACJ,IAAI,eAAe;;CAGnB,IAAI,UAAoB,CAAC;CAEzB,MAAM,oBAA0B;EAC9B,MAAM,YAAY,IAAI;EAEtB,MAAM,gBAAgB,MAAM,QAAQ,UAAU,CAAC,GAAG,QAAQ,UAAU,GAAG,QAAQ,KAAK,MAAM,CAAC,CAAC,GAAG;GAC7F,KAAK,QAAQ,IAAI;GACjB,KAAK;IAAE,GAAG,QAAQ;KAAM,kBAAkB;GAAI;GAC9C,OAAO;GACP,aAAa;EACf,CAAC;EACD,SAAS;EAET,cAAc,GAAG,UAAU,UAAU;GACnC,IAAI,WAAW,eAAe;GAE9B,eAAe;GACf,YAAY,2CAA2C,MAAM,SAAS;GACtE,QAAQ,KAAK,CAAC;EAChB,CAAC;EAED,cAAc,GAAG,SAAS,MAAM,WAAW;GAIzC,IAAI,WAAW,eAAe;GAE9B,SAAS;GACT,gBAAgB;GAKhB,IAAI,cAAc;IAChB,QAAQ,KAAK,SAAS,IAAK,QAAQ,CAAE;IACrC;GACF;GAEA,IAAI,aAA4B;IAC9B,YAAY;IACZ;GACF;GAMA,IAAI,aAAsC;IACxC,QAAQ,KAAK,IAAI;IACjB;GACF;GAIA,KAFgB,WAAW,SAAS,QAAQ,OAAO,MAEpC,uBAAuB,IAAI,IAAI,SAAS,GAAG;IACxD,WACE,sBAAsB,SAAS,iBAAiB,WAAW,oBAAoB,OAAO,eACxF;IACA,YAAY;IACZ;GACF;GAIA,QAAQ,KAAK,SAAS,IAAK,QAAQ,CAAE;EACvC,CAAC;CACH;;;;;;;;;;;;;;CAeA,SAAS,uBAAuB,UAA2B;EACzD,IAAI,WAAW,mBACb,OAAO;EAGT,MAAM,KAAK,IAAI;EAEf,UAAU,CAAC,GAAG,QAAQ,QAAQ,SAAS,KAAK,OAAO,eAAe,GAAG,EAAE;EAEvE,IAAI,QAAQ,SAAS,qBAAqB;GACxC,YACE,8BAA8B,QAAQ,OAAO,YACxC,KAAK,MAAM,kBAAkB,GAAI,EAAE,mFAE1C;GACA,OAAO;EACT;EAEA,OAAO;CACT;CAKA,QAAQ,GAAG,gBAAgB;EACzB,eAAe;CACjB,CAAC;CAED,KAAK,MAAM,UAAU,mBACnB,QAAQ,GAAG,cAAc;EACvB,eAAe;EACf,QAAQ,KAAK,MAAM;CACrB,CAAC;CAGH,YAAY;CAEZ,OAAO,IAAI,cAAqB,CAEhC,CAAC;AACH;;;;;;;;;;AAWA,SAAS,kBAAwB;CAC/B,IAAI;EACF,IAAI,QAAQ,MAAM,OAChB,QAAQ,MAAM,WAAW,KAAK;CAElC,QAAQ,CAER;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"add-command.action.mjs","names":[],"sources":["../../../../../../../core/src/generations/add-command.action.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport {\r\n fileExistsAsync,\r\n getJsonFileAsync,\r\n putFileAsync,\r\n putJsonFileAsync,\r\n} from \"@warlock.js/fs\";\r\nimport { execSync } from \"node:child_process\";\r\nimport { CommandActionData } from \"../commands/types\";\r\nimport {\r\n detectPackageManager,\n getAddCommand,\n getExactAddCommand,\n type PackageManager,\n} from \"../updater/package-manager\";\nimport { rootPath, srcPath } from \"../utils\";\r\nimport { getWarlockVersion } from \"../utils/framework-vesion\";\r\nimport { featuresMap } from \"./features\";\r\n\r\nexport { featuresMap };\r\nexport type { FeatureDefinition } from \"./features\";\r\n\r\n/**\r\n * The parts of a project `package.json` this action reads or writes. Deliberately\r\n * partial — it describes what we touch, not the whole manifest.\r\n */\r\ntype ProjectPackageJson = {\r\n dependencies?: Record<string, string>;\r\n devDependencies?: Record<string, string>;\r\n scripts?: Record<string, string>;\r\n};\r\n\r\nexport const allowedFeatures = Object.keys(featuresMap);\n\nfunction isWarlockPackage(packageName: string): boolean {\n return packageName.startsWith(\"@warlock.js/\");\n}\n\n/**\n * Resolve the internal feature-map placeholder to the version of Core that is\n * actually executing the command. Every @warlock.js package is lockstep, so\n * Core is the single source of truth; non-Warlock dependencies are untouched.\n */\nexport function resolveWarlockDependencyVersions(\n dependencies: Record<string, string>,\n frameworkVersion: string,\n): void {\n for (const dependency of Object.keys(dependencies)) {\n if (isWarlockPackage(dependency)) {\n dependencies[dependency] = frameworkVersion;\n }\n }\n}\n\nfunction resolveFeatures(features: string[], visited = new Set<string>()): string[] {\n const resolved: string[] = [];\r\n\r\n for (const feature of features) {\r\n if (visited.has(feature)) continue;\r\n visited.add(feature);\r\n\r\n const def = featuresMap[feature];\r\n\r\n if (def.requires?.length) {\r\n resolved.push(...resolveFeatures(def.requires, visited));\r\n }\r\n\r\n resolved.push(feature);\r\n }\r\n\r\n return resolved;\r\n}\r\n\r\nexport async function addCommandAction(options: CommandActionData) {\r\n const features = options.args;\r\n const { packageManager, list, noInstall } = options.options;\r\n\r\n if (list) {\r\n console.log(\"Available Features:\");\r\n\r\n for (const feature of allowedFeatures) {\r\n console.log(\r\n `- ${colors.yellowBright(feature)}: ${colors.green(featuresMap[feature].description)}`,\r\n );\r\n }\r\n\r\n process.exit(0);\r\n }\r\n\r\n validateFeatures(features);\r\n\r\n const resolvedFeatures = resolveFeatures(features);\r\n\r\n const dependencies: Record<string, string> = {};\r\n const devDependencies: Record<string, string> = {};\r\n const ejectConfigs: Record<string, { content: string; name: string }> = {};\r\n const scripts: Record<string, string> = {};\r\n\r\n for (const feature of resolvedFeatures) {\r\n const featurePackages = featuresMap[feature as keyof typeof featuresMap];\r\n Object.assign(dependencies, featurePackages.dependencies);\r\n if (featurePackages.devDependencies) {\r\n Object.assign(devDependencies, featurePackages.devDependencies);\r\n }\r\n\r\n if (featurePackages.ejectConfig) {\r\n ejectConfigs[featurePackages.ejectConfig.name] = featurePackages.ejectConfig;\r\n }\r\n\r\n if (featurePackages.script) {\r\n Object.assign(scripts, featurePackages.script);\r\n }\r\n }\r\n\r\n // Pin every @warlock.js/* feature package to the INSTALLED framework version so\r\n // a scaffolded project's features match its core version instead of drifting to\n // the feature map's static range.\n const frameworkVersion = await getWarlockVersion();\n resolveWarlockDependencyVersions(dependencies, frameworkVersion);\n\r\n const currentPackageJson = await getJsonFileAsync<ProjectPackageJson>(rootPath(\"package.json\"));\r\n\r\n // Fresh templates may omit one of the maps — guard before reading.\r\n currentPackageJson.dependencies = currentPackageJson.dependencies ?? {};\r\n currentPackageJson.devDependencies = currentPackageJson.devDependencies ?? {};\r\n\r\n // Skip anything already present so we never downgrade an existing pin.\r\n for (const dependency of Object.keys(dependencies)) {\r\n if (currentPackageJson.dependencies[dependency]) {\r\n console.log(`${colors.yellowBright(dependency)} is already installed, skipping...`);\r\n delete dependencies[dependency];\r\n }\r\n }\r\n\r\n for (const devDependency of Object.keys(devDependencies)) {\r\n if (currentPackageJson.devDependencies[devDependency]) {\r\n console.log(`${colors.yellowBright(devDependency)} is already installed, skipping...`);\r\n delete devDependencies[devDependency];\r\n }\r\n }\r\n\r\n if (noInstall) {\r\n await recordDependencies(dependencies, devDependencies);\r\n } else {\r\n await installDependencies(packageManager as PackageManager | undefined, dependencies, devDependencies);\r\n }\r\n\r\n for (const [name, config] of Object.entries(ejectConfigs)) {\r\n if (await fileExistsAsync(srcPath(`config/${name}.ts`))) {\r\n console.log(`${colors.yellowBright(name)} config already exists, skipping...`);\r\n continue;\r\n }\r\n\r\n console.log(`Creating ${colors.magenta(name)} config...`);\r\n\r\n await putFileAsync(srcPath(`config/${name}.ts`), config.content);\r\n\r\n console.log(`${colors.green(name)} config created successfully`);\r\n }\r\n\r\n // now loop again over features to execute onExecuting\r\n for (const feature of resolvedFeatures) {\r\n const featurePackages = featuresMap[feature as keyof typeof featuresMap];\r\n if (featurePackages.onExecuting) {\r\n await featurePackages.onExecuting(options);\r\n }\r\n }\r\n\r\n if (Object.keys(scripts).length > 0) {\r\n console.log(`Adding scripts ${colors.magenta(Object.keys(scripts).join(\", \"))}`);\r\n const packageJsonPath = rootPath(\"package.json\");\r\n const packageJson = await getJsonFileAsync<ProjectPackageJson>(packageJsonPath);\r\n packageJson.scripts = { ...(packageJson.scripts ?? {}), ...scripts };\r\n await putJsonFileAsync(packageJsonPath, packageJson);\r\n\r\n console.log(`Scripts added successfully ${colors.green(Object.keys(scripts).join(\", \"))}`);\r\n }\r\n}\r\n\r\n/**\r\n * Install the resolved dependency sets through the project's package manager.\r\n * Runs two passes (prod then dev) so each lands in the correct section.\r\n */\r\nexport async function installDependencies(\n packageManager: PackageManager | undefined,\r\n dependencies: Record<string, string>,\r\n devDependencies: Record<string, string>,\r\n) {\r\n // `--package-manager` is optional; without it, fall back to the lockfile.\r\n const resolvedPackageManager = packageManager ?? (await detectPackageManager());\n const packageManagerCommand = getAddCommand(resolvedPackageManager);\n const exactPackageManagerCommand = getExactAddCommand(resolvedPackageManager);\n\n const installDependencySet = (\n dependencySet: Record<string, string>,\n development: boolean,\n ) => {\n const regularSpecs: string[] = [];\n const exactWarlockSpecs: string[] = [];\n\n for (const [name, version] of Object.entries(dependencySet)) {\n const spec = `${name}@${version}`;\n (isWarlockPackage(name) ? exactWarlockSpecs : regularSpecs).push(spec);\n }\n\n const developmentFlag = development ? \" -D\" : \"\";\n const runInstall = (command: string, specs: string[]) => {\n if (specs.length === 0) return;\n\n execSync(`${command} ${specs.join(\" \")}${developmentFlag}`, {\n cwd: process.cwd(),\n stdio: \"inherit\",\n });\n };\n\n runInstall(packageManagerCommand, regularSpecs);\n runInstall(exactPackageManagerCommand, exactWarlockSpecs);\n };\n\r\n if (Object.keys(dependencies).length > 0) {\r\n console.log(`Installing dependencies ${colors.magenta(Object.keys(dependencies).join(\", \"))}`);\r\n\r\n installDependencySet(dependencies, false);\n\r\n console.log(\r\n `Dependencies installed successfully ${colors.green(Object.keys(dependencies).join(\", \"))}`,\r\n );\r\n }\r\n\r\n if (Object.keys(devDependencies).length > 0) {\r\n console.log(\r\n `Installing dev dependencies ${colors.magenta(Object.keys(devDependencies).join(\", \"))}`,\r\n );\r\n\r\n installDependencySet(devDependencies, true);\n\r\n console.log(\r\n `Dev dependencies installed successfully ${colors.green(Object.keys(devDependencies).join(\", \"))}`,\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Write the resolved dependency sets into package.json without installing.\r\n * Used by `--no-install` so a scaffolder can batch every feature into one\r\n * install pass after the command returns. Versions come from the feature map.\r\n */\r\nasync function recordDependencies(\r\n dependencies: Record<string, string>,\r\n devDependencies: Record<string, string>,\r\n) {\r\n if (Object.keys(dependencies).length === 0 && Object.keys(devDependencies).length === 0) {\r\n return;\r\n }\r\n\r\n const packageJsonPath = rootPath(\"package.json\");\r\n const packageJson = await getJsonFileAsync<ProjectPackageJson>(packageJsonPath);\r\n\r\n packageJson.dependencies = packageJson.dependencies ?? {};\r\n packageJson.devDependencies = packageJson.devDependencies ?? {};\r\n\r\n Object.assign(packageJson.dependencies, dependencies);\r\n Object.assign(packageJson.devDependencies, devDependencies);\r\n\r\n await putJsonFileAsync(packageJsonPath, packageJson);\r\n\r\n const recorded = [...Object.keys(dependencies), ...Object.keys(devDependencies)];\r\n\r\n console.log(\r\n `Recorded ${colors.green(recorded.join(\", \"))} in package.json (install skipped via --no-install)`,\r\n );\r\n}\r\n\r\nfunction validateFeatures(features: string[]) {\n for (const feature of features) {\r\n if (!allowedFeatures.includes(feature)) {\r\n console.log(\r\n `Feature ${colors.redBright(feature)} is not allowed, allowed features are: ${colors.green(allowedFeatures.join(\", \"))}`,\r\n );\r\n process.exit(1);\r\n }\r\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"add-command.action.mjs","names":[],"sources":["../../../../../../../core/src/generations/add-command.action.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport {\r\n fileExistsAsync,\r\n getJsonFileAsync,\r\n putFileAsync,\r\n putJsonFileAsync,\r\n} from \"@warlock.js/fs\";\r\nimport { execSync } from \"node:child_process\";\r\nimport type { CommandActionData } from \"../commands/types\";\r\nimport {\r\n detectPackageManager,\n getAddCommand,\n getExactAddCommand,\n type PackageManager,\n} from \"../updater/package-manager\";\nimport { rootPath, srcPath } from \"../utils\";\r\nimport { getWarlockVersion } from \"../utils/framework-vesion\";\r\nimport { featuresMap } from \"./features\";\r\n\r\nexport { featuresMap };\r\nexport type { FeatureDefinition } from \"./features\";\r\n\r\n/**\r\n * The parts of a project `package.json` this action reads or writes. Deliberately\r\n * partial — it describes what we touch, not the whole manifest.\r\n */\r\ntype ProjectPackageJson = {\r\n dependencies?: Record<string, string>;\r\n devDependencies?: Record<string, string>;\r\n scripts?: Record<string, string>;\r\n};\r\n\r\nexport const allowedFeatures = Object.keys(featuresMap);\n\nfunction isWarlockPackage(packageName: string): boolean {\n return packageName.startsWith(\"@warlock.js/\");\n}\n\n/**\n * Resolve the internal feature-map placeholder to the version of Core that is\n * actually executing the command. Every @warlock.js package is lockstep, so\n * Core is the single source of truth; non-Warlock dependencies are untouched.\n */\nexport function resolveWarlockDependencyVersions(\n dependencies: Record<string, string>,\n frameworkVersion: string,\n): void {\n for (const dependency of Object.keys(dependencies)) {\n if (isWarlockPackage(dependency)) {\n dependencies[dependency] = frameworkVersion;\n }\n }\n}\n\nfunction resolveFeatures(features: string[], visited = new Set<string>()): string[] {\n const resolved: string[] = [];\r\n\r\n for (const feature of features) {\r\n if (visited.has(feature)) continue;\r\n visited.add(feature);\r\n\r\n const def = featuresMap[feature];\r\n\r\n if (def.requires?.length) {\r\n resolved.push(...resolveFeatures(def.requires, visited));\r\n }\r\n\r\n resolved.push(feature);\r\n }\r\n\r\n return resolved;\r\n}\r\n\r\nexport async function addCommandAction(options: CommandActionData) {\r\n const features = options.args;\r\n const { packageManager, list, noInstall } = options.options;\r\n\r\n if (list) {\r\n console.log(\"Available Features:\");\r\n\r\n for (const feature of allowedFeatures) {\r\n console.log(\r\n `- ${colors.yellowBright(feature)}: ${colors.green(featuresMap[feature].description)}`,\r\n );\r\n }\r\n\r\n process.exit(0);\r\n }\r\n\r\n validateFeatures(features);\r\n\r\n const resolvedFeatures = resolveFeatures(features);\r\n\r\n const dependencies: Record<string, string> = {};\r\n const devDependencies: Record<string, string> = {};\r\n const ejectConfigs: Record<string, { content: string; name: string }> = {};\r\n const scripts: Record<string, string> = {};\r\n\r\n for (const feature of resolvedFeatures) {\r\n const featurePackages = featuresMap[feature as keyof typeof featuresMap];\r\n Object.assign(dependencies, featurePackages.dependencies);\r\n if (featurePackages.devDependencies) {\r\n Object.assign(devDependencies, featurePackages.devDependencies);\r\n }\r\n\r\n if (featurePackages.ejectConfig) {\r\n ejectConfigs[featurePackages.ejectConfig.name] = featurePackages.ejectConfig;\r\n }\r\n\r\n if (featurePackages.script) {\r\n Object.assign(scripts, featurePackages.script);\r\n }\r\n }\r\n\r\n // Pin every @warlock.js/* feature package to the INSTALLED framework version so\r\n // a scaffolded project's features match its core version instead of drifting to\n // the feature map's static range.\n const frameworkVersion = await getWarlockVersion();\n resolveWarlockDependencyVersions(dependencies, frameworkVersion);\n\r\n const currentPackageJson = await getJsonFileAsync<ProjectPackageJson>(rootPath(\"package.json\"));\r\n\r\n // Fresh templates may omit one of the maps — guard before reading.\r\n currentPackageJson.dependencies = currentPackageJson.dependencies ?? {};\r\n currentPackageJson.devDependencies = currentPackageJson.devDependencies ?? {};\r\n\r\n // Skip anything already present so we never downgrade an existing pin.\r\n for (const dependency of Object.keys(dependencies)) {\r\n if (currentPackageJson.dependencies[dependency]) {\r\n console.log(`${colors.yellowBright(dependency)} is already installed, skipping...`);\r\n delete dependencies[dependency];\r\n }\r\n }\r\n\r\n for (const devDependency of Object.keys(devDependencies)) {\r\n if (currentPackageJson.devDependencies[devDependency]) {\r\n console.log(`${colors.yellowBright(devDependency)} is already installed, skipping...`);\r\n delete devDependencies[devDependency];\r\n }\r\n }\r\n\r\n if (noInstall) {\r\n await recordDependencies(dependencies, devDependencies);\r\n } else {\r\n await installDependencies(packageManager as PackageManager | undefined, dependencies, devDependencies);\r\n }\r\n\r\n for (const [name, config] of Object.entries(ejectConfigs)) {\r\n if (await fileExistsAsync(srcPath(`config/${name}.ts`))) {\r\n console.log(`${colors.yellowBright(name)} config already exists, skipping...`);\r\n continue;\r\n }\r\n\r\n console.log(`Creating ${colors.magenta(name)} config...`);\r\n\r\n await putFileAsync(srcPath(`config/${name}.ts`), config.content);\r\n\r\n console.log(`${colors.green(name)} config created successfully`);\r\n }\r\n\r\n // now loop again over features to execute onExecuting\r\n for (const feature of resolvedFeatures) {\r\n const featurePackages = featuresMap[feature as keyof typeof featuresMap];\r\n if (featurePackages.onExecuting) {\r\n await featurePackages.onExecuting(options);\r\n }\r\n }\r\n\r\n if (Object.keys(scripts).length > 0) {\r\n console.log(`Adding scripts ${colors.magenta(Object.keys(scripts).join(\", \"))}`);\r\n const packageJsonPath = rootPath(\"package.json\");\r\n const packageJson = await getJsonFileAsync<ProjectPackageJson>(packageJsonPath);\r\n packageJson.scripts = { ...(packageJson.scripts ?? {}), ...scripts };\r\n await putJsonFileAsync(packageJsonPath, packageJson);\r\n\r\n console.log(`Scripts added successfully ${colors.green(Object.keys(scripts).join(\", \"))}`);\r\n }\r\n}\r\n\r\n/**\r\n * Install the resolved dependency sets through the project's package manager.\r\n * Runs two passes (prod then dev) so each lands in the correct section.\r\n */\r\nexport async function installDependencies(\n packageManager: PackageManager | undefined,\r\n dependencies: Record<string, string>,\r\n devDependencies: Record<string, string>,\r\n) {\r\n // `--package-manager` is optional; without it, fall back to the lockfile.\r\n const resolvedPackageManager = packageManager ?? (await detectPackageManager());\n const packageManagerCommand = getAddCommand(resolvedPackageManager);\n const exactPackageManagerCommand = getExactAddCommand(resolvedPackageManager);\n\n const installDependencySet = (\n dependencySet: Record<string, string>,\n development: boolean,\n ) => {\n const regularSpecs: string[] = [];\n const exactWarlockSpecs: string[] = [];\n\n for (const [name, version] of Object.entries(dependencySet)) {\n const spec = `${name}@${version}`;\n (isWarlockPackage(name) ? exactWarlockSpecs : regularSpecs).push(spec);\n }\n\n const developmentFlag = development ? \" -D\" : \"\";\n const runInstall = (command: string, specs: string[]) => {\n if (specs.length === 0) return;\n\n execSync(`${command} ${specs.join(\" \")}${developmentFlag}`, {\n cwd: process.cwd(),\n stdio: \"inherit\",\n });\n };\n\n runInstall(packageManagerCommand, regularSpecs);\n runInstall(exactPackageManagerCommand, exactWarlockSpecs);\n };\n\r\n if (Object.keys(dependencies).length > 0) {\r\n console.log(`Installing dependencies ${colors.magenta(Object.keys(dependencies).join(\", \"))}`);\r\n\r\n installDependencySet(dependencies, false);\n\r\n console.log(\r\n `Dependencies installed successfully ${colors.green(Object.keys(dependencies).join(\", \"))}`,\r\n );\r\n }\r\n\r\n if (Object.keys(devDependencies).length > 0) {\r\n console.log(\r\n `Installing dev dependencies ${colors.magenta(Object.keys(devDependencies).join(\", \"))}`,\r\n );\r\n\r\n installDependencySet(devDependencies, true);\n\r\n console.log(\r\n `Dev dependencies installed successfully ${colors.green(Object.keys(devDependencies).join(\", \"))}`,\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Write the resolved dependency sets into package.json without installing.\r\n * Used by `--no-install` so a scaffolder can batch every feature into one\r\n * install pass after the command returns. Versions come from the feature map.\r\n */\r\nasync function recordDependencies(\r\n dependencies: Record<string, string>,\r\n devDependencies: Record<string, string>,\r\n) {\r\n if (Object.keys(dependencies).length === 0 && Object.keys(devDependencies).length === 0) {\r\n return;\r\n }\r\n\r\n const packageJsonPath = rootPath(\"package.json\");\r\n const packageJson = await getJsonFileAsync<ProjectPackageJson>(packageJsonPath);\r\n\r\n packageJson.dependencies = packageJson.dependencies ?? {};\r\n packageJson.devDependencies = packageJson.devDependencies ?? {};\r\n\r\n Object.assign(packageJson.dependencies, dependencies);\r\n Object.assign(packageJson.devDependencies, devDependencies);\r\n\r\n await putJsonFileAsync(packageJsonPath, packageJson);\r\n\r\n const recorded = [...Object.keys(dependencies), ...Object.keys(devDependencies)];\r\n\r\n console.log(\r\n `Recorded ${colors.green(recorded.join(\", \"))} in package.json (install skipped via --no-install)`,\r\n );\r\n}\r\n\r\nfunction validateFeatures(features: string[]) {\n for (const feature of features) {\r\n if (!allowedFeatures.includes(feature)) {\r\n console.log(\r\n `Feature ${colors.redBright(feature)} is not allowed, allowed features are: ${colors.green(allowedFeatures.join(\", \"))}`,\r\n );\r\n process.exit(1);\r\n }\r\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"access.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/access.feature.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport {\r\n ensureDirectoryAsync,\r\n fileExistsAsync,\r\n getFileAsync,\r\n putFileAsync,\r\n} from \"@warlock.js/fs\";\r\nimport { CommandActionData } from \"../../commands/types\";\r\nimport { srcPath } from \"../../utils\";\r\nimport {\r\n accessConfigStub,\r\n accessResolverStub,\r\n accessRoleMigrationStub,\r\n accessRoleModelIndexStub,\r\n accessRoleModelStub,\r\n accessUserRoleMigrationStub,\r\n accessUserRoleModelIndexStub,\r\n accessUserRoleModelStub,\r\n} from \"../stubs\";\r\nimport { migrationTimestamp } from \"./shared/migration-timestamp\";\r\nimport { FeatureDefinition, INSTALLED_WARLOCK_VERSION } from \"./types\";\n\r\nasync function registerAccessLocale() {\r\n // Register the access locale in the project's shared translations file so a\r\n // denied check returns a real sentence, not the raw \"access.errors.forbidden\"\r\n // key. Append when the file exists, create it otherwise; skip if already there.\r\n const localesPath = srcPath(\"app/shared/utils/locales.ts\");\r\n\r\n const accessLocale = `groupedTranslations(\"access\", {\r\n errors: {\r\n forbidden: {\r\n en: \"You do not have permission to perform this action.\",\r\n ar: \"ليس لديك صلاحية لتنفيذ هذا الإجراء.\",\r\n },\r\n },\r\n});\r\n`;\r\n\r\n if (await fileExistsAsync(localesPath)) {\r\n const current = await getFileAsync(localesPath);\r\n\r\n if (current.includes(`groupedTranslations(\"access\"`)) {\r\n console.log(`${colors.yellowBright(\"access\")} locale already registered, skipping...`);\r\n\r\n return;\r\n }\r\n\r\n // The file uses groupedTranslations already iff it calls it — only inject the\r\n // import when no call is present yet.\r\n const importLine = `import { groupedTranslations } from \"@warlock.js/core\";`;\r\n const prefix = current.includes(\"groupedTranslations(\") ? \"\" : `${importLine}\\n\\n`;\r\n\r\n await putFileAsync(localesPath, `${prefix}${current.trimEnd()}\\n\\n${accessLocale}`);\r\n\r\n console.log(\r\n `${colors.green(\"✓\")} Registered the access locale in src/app/shared/utils/locales.ts`,\r\n );\r\n\r\n return;\r\n }\r\n\r\n await ensureDirectoryAsync(srcPath(\"app/shared/utils\"));\r\n\r\n await putFileAsync(\r\n localesPath,\r\n `import { groupedTranslations } from \"@warlock.js/core\";\\n\\n${accessLocale}`,\r\n );\r\n\r\n console.log(`${colors.green(\"✓\")} Created src/app/shared/utils/locales.ts with the access locale`);\r\n}\r\n\r\nasync function scaffoldAccessFiles() {\r\n // The resolver file is the sentinel for \"access already scaffolded\" — its\r\n // presence means the role/user-role model folders and their timestamped\r\n // migrations were created too, so we must not re-emit duplicate migrations on\r\n // a second run.\r\n const resolverPath = srcPath(\"app/access/services/access-resolver.ts\");\r\n\r\n if (await fileExistsAsync(resolverPath)) {\r\n console.log(\r\n `${colors.yellowBright(\"src/app/access\")} already scaffolded, skipping resolver + role tables...`,\r\n );\r\n\r\n return;\r\n }\r\n\r\n // 1. Role catalog model folder (model + barrel + migration). The catalog row\r\n // is role name → granted permissions; managed at runtime in the DB.\r\n await ensureDirectoryAsync(srcPath(\"app/access/models/role\"));\r\n await putFileAsync(srcPath(\"app/access/models/role/role.model.ts\"), accessRoleModelStub);\r\n await putFileAsync(srcPath(\"app/access/models/role/index.ts\"), accessRoleModelIndexStub);\r\n console.log(`${colors.green(\"✓\")} Created src/app/access/models/role`);\r\n\r\n await ensureDirectoryAsync(srcPath(\"app/access/models/role/migrations\"));\r\n\r\n // Migration filenames carry a MM-DD-YYYY_HH-MM-SS prefix so cascade infers\r\n // their createdAt and orders them deterministically (the migrate action\r\n // discovers src/app/*/models/*/migrations/*). The two tables are independent\r\n // (no FK between them), but the user-role migration is stamped a second later\r\n // so the relative order is stable.\r\n const roleMigrationFile = `${migrationTimestamp()}-role.migration.ts`;\r\n await putFileAsync(\r\n srcPath(\"app/access/models/role/migrations\", roleMigrationFile),\r\n accessRoleMigrationStub,\r\n );\r\n console.log(\r\n `${colors.green(\"✓\")} Created src/app/access/models/role/migrations/${roleMigrationFile}`,\r\n );\r\n\r\n // 2. UserRole assignment model folder (model + barrel + migration). The model\r\n // statics scope an unresolved tenant to GLOBAL rows only (security\r\n // invariant) — see the stub for the reasoning.\r\n await ensureDirectoryAsync(srcPath(\"app/access/models/user-role\"));\r\n await putFileAsync(\r\n srcPath(\"app/access/models/user-role/user-role.model.ts\"),\r\n accessUserRoleModelStub,\r\n );\r\n await putFileAsync(\r\n srcPath(\"app/access/models/user-role/index.ts\"),\r\n accessUserRoleModelIndexStub,\r\n );\r\n console.log(`${colors.green(\"✓\")} Created src/app/access/models/user-role`);\r\n\r\n await ensureDirectoryAsync(srcPath(\"app/access/models/user-role/migrations\"));\r\n\r\n const userRoleMigrationFile = `${migrationTimestamp(1)}-user-role.migration.ts`;\r\n await putFileAsync(\r\n srcPath(\"app/access/models/user-role/migrations\", userRoleMigrationFile),\r\n accessUserRoleMigrationStub,\r\n );\r\n console.log(\r\n `${colors.green(\"✓\")} Created src/app/access/models/user-role/migrations/${userRoleMigrationFile}`,\r\n );\r\n\r\n // 3. The DatabaseAccessResolver — the one required config seam, wired into\r\n // config/access.ts by the ejected stub.\r\n await ensureDirectoryAsync(srcPath(\"app/access/services\"));\r\n await putFileAsync(resolverPath, accessResolverStub);\r\n console.log(`${colors.green(\"✓\")} Created src/app/access/services/access-resolver.ts`);\r\n}\r\n\r\nasync function completeAccessInstallation(_options: CommandActionData) {\r\n await registerAccessLocale();\r\n await scaffoldAccessFiles();\r\n}\r\n\r\nexport const accessFeature: FeatureDefinition = {\r\n description:\r\n \"Installs @warlock.js/access — authorization (RBAC + ABAC): permission checks, ABAC policies, and roles. Ejects config/access.ts, the DatabaseAccessResolver + Role/UserRole models and migrations into src/app/access, and registers the access locale in src/app/shared/utils/locales.ts\",\r\n dependencies: {\r\n \"@warlock.js/access\": INSTALLED_WARLOCK_VERSION,\n },\r\n ejectConfig: {\r\n content: accessConfigStub,\r\n name: \"access\",\r\n },\r\n onExecuting: completeAccessInstallation,\r\n};\r\n"],"mappings":";;;;;;;;;AAsBA,eAAe,uBAAuB;CAIpC,MAAM,cAAc,QAAQ,6BAA6B;CAEzD,MAAM,eAAe;;;;;;;;;CAUrB,IAAI,MAAM,gBAAgB,WAAW,GAAG;EACtC,MAAM,UAAU,MAAM,aAAa,WAAW;EAE9C,IAAI,QAAQ,SAAS,8BAA8B,GAAG;GACpD,QAAQ,IAAI,GAAG,OAAO,aAAa,QAAQ,EAAE,wCAAwC;GAErF;EACF;EAOA,MAAM,aAAa,aAAa,GAFjB,QAAQ,SAAS,sBAAsB,IAAI,KAAK,gEAEnB,QAAQ,QAAQ,EAAE,MAAM,cAAc;EAElF,QAAQ,IACN,GAAG,OAAO,MAAM,GAAG,EAAE,iEACvB;EAEA;CACF;CAEA,MAAM,qBAAqB,QAAQ,kBAAkB,CAAC;CAEtD,MAAM,aACJ,aACA,8DAA8D,cAChE;CAEA,QAAQ,IAAI,GAAG,OAAO,MAAM,GAAG,EAAE,gEAAgE;AACnG;AAEA,eAAe,sBAAsB;CAKnC,MAAM,eAAe,QAAQ,wCAAwC;CAErE,IAAI,MAAM,gBAAgB,YAAY,GAAG;EACvC,QAAQ,IACN,GAAG,OAAO,aAAa,gBAAgB,EAAE,wDAC3C;EAEA;CACF;CAIA,MAAM,qBAAqB,QAAQ,wBAAwB,CAAC;CAC5D,MAAM,aAAa,QAAQ,sCAAsC,GAAG,mBAAmB;CACvF,MAAM,aAAa,QAAQ,iCAAiC,GAAG,wBAAwB;CACvF,QAAQ,IAAI,GAAG,OAAO,MAAM,GAAG,EAAE,oCAAoC;CAErE,MAAM,qBAAqB,QAAQ,mCAAmC,CAAC;CAOvE,MAAM,oBAAoB,GAAG,mBAAmB,EAAE;CAClD,MAAM,aACJ,QAAQ,qCAAqC,iBAAiB,GAC9D,uBACF;CACA,QAAQ,IACN,GAAG,OAAO,MAAM,GAAG,EAAE,iDAAiD,mBACxE;CAKA,MAAM,qBAAqB,QAAQ,6BAA6B,CAAC;CACjE,MAAM,aACJ,QAAQ,gDAAgD,GACxD,uBACF;CACA,MAAM,aACJ,QAAQ,sCAAsC,GAC9C,4BACF;CACA,QAAQ,IAAI,GAAG,OAAO,MAAM,GAAG,EAAE,yCAAyC;CAE1E,MAAM,qBAAqB,QAAQ,wCAAwC,CAAC;CAE5E,MAAM,wBAAwB,GAAG,mBAAmB,CAAC,EAAE;CACvD,MAAM,aACJ,QAAQ,0CAA0C,qBAAqB,GACvE,2BACF;CACA,QAAQ,IACN,GAAG,OAAO,MAAM,GAAG,EAAE,sDAAsD,uBAC7E;CAIA,MAAM,qBAAqB,QAAQ,qBAAqB,CAAC;CACzD,MAAM,aAAa,cAAc,kBAAkB;CACnD,QAAQ,IAAI,GAAG,OAAO,MAAM,GAAG,EAAE,oDAAoD;AACvF;AAEA,eAAe,2BAA2B,UAA6B;CACrE,MAAM,qBAAqB;CAC3B,MAAM,oBAAoB;AAC5B;AAEA,MAAa,gBAAmC;CAC9C,aACE;CACF,cAAc,EACZ,sBAAsB,0BACxB;CACA,aAAa;EACX,SAAS;EACT,MAAM;CACR;CACA,aAAa;AACf"}
|
|
1
|
+
{"version":3,"file":"access.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/access.feature.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport {\r\n ensureDirectoryAsync,\r\n fileExistsAsync,\r\n getFileAsync,\r\n putFileAsync,\r\n} from \"@warlock.js/fs\";\r\nimport type { CommandActionData } from \"../../commands/types\";\r\nimport { srcPath } from \"../../utils\";\r\nimport {\r\n accessConfigStub,\r\n accessResolverStub,\r\n accessRoleMigrationStub,\r\n accessRoleModelIndexStub,\r\n accessRoleModelStub,\r\n accessUserRoleMigrationStub,\r\n accessUserRoleModelIndexStub,\r\n accessUserRoleModelStub,\r\n} from \"../stubs\";\r\nimport { migrationTimestamp } from \"./shared/migration-timestamp\";\r\nimport { type FeatureDefinition, INSTALLED_WARLOCK_VERSION } from \"./types\";\n\r\nasync function registerAccessLocale() {\r\n // Register the access locale in the project's shared translations file so a\r\n // denied check returns a real sentence, not the raw \"access.errors.forbidden\"\r\n // key. Append when the file exists, create it otherwise; skip if already there.\r\n const localesPath = srcPath(\"app/shared/utils/locales.ts\");\r\n\r\n const accessLocale = `groupedTranslations(\"access\", {\r\n errors: {\r\n forbidden: {\r\n en: \"You do not have permission to perform this action.\",\r\n ar: \"ليس لديك صلاحية لتنفيذ هذا الإجراء.\",\r\n },\r\n },\r\n});\r\n`;\r\n\r\n if (await fileExistsAsync(localesPath)) {\r\n const current = await getFileAsync(localesPath);\r\n\r\n if (current.includes(`groupedTranslations(\"access\"`)) {\r\n console.log(`${colors.yellowBright(\"access\")} locale already registered, skipping...`);\r\n\r\n return;\r\n }\r\n\r\n // The file uses groupedTranslations already iff it calls it — only inject the\r\n // import when no call is present yet.\r\n const importLine = `import { groupedTranslations } from \"@warlock.js/core\";`;\r\n const prefix = current.includes(\"groupedTranslations(\") ? \"\" : `${importLine}\\n\\n`;\r\n\r\n await putFileAsync(localesPath, `${prefix}${current.trimEnd()}\\n\\n${accessLocale}`);\r\n\r\n console.log(\r\n `${colors.green(\"✓\")} Registered the access locale in src/app/shared/utils/locales.ts`,\r\n );\r\n\r\n return;\r\n }\r\n\r\n await ensureDirectoryAsync(srcPath(\"app/shared/utils\"));\r\n\r\n await putFileAsync(\r\n localesPath,\r\n `import { groupedTranslations } from \"@warlock.js/core\";\\n\\n${accessLocale}`,\r\n );\r\n\r\n console.log(`${colors.green(\"✓\")} Created src/app/shared/utils/locales.ts with the access locale`);\r\n}\r\n\r\nasync function scaffoldAccessFiles() {\r\n // The resolver file is the sentinel for \"access already scaffolded\" — its\r\n // presence means the role/user-role model folders and their timestamped\r\n // migrations were created too, so we must not re-emit duplicate migrations on\r\n // a second run.\r\n const resolverPath = srcPath(\"app/access/services/access-resolver.ts\");\r\n\r\n if (await fileExistsAsync(resolverPath)) {\r\n console.log(\r\n `${colors.yellowBright(\"src/app/access\")} already scaffolded, skipping resolver + role tables...`,\r\n );\r\n\r\n return;\r\n }\r\n\r\n // 1. Role catalog model folder (model + barrel + migration). The catalog row\r\n // is role name → granted permissions; managed at runtime in the DB.\r\n await ensureDirectoryAsync(srcPath(\"app/access/models/role\"));\r\n await putFileAsync(srcPath(\"app/access/models/role/role.model.ts\"), accessRoleModelStub);\r\n await putFileAsync(srcPath(\"app/access/models/role/index.ts\"), accessRoleModelIndexStub);\r\n console.log(`${colors.green(\"✓\")} Created src/app/access/models/role`);\r\n\r\n await ensureDirectoryAsync(srcPath(\"app/access/models/role/migrations\"));\r\n\r\n // Migration filenames carry a MM-DD-YYYY_HH-MM-SS prefix so cascade infers\r\n // their createdAt and orders them deterministically (the migrate action\r\n // discovers src/app/*/models/*/migrations/*). The two tables are independent\r\n // (no FK between them), but the user-role migration is stamped a second later\r\n // so the relative order is stable.\r\n const roleMigrationFile = `${migrationTimestamp()}-role.migration.ts`;\r\n await putFileAsync(\r\n srcPath(\"app/access/models/role/migrations\", roleMigrationFile),\r\n accessRoleMigrationStub,\r\n );\r\n console.log(\r\n `${colors.green(\"✓\")} Created src/app/access/models/role/migrations/${roleMigrationFile}`,\r\n );\r\n\r\n // 2. UserRole assignment model folder (model + barrel + migration). The model\r\n // statics scope an unresolved tenant to GLOBAL rows only (security\r\n // invariant) — see the stub for the reasoning.\r\n await ensureDirectoryAsync(srcPath(\"app/access/models/user-role\"));\r\n await putFileAsync(\r\n srcPath(\"app/access/models/user-role/user-role.model.ts\"),\r\n accessUserRoleModelStub,\r\n );\r\n await putFileAsync(\r\n srcPath(\"app/access/models/user-role/index.ts\"),\r\n accessUserRoleModelIndexStub,\r\n );\r\n console.log(`${colors.green(\"✓\")} Created src/app/access/models/user-role`);\r\n\r\n await ensureDirectoryAsync(srcPath(\"app/access/models/user-role/migrations\"));\r\n\r\n const userRoleMigrationFile = `${migrationTimestamp(1)}-user-role.migration.ts`;\r\n await putFileAsync(\r\n srcPath(\"app/access/models/user-role/migrations\", userRoleMigrationFile),\r\n accessUserRoleMigrationStub,\r\n );\r\n console.log(\r\n `${colors.green(\"✓\")} Created src/app/access/models/user-role/migrations/${userRoleMigrationFile}`,\r\n );\r\n\r\n // 3. The DatabaseAccessResolver — the one required config seam, wired into\r\n // config/access.ts by the ejected stub.\r\n await ensureDirectoryAsync(srcPath(\"app/access/services\"));\r\n await putFileAsync(resolverPath, accessResolverStub);\r\n console.log(`${colors.green(\"✓\")} Created src/app/access/services/access-resolver.ts`);\r\n}\r\n\r\nasync function completeAccessInstallation(_options: CommandActionData) {\r\n await registerAccessLocale();\r\n await scaffoldAccessFiles();\r\n}\r\n\r\nexport const accessFeature: FeatureDefinition = {\r\n description:\r\n \"Installs @warlock.js/access — authorization (RBAC + ABAC): permission checks, ABAC policies, and roles. Ejects config/access.ts, the DatabaseAccessResolver + Role/UserRole models and migrations into src/app/access, and registers the access locale in src/app/shared/utils/locales.ts\",\r\n dependencies: {\r\n \"@warlock.js/access\": INSTALLED_WARLOCK_VERSION,\n },\r\n ejectConfig: {\r\n content: accessConfigStub,\r\n name: \"access\",\r\n },\r\n onExecuting: completeAccessInstallation,\r\n};\r\n"],"mappings":";;;;;;;;;AAsBA,eAAe,uBAAuB;CAIpC,MAAM,cAAc,QAAQ,6BAA6B;CAEzD,MAAM,eAAe;;;;;;;;;CAUrB,IAAI,MAAM,gBAAgB,WAAW,GAAG;EACtC,MAAM,UAAU,MAAM,aAAa,WAAW;EAE9C,IAAI,QAAQ,SAAS,8BAA8B,GAAG;GACpD,QAAQ,IAAI,GAAG,OAAO,aAAa,QAAQ,EAAE,wCAAwC;GAErF;EACF;EAOA,MAAM,aAAa,aAAa,GAFjB,QAAQ,SAAS,sBAAsB,IAAI,KAAK,gEAEnB,QAAQ,QAAQ,EAAE,MAAM,cAAc;EAElF,QAAQ,IACN,GAAG,OAAO,MAAM,GAAG,EAAE,iEACvB;EAEA;CACF;CAEA,MAAM,qBAAqB,QAAQ,kBAAkB,CAAC;CAEtD,MAAM,aACJ,aACA,8DAA8D,cAChE;CAEA,QAAQ,IAAI,GAAG,OAAO,MAAM,GAAG,EAAE,gEAAgE;AACnG;AAEA,eAAe,sBAAsB;CAKnC,MAAM,eAAe,QAAQ,wCAAwC;CAErE,IAAI,MAAM,gBAAgB,YAAY,GAAG;EACvC,QAAQ,IACN,GAAG,OAAO,aAAa,gBAAgB,EAAE,wDAC3C;EAEA;CACF;CAIA,MAAM,qBAAqB,QAAQ,wBAAwB,CAAC;CAC5D,MAAM,aAAa,QAAQ,sCAAsC,GAAG,mBAAmB;CACvF,MAAM,aAAa,QAAQ,iCAAiC,GAAG,wBAAwB;CACvF,QAAQ,IAAI,GAAG,OAAO,MAAM,GAAG,EAAE,oCAAoC;CAErE,MAAM,qBAAqB,QAAQ,mCAAmC,CAAC;CAOvE,MAAM,oBAAoB,GAAG,mBAAmB,EAAE;CAClD,MAAM,aACJ,QAAQ,qCAAqC,iBAAiB,GAC9D,uBACF;CACA,QAAQ,IACN,GAAG,OAAO,MAAM,GAAG,EAAE,iDAAiD,mBACxE;CAKA,MAAM,qBAAqB,QAAQ,6BAA6B,CAAC;CACjE,MAAM,aACJ,QAAQ,gDAAgD,GACxD,uBACF;CACA,MAAM,aACJ,QAAQ,sCAAsC,GAC9C,4BACF;CACA,QAAQ,IAAI,GAAG,OAAO,MAAM,GAAG,EAAE,yCAAyC;CAE1E,MAAM,qBAAqB,QAAQ,wCAAwC,CAAC;CAE5E,MAAM,wBAAwB,GAAG,mBAAmB,CAAC,EAAE;CACvD,MAAM,aACJ,QAAQ,0CAA0C,qBAAqB,GACvE,2BACF;CACA,QAAQ,IACN,GAAG,OAAO,MAAM,GAAG,EAAE,sDAAsD,uBAC7E;CAIA,MAAM,qBAAqB,QAAQ,qBAAqB,CAAC;CACzD,MAAM,aAAa,cAAc,kBAAkB;CACnD,QAAQ,IAAI,GAAG,OAAO,MAAM,GAAG,EAAE,oDAAoD;AACvF;AAEA,eAAe,2BAA2B,UAA6B;CACrE,MAAM,qBAAqB;CAC3B,MAAM,oBAAoB;AAC5B;AAEA,MAAa,gBAAmC;CAC9C,aACE;CACF,cAAc,EACZ,sBAAsB,0BACxB;CACA,aAAa;EACX,SAAS;EACT,MAAM;CACR;CACA,aAAa;AACf"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-anthropic.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/ai-anthropic.feature.ts"],"sourcesContent":["import { FeatureDefinition, INSTALLED_WARLOCK_VERSION } from \"./types\";\n\r\nexport const aiAnthropicFeature: FeatureDefinition = {\r\n description: \"Anthropic (Claude) provider for @warlock.js/ai (pulls the core ai package)\",\r\n requires: [\"ai\"],\r\n dependencies: {\r\n \"@warlock.js/ai-anthropic\": INSTALLED_WARLOCK_VERSION,\n },\r\n};\r\n"],"mappings":";;;AAEA,MAAa,qBAAwC;CACnD,aAAa;CACb,UAAU,CAAC,IAAI;CACf,cAAc,EACZ,4BAA4B,0BAC9B;AACF"}
|
|
1
|
+
{"version":3,"file":"ai-anthropic.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/ai-anthropic.feature.ts"],"sourcesContent":["import { type FeatureDefinition, INSTALLED_WARLOCK_VERSION } from \"./types\";\n\r\nexport const aiAnthropicFeature: FeatureDefinition = {\r\n description: \"Anthropic (Claude) provider for @warlock.js/ai (pulls the core ai package)\",\r\n requires: [\"ai\"],\r\n dependencies: {\r\n \"@warlock.js/ai-anthropic\": INSTALLED_WARLOCK_VERSION,\n },\r\n};\r\n"],"mappings":";;;AAEA,MAAa,qBAAwC;CACnD,aAAa;CACb,UAAU,CAAC,IAAI;CACf,cAAc,EACZ,4BAA4B,0BAC9B;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-bedrock.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/ai-bedrock.feature.ts"],"sourcesContent":["import { FeatureDefinition, INSTALLED_WARLOCK_VERSION } from \"./types\";\n\r\nexport const aiBedrockFeature: FeatureDefinition = {\r\n description: \"AWS Bedrock provider for @warlock.js/ai (pulls the core ai package)\",\r\n requires: [\"ai\"],\r\n dependencies: {\r\n \"@warlock.js/ai-bedrock\": INSTALLED_WARLOCK_VERSION,\n },\r\n};\r\n"],"mappings":";;;AAEA,MAAa,mBAAsC;CACjD,aAAa;CACb,UAAU,CAAC,IAAI;CACf,cAAc,EACZ,0BAA0B,0BAC5B;AACF"}
|
|
1
|
+
{"version":3,"file":"ai-bedrock.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/ai-bedrock.feature.ts"],"sourcesContent":["import { type FeatureDefinition, INSTALLED_WARLOCK_VERSION } from \"./types\";\n\r\nexport const aiBedrockFeature: FeatureDefinition = {\r\n description: \"AWS Bedrock provider for @warlock.js/ai (pulls the core ai package)\",\r\n requires: [\"ai\"],\r\n dependencies: {\r\n \"@warlock.js/ai-bedrock\": INSTALLED_WARLOCK_VERSION,\n },\r\n};\r\n"],"mappings":";;;AAEA,MAAa,mBAAsC;CACjD,aAAa;CACb,UAAU,CAAC,IAAI;CACf,cAAc,EACZ,0BAA0B,0BAC5B;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-google.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/ai-google.feature.ts"],"sourcesContent":["import { FeatureDefinition, INSTALLED_WARLOCK_VERSION } from \"./types\";\n\r\nexport const aiGoogleFeature: FeatureDefinition = {\r\n description: \"Google (Gemini) provider for @warlock.js/ai (pulls the core ai package)\",\r\n requires: [\"ai\"],\r\n dependencies: {\r\n \"@warlock.js/ai-google\": INSTALLED_WARLOCK_VERSION,\n },\r\n};\r\n"],"mappings":";;;AAEA,MAAa,kBAAqC;CAChD,aAAa;CACb,UAAU,CAAC,IAAI;CACf,cAAc,EACZ,yBAAyB,0BAC3B;AACF"}
|
|
1
|
+
{"version":3,"file":"ai-google.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/ai-google.feature.ts"],"sourcesContent":["import { type FeatureDefinition, INSTALLED_WARLOCK_VERSION } from \"./types\";\n\r\nexport const aiGoogleFeature: FeatureDefinition = {\r\n description: \"Google (Gemini) provider for @warlock.js/ai (pulls the core ai package)\",\r\n requires: [\"ai\"],\r\n dependencies: {\r\n \"@warlock.js/ai-google\": INSTALLED_WARLOCK_VERSION,\n },\r\n};\r\n"],"mappings":";;;AAEA,MAAa,kBAAqC;CAChD,aAAa;CACb,UAAU,CAAC,IAAI;CACf,cAAc,EACZ,yBAAyB,0BAC3B;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-ollama.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/ai-ollama.feature.ts"],"sourcesContent":["import { FeatureDefinition, INSTALLED_WARLOCK_VERSION } from \"./types\";\n\r\nexport const aiOllamaFeature: FeatureDefinition = {\r\n description: \"Ollama provider for @warlock.js/ai (pulls the core ai package)\",\r\n requires: [\"ai\"],\r\n dependencies: {\r\n \"@warlock.js/ai-ollama\": INSTALLED_WARLOCK_VERSION,\n },\r\n};\r\n"],"mappings":";;;AAEA,MAAa,kBAAqC;CAChD,aAAa;CACb,UAAU,CAAC,IAAI;CACf,cAAc,EACZ,yBAAyB,0BAC3B;AACF"}
|
|
1
|
+
{"version":3,"file":"ai-ollama.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/ai-ollama.feature.ts"],"sourcesContent":["import { type FeatureDefinition, INSTALLED_WARLOCK_VERSION } from \"./types\";\n\r\nexport const aiOllamaFeature: FeatureDefinition = {\r\n description: \"Ollama provider for @warlock.js/ai (pulls the core ai package)\",\r\n requires: [\"ai\"],\r\n dependencies: {\r\n \"@warlock.js/ai-ollama\": INSTALLED_WARLOCK_VERSION,\n },\r\n};\r\n"],"mappings":";;;AAEA,MAAa,kBAAqC;CAChD,aAAa;CACb,UAAU,CAAC,IAAI;CACf,cAAc,EACZ,yBAAyB,0BAC3B;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-openai.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/ai-openai.feature.ts"],"sourcesContent":["import { FeatureDefinition, INSTALLED_WARLOCK_VERSION } from \"./types\";\n\r\nexport const aiOpenaiFeature: FeatureDefinition = {\r\n description: \"OpenAI provider for @warlock.js/ai (pulls the core ai package)\",\r\n requires: [\"ai\"],\r\n dependencies: {\r\n \"@warlock.js/ai-openai\": INSTALLED_WARLOCK_VERSION,\n },\r\n};\r\n"],"mappings":";;;AAEA,MAAa,kBAAqC;CAChD,aAAa;CACb,UAAU,CAAC,IAAI;CACf,cAAc,EACZ,yBAAyB,0BAC3B;AACF"}
|
|
1
|
+
{"version":3,"file":"ai-openai.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/ai-openai.feature.ts"],"sourcesContent":["import { type FeatureDefinition, INSTALLED_WARLOCK_VERSION } from \"./types\";\n\r\nexport const aiOpenaiFeature: FeatureDefinition = {\r\n description: \"OpenAI provider for @warlock.js/ai (pulls the core ai package)\",\r\n requires: [\"ai\"],\r\n dependencies: {\r\n \"@warlock.js/ai-openai\": INSTALLED_WARLOCK_VERSION,\n },\r\n};\r\n"],"mappings":";;;AAEA,MAAa,kBAAqC;CAChD,aAAa;CACb,UAAU,CAAC,IAAI;CACf,cAAc,EACZ,yBAAyB,0BAC3B;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-panoptic.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/ai-panoptic.feature.ts"],"sourcesContent":["import { linkAiPackageImport } from \"./shared/link-ai-package-import\";\r\nimport { FeatureDefinition, INSTALLED_WARLOCK_VERSION } from \"./types\";\n\r\nexport const aiPanopticFeature: FeatureDefinition = {\r\n description:\r\n \"Installs @warlock.js/ai-panoptic — observability for @warlock.js/ai (collector, exporters, zero-setup local dashboard) via ai.config({ panoptic }) (pulls the core ai package)\",\r\n requires: [\"ai\"],\r\n dependencies: {\r\n \"@warlock.js/ai-panoptic\": INSTALLED_WARLOCK_VERSION,\n },\r\n onExecuting: () => linkAiPackageImport(\"@warlock.js/ai-panoptic\"),\r\n};\r\n"],"mappings":";;;;AAGA,MAAa,oBAAuC;CAClD,aACE;CACF,UAAU,CAAC,IAAI;CACf,cAAc,EACZ,2BAA2B,0BAC7B;CACA,mBAAmB,oBAAoB,yBAAyB;AAClE"}
|
|
1
|
+
{"version":3,"file":"ai-panoptic.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/ai-panoptic.feature.ts"],"sourcesContent":["import { linkAiPackageImport } from \"./shared/link-ai-package-import\";\r\nimport { type FeatureDefinition, INSTALLED_WARLOCK_VERSION } from \"./types\";\n\r\nexport const aiPanopticFeature: FeatureDefinition = {\r\n description:\r\n \"Installs @warlock.js/ai-panoptic — observability for @warlock.js/ai (collector, exporters, zero-setup local dashboard) via ai.config({ panoptic }) (pulls the core ai package)\",\r\n requires: [\"ai\"],\r\n dependencies: {\r\n \"@warlock.js/ai-panoptic\": INSTALLED_WARLOCK_VERSION,\n },\r\n onExecuting: () => linkAiPackageImport(\"@warlock.js/ai-panoptic\"),\r\n};\r\n"],"mappings":";;;;AAGA,MAAa,oBAAuC;CAClD,aACE;CACF,UAAU,CAAC,IAAI;CACf,cAAc,EACZ,2BAA2B,0BAC7B;CACA,mBAAmB,oBAAoB,yBAAyB;AAClE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-tools.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/ai-tools.feature.ts"],"sourcesContent":["import { linkAiPackageImport } from \"./shared/link-ai-package-import\";\r\nimport { FeatureDefinition, INSTALLED_WARLOCK_VERSION } from \"./types\";\n\r\nexport const aiToolsFeature: FeatureDefinition = {\r\n description:\r\n \"Installs @warlock.js/ai-tools — ready-made agent tools (web search, fetch, HTTP, calculator, date-time) + an MCP client/server, under ai.tools.* / ai.mcp (pulls the core ai package)\",\r\n requires: [\"ai\"],\r\n dependencies: {\r\n \"@warlock.js/ai-tools\": INSTALLED_WARLOCK_VERSION,\n },\r\n onExecuting: () => linkAiPackageImport(\"@warlock.js/ai-tools\"),\r\n};\r\n"],"mappings":";;;;AAGA,MAAa,iBAAoC;CAC/C,aACE;CACF,UAAU,CAAC,IAAI;CACf,cAAc,EACZ,wBAAwB,0BAC1B;CACA,mBAAmB,oBAAoB,sBAAsB;AAC/D"}
|
|
1
|
+
{"version":3,"file":"ai-tools.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/ai-tools.feature.ts"],"sourcesContent":["import { linkAiPackageImport } from \"./shared/link-ai-package-import\";\r\nimport { type FeatureDefinition, INSTALLED_WARLOCK_VERSION } from \"./types\";\n\r\nexport const aiToolsFeature: FeatureDefinition = {\r\n description:\r\n \"Installs @warlock.js/ai-tools — ready-made agent tools (web search, fetch, HTTP, calculator, date-time) + an MCP client/server, under ai.tools.* / ai.mcp (pulls the core ai package)\",\r\n requires: [\"ai\"],\r\n dependencies: {\r\n \"@warlock.js/ai-tools\": INSTALLED_WARLOCK_VERSION,\n },\r\n onExecuting: () => linkAiPackageImport(\"@warlock.js/ai-tools\"),\r\n};\r\n"],"mappings":";;;;AAGA,MAAa,iBAAoC;CAC/C,aACE;CACF,UAAU,CAAC,IAAI;CACf,cAAc,EACZ,wBAAwB,0BAC1B;CACA,mBAAmB,oBAAoB,sBAAsB;AAC/D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-workspace.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/ai-workspace.feature.ts"],"sourcesContent":["import { linkAiPackageImport } from \"./shared/link-ai-package-import\";\r\nimport { FeatureDefinition, INSTALLED_WARLOCK_VERSION } from \"./types\";\n\r\nexport const aiWorkspaceFeature: FeatureDefinition = {\r\n description:\r\n \"Installs @warlock.js/ai-workspace — a policy-jailed filesystem + shell workspace for coding agents, as ai.workspace (pulls the core ai package)\",\r\n requires: [\"ai\"],\r\n dependencies: {\r\n \"@warlock.js/ai-workspace\": INSTALLED_WARLOCK_VERSION,\n },\r\n onExecuting: () => linkAiPackageImport(\"@warlock.js/ai-workspace\"),\r\n};\r\n"],"mappings":";;;;AAGA,MAAa,qBAAwC;CACnD,aACE;CACF,UAAU,CAAC,IAAI;CACf,cAAc,EACZ,4BAA4B,0BAC9B;CACA,mBAAmB,oBAAoB,0BAA0B;AACnE"}
|
|
1
|
+
{"version":3,"file":"ai-workspace.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/ai-workspace.feature.ts"],"sourcesContent":["import { linkAiPackageImport } from \"./shared/link-ai-package-import\";\r\nimport { type FeatureDefinition, INSTALLED_WARLOCK_VERSION } from \"./types\";\n\r\nexport const aiWorkspaceFeature: FeatureDefinition = {\r\n description:\r\n \"Installs @warlock.js/ai-workspace — a policy-jailed filesystem + shell workspace for coding agents, as ai.workspace (pulls the core ai package)\",\r\n requires: [\"ai\"],\r\n dependencies: {\r\n \"@warlock.js/ai-workspace\": INSTALLED_WARLOCK_VERSION,\n },\r\n onExecuting: () => linkAiPackageImport(\"@warlock.js/ai-workspace\"),\r\n};\r\n"],"mappings":";;;;AAGA,MAAa,qBAAwC;CACnD,aACE;CACF,UAAU,CAAC,IAAI;CACf,cAAc,EACZ,4BAA4B,0BAC9B;CACA,mBAAmB,oBAAoB,0BAA0B;AACnE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/ai.feature.ts"],"sourcesContent":["import { aiConfigStub } from \"../stubs\";\r\nimport { FeatureDefinition, INSTALLED_WARLOCK_VERSION } from \"./types\";\n\r\nexport const aiFeature: FeatureDefinition = {\r\n description: \"Installs @warlock.js/ai — the core AI toolkit (agents, tools, workflows)\",\r\n dependencies: {\r\n \"@warlock.js/ai\": INSTALLED_WARLOCK_VERSION,\n },\r\n ejectConfig: {\r\n content: aiConfigStub,\r\n name: \"ai\",\r\n },\r\n};\r\n"],"mappings":";;;;AAGA,MAAa,YAA+B;CAC1C,aAAa;CACb,cAAc,EACZ,kBAAkB,0BACpB;CACA,aAAa;EACX,SAAS;EACT,MAAM;CACR;AACF"}
|
|
1
|
+
{"version":3,"file":"ai.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/ai.feature.ts"],"sourcesContent":["import { aiConfigStub } from \"../stubs\";\r\nimport { type FeatureDefinition, INSTALLED_WARLOCK_VERSION } from \"./types\";\n\r\nexport const aiFeature: FeatureDefinition = {\r\n description: \"Installs @warlock.js/ai — the core AI toolkit (agents, tools, workflows)\",\r\n dependencies: {\r\n \"@warlock.js/ai\": INSTALLED_WARLOCK_VERSION,\n },\r\n ejectConfig: {\r\n content: aiConfigStub,\r\n name: \"ai\",\r\n },\r\n};\r\n"],"mappings":";;;;AAGA,MAAa,YAA+B;CAC1C,aAAa;CACb,cAAc,EACZ,kBAAkB,0BACpB;CACA,aAAa;EACX,SAAS;EACT,MAAM;CACR;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"herald.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/herald.feature.ts"],"sourcesContent":["import { communicatorsConfigStub } from \"../stubs\";\r\nimport { FeatureDefinition, INSTALLED_WARLOCK_VERSION } from \"./types\";\n\r\nexport const heraldFeature: FeatureDefinition = {\r\n description: \"Installs herald for message broker (Herald Package)\",\r\n dependencies: {\r\n \"@warlock.js/herald\": INSTALLED_WARLOCK_VERSION,\n amqplib: \"^0.10.0\",\r\n },\r\n devDependencies: {\r\n \"@types/amqplib\": \"^0.10.0\",\r\n },\r\n ejectConfig: {\r\n content: communicatorsConfigStub,\r\n name: \"herald\",\r\n },\r\n};\r\n"],"mappings":";;;;AAGA,MAAa,gBAAmC;CAC9C,aAAa;CACb,cAAc;EACZ,sBAAsB;EACtB,SAAS;CACX;CACA,iBAAiB,EACf,kBAAkB,UACpB;CACA,aAAa;EACX,SAAS;EACT,MAAM;CACR;AACF"}
|
|
1
|
+
{"version":3,"file":"herald.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/herald.feature.ts"],"sourcesContent":["import { communicatorsConfigStub } from \"../stubs\";\r\nimport { type FeatureDefinition, INSTALLED_WARLOCK_VERSION } from \"./types\";\n\r\nexport const heraldFeature: FeatureDefinition = {\r\n description: \"Installs herald for message broker (Herald Package)\",\r\n dependencies: {\r\n \"@warlock.js/herald\": INSTALLED_WARLOCK_VERSION,\n amqplib: \"^0.10.0\",\r\n },\r\n devDependencies: {\r\n \"@types/amqplib\": \"^0.10.0\",\r\n },\r\n ejectConfig: {\r\n content: communicatorsConfigStub,\r\n name: \"herald\",\r\n },\r\n};\r\n"],"mappings":";;;;AAGA,MAAa,gBAAmC;CAC9C,aAAa;CACb,cAAc;EACZ,sBAAsB;EACtB,SAAS;CACX;CACA,iBAAiB,EACf,kBAAkB,UACpB;CACA,aAAa;EACX,SAAS;EACT,MAAM;CACR;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/image.feature.ts"],"sourcesContent":["import { FeatureDefinition } from \"./types\";\r\n\r\nexport const imageFeature: FeatureDefinition = {\r\n description: \"Installs sharp for image processing\",\r\n dependencies: {\r\n sharp: \"^0.34.5\",\r\n },\r\n};\r\n"],"mappings":";AAEA,MAAa,eAAkC;CAC7C,aAAa;CACb,cAAc,EACZ,OAAO,UACT;AACF"}
|
|
1
|
+
{"version":3,"file":"image.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/image.feature.ts"],"sourcesContent":["import type { FeatureDefinition } from \"./types\";\r\n\r\nexport const imageFeature: FeatureDefinition = {\r\n description: \"Installs sharp for image processing\",\r\n dependencies: {\r\n sharp: \"^0.34.5\",\r\n },\r\n};\r\n"],"mappings":";AAEA,MAAa,eAAkC;CAC7C,aAAa;CACb,cAAc,EACZ,OAAO,UACT;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/index.ts"],"sourcesContent":["import { accessFeature } from \"./access.feature\";\r\nimport { aiAnthropicFeature } from \"./ai-anthropic.feature\";\r\nimport { aiBedrockFeature } from \"./ai-bedrock.feature\";\r\nimport { aiGoogleFeature } from \"./ai-google.feature\";\r\nimport { aiOllamaFeature } from \"./ai-ollama.feature\";\r\nimport { aiOpenaiFeature } from \"./ai-openai.feature\";\r\nimport { aiPanopticFeature } from \"./ai-panoptic.feature\";\r\nimport { aiToolsFeature } from \"./ai-tools.feature\";\r\nimport { aiWorkspaceFeature } from \"./ai-workspace.feature\";\r\nimport { aiFeature } from \"./ai.feature\";\r\nimport { heraldFeature } from \"./herald.feature\";\r\nimport { imageFeature } from \"./image.feature\";\r\nimport { mailFeature } from \"./mail.feature\";\r\nimport { mongodbFeature } from \"./mongodb.feature\";\r\nimport { mysqlFeature } from \"./mysql.feature\";\r\nimport { notificationsFeature } from \"./notifications.feature\";\r\nimport { postgresFeature } from \"./postgres.feature\";\r\nimport { reactEmailFeature } from \"./react-email.feature\";\r\nimport { reactFeature } from \"./react.feature\";\r\nimport { redisFeature } from \"./redis.feature\";\r\nimport { s3Feature } from \"./s3.feature\";\r\nimport { schedulerFeature } from \"./scheduler.feature\";\r\nimport { sesFeature } from \"./ses.feature\";\r\nimport { shadcnFeature } from \"./shadcn.feature\";\r\nimport { socketFeature } from \"./socket.feature\";\r\nimport { tailwindFeature } from \"./tailwind.feature\";\r\nimport { testFeature } from \"./test.feature\";\r\nimport { FeatureDefinition } from \"./types\";\r\nimport { webFeature } from \"./web.feature\";\r\n\r\nexport type { FeatureDefinition } from \"./types\";\r\n\r\n/**\r\n * The feature registry `warlock add` dispatches against.\r\n *\r\n * This file is an INDEX, nothing more: every entry lives in its own module\r\n * alongside the `onExecuting` body it runs. Key order is load-bearing — it is\r\n * the order `--list` prints and the order the \"not allowed\" error lists — so\r\n * add new features in the place they should appear, not alphabetically.\r\n */\r\nexport const featuresMap: Record<string, FeatureDefinition> = {\r\n \"react-email\": reactEmailFeature,\r\n react: reactFeature,\r\n image: imageFeature,\r\n mail: mailFeature,\r\n ses: sesFeature,\r\n mongodb: mongodbFeature,\r\n scheduler: schedulerFeature,\r\n // swagger / postman intentionally omitted — those packages do not exist yet;\r\n // they will ship together in the unified @warlock.js/api-docs package.\r\n postgres: postgresFeature,\r\n mysql: mysqlFeature,\r\n redis: redisFeature,\r\n s3: s3Feature,\r\n test: testFeature,\r\n web: webFeature,\r\n // Directly after `web`, and only there: it `requires` it, it is useless\r\n // without it, and a reader scanning `--list` for the page stack should meet\r\n // the two together rather than find styling filed between queues and sockets.\r\n tailwind: tailwindFeature,\r\n // Immediately after `tailwind`, for the same reason `tailwind` follows `web`:\r\n // it `requires` it, it appends to the stylesheet that feature creates, and the\r\n // three of them are one stack a reader should meet in build order.\r\n shadcn: shadcnFeature,\r\n herald: heraldFeature,\r\n socket: socketFeature,\r\n notifications: notificationsFeature,\r\n access: accessFeature,\r\n ai: aiFeature,\r\n \"ai-openai\": aiOpenaiFeature,\r\n \"ai-google\": aiGoogleFeature,\r\n \"ai-anthropic\": aiAnthropicFeature,\r\n \"ai-bedrock\": aiBedrockFeature,\r\n \"ai-ollama\": aiOllamaFeature,\r\n \"ai-tools\": aiToolsFeature,\r\n \"ai-panoptic\": aiPanopticFeature,\r\n \"ai-workspace\": aiWorkspaceFeature,\r\n};\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,MAAa,cAAiD;CAC5D,eAAe;CACf,OAAO;CACP,OAAO;CACP,MAAM;CACN,KAAK;CACL,SAAS;CACT,WAAW;CAGX,UAAU;CACV,OAAO;CACP,OAAO;CACP,IAAI;CACJ,MAAM;CACN,KAAK;CAIL,UAAU;CAIV,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,eAAe;CACf,QAAQ;CACR,IAAI;CACJ,aAAa;CACb,aAAa;CACb,gBAAgB;CAChB,cAAc;CACd,aAAa;CACb,YAAY;CACZ,eAAe;CACf,gBAAgB;AAClB"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/index.ts"],"sourcesContent":["import { accessFeature } from \"./access.feature\";\r\nimport { aiAnthropicFeature } from \"./ai-anthropic.feature\";\r\nimport { aiBedrockFeature } from \"./ai-bedrock.feature\";\r\nimport { aiGoogleFeature } from \"./ai-google.feature\";\r\nimport { aiOllamaFeature } from \"./ai-ollama.feature\";\r\nimport { aiOpenaiFeature } from \"./ai-openai.feature\";\r\nimport { aiPanopticFeature } from \"./ai-panoptic.feature\";\r\nimport { aiToolsFeature } from \"./ai-tools.feature\";\r\nimport { aiWorkspaceFeature } from \"./ai-workspace.feature\";\r\nimport { aiFeature } from \"./ai.feature\";\r\nimport { heraldFeature } from \"./herald.feature\";\r\nimport { imageFeature } from \"./image.feature\";\r\nimport { mailFeature } from \"./mail.feature\";\r\nimport { mongodbFeature } from \"./mongodb.feature\";\r\nimport { mysqlFeature } from \"./mysql.feature\";\r\nimport { notificationsFeature } from \"./notifications.feature\";\r\nimport { postgresFeature } from \"./postgres.feature\";\r\nimport { reactEmailFeature } from \"./react-email.feature\";\r\nimport { reactFeature } from \"./react.feature\";\r\nimport { redisFeature } from \"./redis.feature\";\r\nimport { s3Feature } from \"./s3.feature\";\r\nimport { schedulerFeature } from \"./scheduler.feature\";\r\nimport { sesFeature } from \"./ses.feature\";\r\nimport { shadcnFeature } from \"./shadcn.feature\";\r\nimport { socketFeature } from \"./socket.feature\";\r\nimport { tailwindFeature } from \"./tailwind.feature\";\r\nimport { testFeature } from \"./test.feature\";\r\nimport type { FeatureDefinition } from \"./types\";\r\nimport { webFeature } from \"./web.feature\";\r\n\r\nexport type { FeatureDefinition } from \"./types\";\r\n\r\n/**\r\n * The feature registry `warlock add` dispatches against.\r\n *\r\n * This file is an INDEX, nothing more: every entry lives in its own module\r\n * alongside the `onExecuting` body it runs. Key order is load-bearing — it is\r\n * the order `--list` prints and the order the \"not allowed\" error lists — so\r\n * add new features in the place they should appear, not alphabetically.\r\n */\r\nexport const featuresMap: Record<string, FeatureDefinition> = {\r\n \"react-email\": reactEmailFeature,\r\n react: reactFeature,\r\n image: imageFeature,\r\n mail: mailFeature,\r\n ses: sesFeature,\r\n mongodb: mongodbFeature,\r\n scheduler: schedulerFeature,\r\n // swagger / postman intentionally omitted — those packages do not exist yet;\r\n // they will ship together in the unified @warlock.js/api-docs package.\r\n postgres: postgresFeature,\r\n mysql: mysqlFeature,\r\n redis: redisFeature,\r\n s3: s3Feature,\r\n test: testFeature,\r\n web: webFeature,\r\n // Directly after `web`, and only there: it `requires` it, it is useless\r\n // without it, and a reader scanning `--list` for the page stack should meet\r\n // the two together rather than find styling filed between queues and sockets.\r\n tailwind: tailwindFeature,\r\n // Immediately after `tailwind`, for the same reason `tailwind` follows `web`:\r\n // it `requires` it, it appends to the stylesheet that feature creates, and the\r\n // three of them are one stack a reader should meet in build order.\r\n shadcn: shadcnFeature,\r\n herald: heraldFeature,\r\n socket: socketFeature,\r\n notifications: notificationsFeature,\r\n access: accessFeature,\r\n ai: aiFeature,\r\n \"ai-openai\": aiOpenaiFeature,\r\n \"ai-google\": aiGoogleFeature,\r\n \"ai-anthropic\": aiAnthropicFeature,\r\n \"ai-bedrock\": aiBedrockFeature,\r\n \"ai-ollama\": aiOllamaFeature,\r\n \"ai-tools\": aiToolsFeature,\r\n \"ai-panoptic\": aiPanopticFeature,\r\n \"ai-workspace\": aiWorkspaceFeature,\r\n};\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,MAAa,cAAiD;CAC5D,eAAe;CACf,OAAO;CACP,OAAO;CACP,MAAM;CACN,KAAK;CACL,SAAS;CACT,WAAW;CAGX,UAAU;CACV,OAAO;CACP,OAAO;CACP,IAAI;CACJ,MAAM;CACN,KAAK;CAIL,UAAU;CAIV,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,eAAe;CACf,QAAQ;CACR,IAAI;CACJ,aAAa;CACb,aAAa;CACb,gBAAgB;CAChB,cAAc;CACd,aAAa;CACb,YAAY;CACZ,eAAe;CACf,gBAAgB;AAClB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mail.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/mail.feature.ts"],"sourcesContent":["import { FeatureDefinition } from \"./types\";\r\n\r\nexport const mailFeature: FeatureDefinition = {\r\n description: \"Installs nodemailer for sending emails\",\r\n dependencies: {\r\n nodemailer: \"^8.0.5\",\r\n },\r\n devDependencies: {\r\n \"@types/nodemailer\": \"^8.0.0\",\r\n },\r\n};\r\n"],"mappings":";AAEA,MAAa,cAAiC;CAC5C,aAAa;CACb,cAAc,EACZ,YAAY,SACd;CACA,iBAAiB,EACf,qBAAqB,SACvB;AACF"}
|
|
1
|
+
{"version":3,"file":"mail.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/mail.feature.ts"],"sourcesContent":["import type { FeatureDefinition } from \"./types\";\r\n\r\nexport const mailFeature: FeatureDefinition = {\r\n description: \"Installs nodemailer for sending emails\",\r\n dependencies: {\r\n nodemailer: \"^8.0.5\",\r\n },\r\n devDependencies: {\r\n \"@types/nodemailer\": \"^8.0.0\",\r\n },\r\n};\r\n"],"mappings":";AAEA,MAAa,cAAiC;CAC5C,aAAa;CACb,cAAc,EACZ,YAAY,SACd;CACA,iBAAiB,EACf,qBAAqB,SACvB;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mongodb.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/mongodb.feature.ts"],"sourcesContent":["import { FeatureDefinition } from \"./types\";\r\n\r\nexport const mongodbFeature: FeatureDefinition = {\r\n description: \"Installs mongodb driver for database driver (Cascade Package)\",\r\n dependencies: {\r\n mongodb: \"^7.0.0\",\r\n },\r\n};\r\n"],"mappings":";AAEA,MAAa,iBAAoC;CAC/C,aAAa;CACb,cAAc,EACZ,SAAS,SACX;AACF"}
|
|
1
|
+
{"version":3,"file":"mongodb.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/mongodb.feature.ts"],"sourcesContent":["import type { FeatureDefinition } from \"./types\";\r\n\r\nexport const mongodbFeature: FeatureDefinition = {\r\n description: \"Installs mongodb driver for database driver (Cascade Package)\",\r\n dependencies: {\r\n mongodb: \"^7.0.0\",\r\n },\r\n};\r\n"],"mappings":";AAEA,MAAa,iBAAoC;CAC/C,aAAa;CACb,cAAc,EACZ,SAAS,SACX;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mysql.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/mysql.feature.ts"],"sourcesContent":["import { FeatureDefinition } from \"./types\";\r\n\r\nexport const mysqlFeature: FeatureDefinition = {\r\n description: \"Installs mysql2 for MySQL database driver (Cascade Package)\",\r\n dependencies: {\r\n mysql2: \"^3.5.0\",\r\n },\r\n};\r\n"],"mappings":";AAEA,MAAa,eAAkC;CAC7C,aAAa;CACb,cAAc,EACZ,QAAQ,SACV;AACF"}
|
|
1
|
+
{"version":3,"file":"mysql.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/mysql.feature.ts"],"sourcesContent":["import type { FeatureDefinition } from \"./types\";\r\n\r\nexport const mysqlFeature: FeatureDefinition = {\r\n description: \"Installs mysql2 for MySQL database driver (Cascade Package)\",\r\n dependencies: {\r\n mysql2: \"^3.5.0\",\r\n },\r\n};\r\n"],"mappings":";AAEA,MAAa,eAAkC;CAC7C,aAAa;CACb,cAAc,EACZ,QAAQ,SACV;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notifications.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/notifications.feature.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport { ensureDirectoryAsync, fileExistsAsync, putFileAsync } from \"@warlock.js/fs\";\r\nimport { CommandActionData } from \"../../commands/types\";\r\nimport { srcPath } from \"../../utils\";\r\nimport {\r\n notificationControllersStub,\r\n notificationMigrationStub,\r\n notificationModelStub,\r\n notificationRoutesStub,\r\n notificationsConfigStub,\r\n} from \"../stubs\";\r\nimport { migrationTimestamp } from \"./shared/migration-timestamp\";\r\nimport { FeatureDefinition, INSTALLED_WARLOCK_VERSION } from \"./types\";\n\r\nasync function completeNotificationsInstallation(_options: CommandActionData) {\r\n const modelPath = srcPath(\"app/notifications/notification.model.ts\");\r\n\r\n // The model file is the sentinel for \"notifications already scaffolded\" —\r\n // its presence means the migration was created too (timestamped, so we must\r\n // not re-emit a duplicate on a second run).\r\n if (await fileExistsAsync(modelPath)) {\r\n console.log(\r\n `${colors.yellowBright(\"src/app/notifications\")} already scaffolded, skipping model + migration...`,\r\n );\r\n return;\r\n }\r\n\r\n // 1. Notification model — extends the package's DatabaseNotification base.\r\n await ensureDirectoryAsync(srcPath(\"app/notifications\"));\r\n await putFileAsync(modelPath, notificationModelStub);\r\n console.log(`${colors.green(\"✓\")} Created src/app/notifications/notification.model.ts`);\r\n\r\n // 2. Migration — timestamped MM-DD-YYYY_HH-MM-SS prefix so cascade infers its\r\n // createdAt and orders it deterministically (migrate-action discovers\r\n // src/app/*/migrations/*).\r\n await ensureDirectoryAsync(srcPath(\"app/notifications/migrations\"));\r\n\r\n const migrationFile = `${migrationTimestamp()}-notification.migration.ts`;\r\n\r\n await putFileAsync(\r\n srcPath(\"app/notifications/migrations\", migrationFile),\r\n notificationMigrationStub,\r\n );\r\n console.log(\r\n `${colors.green(\"✓\")} Created src/app/notifications/migrations/${migrationFile}`,\r\n );\r\n\r\n // 3. HTTP surface — the in-app read/dismiss endpoints (routes + controllers),\r\n // gated by authMiddleware. Delete if the app exposes notifications another way.\r\n await ensureDirectoryAsync(srcPath(\"app/notifications/controllers\"));\r\n await putFileAsync(\r\n srcPath(\"app/notifications/controllers/notifications.controller.ts\"),\r\n notificationControllersStub,\r\n );\r\n await putFileAsync(srcPath(\"app/notifications/routes.ts\"), notificationRoutesStub);\r\n console.log(`${colors.green(\"✓\")} Created src/app/notifications/routes.ts + controllers`);\r\n}\r\n\r\nexport const notificationsFeature: FeatureDefinition = {\r\n description:\r\n \"Installs @warlock.js/notifications — multi-channel notifications (mail + in-app database). Pulls the mail feature, ejects config/notifications.ts, and scaffolds the Notification model + migration plus the recipient-scoped read/dismiss routes + controllers into src/app/notifications\",\r\n // The ejected config wires a `mail` channel by default (needs nodemailer,\r\n // via the `mail` feature); the scaffolded routes are gated by\r\n // `authMiddleware`, so `@warlock.js/auth` is pulled in too.\r\n requires: [\"mail\"],\r\n dependencies: {\r\n \"@warlock.js/notifications\": INSTALLED_WARLOCK_VERSION,\n \"@warlock.js/auth\": INSTALLED_WARLOCK_VERSION,\n },\r\n ejectConfig: {\r\n content: notificationsConfigStub,\r\n name: \"notifications\",\r\n },\r\n onExecuting: completeNotificationsInstallation,\r\n};\r\n"],"mappings":";;;;;;;;;AAcA,eAAe,kCAAkC,UAA6B;CAC5E,MAAM,YAAY,QAAQ,yCAAyC;CAKnE,IAAI,MAAM,gBAAgB,SAAS,GAAG;EACpC,QAAQ,IACN,GAAG,OAAO,aAAa,uBAAuB,EAAE,mDAClD;EACA;CACF;CAGA,MAAM,qBAAqB,QAAQ,mBAAmB,CAAC;CACvD,MAAM,aAAa,WAAW,qBAAqB;CACnD,QAAQ,IAAI,GAAG,OAAO,MAAM,GAAG,EAAE,qDAAqD;CAKtF,MAAM,qBAAqB,QAAQ,8BAA8B,CAAC;CAElE,MAAM,gBAAgB,GAAG,mBAAmB,EAAE;CAE9C,MAAM,aACJ,QAAQ,gCAAgC,aAAa,GACrD,yBACF;CACA,QAAQ,IACN,GAAG,OAAO,MAAM,GAAG,EAAE,4CAA4C,eACnE;CAIA,MAAM,qBAAqB,QAAQ,+BAA+B,CAAC;CACnE,MAAM,aACJ,QAAQ,2DAA2D,GACnE,2BACF;CACA,MAAM,aAAa,QAAQ,6BAA6B,GAAG,sBAAsB;CACjF,QAAQ,IAAI,GAAG,OAAO,MAAM,GAAG,EAAE,uDAAuD;AAC1F;AAEA,MAAa,uBAA0C;CACrD,aACE;CAIF,UAAU,CAAC,MAAM;CACjB,cAAc;EACZ,6BAA6B;EAC7B,oBAAoB;CACtB;CACA,aAAa;EACX,SAAS;EACT,MAAM;CACR;CACA,aAAa;AACf"}
|
|
1
|
+
{"version":3,"file":"notifications.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/notifications.feature.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport { ensureDirectoryAsync, fileExistsAsync, putFileAsync } from \"@warlock.js/fs\";\r\nimport type { CommandActionData } from \"../../commands/types\";\r\nimport { srcPath } from \"../../utils\";\r\nimport {\r\n notificationControllersStub,\r\n notificationMigrationStub,\r\n notificationModelStub,\r\n notificationRoutesStub,\r\n notificationsConfigStub,\r\n} from \"../stubs\";\r\nimport { migrationTimestamp } from \"./shared/migration-timestamp\";\r\nimport { type FeatureDefinition, INSTALLED_WARLOCK_VERSION } from \"./types\";\n\r\nasync function completeNotificationsInstallation(_options: CommandActionData) {\r\n const modelPath = srcPath(\"app/notifications/notification.model.ts\");\r\n\r\n // The model file is the sentinel for \"notifications already scaffolded\" —\r\n // its presence means the migration was created too (timestamped, so we must\r\n // not re-emit a duplicate on a second run).\r\n if (await fileExistsAsync(modelPath)) {\r\n console.log(\r\n `${colors.yellowBright(\"src/app/notifications\")} already scaffolded, skipping model + migration...`,\r\n );\r\n return;\r\n }\r\n\r\n // 1. Notification model — extends the package's DatabaseNotification base.\r\n await ensureDirectoryAsync(srcPath(\"app/notifications\"));\r\n await putFileAsync(modelPath, notificationModelStub);\r\n console.log(`${colors.green(\"✓\")} Created src/app/notifications/notification.model.ts`);\r\n\r\n // 2. Migration — timestamped MM-DD-YYYY_HH-MM-SS prefix so cascade infers its\r\n // createdAt and orders it deterministically (migrate-action discovers\r\n // src/app/*/migrations/*).\r\n await ensureDirectoryAsync(srcPath(\"app/notifications/migrations\"));\r\n\r\n const migrationFile = `${migrationTimestamp()}-notification.migration.ts`;\r\n\r\n await putFileAsync(\r\n srcPath(\"app/notifications/migrations\", migrationFile),\r\n notificationMigrationStub,\r\n );\r\n console.log(\r\n `${colors.green(\"✓\")} Created src/app/notifications/migrations/${migrationFile}`,\r\n );\r\n\r\n // 3. HTTP surface — the in-app read/dismiss endpoints (routes + controllers),\r\n // gated by authMiddleware. Delete if the app exposes notifications another way.\r\n await ensureDirectoryAsync(srcPath(\"app/notifications/controllers\"));\r\n await putFileAsync(\r\n srcPath(\"app/notifications/controllers/notifications.controller.ts\"),\r\n notificationControllersStub,\r\n );\r\n await putFileAsync(srcPath(\"app/notifications/routes.ts\"), notificationRoutesStub);\r\n console.log(`${colors.green(\"✓\")} Created src/app/notifications/routes.ts + controllers`);\r\n}\r\n\r\nexport const notificationsFeature: FeatureDefinition = {\r\n description:\r\n \"Installs @warlock.js/notifications — multi-channel notifications (mail + in-app database). Pulls the mail feature, ejects config/notifications.ts, and scaffolds the Notification model + migration plus the recipient-scoped read/dismiss routes + controllers into src/app/notifications\",\r\n // The ejected config wires a `mail` channel by default (needs nodemailer,\r\n // via the `mail` feature); the scaffolded routes are gated by\r\n // `authMiddleware`, so `@warlock.js/auth` is pulled in too.\r\n requires: [\"mail\"],\r\n dependencies: {\r\n \"@warlock.js/notifications\": INSTALLED_WARLOCK_VERSION,\n \"@warlock.js/auth\": INSTALLED_WARLOCK_VERSION,\n },\r\n ejectConfig: {\r\n content: notificationsConfigStub,\r\n name: \"notifications\",\r\n },\r\n onExecuting: completeNotificationsInstallation,\r\n};\r\n"],"mappings":";;;;;;;;;AAcA,eAAe,kCAAkC,UAA6B;CAC5E,MAAM,YAAY,QAAQ,yCAAyC;CAKnE,IAAI,MAAM,gBAAgB,SAAS,GAAG;EACpC,QAAQ,IACN,GAAG,OAAO,aAAa,uBAAuB,EAAE,mDAClD;EACA;CACF;CAGA,MAAM,qBAAqB,QAAQ,mBAAmB,CAAC;CACvD,MAAM,aAAa,WAAW,qBAAqB;CACnD,QAAQ,IAAI,GAAG,OAAO,MAAM,GAAG,EAAE,qDAAqD;CAKtF,MAAM,qBAAqB,QAAQ,8BAA8B,CAAC;CAElE,MAAM,gBAAgB,GAAG,mBAAmB,EAAE;CAE9C,MAAM,aACJ,QAAQ,gCAAgC,aAAa,GACrD,yBACF;CACA,QAAQ,IACN,GAAG,OAAO,MAAM,GAAG,EAAE,4CAA4C,eACnE;CAIA,MAAM,qBAAqB,QAAQ,+BAA+B,CAAC;CACnE,MAAM,aACJ,QAAQ,2DAA2D,GACnE,2BACF;CACA,MAAM,aAAa,QAAQ,6BAA6B,GAAG,sBAAsB;CACjF,QAAQ,IAAI,GAAG,OAAO,MAAM,GAAG,EAAE,uDAAuD;AAC1F;AAEA,MAAa,uBAA0C;CACrD,aACE;CAIF,UAAU,CAAC,MAAM;CACjB,cAAc;EACZ,6BAA6B;EAC7B,oBAAoB;CACtB;CACA,aAAa;EACX,SAAS;EACT,MAAM;CACR;CACA,aAAa;AACf"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgres.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/postgres.feature.ts"],"sourcesContent":["import { FeatureDefinition } from \"./types\";\r\n\r\nexport const postgresFeature: FeatureDefinition = {\r\n description: \"Installs pg for Postgres database (Cascade Package)\",\r\n dependencies: {\r\n pg: \"^8.11.0\",\r\n },\r\n};\r\n"],"mappings":";AAEA,MAAa,kBAAqC;CAChD,aAAa;CACb,cAAc,EACZ,IAAI,UACN;AACF"}
|
|
1
|
+
{"version":3,"file":"postgres.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/postgres.feature.ts"],"sourcesContent":["import type { FeatureDefinition } from \"./types\";\r\n\r\nexport const postgresFeature: FeatureDefinition = {\r\n description: \"Installs pg for Postgres database (Cascade Package)\",\r\n dependencies: {\r\n pg: \"^8.11.0\",\r\n },\r\n};\r\n"],"mappings":";AAEA,MAAa,kBAAqC;CAChD,aAAa;CACb,cAAc,EACZ,IAAI,UACN;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react-email.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/react-email.feature.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport {\r\n ensureDirectoryAsync,\r\n fileExistsAsync,\r\n getJsonFileAsync,\r\n putFileAsync,\r\n putJsonFileAsync,\r\n} from \"@warlock.js/fs\";\r\nimport { CommandActionData } from \"../../commands/types\";\r\nimport { rootPath } from \"../../utils\";\r\nimport { FeatureDefinition } from \"./types\";\r\n\r\n/**\r\n * The part of a project `tsconfig.json` this action patches.\r\n */\r\ntype ProjectTsConfig = {\r\n include?: string[];\r\n};\r\n\r\nasync function completeReactEmailInstallation(_options: CommandActionData) {\r\n // 1. Create emails/ folder with a sample component\r\n const emailsFolderPath = rootPath(\"emails\");\r\n const sampleEmailPath = rootPath(\"emails/welcome-email.tsx\");\r\n\r\n if (!(await fileExistsAsync(sampleEmailPath))) {\r\n await ensureDirectoryAsync(emailsFolderPath);\r\n await putFileAsync(\r\n sampleEmailPath,\r\n `import { Body, Container, Head, Html, Text } from \"@react-email/components\";\r\nimport { Tailwind } from \"@react-email/tailwind\";\r\n\r\ninterface WelcomeEmailProps {\r\n name: string;\r\n}\r\n\r\n/**\r\n * Sample welcome email component.\r\n * Preview with: yarn email:preview\r\n */\r\nexport default function WelcomeEmail({ name }: WelcomeEmailProps) {\r\n return (\r\n <Html>\r\n <Head />\r\n <Tailwind>\r\n <Body className=\"bg-gray-100 font-sans\">\r\n <Container className=\"mx-auto max-w-xl py-8 px-4\">\r\n <Text className=\"text-2xl font-bold text-gray-900\">\r\n Welcome, {name}!\r\n </Text>\r\n <Text className=\"text-gray-600 mt-2\">\r\n You're all set. We're glad to have you on board.\r\n </Text>\r\n </Container>\r\n </Body>\r\n </Tailwind>\r\n </Html>\r\n );\r\n}\r\n`,\r\n );\r\n console.log(`${colors.green(\"✓\")} Created emails/welcome-email.tsx`);\r\n }\r\n\r\n // 2. Patch tsconfig.json — add \"emails\" to include if missing\r\n const tsconfigPath = rootPath(\"tsconfig.json\");\r\n const tsconfig = await getJsonFileAsync<ProjectTsConfig>(tsconfigPath);\r\n\r\n if (!tsconfig.include) {\r\n tsconfig.include = [];\r\n }\r\n\r\n if (!tsconfig.include.includes(\"emails\")) {\r\n tsconfig.include.push(\"emails\");\r\n await putJsonFileAsync(tsconfigPath, tsconfig);\r\n console.log(`${colors.green(\"✓\")} Added \"emails\" to tsconfig.json include`);\r\n }\r\n}\r\n\r\nexport const reactEmailFeature: FeatureDefinition = {\r\n description: \"Installs react-email for building email templates with React and Tailwind\",\r\n requires: [\"mail\", \"react\"],\r\n dependencies: {\r\n \"react-email\": \"^5.2.10\",\r\n \"@react-email/components\": \"^1.0.11\",\r\n \"@react-email/render\": \"^2.0.5\",\r\n \"@react-email/tailwind\": \"^2.0.7\",\r\n },\r\n devDependencies: {\r\n \"@react-email/preview-server\": \"5.2.10\",\r\n },\r\n script: {\r\n \"email:preview\": \"npx react-email dev\",\r\n },\r\n onExecuting: completeReactEmailInstallation,\r\n};\r\n"],"mappings":";;;;;;AAmBA,eAAe,+BAA+B,UAA6B;CAEzE,MAAM,mBAAmB,SAAS,QAAQ;CAC1C,MAAM,kBAAkB,SAAS,0BAA0B;CAE3D,IAAI,CAAE,MAAM,gBAAgB,eAAe,GAAI;EAC7C,MAAM,qBAAqB,gBAAgB;EAC3C,MAAM,aACJ,iBACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+BF;EACA,QAAQ,IAAI,GAAG,OAAO,MAAM,GAAG,EAAE,kCAAkC;CACrE;CAGA,MAAM,eAAe,SAAS,eAAe;CAC7C,MAAM,WAAW,MAAM,iBAAkC,YAAY;CAErE,IAAI,CAAC,SAAS,SACZ,SAAS,UAAU,CAAC;CAGtB,IAAI,CAAC,SAAS,QAAQ,SAAS,QAAQ,GAAG;EACxC,SAAS,QAAQ,KAAK,QAAQ;EAC9B,MAAM,iBAAiB,cAAc,QAAQ;EAC7C,QAAQ,IAAI,GAAG,OAAO,MAAM,GAAG,EAAE,yCAAyC;CAC5E;AACF;AAEA,MAAa,oBAAuC;CAClD,aAAa;CACb,UAAU,CAAC,QAAQ,OAAO;CAC1B,cAAc;EACZ,eAAe;EACf,2BAA2B;EAC3B,uBAAuB;EACvB,yBAAyB;CAC3B;CACA,iBAAiB,EACf,+BAA+B,SACjC;CACA,QAAQ,EACN,iBAAiB,sBACnB;CACA,aAAa;AACf"}
|
|
1
|
+
{"version":3,"file":"react-email.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/react-email.feature.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport {\r\n ensureDirectoryAsync,\r\n fileExistsAsync,\r\n getJsonFileAsync,\r\n putFileAsync,\r\n putJsonFileAsync,\r\n} from \"@warlock.js/fs\";\r\nimport type { CommandActionData } from \"../../commands/types\";\r\nimport { rootPath } from \"../../utils\";\r\nimport type { FeatureDefinition } from \"./types\";\r\n\r\n/**\r\n * The part of a project `tsconfig.json` this action patches.\r\n */\r\ntype ProjectTsConfig = {\r\n include?: string[];\r\n};\r\n\r\nasync function completeReactEmailInstallation(_options: CommandActionData) {\r\n // 1. Create emails/ folder with a sample component\r\n const emailsFolderPath = rootPath(\"emails\");\r\n const sampleEmailPath = rootPath(\"emails/welcome-email.tsx\");\r\n\r\n if (!(await fileExistsAsync(sampleEmailPath))) {\r\n await ensureDirectoryAsync(emailsFolderPath);\r\n await putFileAsync(\r\n sampleEmailPath,\r\n `import { Body, Container, Head, Html, Text } from \"@react-email/components\";\r\nimport { Tailwind } from \"@react-email/tailwind\";\r\n\r\ninterface WelcomeEmailProps {\r\n name: string;\r\n}\r\n\r\n/**\r\n * Sample welcome email component.\r\n * Preview with: yarn email:preview\r\n */\r\nexport default function WelcomeEmail({ name }: WelcomeEmailProps) {\r\n return (\r\n <Html>\r\n <Head />\r\n <Tailwind>\r\n <Body className=\"bg-gray-100 font-sans\">\r\n <Container className=\"mx-auto max-w-xl py-8 px-4\">\r\n <Text className=\"text-2xl font-bold text-gray-900\">\r\n Welcome, {name}!\r\n </Text>\r\n <Text className=\"text-gray-600 mt-2\">\r\n You're all set. We're glad to have you on board.\r\n </Text>\r\n </Container>\r\n </Body>\r\n </Tailwind>\r\n </Html>\r\n );\r\n}\r\n`,\r\n );\r\n console.log(`${colors.green(\"✓\")} Created emails/welcome-email.tsx`);\r\n }\r\n\r\n // 2. Patch tsconfig.json — add \"emails\" to include if missing\r\n const tsconfigPath = rootPath(\"tsconfig.json\");\r\n const tsconfig = await getJsonFileAsync<ProjectTsConfig>(tsconfigPath);\r\n\r\n if (!tsconfig.include) {\r\n tsconfig.include = [];\r\n }\r\n\r\n if (!tsconfig.include.includes(\"emails\")) {\r\n tsconfig.include.push(\"emails\");\r\n await putJsonFileAsync(tsconfigPath, tsconfig);\r\n console.log(`${colors.green(\"✓\")} Added \"emails\" to tsconfig.json include`);\r\n }\r\n}\r\n\r\nexport const reactEmailFeature: FeatureDefinition = {\r\n description: \"Installs react-email for building email templates with React and Tailwind\",\r\n requires: [\"mail\", \"react\"],\r\n dependencies: {\r\n \"react-email\": \"^5.2.10\",\r\n \"@react-email/components\": \"^1.0.11\",\r\n \"@react-email/render\": \"^2.0.5\",\r\n \"@react-email/tailwind\": \"^2.0.7\",\r\n },\r\n devDependencies: {\r\n \"@react-email/preview-server\": \"5.2.10\",\r\n },\r\n script: {\r\n \"email:preview\": \"npx react-email dev\",\r\n },\r\n onExecuting: completeReactEmailInstallation,\r\n};\r\n"],"mappings":";;;;;;AAmBA,eAAe,+BAA+B,UAA6B;CAEzE,MAAM,mBAAmB,SAAS,QAAQ;CAC1C,MAAM,kBAAkB,SAAS,0BAA0B;CAE3D,IAAI,CAAE,MAAM,gBAAgB,eAAe,GAAI;EAC7C,MAAM,qBAAqB,gBAAgB;EAC3C,MAAM,aACJ,iBACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+BF;EACA,QAAQ,IAAI,GAAG,OAAO,MAAM,GAAG,EAAE,kCAAkC;CACrE;CAGA,MAAM,eAAe,SAAS,eAAe;CAC7C,MAAM,WAAW,MAAM,iBAAkC,YAAY;CAErE,IAAI,CAAC,SAAS,SACZ,SAAS,UAAU,CAAC;CAGtB,IAAI,CAAC,SAAS,QAAQ,SAAS,QAAQ,GAAG;EACxC,SAAS,QAAQ,KAAK,QAAQ;EAC9B,MAAM,iBAAiB,cAAc,QAAQ;EAC7C,QAAQ,IAAI,GAAG,OAAO,MAAM,GAAG,EAAE,yCAAyC;CAC5E;AACF;AAEA,MAAa,oBAAuC;CAClD,aAAa;CACb,UAAU,CAAC,QAAQ,OAAO;CAC1B,cAAc;EACZ,eAAe;EACf,2BAA2B;EAC3B,uBAAuB;EACvB,yBAAyB;CAC3B;CACA,iBAAiB,EACf,+BAA+B,SACjC;CACA,QAAQ,EACN,iBAAiB,sBACnB;CACA,aAAa;AACf"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/react.feature.ts"],"sourcesContent":["import { FeatureDefinition } from \"./types\";\r\n\r\nexport const reactFeature: FeatureDefinition = {\r\n description:\r\n \"Installs React and React dom for rendering React components (non-interactive), useful for sending mails and generating HTML\",\r\n dependencies: {\r\n react: \"^19.2.3\",\r\n \"react-dom\": \"^19.2.3\",\r\n },\r\n devDependencies: {\r\n \"@types/react\": \"^19.2.7\",\r\n \"@types/react-dom\": \"^19.2.3\",\r\n },\r\n};\r\n"],"mappings":";AAEA,MAAa,eAAkC;CAC7C,aACE;CACF,cAAc;EACZ,OAAO;EACP,aAAa;CACf;CACA,iBAAiB;EACf,gBAAgB;EAChB,oBAAoB;CACtB;AACF"}
|
|
1
|
+
{"version":3,"file":"react.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/react.feature.ts"],"sourcesContent":["import type { FeatureDefinition } from \"./types\";\r\n\r\nexport const reactFeature: FeatureDefinition = {\r\n description:\r\n \"Installs React and React dom for rendering React components (non-interactive), useful for sending mails and generating HTML\",\r\n dependencies: {\r\n react: \"^19.2.3\",\r\n \"react-dom\": \"^19.2.3\",\r\n },\r\n devDependencies: {\r\n \"@types/react\": \"^19.2.7\",\r\n \"@types/react-dom\": \"^19.2.3\",\r\n },\r\n};\r\n"],"mappings":";AAEA,MAAa,eAAkC;CAC7C,aACE;CACF,cAAc;EACZ,OAAO;EACP,aAAa;CACf;CACA,iBAAiB;EACf,gBAAgB;EAChB,oBAAoB;CACtB;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redis.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/redis.feature.ts"],"sourcesContent":["import { FeatureDefinition } from \"./types\";\r\n\r\nexport const redisFeature: FeatureDefinition = {\r\n description: \"Installs redis for Redis cache driver (Cache Package)\",\r\n dependencies: {\r\n redis: \"^4.6.13\",\r\n },\r\n};\r\n"],"mappings":";AAEA,MAAa,eAAkC;CAC7C,aAAa;CACb,cAAc,EACZ,OAAO,UACT;AACF"}
|
|
1
|
+
{"version":3,"file":"redis.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/redis.feature.ts"],"sourcesContent":["import type { FeatureDefinition } from \"./types\";\r\n\r\nexport const redisFeature: FeatureDefinition = {\r\n description: \"Installs redis for Redis cache driver (Cache Package)\",\r\n dependencies: {\r\n redis: \"^4.6.13\",\r\n },\r\n};\r\n"],"mappings":";AAEA,MAAa,eAAkC;CAC7C,aAAa;CACb,cAAc,EACZ,OAAO,UACT;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"s3.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/s3.feature.ts"],"sourcesContent":["import { FeatureDefinition } from \"./types\";\r\n\r\nexport const s3Feature: FeatureDefinition = {\r\n description: \"Installs AWS SDK for Cloud storage (Storage Package)\",\r\n dependencies: {\r\n \"@aws-sdk/client-s3\": \"^3.955.0\",\r\n \"@aws-sdk/lib-storage\": \"^3.955.0\",\r\n \"@aws-sdk/s3-request-presigner\": \"^3.955.0\",\r\n },\r\n};\r\n"],"mappings":";AAEA,MAAa,YAA+B;CAC1C,aAAa;CACb,cAAc;EACZ,sBAAsB;EACtB,wBAAwB;EACxB,iCAAiC;CACnC;AACF"}
|
|
1
|
+
{"version":3,"file":"s3.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/s3.feature.ts"],"sourcesContent":["import type { FeatureDefinition } from \"./types\";\r\n\r\nexport const s3Feature: FeatureDefinition = {\r\n description: \"Installs AWS SDK for Cloud storage (Storage Package)\",\r\n dependencies: {\r\n \"@aws-sdk/client-s3\": \"^3.955.0\",\r\n \"@aws-sdk/lib-storage\": \"^3.955.0\",\r\n \"@aws-sdk/s3-request-presigner\": \"^3.955.0\",\r\n },\r\n};\r\n"],"mappings":";AAEA,MAAa,YAA+B;CAC1C,aAAa;CACb,cAAc;EACZ,sBAAsB;EACtB,wBAAwB;EACxB,iCAAiC;CACnC;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scheduler.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/scheduler.feature.ts"],"sourcesContent":["import { FeatureDefinition, INSTALLED_WARLOCK_VERSION } from \"./types\";\n\r\nexport const schedulerFeature: FeatureDefinition = {\r\n description: \"Installs warlock scheduler for scheduling tasks\",\r\n dependencies: {\r\n \"@warlock.js/scheduler\": INSTALLED_WARLOCK_VERSION,\n },\r\n};\r\n"],"mappings":";;;AAEA,MAAa,mBAAsC;CACjD,aAAa;CACb,cAAc,EACZ,yBAAyB,0BAC3B;AACF"}
|
|
1
|
+
{"version":3,"file":"scheduler.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/scheduler.feature.ts"],"sourcesContent":["import { type FeatureDefinition, INSTALLED_WARLOCK_VERSION } from \"./types\";\n\r\nexport const schedulerFeature: FeatureDefinition = {\r\n description: \"Installs warlock scheduler for scheduling tasks\",\r\n dependencies: {\r\n \"@warlock.js/scheduler\": INSTALLED_WARLOCK_VERSION,\n },\r\n};\r\n"],"mappings":";;;AAEA,MAAa,mBAAsC;CACjD,aAAa;CACb,cAAc,EACZ,yBAAyB,0BAC3B;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ses.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/ses.feature.ts"],"sourcesContent":["import { FeatureDefinition } from \"./types\";\r\n\r\nexport const sesFeature: FeatureDefinition = {\r\n description: \"Installs AWS SES SDK for sending emails via Amazon SES\",\r\n dependencies: {\r\n \"@aws-sdk/client-sesv2\": \"^3.1025.0\",\r\n },\r\n};\r\n"],"mappings":";AAEA,MAAa,aAAgC;CAC3C,aAAa;CACb,cAAc,EACZ,yBAAyB,YAC3B;AACF"}
|
|
1
|
+
{"version":3,"file":"ses.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/ses.feature.ts"],"sourcesContent":["import type { FeatureDefinition } from \"./types\";\r\n\r\nexport const sesFeature: FeatureDefinition = {\r\n description: \"Installs AWS SES SDK for sending emails via Amazon SES\",\r\n dependencies: {\r\n \"@aws-sdk/client-sesv2\": \"^3.1025.0\",\r\n },\r\n};\r\n"],"mappings":";AAEA,MAAa,aAAgC;CAC3C,aAAa;CACb,cAAc,EACZ,yBAAyB,YAC3B;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shadcn.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/shadcn.feature.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\nimport { ensureDirectoryAsync, fileExistsAsync, getFileAsync, putFileAsync } from \"@warlock.js/fs\";\nimport { CommandActionData } from \"../../commands/types\";\nimport { rootPath, srcPath } from \"../../utils\";\nimport { FeatureDefinition } from \"./types\";\n\n/**\n * `warlock add shadcn` installs the PREREQUISITES for shadcn/ui. It does not\n * wrap `shadcn add`, and it never will.\n *\n * shadcn/ui is a copy-in generator with its own CLI and its own registry, not a\n * dependency: components are written into your source tree and become yours the\n * moment they land. Wrapping their CLI would make their moving target our bug\n * reports, for a command that adds nothing but a rename.\n *\n * What is worth owning is the part their CLI gets wrong here. Measured against\n * this framework's layout on 2026-08-25, `shadcn add button card` exited 0 and\n * produced components that rendered COMPLETELY UNSTYLED. It writes components\n * that import `cn` and reference `bg-primary` / `ring-ring`, but only\n * `shadcn init` creates `lib/utils` and the theme tokens those names resolve\n * against. The class was in the generated CSS and the element carried it, and\n * the rule still evaluated to an empty `var()` — a silent failure with a zero\n * exit code, which is the worst kind to hand a user.\n *\n * So this feature ships the five things that make `npx shadcn add <component>`\n * work the first time:\n *\n * 1. `components.json` written for OUR layout (their defaults assume `src/app`\n * or a bare `components/`, neither of which is where pages live here).\n * 2. `src/web/lib/utils.ts` exporting `cn`.\n * 3. The design tokens appended to `src/web/app.css`.\n * 4. A `web/*` entry in tsconfig `paths`, so the generated imports typecheck.\n * 5. The packages `init` would have installed — see `printNextStep` below.\n * Skipping `init` is correct; skipping its dependency list was a bug, and\n * it cost a generated button that imports `cva` and cannot compile.\n *\n * After that, the user talks to shadcn directly, and their docs are true.\n */\n\n/**\n * The aliases shadcn's CLI rewrites every generated import against.\n *\n * These are the whole reason this feature exists. shadcn's defaults do not fit\n * `src/web`, and no user would guess this mapping: the alias keys are shadcn's\n * vocabulary, the values are tsconfig `paths` prefixes (hence `web/...`, not\n * `src/web/...`), and `tailwind.css` is a real path from the project root\n * (hence `src/web/app.css`, WITH the `src`). Getting one of them wrong produces\n * components in the wrong folder importing `cn` from somewhere that does not\n * exist.\n *\n * `tailwind.config` is deliberately empty: v4 is CSS-first and there is no\n * config file for it to point at. `rsc: false` because these are SSR React\n * pages rendered by the Warlock HTTP server, not React Server Components.\n */\nconst componentsJsonStub = `{\n \"$schema\": \"https://ui.shadcn.com/schema.json\",\n \"style\": \"new-york\",\n \"rsc\": false,\n \"tsx\": true,\n \"tailwind\": {\n \"config\": \"\",\n \"css\": \"src/web/app.css\",\n \"baseColor\": \"neutral\",\n \"cssVariables\": true,\n \"prefix\": \"\"\n },\n \"aliases\": {\n \"components\": \"web/components\",\n \"utils\": \"web/lib/utils\",\n \"ui\": \"web/components/ui\",\n \"lib\": \"web/lib\",\n \"hooks\": \"web/hooks\"\n },\n \"iconLibrary\": \"lucide\"\n}\n`;\n\n/**\n * `cn` — the one import every single shadcn component makes.\n *\n * `clsx` resolves the conditional/array/object class syntax, and `tailwind-merge`\n * then de-duplicates conflicting Tailwind utilities so a caller's `className`\n * actually beats the component's own default rather than depending on which one\n * happens to come later in the generated stylesheet. Both halves are required:\n * clsx alone leaves `px-2 px-4` in the attribute and the loser wins at random.\n */\nconst cnUtilStub = `import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names, with later Tailwind utilities winning over earlier ones.\n *\n * Every shadcn/ui component imports this. Keep the export name and signature.\n */\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n`;\n\n/**\n * Marker for \"the tokens are already in this stylesheet\".\n *\n * A comment rather than a token name, because a user is free to retune every\n * value below and we must still recognise our own block on a re-run.\n */\nconst SHADCN_TOKENS_MARKER = \"shadcn/ui design tokens\";\n\n/**\n * The shadcn token set, as a plain `@theme` block with LITERAL values.\n *\n * Not `@theme inline` over `:root` custom properties, which is what shadcn's own\n * `init` writes. That form was verified against this stylesheet pipeline on\n * 2026-08-25 and it produces utilities that resolve to nothing: the class is in\n * the generated CSS, the class is on the element, and the rule still evaluates\n * to an empty `var()`. Literal values in `@theme` are what make `bg-primary`\n * emit a colour instead of a dangling reference.\n *\n * Dark mode still works, and works the way shadcn expects: `@theme` emits these\n * as real custom properties, so the `.dark` block re-declares the same\n * `--color-*` names and the cascade does the rest. `@custom-variant dark` is\n * what points Tailwind's `dark:` prefix at that class instead of the OS setting.\n *\n * Values are shadcn's `neutral` base. They are the starting point, not the\n * answer — this is the block to edit when the project gets a real palette.\n */\nconst shadcnTokensStub = `\n/* ${SHADCN_TOKENS_MARKER} — edit freely, this is your palette now.\n\n These are LITERAL values in a plain @theme block, on purpose. shadcn's own\n \\`init\\` writes \\`@theme inline\\` over :root custom properties; that form emits\n utilities which resolve to an empty var() here, so every component renders\n unstyled while the class sits right there on the element. Do not convert it.\n*/\n@custom-variant dark (&:is(.dark *));\n\n@theme {\n --color-background: oklch(1 0 0);\n --color-foreground: oklch(0.145 0 0);\n --color-card: oklch(1 0 0);\n --color-card-foreground: oklch(0.145 0 0);\n --color-popover: oklch(1 0 0);\n --color-popover-foreground: oklch(0.145 0 0);\n --color-primary: oklch(0.205 0 0);\n --color-primary-foreground: oklch(0.985 0 0);\n --color-secondary: oklch(0.97 0 0);\n --color-secondary-foreground: oklch(0.205 0 0);\n --color-muted: oklch(0.97 0 0);\n --color-muted-foreground: oklch(0.556 0 0);\n --color-accent: oklch(0.97 0 0);\n --color-accent-foreground: oklch(0.205 0 0);\n --color-destructive: oklch(0.577 0.245 27.325);\n --color-destructive-foreground: oklch(0.985 0 0);\n --color-border: oklch(0.922 0 0);\n --color-input: oklch(0.922 0 0);\n --color-ring: oklch(0.708 0 0);\n\n --color-chart-1: oklch(0.646 0.222 41.116);\n --color-chart-2: oklch(0.6 0.118 184.704);\n --color-chart-3: oklch(0.398 0.07 227.392);\n --color-chart-4: oklch(0.828 0.189 84.429);\n --color-chart-5: oklch(0.769 0.188 70.08);\n\n --color-sidebar: oklch(0.985 0 0);\n --color-sidebar-foreground: oklch(0.145 0 0);\n --color-sidebar-primary: oklch(0.205 0 0);\n --color-sidebar-primary-foreground: oklch(0.985 0 0);\n --color-sidebar-accent: oklch(0.97 0 0);\n --color-sidebar-accent-foreground: oklch(0.205 0 0);\n --color-sidebar-border: oklch(0.922 0 0);\n --color-sidebar-ring: oklch(0.708 0 0);\n\n /* shadcn components reach for rounded-lg/md/sm and expect them to track one\n radius. Changing --radius-lg here also retunes Tailwind's own rounded-lg,\n which is the intended trade: one radius scale per project, not two. */\n --radius-sm: 0.375rem;\n --radius-md: 0.5rem;\n --radius-lg: 0.625rem;\n --radius-xl: 1rem;\n}\n\n.dark {\n --color-background: oklch(0.145 0 0);\n --color-foreground: oklch(0.985 0 0);\n --color-card: oklch(0.205 0 0);\n --color-card-foreground: oklch(0.985 0 0);\n --color-popover: oklch(0.205 0 0);\n --color-popover-foreground: oklch(0.985 0 0);\n --color-primary: oklch(0.922 0 0);\n --color-primary-foreground: oklch(0.205 0 0);\n --color-secondary: oklch(0.269 0 0);\n --color-secondary-foreground: oklch(0.985 0 0);\n --color-muted: oklch(0.269 0 0);\n --color-muted-foreground: oklch(0.708 0 0);\n --color-accent: oklch(0.269 0 0);\n --color-accent-foreground: oklch(0.985 0 0);\n --color-destructive: oklch(0.704 0.191 22.216);\n --color-destructive-foreground: oklch(0.985 0 0);\n --color-border: oklch(1 0 0 / 10%);\n --color-input: oklch(1 0 0 / 15%);\n --color-ring: oklch(0.556 0 0);\n\n --color-chart-1: oklch(0.488 0.243 264.376);\n --color-chart-2: oklch(0.696 0.17 162.48);\n --color-chart-3: oklch(0.769 0.188 70.08);\n --color-chart-4: oklch(0.627 0.265 303.9);\n --color-chart-5: oklch(0.645 0.246 16.439);\n\n --color-sidebar: oklch(0.205 0 0);\n --color-sidebar-foreground: oklch(0.985 0 0);\n --color-sidebar-primary: oklch(0.488 0.243 264.376);\n --color-sidebar-primary-foreground: oklch(0.985 0 0);\n --color-sidebar-accent: oklch(0.269 0 0);\n --color-sidebar-accent-foreground: oklch(0.985 0 0);\n --color-sidebar-border: oklch(1 0 0 / 10%);\n --color-sidebar-ring: oklch(0.556 0 0);\n}\n`;\n\n/**\n * Write `components.json` — the sentinel for \"this feature already ran\".\n *\n * Nothing in the project template creates this file, so its presence means\n * `add shadcn` has been here and a human may since have retuned the aliases,\n * the style, or the base colour. It is never rewritten: shadcn's CLI reads this\n * file on every `add`, so overwriting it would silently relocate a project's\n * component folder out from under the components already in it.\n */\nasync function writeComponentsJson(): Promise<void> {\n const componentsJsonPath = rootPath(\"components.json\");\n\n if (await fileExistsAsync(componentsJsonPath)) {\n console.log(`${colors.yellowBright(\"components.json\")} already exists, skipping...`);\n\n return;\n }\n\n await putFileAsync(componentsJsonPath, componentsJsonStub);\n console.log(`${colors.green(\"✓\")} Created components.json`);\n}\n\n/**\n * Write `src/web/lib/utils.ts`.\n *\n * Guarded on its own rather than on the sentinel above, because `lib/utils` is\n * a name a project may well already own — and if it does, whatever is in there\n * is user code with other callers. We print instead of merging.\n */\nasync function writeCnUtil(): Promise<void> {\n const utilsFile = srcPath(\"web/lib/utils.ts\");\n\n if (await fileExistsAsync(utilsFile)) {\n const current = await getFileAsync(utilsFile).catch(() => \"\");\n\n if (/export\\s+(function|const)\\s+cn\\b/.test(current)) {\n console.log(`${colors.yellowBright(\"src/web/lib/utils.ts\")} already exports cn, skipping...`);\n } else {\n console.log(\n `${colors.yellowBright(\"!\")} ${colors.yellowBright(\"src/web/lib/utils.ts\")} exists but does not export ` +\n `${colors.yellowBright(\"cn\")} — add it yourself:\\n` +\n \" export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); }\\n\" +\n \" Every shadcn component imports it, and none of them will compile until it is there.\",\n );\n }\n\n return;\n }\n\n await ensureDirectoryAsync(srcPath(\"web/lib\"));\n await putFileAsync(utilsFile, cnUtilStub);\n console.log(`${colors.green(\"✓\")} Created src/web/lib/utils.ts`);\n}\n\n/**\n * Append the token block to `src/web/app.css`.\n *\n * The stylesheet is guaranteed to exist by the time this runs: `requires:\n * [\"tailwind\"]` makes the add command resolve `tailwind` first and run its\n * `onExecuting` ahead of this one, and that is what creates the file. The check\n * below is for a stylesheet a human has since moved or deleted — worth a\n * printed instruction, not a failure.\n *\n * Appended, never rewritten. Everything already in that file is either\n * Tailwind's own `@import` or the project's design system.\n */\nasync function appendThemeTokens(): Promise<void> {\n const cssFile = srcPath(\"web/app.css\");\n\n if (!(await fileExistsAsync(cssFile))) {\n console.log(\n `${colors.yellowBright(\"!\")} ${colors.yellowBright(\"src/web/app.css\")} not found — ` +\n \"append the shadcn token block to your Tailwind stylesheet yourself.\\n\" +\n \" Without the tokens, shadcn components render unstyled: the classes are emitted and applied, \" +\n \"but `bg-primary` and friends resolve to an empty var().\",\n );\n\n return;\n }\n\n const current = await getFileAsync(cssFile);\n\n if (current.includes(SHADCN_TOKENS_MARKER) || current.includes(\"--color-primary-foreground\")) {\n console.log(`${colors.yellowBright(\"src/web/app.css\")} already has the tokens, skipping...`);\n\n return;\n }\n\n await putFileAsync(cssFile, `${current.trimEnd()}\\n${shadcnTokensStub}`);\n console.log(`${colors.green(\"✓\")} Appended the shadcn tokens to src/web/app.css`);\n}\n\n/** The tsconfig `paths` entry the generated imports resolve through. */\nconst WEB_PATH_ALIAS = '\"web/*\": [\"./src/web/*\"]';\n\n/**\n * Add `web/*` to tsconfig `compilerOptions.paths`.\n *\n * The template declares only `app/*`, so every import shadcn generates against\n * the aliases above (`web/lib/utils`, `web/components/ui/button`) would fail to\n * typecheck the moment it lands. This is the first `add` feature to patch\n * `paths` rather than `include`.\n *\n * String surgery, and NOT the parse-and-write that the `include` patches use.\n * The project template's `tsconfig.json` carries `//` comments — it is JSONC,\n * and `JSON.parse` throws on it — so a parse-first patch would fail on exactly\n * the projects this feature is for. Editing the text also preserves those\n * comments, which are load-bearing documentation in that file.\n */\nasync function addWebPathAlias(): Promise<void> {\n const tsconfigPath = rootPath(\"tsconfig.json\");\n\n const printManualInstruction = (reason: string) => {\n console.log(\n `${colors.yellowBright(\"!\")} ${colors.yellowBright(\"tsconfig.json\")} ${reason} — ` +\n \"add this to `compilerOptions.paths` yourself:\\n\" +\n ` ${WEB_PATH_ALIAS}\\n` +\n \" Without it, every import shadcn generates against the `web/*` aliases fails to typecheck.\",\n );\n };\n\n if (!(await fileExistsAsync(tsconfigPath))) {\n printManualInstruction(\"not found\");\n\n return;\n }\n\n const current = await getFileAsync(tsconfigPath);\n\n // Matches the alias whichever quote style and spacing the file uses, so a\n // re-run against a hand-edited tsconfig does not stack a second entry.\n if (/[\"']web\\/\\*[\"']\\s*:/.test(current)) {\n console.log(`${colors.yellowBright(\"tsconfig.json\")} already maps web/*, skipping...`);\n\n return;\n }\n\n let next: string;\n\n if (/\"paths\"\\s*:\\s*\\{/.test(current)) {\n next = current.replace(/\"paths\"\\s*:\\s*\\{/, `$&\\n ${WEB_PATH_ALIAS},`);\n } else if (/\"compilerOptions\"\\s*:\\s*\\{/.test(current)) {\n next = current.replace(\n /\"compilerOptions\"\\s*:\\s*\\{/,\n `$&\\n \"paths\": {\\n ${WEB_PATH_ALIAS}\\n },`,\n );\n } else {\n printManualInstruction(\"has no recognisable compilerOptions block\");\n\n return;\n }\n\n await putFileAsync(tsconfigPath, next);\n console.log(`${colors.green(\"✓\")} Added ${WEB_PATH_ALIAS} to tsconfig.json paths`);\n}\n\n/**\n * Tell the user the next command is theirs to run.\n *\n * This is the seam. Everything above is prerequisite; from here the shadcn docs\n * apply verbatim, which is the entire point of not wrapping their CLI.\n *\n * WHY THIS FEATURE DECLARES cva AND lucide-react, and where the list came from.\n *\n * shadcn's registry splits dependencies across two levels. Each component item\n * (`.../new-york-v4/button.json`) declares only what that file pulls beyond the\n * baseline — for button, `radix-ui` and nothing else. Everything the baseline\n * assumes lives on the STYLE INDEX (`.../new-york-v4/index.json`), which is\n * fetched by `init` and only by `init`:\n *\n * dependencies: class-variance-authority, lucide-react, radix-ui\n * devDependencies: tw-animate-css, shadcn\n *\n * We skip `init` on purpose — it would rewrite components.json and replace the\n * literal tokens with the `@theme inline` block that resolves to nothing here.\n * Skipping it is right; inheriting nothing from it was the bug. `shadcn add\n * button` exits 0 and writes `import { cva } from \"class-variance-authority\"`\n * against a package no one installed. Zero exit code, TS2307, blank page.\n *\n * `radix-ui` stays off our list: it is the one style-index dependency that is\n * ALSO declared per-component, so `add` really does install it on demand, and\n * declaring it here would pull the whole primitive set into projects using two\n * components. `shadcn` itself stays off too — it is the CLI, and the user is\n * invoking it via `npx`.\n *\n * `tw-animate-css` was checked and deliberately EXCLUDED. It is not imported by\n * any component; it is a plain stylesheet whose only entry point is the\n * `@import \"tw-animate-css\"` line that `init` writes into the CSS — and we do\n * not write that line, so the package would install and never load. Nothing\n * fails to compile or render without it. What you lose is the enter/exit\n * animation on overlay components (dialog, dropdown, tooltip, sheet): their\n * `animate-in` / `fade-in-0` classes are simply never generated, so the overlay\n * appears instantly instead of fading. That is opt-in, and the note below is how\n * a user opts in — the `@import` has to go at the TOP of app.css, next to\n * Tailwind's own, which is why this feature cannot append it to the token block.\n */\nfunction printNextStep(): void {\n console.log(\n `\\n${colors.green(\"✓\")} shadcn/ui prerequisites are in place. Add components with shadcn's own CLI:\\n` +\n ` ${colors.yellowBright(\"npx shadcn@latest add button card\")}\\n` +\n \" Skip `shadcn init` — this feature did its job, and running it would rewrite components.json\\n\" +\n \" and replace the theme tokens with an `@theme inline` block that resolves to nothing here.\\n\" +\n ` ${colors.yellowBright(\"class-variance-authority\")} and ${colors.yellowBright(\"lucide-react\")} are already installed: shadcn declares\\n` +\n \" them on the style index that only `init` reads, so `add` would never install them for you.\\n\" +\n ` ${colors.yellowBright(\"radix-ui\")} (the unified package, not @radix-ui/react-*) IS declared per component,\\n` +\n \" so shadcn's CLI installs that one itself as each component needs it.\\n\" +\n ` For overlay animations, add ${colors.yellowBright(\"tw-animate-css\")} and put ${colors.yellowBright('@import \"tw-animate-css\";')}\\n` +\n \" at the TOP of src/web/app.css, under the Tailwind import. Without it dialogs and dropdowns\\n\" +\n \" still work, they just appear instantly instead of animating.\",\n );\n}\n\n/**\n * Lay the ground shadcn's CLI expects to find, and nothing more.\n *\n * Four files, none of which needs `node_modules` to be populated: on the\n * `create-warlock` path this runs under `--no-install`, so the dependencies\n * declared below are only recorded in `package.json` and nothing here may\n * import, resolve, or execute shadcn, clsx, or Tailwind.\n */\nasync function completeShadcnInstallation(_options: CommandActionData) {\n await writeComponentsJson();\n await writeCnUtil();\n await appendThemeTokens();\n await addWebPathAlias();\n printNextStep();\n}\n\nexport const shadcnFeature: FeatureDefinition = {\n description:\n \"Sets up the prerequisites for shadcn/ui so `npx shadcn add <component>` works first time: components.json aliased to src/web, src/web/lib/utils.ts (cn), the design tokens in src/web/app.css, and a web/* tsconfig path. It does NOT wrap shadcn's CLI — components stay theirs to generate and yours to own.\",\n // `tailwind` owns src/web/app.css, which the token block is appended to.\n // Requiring it also fixes the order: the add command resolves requirements\n // depth-first, so the stylesheet exists before this feature writes into it.\n requires: [\"tailwind\"],\n // Everything `shadcn init` would have installed, minus the parts their CLI\n // genuinely does install per-component. See the note above `printNextStep`\n // for how this list was derived and what is deliberately NOT in it.\n dependencies: {\n // Both are runtime dependencies of `cn`, which every generated component\n // calls on every render — not build-time tooling.\n clsx: \"^2.1.1\",\n // v3 is the Tailwind v4 line; tailwind-merge v2 knows the v3 utility set and\n // silently fails to de-duplicate against v4 class names.\n \"tailwind-merge\": \"^3.3.1\",\n // `cva` is imported on line 2 of the generated button — and of every other\n // component with a `variant` prop. The registry declares it ONCE, on the\n // style index that only `init` applies, so `shadcn add button` resolves the\n // component's own deps, exits 0, and leaves TS2307 on a file it just wrote.\n // Still 0.x upstream, so this caret pins to 0.7.x; expect 0.7.1 to land.\n \"class-variance-authority\": \"^0.7.1\",\n // Same trap, one layer further in. `components.json` declares\n // `iconLibrary: \"lucide\"`, and dialog/select/checkbox/dropdown-menu all\n // import `lucide-react` in their source while declaring only `radix-ui` —\n // verified against the new-york-v4 registry items. So the CLI installs it\n // for nobody, and the first icon-bearing component fails to resolve.\n // Peer range covers React 19, which is what `web` brings.\n \"lucide-react\": \"^1.34.0\",\n },\n onExecuting: completeShadcnInstallation,\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"shadcn.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/shadcn.feature.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\nimport { ensureDirectoryAsync, fileExistsAsync, getFileAsync, putFileAsync } from \"@warlock.js/fs\";\nimport type { CommandActionData } from \"../../commands/types\";\nimport { rootPath, srcPath } from \"../../utils\";\nimport type { FeatureDefinition } from \"./types\";\n\n/**\n * `warlock add shadcn` installs the PREREQUISITES for shadcn/ui. It does not\n * wrap `shadcn add`, and it never will.\n *\n * shadcn/ui is a copy-in generator with its own CLI and its own registry, not a\n * dependency: components are written into your source tree and become yours the\n * moment they land. Wrapping their CLI would make their moving target our bug\n * reports, for a command that adds nothing but a rename.\n *\n * What is worth owning is the part their CLI gets wrong here. Measured against\n * this framework's layout on 2026-08-25, `shadcn add button card` exited 0 and\n * produced components that rendered COMPLETELY UNSTYLED. It writes components\n * that import `cn` and reference `bg-primary` / `ring-ring`, but only\n * `shadcn init` creates `lib/utils` and the theme tokens those names resolve\n * against. The class was in the generated CSS and the element carried it, and\n * the rule still evaluated to an empty `var()` — a silent failure with a zero\n * exit code, which is the worst kind to hand a user.\n *\n * So this feature ships the five things that make `npx shadcn add <component>`\n * work the first time:\n *\n * 1. `components.json` written for OUR layout (their defaults assume `src/app`\n * or a bare `components/`, neither of which is where pages live here).\n * 2. `src/web/lib/utils.ts` exporting `cn`.\n * 3. The design tokens appended to `src/web/app.css`.\n * 4. A `web/*` entry in tsconfig `paths`, so the generated imports typecheck.\n * 5. The packages `init` would have installed — see `printNextStep` below.\n * Skipping `init` is correct; skipping its dependency list was a bug, and\n * it cost a generated button that imports `cva` and cannot compile.\n *\n * After that, the user talks to shadcn directly, and their docs are true.\n */\n\n/**\n * The aliases shadcn's CLI rewrites every generated import against.\n *\n * These are the whole reason this feature exists. shadcn's defaults do not fit\n * `src/web`, and no user would guess this mapping: the alias keys are shadcn's\n * vocabulary, the values are tsconfig `paths` prefixes (hence `web/...`, not\n * `src/web/...`), and `tailwind.css` is a real path from the project root\n * (hence `src/web/app.css`, WITH the `src`). Getting one of them wrong produces\n * components in the wrong folder importing `cn` from somewhere that does not\n * exist.\n *\n * `tailwind.config` is deliberately empty: v4 is CSS-first and there is no\n * config file for it to point at. `rsc: false` because these are SSR React\n * pages rendered by the Warlock HTTP server, not React Server Components.\n */\nconst componentsJsonStub = `{\n \"$schema\": \"https://ui.shadcn.com/schema.json\",\n \"style\": \"new-york\",\n \"rsc\": false,\n \"tsx\": true,\n \"tailwind\": {\n \"config\": \"\",\n \"css\": \"src/web/app.css\",\n \"baseColor\": \"neutral\",\n \"cssVariables\": true,\n \"prefix\": \"\"\n },\n \"aliases\": {\n \"components\": \"web/components\",\n \"utils\": \"web/lib/utils\",\n \"ui\": \"web/components/ui\",\n \"lib\": \"web/lib\",\n \"hooks\": \"web/hooks\"\n },\n \"iconLibrary\": \"lucide\"\n}\n`;\n\n/**\n * `cn` — the one import every single shadcn component makes.\n *\n * `clsx` resolves the conditional/array/object class syntax, and `tailwind-merge`\n * then de-duplicates conflicting Tailwind utilities so a caller's `className`\n * actually beats the component's own default rather than depending on which one\n * happens to come later in the generated stylesheet. Both halves are required:\n * clsx alone leaves `px-2 px-4` in the attribute and the loser wins at random.\n */\nconst cnUtilStub = `import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge class names, with later Tailwind utilities winning over earlier ones.\n *\n * Every shadcn/ui component imports this. Keep the export name and signature.\n */\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n`;\n\n/**\n * Marker for \"the tokens are already in this stylesheet\".\n *\n * A comment rather than a token name, because a user is free to retune every\n * value below and we must still recognise our own block on a re-run.\n */\nconst SHADCN_TOKENS_MARKER = \"shadcn/ui design tokens\";\n\n/**\n * The shadcn token set, as a plain `@theme` block with LITERAL values.\n *\n * Not `@theme inline` over `:root` custom properties, which is what shadcn's own\n * `init` writes. That form was verified against this stylesheet pipeline on\n * 2026-08-25 and it produces utilities that resolve to nothing: the class is in\n * the generated CSS, the class is on the element, and the rule still evaluates\n * to an empty `var()`. Literal values in `@theme` are what make `bg-primary`\n * emit a colour instead of a dangling reference.\n *\n * Dark mode still works, and works the way shadcn expects: `@theme` emits these\n * as real custom properties, so the `.dark` block re-declares the same\n * `--color-*` names and the cascade does the rest. `@custom-variant dark` is\n * what points Tailwind's `dark:` prefix at that class instead of the OS setting.\n *\n * Values are shadcn's `neutral` base. They are the starting point, not the\n * answer — this is the block to edit when the project gets a real palette.\n */\nconst shadcnTokensStub = `\n/* ${SHADCN_TOKENS_MARKER} — edit freely, this is your palette now.\n\n These are LITERAL values in a plain @theme block, on purpose. shadcn's own\n \\`init\\` writes \\`@theme inline\\` over :root custom properties; that form emits\n utilities which resolve to an empty var() here, so every component renders\n unstyled while the class sits right there on the element. Do not convert it.\n*/\n@custom-variant dark (&:is(.dark *));\n\n@theme {\n --color-background: oklch(1 0 0);\n --color-foreground: oklch(0.145 0 0);\n --color-card: oklch(1 0 0);\n --color-card-foreground: oklch(0.145 0 0);\n --color-popover: oklch(1 0 0);\n --color-popover-foreground: oklch(0.145 0 0);\n --color-primary: oklch(0.205 0 0);\n --color-primary-foreground: oklch(0.985 0 0);\n --color-secondary: oklch(0.97 0 0);\n --color-secondary-foreground: oklch(0.205 0 0);\n --color-muted: oklch(0.97 0 0);\n --color-muted-foreground: oklch(0.556 0 0);\n --color-accent: oklch(0.97 0 0);\n --color-accent-foreground: oklch(0.205 0 0);\n --color-destructive: oklch(0.577 0.245 27.325);\n --color-destructive-foreground: oklch(0.985 0 0);\n --color-border: oklch(0.922 0 0);\n --color-input: oklch(0.922 0 0);\n --color-ring: oklch(0.708 0 0);\n\n --color-chart-1: oklch(0.646 0.222 41.116);\n --color-chart-2: oklch(0.6 0.118 184.704);\n --color-chart-3: oklch(0.398 0.07 227.392);\n --color-chart-4: oklch(0.828 0.189 84.429);\n --color-chart-5: oklch(0.769 0.188 70.08);\n\n --color-sidebar: oklch(0.985 0 0);\n --color-sidebar-foreground: oklch(0.145 0 0);\n --color-sidebar-primary: oklch(0.205 0 0);\n --color-sidebar-primary-foreground: oklch(0.985 0 0);\n --color-sidebar-accent: oklch(0.97 0 0);\n --color-sidebar-accent-foreground: oklch(0.205 0 0);\n --color-sidebar-border: oklch(0.922 0 0);\n --color-sidebar-ring: oklch(0.708 0 0);\n\n /* shadcn components reach for rounded-lg/md/sm and expect them to track one\n radius. Changing --radius-lg here also retunes Tailwind's own rounded-lg,\n which is the intended trade: one radius scale per project, not two. */\n --radius-sm: 0.375rem;\n --radius-md: 0.5rem;\n --radius-lg: 0.625rem;\n --radius-xl: 1rem;\n}\n\n.dark {\n --color-background: oklch(0.145 0 0);\n --color-foreground: oklch(0.985 0 0);\n --color-card: oklch(0.205 0 0);\n --color-card-foreground: oklch(0.985 0 0);\n --color-popover: oklch(0.205 0 0);\n --color-popover-foreground: oklch(0.985 0 0);\n --color-primary: oklch(0.922 0 0);\n --color-primary-foreground: oklch(0.205 0 0);\n --color-secondary: oklch(0.269 0 0);\n --color-secondary-foreground: oklch(0.985 0 0);\n --color-muted: oklch(0.269 0 0);\n --color-muted-foreground: oklch(0.708 0 0);\n --color-accent: oklch(0.269 0 0);\n --color-accent-foreground: oklch(0.985 0 0);\n --color-destructive: oklch(0.704 0.191 22.216);\n --color-destructive-foreground: oklch(0.985 0 0);\n --color-border: oklch(1 0 0 / 10%);\n --color-input: oklch(1 0 0 / 15%);\n --color-ring: oklch(0.556 0 0);\n\n --color-chart-1: oklch(0.488 0.243 264.376);\n --color-chart-2: oklch(0.696 0.17 162.48);\n --color-chart-3: oklch(0.769 0.188 70.08);\n --color-chart-4: oklch(0.627 0.265 303.9);\n --color-chart-5: oklch(0.645 0.246 16.439);\n\n --color-sidebar: oklch(0.205 0 0);\n --color-sidebar-foreground: oklch(0.985 0 0);\n --color-sidebar-primary: oklch(0.488 0.243 264.376);\n --color-sidebar-primary-foreground: oklch(0.985 0 0);\n --color-sidebar-accent: oklch(0.269 0 0);\n --color-sidebar-accent-foreground: oklch(0.985 0 0);\n --color-sidebar-border: oklch(1 0 0 / 10%);\n --color-sidebar-ring: oklch(0.556 0 0);\n}\n`;\n\n/**\n * Write `components.json` — the sentinel for \"this feature already ran\".\n *\n * Nothing in the project template creates this file, so its presence means\n * `add shadcn` has been here and a human may since have retuned the aliases,\n * the style, or the base colour. It is never rewritten: shadcn's CLI reads this\n * file on every `add`, so overwriting it would silently relocate a project's\n * component folder out from under the components already in it.\n */\nasync function writeComponentsJson(): Promise<void> {\n const componentsJsonPath = rootPath(\"components.json\");\n\n if (await fileExistsAsync(componentsJsonPath)) {\n console.log(`${colors.yellowBright(\"components.json\")} already exists, skipping...`);\n\n return;\n }\n\n await putFileAsync(componentsJsonPath, componentsJsonStub);\n console.log(`${colors.green(\"✓\")} Created components.json`);\n}\n\n/**\n * Write `src/web/lib/utils.ts`.\n *\n * Guarded on its own rather than on the sentinel above, because `lib/utils` is\n * a name a project may well already own — and if it does, whatever is in there\n * is user code with other callers. We print instead of merging.\n */\nasync function writeCnUtil(): Promise<void> {\n const utilsFile = srcPath(\"web/lib/utils.ts\");\n\n if (await fileExistsAsync(utilsFile)) {\n const current = await getFileAsync(utilsFile).catch(() => \"\");\n\n if (/export\\s+(function|const)\\s+cn\\b/.test(current)) {\n console.log(`${colors.yellowBright(\"src/web/lib/utils.ts\")} already exports cn, skipping...`);\n } else {\n console.log(\n `${colors.yellowBright(\"!\")} ${colors.yellowBright(\"src/web/lib/utils.ts\")} exists but does not export ` +\n `${colors.yellowBright(\"cn\")} — add it yourself:\\n` +\n \" export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); }\\n\" +\n \" Every shadcn component imports it, and none of them will compile until it is there.\",\n );\n }\n\n return;\n }\n\n await ensureDirectoryAsync(srcPath(\"web/lib\"));\n await putFileAsync(utilsFile, cnUtilStub);\n console.log(`${colors.green(\"✓\")} Created src/web/lib/utils.ts`);\n}\n\n/**\n * Append the token block to `src/web/app.css`.\n *\n * The stylesheet is guaranteed to exist by the time this runs: `requires:\n * [\"tailwind\"]` makes the add command resolve `tailwind` first and run its\n * `onExecuting` ahead of this one, and that is what creates the file. The check\n * below is for a stylesheet a human has since moved or deleted — worth a\n * printed instruction, not a failure.\n *\n * Appended, never rewritten. Everything already in that file is either\n * Tailwind's own `@import` or the project's design system.\n */\nasync function appendThemeTokens(): Promise<void> {\n const cssFile = srcPath(\"web/app.css\");\n\n if (!(await fileExistsAsync(cssFile))) {\n console.log(\n `${colors.yellowBright(\"!\")} ${colors.yellowBright(\"src/web/app.css\")} not found — ` +\n \"append the shadcn token block to your Tailwind stylesheet yourself.\\n\" +\n \" Without the tokens, shadcn components render unstyled: the classes are emitted and applied, \" +\n \"but `bg-primary` and friends resolve to an empty var().\",\n );\n\n return;\n }\n\n const current = await getFileAsync(cssFile);\n\n if (current.includes(SHADCN_TOKENS_MARKER) || current.includes(\"--color-primary-foreground\")) {\n console.log(`${colors.yellowBright(\"src/web/app.css\")} already has the tokens, skipping...`);\n\n return;\n }\n\n await putFileAsync(cssFile, `${current.trimEnd()}\\n${shadcnTokensStub}`);\n console.log(`${colors.green(\"✓\")} Appended the shadcn tokens to src/web/app.css`);\n}\n\n/** The tsconfig `paths` entry the generated imports resolve through. */\nconst WEB_PATH_ALIAS = '\"web/*\": [\"./src/web/*\"]';\n\n/**\n * Add `web/*` to tsconfig `compilerOptions.paths`.\n *\n * The template declares only `app/*`, so every import shadcn generates against\n * the aliases above (`web/lib/utils`, `web/components/ui/button`) would fail to\n * typecheck the moment it lands. This is the first `add` feature to patch\n * `paths` rather than `include`.\n *\n * String surgery, and NOT the parse-and-write that the `include` patches use.\n * The project template's `tsconfig.json` carries `//` comments — it is JSONC,\n * and `JSON.parse` throws on it — so a parse-first patch would fail on exactly\n * the projects this feature is for. Editing the text also preserves those\n * comments, which are load-bearing documentation in that file.\n */\nasync function addWebPathAlias(): Promise<void> {\n const tsconfigPath = rootPath(\"tsconfig.json\");\n\n const printManualInstruction = (reason: string) => {\n console.log(\n `${colors.yellowBright(\"!\")} ${colors.yellowBright(\"tsconfig.json\")} ${reason} — ` +\n \"add this to `compilerOptions.paths` yourself:\\n\" +\n ` ${WEB_PATH_ALIAS}\\n` +\n \" Without it, every import shadcn generates against the `web/*` aliases fails to typecheck.\",\n );\n };\n\n if (!(await fileExistsAsync(tsconfigPath))) {\n printManualInstruction(\"not found\");\n\n return;\n }\n\n const current = await getFileAsync(tsconfigPath);\n\n // Matches the alias whichever quote style and spacing the file uses, so a\n // re-run against a hand-edited tsconfig does not stack a second entry.\n if (/[\"']web\\/\\*[\"']\\s*:/.test(current)) {\n console.log(`${colors.yellowBright(\"tsconfig.json\")} already maps web/*, skipping...`);\n\n return;\n }\n\n let next: string;\n\n if (/\"paths\"\\s*:\\s*\\{/.test(current)) {\n next = current.replace(/\"paths\"\\s*:\\s*\\{/, `$&\\n ${WEB_PATH_ALIAS},`);\n } else if (/\"compilerOptions\"\\s*:\\s*\\{/.test(current)) {\n next = current.replace(\n /\"compilerOptions\"\\s*:\\s*\\{/,\n `$&\\n \"paths\": {\\n ${WEB_PATH_ALIAS}\\n },`,\n );\n } else {\n printManualInstruction(\"has no recognisable compilerOptions block\");\n\n return;\n }\n\n await putFileAsync(tsconfigPath, next);\n console.log(`${colors.green(\"✓\")} Added ${WEB_PATH_ALIAS} to tsconfig.json paths`);\n}\n\n/**\n * Tell the user the next command is theirs to run.\n *\n * This is the seam. Everything above is prerequisite; from here the shadcn docs\n * apply verbatim, which is the entire point of not wrapping their CLI.\n *\n * WHY THIS FEATURE DECLARES cva AND lucide-react, and where the list came from.\n *\n * shadcn's registry splits dependencies across two levels. Each component item\n * (`.../new-york-v4/button.json`) declares only what that file pulls beyond the\n * baseline — for button, `radix-ui` and nothing else. Everything the baseline\n * assumes lives on the STYLE INDEX (`.../new-york-v4/index.json`), which is\n * fetched by `init` and only by `init`:\n *\n * dependencies: class-variance-authority, lucide-react, radix-ui\n * devDependencies: tw-animate-css, shadcn\n *\n * We skip `init` on purpose — it would rewrite components.json and replace the\n * literal tokens with the `@theme inline` block that resolves to nothing here.\n * Skipping it is right; inheriting nothing from it was the bug. `shadcn add\n * button` exits 0 and writes `import { cva } from \"class-variance-authority\"`\n * against a package no one installed. Zero exit code, TS2307, blank page.\n *\n * `radix-ui` stays off our list: it is the one style-index dependency that is\n * ALSO declared per-component, so `add` really does install it on demand, and\n * declaring it here would pull the whole primitive set into projects using two\n * components. `shadcn` itself stays off too — it is the CLI, and the user is\n * invoking it via `npx`.\n *\n * `tw-animate-css` was checked and deliberately EXCLUDED. It is not imported by\n * any component; it is a plain stylesheet whose only entry point is the\n * `@import \"tw-animate-css\"` line that `init` writes into the CSS — and we do\n * not write that line, so the package would install and never load. Nothing\n * fails to compile or render without it. What you lose is the enter/exit\n * animation on overlay components (dialog, dropdown, tooltip, sheet): their\n * `animate-in` / `fade-in-0` classes are simply never generated, so the overlay\n * appears instantly instead of fading. That is opt-in, and the note below is how\n * a user opts in — the `@import` has to go at the TOP of app.css, next to\n * Tailwind's own, which is why this feature cannot append it to the token block.\n */\nfunction printNextStep(): void {\n console.log(\n `\\n${colors.green(\"✓\")} shadcn/ui prerequisites are in place. Add components with shadcn's own CLI:\\n` +\n ` ${colors.yellowBright(\"npx shadcn@latest add button card\")}\\n` +\n \" Skip `shadcn init` — this feature did its job, and running it would rewrite components.json\\n\" +\n \" and replace the theme tokens with an `@theme inline` block that resolves to nothing here.\\n\" +\n ` ${colors.yellowBright(\"class-variance-authority\")} and ${colors.yellowBright(\"lucide-react\")} are already installed: shadcn declares\\n` +\n \" them on the style index that only `init` reads, so `add` would never install them for you.\\n\" +\n ` ${colors.yellowBright(\"radix-ui\")} (the unified package, not @radix-ui/react-*) IS declared per component,\\n` +\n \" so shadcn's CLI installs that one itself as each component needs it.\\n\" +\n ` For overlay animations, add ${colors.yellowBright(\"tw-animate-css\")} and put ${colors.yellowBright('@import \"tw-animate-css\";')}\\n` +\n \" at the TOP of src/web/app.css, under the Tailwind import. Without it dialogs and dropdowns\\n\" +\n \" still work, they just appear instantly instead of animating.\",\n );\n}\n\n/**\n * Lay the ground shadcn's CLI expects to find, and nothing more.\n *\n * Four files, none of which needs `node_modules` to be populated: on the\n * `create-warlock` path this runs under `--no-install`, so the dependencies\n * declared below are only recorded in `package.json` and nothing here may\n * import, resolve, or execute shadcn, clsx, or Tailwind.\n */\nasync function completeShadcnInstallation(_options: CommandActionData) {\n await writeComponentsJson();\n await writeCnUtil();\n await appendThemeTokens();\n await addWebPathAlias();\n printNextStep();\n}\n\nexport const shadcnFeature: FeatureDefinition = {\n description:\n \"Sets up the prerequisites for shadcn/ui so `npx shadcn add <component>` works first time: components.json aliased to src/web, src/web/lib/utils.ts (cn), the design tokens in src/web/app.css, and a web/* tsconfig path. It does NOT wrap shadcn's CLI — components stay theirs to generate and yours to own.\",\n // `tailwind` owns src/web/app.css, which the token block is appended to.\n // Requiring it also fixes the order: the add command resolves requirements\n // depth-first, so the stylesheet exists before this feature writes into it.\n requires: [\"tailwind\"],\n // Everything `shadcn init` would have installed, minus the parts their CLI\n // genuinely does install per-component. See the note above `printNextStep`\n // for how this list was derived and what is deliberately NOT in it.\n dependencies: {\n // Both are runtime dependencies of `cn`, which every generated component\n // calls on every render — not build-time tooling.\n clsx: \"^2.1.1\",\n // v3 is the Tailwind v4 line; tailwind-merge v2 knows the v3 utility set and\n // silently fails to de-duplicate against v4 class names.\n \"tailwind-merge\": \"^3.3.1\",\n // `cva` is imported on line 2 of the generated button — and of every other\n // component with a `variant` prop. The registry declares it ONCE, on the\n // style index that only `init` applies, so `shadcn add button` resolves the\n // component's own deps, exits 0, and leaves TS2307 on a file it just wrote.\n // Still 0.x upstream, so this caret pins to 0.7.x; expect 0.7.1 to land.\n \"class-variance-authority\": \"^0.7.1\",\n // Same trap, one layer further in. `components.json` declares\n // `iconLibrary: \"lucide\"`, and dialog/select/checkbox/dropdown-menu all\n // import `lucide-react` in their source while declaring only `radix-ui` —\n // verified against the new-york-v4 registry items. So the CLI installs it\n // for nobody, and the first icon-bearing component fails to resolve.\n // Peer range covers React 19, which is what `web` brings.\n \"lucide-react\": \"^1.34.0\",\n },\n onExecuting: completeShadcnInstallation,\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"socket.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/socket.feature.ts"],"sourcesContent":["import { socketConfigStub } from \"../stubs\";\r\nimport { FeatureDefinition } from \"./types\";\r\n\r\nexport const socketFeature: FeatureDefinition = {\r\n description: \"Installs socket.io for the realtime socket server (Socket Connector)\",\r\n dependencies: {\r\n \"socket.io\": \"^4.8.3\",\r\n },\r\n ejectConfig: {\r\n content: socketConfigStub,\r\n name: \"socket\",\r\n },\r\n};\r\n"],"mappings":";;;AAGA,MAAa,gBAAmC;CAC9C,aAAa;CACb,cAAc,EACZ,aAAa,SACf;CACA,aAAa;EACX,SAAS;EACT,MAAM;CACR;AACF"}
|
|
1
|
+
{"version":3,"file":"socket.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/socket.feature.ts"],"sourcesContent":["import { socketConfigStub } from \"../stubs\";\r\nimport type { FeatureDefinition } from \"./types\";\r\n\r\nexport const socketFeature: FeatureDefinition = {\r\n description: \"Installs socket.io for the realtime socket server (Socket Connector)\",\r\n dependencies: {\r\n \"socket.io\": \"^4.8.3\",\r\n },\r\n ejectConfig: {\r\n content: socketConfigStub,\r\n name: \"socket\",\r\n },\r\n};\r\n"],"mappings":";;;AAGA,MAAa,gBAAmC;CAC9C,aAAa;CACb,cAAc,EACZ,aAAa,SACf;CACA,aAAa;EACX,SAAS;EACT,MAAM;CACR;AACF"}
|