@warlock.js/core 5.5.0 → 5.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -0
- package/esm/application/app.d.mts.map +1 -1
- package/esm/application/app.mjs +7 -1
- package/esm/application/app.mjs.map +1 -1
- package/esm/benchmark/profiler.d.mts.map +1 -1
- package/esm/benchmark/profiler.mjs +4 -3
- package/esm/benchmark/profiler.mjs.map +1 -1
- package/esm/cli/cli-commands.manager.mjs +10 -7
- package/esm/cli/cli-commands.manager.mjs.map +1 -1
- package/esm/cli/cli-commands.utils.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/optional-peers.check.mjs.map +1 -1
- package/esm/cli/commands/doctor/checks/release-hygiene.check.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/column-dsl-parser.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/migration.generator.mjs.map +1 -1
- package/esm/cli/commands/routes/format-routes-table.mjs +3 -3
- package/esm/cli/commands/routes/format-routes-table.mjs.map +1 -1
- package/esm/cli/commands/routes/route-row.mjs.map +1 -1
- package/esm/cli/commands/routes.command.mjs.map +1 -1
- package/esm/cli/framework-cli-commands.mjs.map +1 -1
- package/esm/cli/parse-cli-args.mjs +1 -0
- package/esm/cli/parse-cli-args.mjs.map +1 -1
- package/esm/cli/string-similarity.mjs +13 -4
- package/esm/cli/string-similarity.mjs.map +1 -1
- package/esm/commands/cli-command.d.mts.map +1 -1
- package/esm/commands/cli-command.mjs +5 -2
- package/esm/commands/cli-command.mjs.map +1 -1
- package/esm/config/config-loader.mjs +3 -1
- package/esm/config/config-loader.mjs.map +1 -1
- package/esm/config/locale-configuration.mjs +11 -1
- package/esm/config/locale-configuration.mjs.map +1 -1
- package/esm/container/container-instance-registry.mjs +40 -0
- package/esm/container/container-instance-registry.mjs.map +1 -0
- package/esm/container/index.d.mts +11 -0
- package/esm/container/index.d.mts.map +1 -1
- package/esm/container/index.mjs +11 -0
- package/esm/container/index.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/dev-server/dependency-graph.d.mts.map +1 -1
- package/esm/dev-server/dependency-graph.mjs +2 -1
- package/esm/dev-server/dependency-graph.mjs.map +1 -1
- package/esm/dev-server/dev-logger.mjs +7 -5
- package/esm/dev-server/dev-logger.mjs.map +1 -1
- package/esm/dev-server/file-event-handler.mjs.map +1 -1
- package/esm/dev-server/files-orchestrator.mjs +1 -1
- package/esm/dev-server/files-orchestrator.mjs.map +1 -1
- package/esm/dev-server/layer-executor.mjs.map +1 -1
- package/esm/dev-server/loader/build-transpile-init.mjs.map +1 -1
- package/esm/dev-server/loader/load-hook.mjs.map +1 -1
- package/esm/dev-server/loader/register-loader.mjs.map +1 -1
- package/esm/dev-server/loader/resolve-hook.mjs.map +1 -1
- package/esm/dev-server/loader/transpile-cache.mjs.map +1 -1
- package/esm/dev-server/module-loader.mjs.map +1 -1
- package/esm/dev-server/parse-imports.mjs +22 -8
- package/esm/dev-server/parse-imports.mjs.map +1 -1
- package/esm/dev-server/type-generator.mjs.map +1 -1
- package/esm/dev-server/types.d.mts.map +1 -1
- package/esm/dev-server/utils.mjs.map +1 -1
- package/esm/encryption/encrypt.mjs +1 -0
- package/esm/encryption/encrypt.mjs.map +1 -1
- package/esm/errors/container-key-missing-error.mjs +28 -0
- package/esm/errors/container-key-missing-error.mjs.map +1 -0
- package/esm/errors/unknown-locale-error.mjs +28 -0
- package/esm/errors/unknown-locale-error.mjs.map +1 -0
- package/esm/generations/add-command.action.mjs +8 -2
- 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/notifications.feature.mjs.map +1 -1
- package/esm/generations/features/scheduler.feature.mjs.map +1 -1
- package/esm/generations/features/shared/patch-tsconfig-include.mjs.map +1 -1
- package/esm/generations/features/shared/relocate-conflicting-home-route.mjs +6 -1
- package/esm/generations/features/shared/relocate-conflicting-home-route.mjs.map +1 -1
- package/esm/generations/features/tailwind.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/context/request-memo.mjs.map +1 -1
- package/esm/http/middleware/cache-response-middleware.d.mts.map +1 -1
- package/esm/http/middleware/cache-response-middleware.mjs.map +1 -1
- package/esm/http/middleware/concurrency-limit.middleware.mjs.map +1 -1
- package/esm/http/middleware/idempotency.middleware.d.mts.map +1 -1
- package/esm/http/middleware/idempotency.middleware.mjs.map +1 -1
- package/esm/http/middleware/maintenance.middleware.mjs +1 -2
- package/esm/http/middleware/maintenance.middleware.mjs.map +1 -1
- package/esm/http/middleware/max-body-size.middleware.mjs.map +1 -1
- package/esm/http/middleware/rate-limit.middleware.mjs.map +1 -1
- package/esm/http/middleware/utils/cidr-match.d.mts.map +1 -1
- package/esm/http/middleware/utils/cidr-match.mjs +1 -0
- package/esm/http/middleware/utils/cidr-match.mjs.map +1 -1
- package/esm/http/port-preflight.d.mts.map +1 -1
- package/esm/http/port-preflight.mjs.map +1 -1
- package/esm/http/request.d.mts.map +1 -1
- package/esm/http/request.mjs +16 -9
- package/esm/http/request.mjs.map +1 -1
- package/esm/http/response.d.mts +38 -0
- package/esm/http/response.d.mts.map +1 -1
- package/esm/http/response.mjs +47 -0
- package/esm/http/response.mjs.map +1 -1
- package/esm/http/server.d.mts.map +1 -1
- package/esm/http/server.mjs.map +1 -1
- package/esm/http/types.d.mts.map +1 -1
- package/esm/image/image.d.mts.map +1 -1
- package/esm/image/image.mjs.map +1 -1
- package/esm/mail/send-mail.mjs.map +1 -1
- package/esm/production/build-contributions.mjs.map +1 -1
- package/esm/production/bundle-framework-dependencies.mjs +152 -0
- package/esm/production/bundle-framework-dependencies.mjs.map +1 -0
- package/esm/production/production-builder.mjs +30 -3
- package/esm/production/production-builder.mjs.map +1 -1
- package/esm/production/production-supervisor.mjs.map +1 -1
- package/esm/production/tsconfig-path-aliases.mjs +99 -0
- package/esm/production/tsconfig-path-aliases.mjs.map +1 -0
- package/esm/react/index.d.mts.map +1 -1
- package/esm/react/index.mjs.map +1 -1
- package/esm/repositories/adapters/cascade/filter-applicator.mjs +4 -2
- package/esm/repositories/adapters/cascade/filter-applicator.mjs.map +1 -1
- package/esm/repositories/contracts/query-builder.contract.d.mts.map +1 -1
- package/esm/repositories/contracts/types.d.mts.map +1 -1
- package/esm/resource/resource-field-builder.d.mts.map +1 -1
- package/esm/resource/resource-field-builder.mjs.map +1 -1
- package/esm/resource/types.d.mts.map +1 -1
- package/esm/router/positional-handler-diagnostics.d.mts.map +1 -1
- package/esm/router/positional-handler-diagnostics.mjs +9 -3
- package/esm/router/positional-handler-diagnostics.mjs.map +1 -1
- package/esm/router/route-registry.mjs +1 -1
- package/esm/router/route-registry.mjs.map +1 -1
- package/esm/router/router.d.mts.map +1 -1
- package/esm/router/router.mjs +1 -2
- package/esm/router/router.mjs.map +1 -1
- package/esm/router/types.d.mts.map +1 -1
- package/esm/storage/drivers/cloud-driver.d.mts.map +1 -1
- package/esm/storage/drivers/cloud-driver.mjs.map +1 -1
- package/esm/storage/scoped-storage.d.mts.map +1 -1
- package/esm/storage/scoped-storage.mjs +1 -0
- package/esm/storage/scoped-storage.mjs.map +1 -1
- package/esm/storage/storage.d.mts.map +1 -1
- package/esm/storage/storage.mjs +6 -2
- package/esm/storage/storage.mjs.map +1 -1
- package/esm/storage/types.d.mts.map +1 -1
- package/esm/storage/utils/contain-path.mjs.map +1 -1
- package/esm/storage/utils/safe-fetch.d.mts.map +1 -1
- package/esm/storage/utils/safe-fetch.mjs +6 -1
- package/esm/storage/utils/safe-fetch.mjs.map +1 -1
- package/esm/tests/start-http-development-server.d.mts.map +1 -1
- package/esm/tests/start-http-development-server.mjs.map +1 -1
- package/esm/tests/test-lifecycle-state.mjs.map +1 -1
- package/esm/tests/test-setup-timeout.mjs.map +1 -1
- package/esm/tests/vitest-setup.d.mts.map +1 -1
- package/esm/tests/vitest-setup.mjs.map +1 -1
- package/esm/use-cases/use-cases-registry.d.mts.map +1 -1
- package/esm/use-cases/use-cases-registry.mjs.map +1 -1
- package/esm/utils/app-log.mjs.map +1 -1
- package/esm/utils/queue.mjs.map +1 -1
- package/esm/utils/sleep.mjs.map +1 -1
- package/esm/utils/version-compare.d.mts.map +1 -1
- package/esm/utils/version-compare.mjs +12 -4
- package/esm/utils/version-compare.mjs.map +1 -1
- package/esm/validation/plugins/database-plugin.mjs.map +1 -1
- package/esm/vite/lower-stage3-decorators.mjs +1 -0
- package/esm/vite/lower-stage3-decorators.mjs.map +1 -1
- package/package.json +12 -12
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"encrypt.mjs","names":[],"sources":["../../../../../../../core/src/encryption/encrypt.ts"],"sourcesContent":["import crypto from \"crypto\";\nimport { config } from \"../config\";\nimport { type EncryptionConfigurations } from \"./types\";\n\n/**\n * Default encryption algorithm.\n */\nconst DEFAULT_ALGORITHM = \"aes-256-gcm\";\n\n/**\n * Get the encryption key from config, validated as a 32-byte hex string.\n */\nfunction getEncryptionKey(): Buffer {\n const key = config.key<EncryptionConfigurations[\"key\"]>(\"encryption.key\");\n\n if (!key) {\n throw new Error(\n \"Missing encryption key. Set 'encryption.key' in your config (64 hex characters = 32 bytes).\",\n );\n }\n\n const buffer = Buffer.from(key, \"hex\");\n\n if (buffer.length !== 32) {\n throw new Error(\n `Encryption key must be exactly 32 bytes (64 hex characters). Got ${buffer.length} bytes for key ${key}.`,\n );\n }\n\n return buffer;\n}\n\n/**\n * Get the configured encryption algorithm, defaults to aes-256-gcm.\n */\nfunction getAlgorithm(): string {\n return config.key<EncryptionConfigurations[\"algorithm\"]>(\n \"encryption.algorithm\",\n DEFAULT_ALGORITHM,\n )!;\n}\n\n/**\n * Encrypts a plaintext string using AES-256-GCM (or the configured algorithm).\n * Returns a combined string in the format `iv:ciphertext:authTag`.\n *\n * @example\n * import { encrypt } from \"@warlock.js/core\";\n *\n * const encrypted = encrypt(\"sk-proj-12345\");\n * // => \"a1b2c3...:d4e5f6...:g7h8i9...\"\n */\nexport function encrypt(plainText: string): string {\n if (!plainText) return plainText;\n\n const keyBuffer = getEncryptionKey();\n const algorithm = getAlgorithm();\n\n try {\n // IV must be unique per encryption. 16 bytes is standard for AES.\n const iv = crypto.randomBytes(16);\n const cipher = crypto.createCipheriv(algorithm, keyBuffer, iv);\n\n let encrypted = cipher.update(plainText, \"utf8\", \"hex\");\n encrypted += cipher.final(\"hex\");\n\n // Auth tag validates ciphertext integrity (tamper detection)\n const authTag = (cipher as crypto.CipherGCM).getAuthTag().toString(\"hex\");\n\n return `${iv.toString(\"hex\")}:${encrypted}:${authTag}`;\n } catch (error) {\n throw new Error(`Encryption failed: ${(error as Error).message}`);\n }\n}\n\n/**\n * Decrypts a string previously encrypted with {@link encrypt}.\n * Expects the format `iv:ciphertext:authTag`.\n *\n * @example\n * import { decrypt } from \"@warlock.js/core\";\n *\n * const original = decrypt(encryptedString);\n * // => \"sk-proj-12345\"\n */\nexport function decrypt(cipherText: string): string {\n if (!cipherText) return cipherText;\n\n const keyBuffer = getEncryptionKey();\n const algorithm = getAlgorithm();\n\n try {\n const parts = cipherText.split(\":\");\n\n if (parts.length !== 3) {\n throw new Error(\"Invalid encrypted format. Expected iv:ciphertext:authTag\");\n }\n\n const [ivHex, encryptedHex, authTagHex] = parts;\n\n const decipher = crypto.createDecipheriv(algorithm, keyBuffer, Buffer.from(ivHex, \"hex\"));\n\n (decipher as crypto.DecipherGCM).setAuthTag(Buffer.from(authTagHex, \"hex\"));\n\n let decrypted = decipher.update(encryptedHex, \"hex\", \"utf8\");\n // .final() throws if auth tag is invalid (data tampered)\n decrypted += decipher.final(\"utf8\");\n\n return decrypted;\n } catch (error) {\n throw new Error(`Decryption failed: ${(error as Error).message}`);\n }\n}\n"],"mappings":";;;;;;;;AAOA,MAAM,oBAAoB;;;;AAK1B,SAAS,mBAA2B;CAClC,MAAM,MAAM,OAAO,IAAqC,gBAAgB;CAExE,IAAI,CAAC,KACH,MAAM,IAAI,MACR,6FACF;CAGF,MAAM,SAAS,OAAO,KAAK,KAAK,KAAK;CAErC,IAAI,OAAO,WAAW,IACpB,MAAM,IAAI,MACR,oEAAoE,OAAO,OAAO,iBAAiB,IAAI,EACzG;CAGF,OAAO;AACT;;;;AAKA,SAAS,eAAuB;CAC9B,OAAO,OAAO,IACZ,wBACA,iBACF;AACF;;;;;;;;;;;AAYA,SAAgB,QAAQ,WAA2B;CACjD,IAAI,CAAC,WAAW,OAAO;CAEvB,MAAM,YAAY,iBAAiB;CACnC,MAAM,YAAY,aAAa;CAE/B,IAAI;EAEF,MAAM,KAAK,OAAO,YAAY,EAAE;EAChC,MAAM,SAAS,OAAO,eAAe,WAAW,WAAW,EAAE;EAE7D,IAAI,YAAY,OAAO,OAAO,WAAW,QAAQ,KAAK;EACtD,aAAa,OAAO,MAAM,KAAK;EAG/B,MAAM,UAAW,OAA4B,WAAW,CAAC,CAAC,SAAS,KAAK;EAExE,OAAO,GAAG,GAAG,SAAS,KAAK,EAAE,GAAG,UAAU,GAAG;CAC/C,SAAS,OAAO;EACd,MAAM,IAAI,MAAM,sBAAuB,MAAgB,SAAS;CAClE;AACF;;;;;;;;;;;AAYA,SAAgB,QAAQ,YAA4B;CAClD,IAAI,CAAC,YAAY,OAAO;CAExB,MAAM,YAAY,iBAAiB;CACnC,MAAM,YAAY,aAAa;CAE/B,IAAI;EACF,MAAM,QAAQ,WAAW,MAAM,GAAG;EAElC,IAAI,MAAM,WAAW,GACnB,MAAM,IAAI,MAAM,0DAA0D;EAG5E,MAAM,CAAC,OAAO,cAAc,cAAc;EAE1C,MAAM,WAAW,OAAO,iBAAiB,WAAW,WAAW,OAAO,KAAK,OAAO,KAAK,CAAC;EAExF,AAAC,SAAgC,WAAW,OAAO,KAAK,YAAY,KAAK,CAAC;EAE1E,IAAI,YAAY,SAAS,OAAO,cAAc,OAAO,MAAM;EAE3D,aAAa,SAAS,MAAM,MAAM;EAElC,OAAO;CACT,SAAS,OAAO;EACd,MAAM,IAAI,MAAM,sBAAuB,MAAgB,SAAS;CAClE;AACF"}
|
|
1
|
+
{"version":3,"file":"encrypt.mjs","names":[],"sources":["../../../../../../../core/src/encryption/encrypt.ts"],"sourcesContent":["import crypto from \"crypto\";\nimport { config } from \"../config\";\nimport { type EncryptionConfigurations } from \"./types\";\n\n/**\n * Default encryption algorithm.\n */\nconst DEFAULT_ALGORITHM = \"aes-256-gcm\";\n\n/**\n * Get the encryption key from config, validated as a 32-byte hex string.\n */\nfunction getEncryptionKey(): Buffer {\n const key = config.key<EncryptionConfigurations[\"key\"]>(\"encryption.key\");\n\n if (!key) {\n throw new Error(\n \"Missing encryption key. Set 'encryption.key' in your config (64 hex characters = 32 bytes).\",\n );\n }\n\n const buffer = Buffer.from(key, \"hex\");\n\n if (buffer.length !== 32) {\n throw new Error(\n `Encryption key must be exactly 32 bytes (64 hex characters). Got ${buffer.length} bytes for key ${key}.`,\n );\n }\n\n return buffer;\n}\n\n/**\n * Get the configured encryption algorithm, defaults to aes-256-gcm.\n */\nfunction getAlgorithm(): string {\n return config.key<EncryptionConfigurations[\"algorithm\"]>(\n \"encryption.algorithm\",\n DEFAULT_ALGORITHM,\n )!;\n}\n\n/**\n * Encrypts a plaintext string using AES-256-GCM (or the configured algorithm).\n * Returns a combined string in the format `iv:ciphertext:authTag`.\n *\n * @example\n * import { encrypt } from \"@warlock.js/core\";\n *\n * const encrypted = encrypt(\"sk-proj-12345\");\n * // => \"a1b2c3...:d4e5f6...:g7h8i9...\"\n */\nexport function encrypt(plainText: string): string {\n if (!plainText) return plainText;\n\n const keyBuffer = getEncryptionKey();\n const algorithm = getAlgorithm();\n\n try {\n // IV must be unique per encryption. 16 bytes is standard for AES.\n const iv = crypto.randomBytes(16);\n const cipher = crypto.createCipheriv(algorithm, keyBuffer, iv);\n\n let encrypted = cipher.update(plainText, \"utf8\", \"hex\");\n encrypted += cipher.final(\"hex\");\n\n // Auth tag validates ciphertext integrity (tamper detection)\n const authTag = (cipher as crypto.CipherGCM).getAuthTag().toString(\"hex\");\n\n return `${iv.toString(\"hex\")}:${encrypted}:${authTag}`;\n } catch (error) {\n throw new Error(`Encryption failed: ${(error as Error).message}`);\n }\n}\n\n/**\n * Decrypts a string previously encrypted with {@link encrypt}.\n * Expects the format `iv:ciphertext:authTag`.\n *\n * @example\n * import { decrypt } from \"@warlock.js/core\";\n *\n * const original = decrypt(encryptedString);\n * // => \"sk-proj-12345\"\n */\nexport function decrypt(cipherText: string): string {\n if (!cipherText) return cipherText;\n\n const keyBuffer = getEncryptionKey();\n const algorithm = getAlgorithm();\n\n try {\n const parts = cipherText.split(\":\");\n\n if (parts.length !== 3) {\n throw new Error(\"Invalid encrypted format. Expected iv:ciphertext:authTag\");\n }\n\n const [ivHex, encryptedHex, authTagHex] = parts;\n\n if (ivHex === undefined || encryptedHex === undefined || authTagHex === undefined) {\n throw new Error(\"Invalid encrypted format. Expected iv:ciphertext:authTag\");\n }\n\n const decipher = crypto.createDecipheriv(algorithm, keyBuffer, Buffer.from(ivHex, \"hex\"));\n\n (decipher as crypto.DecipherGCM).setAuthTag(Buffer.from(authTagHex, \"hex\"));\n\n let decrypted = decipher.update(encryptedHex, \"hex\", \"utf8\");\n // .final() throws if auth tag is invalid (data tampered)\n decrypted += decipher.final(\"utf8\");\n\n return decrypted;\n } catch (error) {\n throw new Error(`Decryption failed: ${(error as Error).message}`);\n }\n}\n"],"mappings":";;;;;;;;AAOA,MAAM,oBAAoB;;;;AAK1B,SAAS,mBAA2B;CAClC,MAAM,MAAM,OAAO,IAAqC,gBAAgB;CAExE,IAAI,CAAC,KACH,MAAM,IAAI,MACR,6FACF;CAGF,MAAM,SAAS,OAAO,KAAK,KAAK,KAAK;CAErC,IAAI,OAAO,WAAW,IACpB,MAAM,IAAI,MACR,oEAAoE,OAAO,OAAO,iBAAiB,IAAI,EACzG;CAGF,OAAO;AACT;;;;AAKA,SAAS,eAAuB;CAC9B,OAAO,OAAO,IACZ,wBACA,iBACF;AACF;;;;;;;;;;;AAYA,SAAgB,QAAQ,WAA2B;CACjD,IAAI,CAAC,WAAW,OAAO;CAEvB,MAAM,YAAY,iBAAiB;CACnC,MAAM,YAAY,aAAa;CAE/B,IAAI;EAEF,MAAM,KAAK,OAAO,YAAY,EAAE;EAChC,MAAM,SAAS,OAAO,eAAe,WAAW,WAAW,EAAE;EAE7D,IAAI,YAAY,OAAO,OAAO,WAAW,QAAQ,KAAK;EACtD,aAAa,OAAO,MAAM,KAAK;EAG/B,MAAM,UAAW,OAA4B,WAAW,CAAC,CAAC,SAAS,KAAK;EAExE,OAAO,GAAG,GAAG,SAAS,KAAK,EAAE,GAAG,UAAU,GAAG;CAC/C,SAAS,OAAO;EACd,MAAM,IAAI,MAAM,sBAAuB,MAAgB,SAAS;CAClE;AACF;;;;;;;;;;;AAYA,SAAgB,QAAQ,YAA4B;CAClD,IAAI,CAAC,YAAY,OAAO;CAExB,MAAM,YAAY,iBAAiB;CACnC,MAAM,YAAY,aAAa;CAE/B,IAAI;EACF,MAAM,QAAQ,WAAW,MAAM,GAAG;EAElC,IAAI,MAAM,WAAW,GACnB,MAAM,IAAI,MAAM,0DAA0D;EAG5E,MAAM,CAAC,OAAO,cAAc,cAAc;EAE1C,IAAI,UAAU,UAAa,iBAAiB,UAAa,eAAe,QACtE,MAAM,IAAI,MAAM,0DAA0D;EAG5E,MAAM,WAAW,OAAO,iBAAiB,WAAW,WAAW,OAAO,KAAK,OAAO,KAAK,CAAC;EAExF,AAAC,SAAgC,WAAW,OAAO,KAAK,YAAY,KAAK,CAAC;EAE1E,IAAI,YAAY,SAAS,OAAO,cAAc,OAAO,MAAM;EAE3D,aAAa,SAAS,MAAM,MAAM;EAElC,OAAO;CACT,SAAS,OAAO;EACd,MAAM,IAAI,MAAM,sBAAuB,MAAgB,SAAS;CAClE;AACF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
//#region ../core/src/errors/container-key-missing-error.ts
|
|
2
|
+
/**
|
|
3
|
+
* Thrown by `container.getOrFail(key)` when `key` is not registered.
|
|
4
|
+
*
|
|
5
|
+
* On a single-instance runtime this is an ordinary "you forgot to register
|
|
6
|
+
* this" error, worded exactly as before. When more than one copy of
|
|
7
|
+
* `@warlock.js/core`'s container module is loaded in the same process —
|
|
8
|
+
* confirmed via `container-instance-registry.ts`, which every loaded copy
|
|
9
|
+
* registers itself into through `globalThis` — the miss may instead mean
|
|
10
|
+
* the value WAS set, just on the other instance. That is a dev/
|
|
11
|
+
* source-consumption condition only: a published install resolves to a
|
|
12
|
+
* single `node_modules` copy, so a single instance is always registered
|
|
13
|
+
* there.
|
|
14
|
+
*/
|
|
15
|
+
var ContainerKeyMissingError = class ContainerKeyMissingError extends Error {
|
|
16
|
+
constructor(key, instanceCount) {
|
|
17
|
+
super(ContainerKeyMissingError.buildMessage(key, instanceCount));
|
|
18
|
+
this.name = "ContainerKeyMissingError";
|
|
19
|
+
}
|
|
20
|
+
static buildMessage(key, instanceCount) {
|
|
21
|
+
if (instanceCount <= 1) return `Container key "${key}" is not registered.`;
|
|
22
|
+
return `Container key "${key}" is not registered on this instance, but ${instanceCount} separate copies of @warlock.js/core's container are currently loaded in this process. The value may have been set on one of the other instances, not this one — this happens when a source-consumption dev path (e.g. Vite's SSR module runner alongside Node's ESM loader) evaluates the package more than once. A published install resolves to a single copy and is not affected.`;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
//#endregion
|
|
27
|
+
export { ContainerKeyMissingError };
|
|
28
|
+
//# sourceMappingURL=container-key-missing-error.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"container-key-missing-error.mjs","names":[],"sources":["../../../../../../../core/src/errors/container-key-missing-error.ts"],"sourcesContent":["/**\n * Thrown by `container.getOrFail(key)` when `key` is not registered.\n *\n * On a single-instance runtime this is an ordinary \"you forgot to register\n * this\" error, worded exactly as before. When more than one copy of\n * `@warlock.js/core`'s container module is loaded in the same process —\n * confirmed via `container-instance-registry.ts`, which every loaded copy\n * registers itself into through `globalThis` — the miss may instead mean\n * the value WAS set, just on the other instance. That is a dev/\n * source-consumption condition only: a published install resolves to a\n * single `node_modules` copy, so a single instance is always registered\n * there.\n */\nexport class ContainerKeyMissingError extends Error {\n public constructor(key: string, instanceCount: number) {\n super(ContainerKeyMissingError.buildMessage(key, instanceCount));\n this.name = \"ContainerKeyMissingError\";\n }\n\n private static buildMessage(key: string, instanceCount: number): string {\n if (instanceCount <= 1) {\n return `Container key \"${key}\" is not registered.`;\n }\n\n return (\n `Container key \"${key}\" is not registered on this instance, but ${instanceCount} ` +\n `separate copies of @warlock.js/core's container are currently loaded in this ` +\n `process. The value may have been set on one of the other instances, not this ` +\n `one — this happens when a source-consumption dev path (e.g. Vite's SSR module ` +\n `runner alongside Node's ESM loader) evaluates the package more than once. A ` +\n `published install resolves to a single copy and is not affected.`\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AAaA,IAAa,2BAAb,MAAa,iCAAiC,MAAM;CAClD,AAAO,YAAY,KAAa,eAAuB;EACrD,MAAM,yBAAyB,aAAa,KAAK,aAAa,CAAC;EAC/D,KAAK,OAAO;CACd;CAEA,OAAe,aAAa,KAAa,eAA+B;EACtE,IAAI,iBAAiB,GACnB,OAAO,kBAAkB,IAAI;EAG/B,OACE,kBAAkB,IAAI,4CAA4C,cAAc;CAOpF;AACF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
//#region ../core/src/errors/unknown-locale-error.ts
|
|
2
|
+
/**
|
|
3
|
+
* Thrown by `response.setLocale(locale)` when `locale` is outside the
|
|
4
|
+
* application's declared `app.localeCodes` allow-list.
|
|
5
|
+
*
|
|
6
|
+
* Silently writing a locale cookie for a locale the app cannot serve is a
|
|
7
|
+
* NEW silent failure — `request.locale` would read the cookie back, fail its
|
|
8
|
+
* own allow-list check (`Request.cacheLocale`), and fall back to the default
|
|
9
|
+
* with nothing telling the caller why the switch never took. Failing here,
|
|
10
|
+
* at the write, names the mistake at its source instead of downstream.
|
|
11
|
+
*
|
|
12
|
+
* Only raised when `app.localeCodes` is declared — with no list declared
|
|
13
|
+
* there is nothing to fail against, matching `Request.cacheLocale`'s own
|
|
14
|
+
* fail-open rule.
|
|
15
|
+
*/
|
|
16
|
+
var UnknownLocaleError = class UnknownLocaleError extends Error {
|
|
17
|
+
constructor(locale, localeCodes) {
|
|
18
|
+
super(UnknownLocaleError.buildMessage(locale, localeCodes));
|
|
19
|
+
this.name = "UnknownLocaleError";
|
|
20
|
+
}
|
|
21
|
+
static buildMessage(locale, localeCodes) {
|
|
22
|
+
return `Cannot set locale "${locale}": it is not in the configured "app.localeCodes" list (${localeCodes.map((code) => `"${code}"`).join(", ")}). Add "${locale}" to "app.localeCodes" or pass one of the configured locales instead.`;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
//#endregion
|
|
27
|
+
export { UnknownLocaleError };
|
|
28
|
+
//# sourceMappingURL=unknown-locale-error.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unknown-locale-error.mjs","names":[],"sources":["../../../../../../../core/src/errors/unknown-locale-error.ts"],"sourcesContent":["/**\n * Thrown by `response.setLocale(locale)` when `locale` is outside the\n * application's declared `app.localeCodes` allow-list.\n *\n * Silently writing a locale cookie for a locale the app cannot serve is a\n * NEW silent failure — `request.locale` would read the cookie back, fail its\n * own allow-list check (`Request.cacheLocale`), and fall back to the default\n * with nothing telling the caller why the switch never took. Failing here,\n * at the write, names the mistake at its source instead of downstream.\n *\n * Only raised when `app.localeCodes` is declared — with no list declared\n * there is nothing to fail against, matching `Request.cacheLocale`'s own\n * fail-open rule.\n */\nexport class UnknownLocaleError extends Error {\n public constructor(locale: string, localeCodes: string[]) {\n super(UnknownLocaleError.buildMessage(locale, localeCodes));\n this.name = \"UnknownLocaleError\";\n }\n\n private static buildMessage(locale: string, localeCodes: string[]): string {\n return (\n `Cannot set locale \"${locale}\": it is not in the configured \"app.localeCodes\" list ` +\n `(${localeCodes.map((code) => `\"${code}\"`).join(\", \")}). Add \"${locale}\" to ` +\n `\"app.localeCodes\" or pass one of the configured locales instead.`\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;AAcA,IAAa,qBAAb,MAAa,2BAA2B,MAAM;CAC5C,AAAO,YAAY,QAAgB,aAAuB;EACxD,MAAM,mBAAmB,aAAa,QAAQ,WAAW,CAAC;EAC1D,KAAK,OAAO;CACd;CAEA,OAAe,aAAa,QAAgB,aAA+B;EACzE,OACE,sBAAsB,OAAO,yDACzB,YAAY,KAAK,SAAS,IAAI,KAAK,EAAE,CAAC,CAAC,KAAK,IAAI,EAAE,UAAU,OAAO;CAG3E;AACF"}
|
|
@@ -26,6 +26,7 @@ function resolveFeatures(features, visited = /* @__PURE__ */ new Set()) {
|
|
|
26
26
|
if (visited.has(feature)) continue;
|
|
27
27
|
visited.add(feature);
|
|
28
28
|
const def = featuresMap[feature];
|
|
29
|
+
if (def === void 0) continue;
|
|
29
30
|
if (def.requires?.length) resolved.push(...resolveFeatures(def.requires, visited));
|
|
30
31
|
resolved.push(feature);
|
|
31
32
|
}
|
|
@@ -36,7 +37,11 @@ async function addCommandAction(options) {
|
|
|
36
37
|
const { packageManager, list, noInstall } = options.options;
|
|
37
38
|
if (list) {
|
|
38
39
|
console.log("Available Features:");
|
|
39
|
-
for (const feature of allowedFeatures)
|
|
40
|
+
for (const feature of allowedFeatures) {
|
|
41
|
+
const definition = featuresMap[feature];
|
|
42
|
+
if (definition === void 0) continue;
|
|
43
|
+
console.log(`- ${colors.yellowBright(feature)}: ${colors.green(definition.description)}`);
|
|
44
|
+
}
|
|
40
45
|
process.exit(0);
|
|
41
46
|
}
|
|
42
47
|
validateFeatures(features);
|
|
@@ -47,6 +52,7 @@ async function addCommandAction(options) {
|
|
|
47
52
|
const scripts = {};
|
|
48
53
|
for (const feature of resolvedFeatures) {
|
|
49
54
|
const featurePackages = featuresMap[feature];
|
|
55
|
+
if (featurePackages === void 0) continue;
|
|
50
56
|
Object.assign(dependencies, featurePackages.dependencies);
|
|
51
57
|
if (featurePackages.devDependencies) Object.assign(devDependencies, featurePackages.devDependencies);
|
|
52
58
|
if (featurePackages.ejectConfig) ejectConfigs[featurePackages.ejectConfig.name] = featurePackages.ejectConfig;
|
|
@@ -77,7 +83,7 @@ async function addCommandAction(options) {
|
|
|
77
83
|
}
|
|
78
84
|
for (const feature of resolvedFeatures) {
|
|
79
85
|
const featurePackages = featuresMap[feature];
|
|
80
|
-
if (featurePackages
|
|
86
|
+
if (featurePackages?.onExecuting) await featurePackages.onExecuting(options);
|
|
81
87
|
}
|
|
82
88
|
if (Object.keys(scripts).length > 0) {
|
|
83
89
|
console.log(`Adding scripts ${colors.magenta(Object.keys(scripts).join(", "))}`);
|
|
@@ -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 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
|
+
{"version":3,"file":"add-command.action.mjs","names":[],"sources":["../../../../../../../core/src/generations/add-command.action.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport { fileExistsAsync, getJsonFileAsync, putFileAsync, putJsonFileAsync } from \"@warlock.js/fs\";\r\nimport { execSync } from \"node:child_process\";\r\nimport type { CommandActionData } from \"../commands/types\";\r\nimport {\r\n detectPackageManager,\r\n getAddCommand,\r\n getExactAddCommand,\r\n type PackageManager,\r\n} from \"../updater/package-manager\";\r\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);\r\n\r\nfunction isWarlockPackage(packageName: string): boolean {\r\n return packageName.startsWith(\"@warlock.js/\");\r\n}\r\n\r\n/**\r\n * Resolve the internal feature-map placeholder to the version of Core that is\r\n * actually executing the command. Every @warlock.js package is lockstep, so\r\n * Core is the single source of truth; non-Warlock dependencies are untouched.\r\n */\r\nexport function resolveWarlockDependencyVersions(\r\n dependencies: Record<string, string>,\r\n frameworkVersion: string,\r\n): void {\r\n for (const dependency of Object.keys(dependencies)) {\r\n if (isWarlockPackage(dependency)) {\r\n dependencies[dependency] = frameworkVersion;\r\n }\r\n }\r\n}\r\n\r\nfunction resolveFeatures(features: string[], visited = new Set<string>()): string[] {\r\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 // `addCommandAction` runs `validateFeatures()` before calling this, and the\r\n // recursive calls below pass `requires` entries that come out of the map\r\n // itself — so every lookup resolves. Skipping an unresolvable one keeps a\r\n // typo from crashing the command with `Cannot read properties of\r\n // undefined`, which names neither the feature nor the fact that it was the\r\n // ARGUMENT that was wrong.\r\n if (def === undefined) continue;\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 const definition = featuresMap[feature];\r\n\r\n // `allowedFeatures` is derived from this same map, so this resolves.\r\n if (definition === undefined) continue;\r\n\r\n console.log(\r\n `- ${colors.yellowBright(feature)}: ${colors.green(definition.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\r\n // Validated upstream; see the note in resolveFeatures above.\r\n if (featurePackages === undefined) continue;\r\n\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\r\n // the feature map's static range.\r\n const frameworkVersion = await getWarlockVersion();\r\n resolveWarlockDependencyVersions(dependencies, frameworkVersion);\r\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(\r\n packageManager as PackageManager | undefined,\r\n dependencies,\r\n devDependencies,\r\n );\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\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(\r\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());\r\n const packageManagerCommand = getAddCommand(resolvedPackageManager);\r\n const exactPackageManagerCommand = getExactAddCommand(resolvedPackageManager);\r\n\r\n const installDependencySet = (dependencySet: Record<string, string>, development: boolean) => {\r\n const regularSpecs: string[] = [];\r\n const exactWarlockSpecs: string[] = [];\r\n\r\n for (const [name, version] of Object.entries(dependencySet)) {\r\n const spec = `${name}@${version}`;\r\n (isWarlockPackage(name) ? exactWarlockSpecs : regularSpecs).push(spec);\r\n }\r\n\r\n const developmentFlag = development ? \" -D\" : \"\";\r\n const runInstall = (command: string, specs: string[]) => {\r\n if (specs.length === 0) return;\r\n\r\n execSync(`${command} ${specs.join(\" \")}${developmentFlag}`, {\r\n cwd: process.cwd(),\r\n stdio: \"inherit\",\r\n });\r\n };\r\n\r\n runInstall(packageManagerCommand, regularSpecs);\r\n runInstall(exactPackageManagerCommand, exactWarlockSpecs);\r\n };\r\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);\r\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);\r\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[]) {\r\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 }\r\n}\r\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 {
|
|
1
|
+
{"version":3,"file":"access.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/access.feature.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport { ensureDirectoryAsync, fileExistsAsync, getFileAsync, putFileAsync } 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\";\r\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(\r\n `${colors.green(\"✓\")} Created src/app/shared/utils/locales.ts with the access locale`,\r\n );\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(srcPath(\"app/access/models/user-role/index.ts\"), accessUserRoleModelIndexStub);\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,\r\n },\r\n ejectConfig: {\r\n content: accessConfigStub,\r\n name: \"access\",\r\n },\r\n onExecuting: completeAccessInstallation,\r\n};\r\n"],"mappings":";;;;;;;;;AAiBA,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,IACN,GAAG,OAAO,MAAM,GAAG,EAAE,gEACvB;AACF;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,aAAa,QAAQ,sCAAsC,GAAG,4BAA4B;CAChG,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 { 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
|
+
{"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\";\r\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,\r\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 { 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
|
+
{"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\";\r\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,\r\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 { 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
|
+
{"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\";\r\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,\r\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 { 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
|
+
{"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\";\r\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,\r\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 { 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
|
+
{"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\";\r\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,\r\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 { 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
|
+
{"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\";\r\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,\r\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 { 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
|
+
{"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\";\r\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,\r\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 { 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
|
+
{"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\";\r\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,\r\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 { 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
|
+
{"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\";\r\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,\r\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 { 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
|
+
{"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\";\r\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,\r\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":"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(
|
|
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\";\r\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(`${colors.green(\"✓\")} Created src/app/notifications/migrations/${migrationFile}`);\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,\r\n \"@warlock.js/auth\": INSTALLED_WARLOCK_VERSION,\r\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,IAAI,GAAG,OAAO,MAAM,GAAG,EAAE,4CAA4C,eAAe;CAI5F,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":"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
|
+
{"version":3,"file":"scheduler.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/scheduler.feature.ts"],"sourcesContent":["import { type FeatureDefinition, INSTALLED_WARLOCK_VERSION } from \"./types\";\r\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,\r\n },\r\n};\r\n"],"mappings":";;;AAEA,MAAa,mBAAsC;CACjD,aAAa;CACb,cAAc,EACZ,yBAAyB,0BAC3B;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"patch-tsconfig-include.mjs","names":[],"sources":["../../../../../../../../../core/src/generations/features/shared/patch-tsconfig-include.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\nimport { fileExistsAsync, getFileAsync, putFileAsync } from \"@warlock.js/fs\";\nimport { rootPath } from \"../../../utils\";\n\n/** What {@link insertIncludeEntry} did, or could not do, to the source it was given. */\nexport type IncludeInsertion =\n
|
|
1
|
+
{"version":3,"file":"patch-tsconfig-include.mjs","names":[],"sources":["../../../../../../../../../core/src/generations/features/shared/patch-tsconfig-include.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\nimport { fileExistsAsync, getFileAsync, putFileAsync } from \"@warlock.js/fs\";\nimport { rootPath } from \"../../../utils\";\n\n/** What {@link insertIncludeEntry} did, or could not do, to the source it was given. */\nexport type IncludeInsertion =\n { status: \"added\"; next: string } | { status: \"already-present\" } | { status: \"unrecognised\" };\n\n/**\n * Insert one entry into the `include` array of a tsconfig's SOURCE TEXT.\n *\n * **String surgery, never parse-and-write.** The project template's\n * `tsconfig.json` carries `//` comments — it is JSONC, which TypeScript accepts\n * and `JSON.parse` rejects. A parse-first patch therefore throws on exactly the\n * projects a feature is generated into: `warlock add react-email` failed on\n * every freshly scaffolded app with\n *\n * ```\n * ✖ add <features...> failed\n * Expected double-quoted property name in JSON at position 454 (line 17 column 5)\n * ```\n *\n * where line 17 is the comment explaining the project's `moduleResolution`. And\n * even where a parse succeeded, writing the object back would silently delete\n * every comment in the file — comments that are load-bearing documentation\n * there.\n *\n * `shadcn.feature.ts`'s `addWebPathAlias` already reached this conclusion for\n * `compilerOptions.paths`; this is the same rule for `include`, kept in one\n * place so the next feature that needs it cannot get it wrong again.\n *\n * @param source The tsconfig file's current text.\n * @param entry The include entry to add, e.g. `\"emails\"`.\n * @returns What happened, and the new text when there is any.\n */\nexport function insertIncludeEntry(source: string, entry: string): IncludeInsertion {\n // Matches the entry whichever quote style the file uses, so a re-run against a\n // hand-edited tsconfig does not stack a second copy.\n if (new RegExp(`[\"']${escapeForRegExp(entry)}[\"']`).test(source)) {\n return { status: \"already-present\" };\n }\n\n const includeArray = /\"include\"\\s*:\\s*\\[/.exec(source);\n\n if (!includeArray) {\n return { status: \"unrecognised\" };\n }\n\n const insertAt = includeArray.index + includeArray[0].length;\n\n return {\n status: \"added\",\n next: `${source.slice(0, insertAt)}${JSON.stringify(entry)}, ${source.slice(insertAt)}`,\n };\n}\n\n/**\n * Add one entry to the project's `tsconfig.json` `include` array.\n *\n * Never throws. A tsconfig this cannot patch produces a printed instruction the\n * developer can follow by hand — a feature must not abort a whole `add` because\n * one optional convenience could not be applied.\n *\n * @param entry The include entry to add, e.g. `\"emails\"`.\n * @param reason One line explaining what breaks without it, printed when the\n * patch has to be handed back to the developer.\n */\nexport async function patchTsconfigInclude(entry: string, reason: string): Promise<void> {\n const tsconfigPath = rootPath(\"tsconfig.json\");\n\n const printManualInstruction = (problem: string) => {\n console.log(\n `${colors.yellowBright(\"!\")} ${colors.yellowBright(\"tsconfig.json\")} ${problem} — ` +\n `add ${JSON.stringify(entry)} to its \\`include\\` array yourself.\\n ${reason}`,\n );\n };\n\n if (!(await fileExistsAsync(tsconfigPath))) {\n printManualInstruction(\"not found\");\n\n return;\n }\n\n const insertion = insertIncludeEntry(await getFileAsync(tsconfigPath), entry);\n\n if (insertion.status === \"already-present\") {\n return;\n }\n\n if (insertion.status === \"unrecognised\") {\n printManualInstruction(\"has no recognisable `include` array\");\n\n return;\n }\n\n await putFileAsync(tsconfigPath, insertion.next);\n console.log(`${colors.green(\"✓\")} Added ${JSON.stringify(entry)} to tsconfig.json include`);\n}\n\n/**\n * Escape a literal string for embedding in a regular expression.\n *\n * @param value The literal to escape.\n * @returns The escaped literal.\n */\nfunction escapeForRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,SAAgB,mBAAmB,QAAgB,OAAiC;CAGlF,IAAI,IAAI,OAAO,OAAO,gBAAgB,KAAK,EAAE,KAAK,CAAC,CAAC,KAAK,MAAM,GAC7D,OAAO,EAAE,QAAQ,kBAAkB;CAGrC,MAAM,eAAe,qBAAqB,KAAK,MAAM;CAErD,IAAI,CAAC,cACH,OAAO,EAAE,QAAQ,eAAe;CAGlC,MAAM,WAAW,aAAa,QAAQ,aAAa,EAAE,CAAC;CAEtD,OAAO;EACL,QAAQ;EACR,MAAM,GAAG,OAAO,MAAM,GAAG,QAAQ,IAAI,KAAK,UAAU,KAAK,EAAE,IAAI,OAAO,MAAM,QAAQ;CACtF;AACF;;;;;;;;;;;;AAaA,eAAsB,qBAAqB,OAAe,QAA+B;CACvF,MAAM,eAAe,SAAS,eAAe;CAE7C,MAAM,0BAA0B,YAAoB;EAClD,QAAQ,IACN,GAAG,OAAO,aAAa,GAAG,EAAE,GAAG,OAAO,aAAa,eAAe,EAAE,GAAG,QAAQ,SACtE,KAAK,UAAU,KAAK,EAAE,yCAAyC,QAC1E;CACF;CAEA,IAAI,CAAE,MAAM,gBAAgB,YAAY,GAAI;EAC1C,uBAAuB,WAAW;EAElC;CACF;CAEA,MAAM,YAAY,mBAAmB,MAAM,aAAa,YAAY,GAAG,KAAK;CAE5E,IAAI,UAAU,WAAW,mBACvB;CAGF,IAAI,UAAU,WAAW,gBAAgB;EACvC,uBAAuB,qCAAqC;EAE5D;CACF;CAEA,MAAM,aAAa,cAAc,UAAU,IAAI;CAC/C,QAAQ,IAAI,GAAG,OAAO,MAAM,GAAG,EAAE,SAAS,KAAK,UAAU,KAAK,EAAE,0BAA0B;AAC5F;;;;;;;AAQA,SAAS,gBAAgB,OAAuB;CAC9C,OAAO,MAAM,QAAQ,uBAAuB,MAAM;AACpD"}
|
|
@@ -50,7 +50,12 @@ function resolveHomeRouteCollision(files) {
|
|
|
50
50
|
},
|
|
51
51
|
rewrites: []
|
|
52
52
|
};
|
|
53
|
-
const
|
|
53
|
+
const claimant = claimants[0];
|
|
54
|
+
if (claimant === void 0) return {
|
|
55
|
+
collision: { outcome: "absent" },
|
|
56
|
+
rewrites: []
|
|
57
|
+
};
|
|
58
|
+
const { file, matches } = claimant;
|
|
54
59
|
if (matches.length > 1) return {
|
|
55
60
|
collision: {
|
|
56
61
|
outcome: "conflict",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relocate-conflicting-home-route.mjs","names":[],"sources":["../../../../../../../../../core/src/generations/features/shared/relocate-conflicting-home-route.ts"],"sourcesContent":["import { getFileAsync, putFileAsync } from \"@warlock.js/fs\";\nimport glob from \"fast-glob\";\nimport { srcPath } from \"../../../utils\";\n\n/**\n * A TOP-LEVEL `router.get(\"/\", ...)` — anchored at column 0 on purpose.\n *\n * Routes nested in a `router.group({ prefix: \"/x\" }, ...)` are indented by every\n * formatter this codebase runs, and their real path is `/x`, not `/`. Anchoring\n * is what keeps the notifications feature's own `router.get(\"/\", ...)` (inside\n * the `/notifications` group) from reading as a homepage collision.\n *\n * Only the path literal is captured. The handler — a bare identifier in the\n * template, but possibly an inline arrow spanning lines — is never matched, so\n * the rewrite below cannot damage it.\n */\nconst TOP_LEVEL_ROOT_GET = /^router\\s*\\.\\s*get\\(\\s*([\"'`])\\/\\1/gm;\n\n/**\n * Whether `/welcome` is already spoken for, so relocating onto it would trade\n * one duplicate-route 500 for another.\n */\nconst TOP_LEVEL_WELCOME_GET = /^router\\s*\\.\\s*get\\(\\s*([\"'`])\\/welcome\\1/m;\n\nexport type HomeRouteCollision =\n /** Nothing under `src/app` claims a top-level `/` — write the page as normal. */\n | { outcome: \"absent\" }\n /** The one file that claimed `/` was moved to `/welcome`; the page is safe to write. */\n | { outcome: \"relocated\"; relativePath: string }\n /** Something claims `/` that we will not rewrite. The page is NOT written. */\n | { outcome: \"conflict\"; reason: string }\n /** We tried to relocate and could not. The page is NOT written. */\n | { outcome: \"failed\"; reason: string };\n\n/** One routes source file, as read from disk (or handed to the pure resolver in a test). */\nexport type RoutesFileSource = {\n relativePath: string;\n source: string;\n};\n\n/** What {@link resolveHomeRouteCollision} decided, plus the sources it rewrote. */\nexport type HomeRouteResolution = {\n collision: HomeRouteCollision;\n rewrites: RoutesFileSource[];\n};\n\n/**\n * Decide what to do about `src/app/**\\/routes.{ts,tsx}` claiming `/`, and\n * produce the rewritten source when exactly one file does.\n *\n * Pure by design: no filesystem access here, so this is the part a test can\n * exercise directly without touching disk. {@link relocateConflictingHomeRoute}\n * is the thin I/O wrapper — it reads the files, calls this, and writes back\n * whatever this returns in `rewrites`.\n *\n * @param files Every `routes.{ts,tsx}` file under `src/app`, with its current text.\n * @returns The collision outcome, and the (possibly empty) list of files to write back.\n */\nexport function resolveHomeRouteCollision(files: RoutesFileSource[]): HomeRouteResolution {\n const claimants = files\n .map((file) => ({ file, matches: file.source.match(TOP_LEVEL_ROOT_GET) ?? [] }))\n .filter(({ matches }) => matches.length > 0);\n\n if (claimants.length === 0) {\n return { collision: { outcome: \"absent\" }, rewrites: [] };\n }\n\n if (claimants.length > 1) {\n const names = claimants.map(({ file }) => file.relativePath).join(\", \");\n\n return {\n collision: {\n outcome: \"conflict\",\n reason: `multiple files declare a top-level GET \"/\" (${names})`,\n },\n rewrites: [],\n };\n }\n\n const [{ file, matches }
|
|
1
|
+
{"version":3,"file":"relocate-conflicting-home-route.mjs","names":[],"sources":["../../../../../../../../../core/src/generations/features/shared/relocate-conflicting-home-route.ts"],"sourcesContent":["import { getFileAsync, putFileAsync } from \"@warlock.js/fs\";\nimport glob from \"fast-glob\";\nimport { srcPath } from \"../../../utils\";\n\n/**\n * A TOP-LEVEL `router.get(\"/\", ...)` — anchored at column 0 on purpose.\n *\n * Routes nested in a `router.group({ prefix: \"/x\" }, ...)` are indented by every\n * formatter this codebase runs, and their real path is `/x`, not `/`. Anchoring\n * is what keeps the notifications feature's own `router.get(\"/\", ...)` (inside\n * the `/notifications` group) from reading as a homepage collision.\n *\n * Only the path literal is captured. The handler — a bare identifier in the\n * template, but possibly an inline arrow spanning lines — is never matched, so\n * the rewrite below cannot damage it.\n */\nconst TOP_LEVEL_ROOT_GET = /^router\\s*\\.\\s*get\\(\\s*([\"'`])\\/\\1/gm;\n\n/**\n * Whether `/welcome` is already spoken for, so relocating onto it would trade\n * one duplicate-route 500 for another.\n */\nconst TOP_LEVEL_WELCOME_GET = /^router\\s*\\.\\s*get\\(\\s*([\"'`])\\/welcome\\1/m;\n\nexport type HomeRouteCollision =\n /** Nothing under `src/app` claims a top-level `/` — write the page as normal. */\n | { outcome: \"absent\" }\n /** The one file that claimed `/` was moved to `/welcome`; the page is safe to write. */\n | { outcome: \"relocated\"; relativePath: string }\n /** Something claims `/` that we will not rewrite. The page is NOT written. */\n | { outcome: \"conflict\"; reason: string }\n /** We tried to relocate and could not. The page is NOT written. */\n | { outcome: \"failed\"; reason: string };\n\n/** One routes source file, as read from disk (or handed to the pure resolver in a test). */\nexport type RoutesFileSource = {\n relativePath: string;\n source: string;\n};\n\n/** What {@link resolveHomeRouteCollision} decided, plus the sources it rewrote. */\nexport type HomeRouteResolution = {\n collision: HomeRouteCollision;\n rewrites: RoutesFileSource[];\n};\n\n/**\n * Decide what to do about `src/app/**\\/routes.{ts,tsx}` claiming `/`, and\n * produce the rewritten source when exactly one file does.\n *\n * Pure by design: no filesystem access here, so this is the part a test can\n * exercise directly without touching disk. {@link relocateConflictingHomeRoute}\n * is the thin I/O wrapper — it reads the files, calls this, and writes back\n * whatever this returns in `rewrites`.\n *\n * @param files Every `routes.{ts,tsx}` file under `src/app`, with its current text.\n * @returns The collision outcome, and the (possibly empty) list of files to write back.\n */\nexport function resolveHomeRouteCollision(files: RoutesFileSource[]): HomeRouteResolution {\n const claimants = files\n .map((file) => ({ file, matches: file.source.match(TOP_LEVEL_ROOT_GET) ?? [] }))\n .filter(({ matches }) => matches.length > 0);\n\n if (claimants.length === 0) {\n return { collision: { outcome: \"absent\" }, rewrites: [] };\n }\n\n if (claimants.length > 1) {\n const names = claimants.map(({ file }) => file.relativePath).join(\", \");\n\n return {\n collision: {\n outcome: \"conflict\",\n reason: `multiple files declare a top-level GET \"/\" (${names})`,\n },\n rewrites: [],\n };\n }\n\n const claimant = claimants[0];\n\n // The two guards above establish exactly one claimant, but the compiler does\n // not narrow a destructured element from a length check. Reporting \"absent\"\n // is the same answer the empty case gives, and the honest one: with no\n // claimant there is no conflicting home route to relocate.\n if (claimant === undefined) {\n return { collision: { outcome: \"absent\" }, rewrites: [] };\n }\n\n const { file, matches } = claimant;\n\n if (matches.length > 1) {\n return {\n collision: {\n outcome: \"conflict\",\n reason: `${file.relativePath} declares ${matches.length} top-level GET \"/\" routes`,\n },\n rewrites: [],\n };\n }\n\n if (TOP_LEVEL_WELCOME_GET.test(file.source)) {\n return {\n collision: {\n outcome: \"conflict\",\n reason: `${file.relativePath} already declares GET \"/welcome\", so the usual relocation target is taken`,\n },\n rewrites: [],\n };\n }\n\n const next = file.source.replace(TOP_LEVEL_ROOT_GET, (match: string, quote: string) =>\n match.replace(`${quote}/${quote}`, `${quote}/welcome${quote}`),\n );\n\n if (next === file.source) {\n return {\n collision: {\n outcome: \"conflict\",\n reason: `${file.relativePath}'s GET \"/\" route could not be rewritten`,\n },\n rewrites: [],\n };\n }\n\n return {\n collision: { outcome: \"relocated\", relativePath: file.relativePath },\n rewrites: [{ relativePath: file.relativePath, source: next }],\n };\n}\n\n/**\n * Make room for a page that declares `route.path = \"/\"`.\n *\n * The project template registers `router.get(\"/\", homePageController)` and the\n * page stub declares `route.path = \"/\"`. Fastify rejects the second registration\n * (`Method 'GET' already declared for route '/'`) and the homepage 500s at\n * request time — so `warlock add web` cannot just write the page and hope.\n *\n * This used to check exactly one hardcoded path, `src/app/shared/routes.ts`.\n * That guard never fired on a real scaffold: the template registers the home\n * route at `src/app/home/routes.ts`, `fileExistsAsync` on the wrong path always\n * failed, and every call silently returned `absent` — the collision-avoidance\n * logic below existed and was correct, but was pointed at a file that does not\n * exist, so it never ran. It now scans every `src/app/**\\/routes.{ts,tsx}`\n * instead of trusting one filename, so renaming the template's routes file\n * again cannot reintroduce the same silent no-op.\n *\n * Of the three ways out, this RELOCATES the JSON route to `/welcome` rather than\n * deleting it or refusing to scaffold:\n *\n * - Deleting the controller is what the scaffolder's own `react` feature does,\n * but it may do that: it owns the file it is deleting, seconds after writing\n * it. `warlock add web` runs against a project a human has been living in, and\n * silently unlinking their code is not a thing an `add` command gets to do.\n * - Writing the page anyway and printing a warning ships a project whose\n * homepage 500s. A warning above a broken app is still a broken app.\n * - Relocating keeps BOTH surfaces working: the React homepage takes `/`, the\n * JSON welcome answers at `/welcome`, and no line of user code disappears.\n *\n * Only the exact top-level shape is rewritten, and only the path literal inside\n * it. Anything else that claims `/` is reported and left completely alone — we\n * do not guess at code we cannot recognise.\n */\nexport async function relocateConflictingHomeRoute(): Promise<HomeRouteCollision> {\n const relativePaths = await glob(\"**/routes.{ts,tsx}\", {\n cwd: srcPath(\"app\"),\n absolute: false,\n });\n\n if (relativePaths.length === 0) {\n return { outcome: \"absent\" };\n }\n\n let files: RoutesFileSource[];\n\n try {\n files = await Promise.all(\n relativePaths.map(async (relativePath) => ({\n relativePath: `app/${relativePath}`,\n source: await getFileAsync(srcPath(\"app\", relativePath)),\n })),\n );\n } catch (error) {\n return {\n outcome: \"failed\",\n reason: `could not be read (${(error as Error).message})`,\n };\n }\n\n const { collision, rewrites } = resolveHomeRouteCollision(files);\n\n if (rewrites.length === 0) {\n return collision;\n }\n\n try {\n await Promise.all(\n rewrites.map((file) => putFileAsync(srcPath(file.relativePath), file.source)),\n );\n } catch (error) {\n return {\n outcome: \"failed\",\n reason: `could not be written (${(error as Error).message})`,\n };\n }\n\n return collision;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAgBA,MAAM,qBAAqB;;;;;AAM3B,MAAM,wBAAwB;;;;;;;;;;;;;AAoC9B,SAAgB,0BAA0B,OAAgD;CACxF,MAAM,YAAY,MACf,KAAK,UAAU;EAAE;EAAM,SAAS,KAAK,OAAO,MAAM,kBAAkB,KAAK,CAAC;CAAE,EAAE,CAAC,CAC/E,QAAQ,EAAE,cAAc,QAAQ,SAAS,CAAC;CAE7C,IAAI,UAAU,WAAW,GACvB,OAAO;EAAE,WAAW,EAAE,SAAS,SAAS;EAAG,UAAU,CAAC;CAAE;CAG1D,IAAI,UAAU,SAAS,GAGrB,OAAO;EACL,WAAW;GACT,SAAS;GACT,QAAQ,+CALE,UAAU,KAAK,EAAE,WAAW,KAAK,YAAY,CAAC,CAAC,KAAK,IAKH,EAAE;EAC/D;EACA,UAAU,CAAC;CACb;CAGF,MAAM,WAAW,UAAU;CAM3B,IAAI,aAAa,QACf,OAAO;EAAE,WAAW,EAAE,SAAS,SAAS;EAAG,UAAU,CAAC;CAAE;CAG1D,MAAM,EAAE,MAAM,YAAY;CAE1B,IAAI,QAAQ,SAAS,GACnB,OAAO;EACL,WAAW;GACT,SAAS;GACT,QAAQ,GAAG,KAAK,aAAa,YAAY,QAAQ,OAAO;EAC1D;EACA,UAAU,CAAC;CACb;CAGF,IAAI,sBAAsB,KAAK,KAAK,MAAM,GACxC,OAAO;EACL,WAAW;GACT,SAAS;GACT,QAAQ,GAAG,KAAK,aAAa;EAC/B;EACA,UAAU,CAAC;CACb;CAGF,MAAM,OAAO,KAAK,OAAO,QAAQ,qBAAqB,OAAe,UACnE,MAAM,QAAQ,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,UAAU,OAAO,CAC/D;CAEA,IAAI,SAAS,KAAK,QAChB,OAAO;EACL,WAAW;GACT,SAAS;GACT,QAAQ,GAAG,KAAK,aAAa;EAC/B;EACA,UAAU,CAAC;CACb;CAGF,OAAO;EACL,WAAW;GAAE,SAAS;GAAa,cAAc,KAAK;EAAa;EACnE,UAAU,CAAC;GAAE,cAAc,KAAK;GAAc,QAAQ;EAAK,CAAC;CAC9D;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,eAAsB,+BAA4D;CAChF,MAAM,gBAAgB,MAAM,KAAK,sBAAsB;EACrD,KAAK,QAAQ,KAAK;EAClB,UAAU;CACZ,CAAC;CAED,IAAI,cAAc,WAAW,GAC3B,OAAO,EAAE,SAAS,SAAS;CAG7B,IAAI;CAEJ,IAAI;EACF,QAAQ,MAAM,QAAQ,IACpB,cAAc,IAAI,OAAO,kBAAkB;GACzC,cAAc,OAAO;GACrB,QAAQ,MAAM,aAAa,QAAQ,OAAO,YAAY,CAAC;EACzD,EAAE,CACJ;CACF,SAAS,OAAO;EACd,OAAO;GACL,SAAS;GACT,QAAQ,sBAAuB,MAAgB,QAAQ;EACzD;CACF;CAEA,MAAM,EAAE,WAAW,aAAa,0BAA0B,KAAK;CAE/D,IAAI,SAAS,WAAW,GACtB,OAAO;CAGT,IAAI;EACF,MAAM,QAAQ,IACZ,SAAS,KAAK,SAAS,aAAa,QAAQ,KAAK,YAAY,GAAG,KAAK,MAAM,CAAC,CAC9E;CACF,SAAS,OAAO;EACd,OAAO;GACL,SAAS;GACT,QAAQ,yBAA0B,MAAgB,QAAQ;EAC5D;CACF;CAEA,OAAO;AACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tailwind.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/tailwind.feature.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\nimport {\n ensureDirectoryAsync,\n fileExistsAsync,\n getFileAsync,\n putFileAsync,\n} from \"@warlock.js/fs\";\nimport type { CommandActionData } from \"../../commands/types\";\nimport { rootPath, srcPath } from \"../../utils\";\nimport type { FeatureDefinition } from \"./types\";\n\n/**\n * The stylesheet entry, and the whole of Tailwind's configuration.\n *\n * Tailwind v4 is CSS-first: there is no `tailwind.config.js` and no `content`\n * globs to keep in sync with the project layout — the engine discovers the\n * templates that reach it through the bundler graph, and everything a v3 config\n * held (`theme`, `plugins`, `darkMode`) is expressed in CSS beside the import.\n * So this one file is the config, which is why the feature writes it rather\n * than an `ejectConfig` entry: `ejectConfig` lands in `src/config/*.ts`, and a\n * stylesheet that must sit in the bundler's path does not belong there.\n */\nconst appCssStub = `@import \"tailwindcss\";\n\n/*\n Tailwind v4 configures itself from CSS — there is no tailwind.config.js.\n\n Design tokens go in an @theme block, and each one becomes both a CSS variable\n and a utility class:\n\n @theme {\n --color-brand: oklch(0.62 0.19 259);\n --font-display: \"Inter\", sans-serif;\n }\n\n gives you \\`bg-brand\\`, \\`text-brand\\`, \\`font-display\\` and \\`var(--color-brand)\\`.\n\n Plugins are imported here too — \\`@plugin \"@tailwindcss/typography\";\\` — and\n your own non-utility CSS can simply follow this comment.\n*/\n`;\n\n/**\n * PostCSS rather than a Vite config file, and deliberately at the project root.\n *\n * Vite discovers `postcss.config.mjs` from the project root on its own, in the\n * dev server and in `vite build` alike, without the application owning a Vite\n * config at all. That matters here: the only app-facing Vite plugin array in\n * this framework is `webConnector({ plugins })`, which is wired into the DEV\n * server exclusively — the client build composes its own plugin list from its\n * caller. Registering `@tailwindcss/vite` there would produce styles in `dev`\n * and silently drop them from a production build, which is a worse failure than\n * any config file, because it only shows up after deploy.\n *\n * `.mjs` because a Warlock project's `package.json` is not guaranteed to set\n * `\"type\": \"module\"`, and `export default` in a `.js` file would throw in a\n * CommonJS project.\n */\nconst postcssConfigStub = `/**\n * Tailwind v4 runs as a PostCSS plugin. Vite loads this file automatically —\n * in dev and in build — so no Vite configuration is required.\n *\n * The plugin lives in its own package in v4: \\`tailwindcss\\` is the engine,\n * \\`@tailwindcss/postcss\\` is the adapter. Naming \\`tailwindcss\\` here directly is\n * the v3 spelling and will not work.\n */\nexport default {\n plugins: {\n \"@tailwindcss/postcss\": {},\n },\n};\n`;\n\n/**\n * Every filename Vite/PostCSS will pick up from the project root.\n *\n * Checked as a set rather than just writing our own: two PostCSS configs in one\n * directory is not a merge, it is a coin toss over which one loads, so a project\n * that already has one gets instructions instead of a second file.\n */\nconst POSTCSS_CONFIG_FILES = [\n \"postcss.config.mjs\",\n \"postcss.config.js\",\n \"postcss.config.cjs\",\n \"postcss.config.ts\",\n \"postcss.config.json\",\n \".postcssrc\",\n \".postcssrc.js\",\n \".postcssrc.json\",\n];\n\n/** The side-effect import that pulls the stylesheet into the bundler graph. */\nconst APP_CSS_IMPORT = 'import \"./app.css\";';\n\n/**\n * Create `src/web/app.css` — the sentinel for \"this feature already ran\".\n *\n * Nothing in the project template creates this file (the scaffold ships a\n * static `public/home.css` instead), so its presence means `add tailwind` has\n * been here, and its contents may since have been edited into a real design\n * system. It is never rewritten.\n */\nasync function createStylesheet(): Promise<boolean> {\n const cssFile = srcPath(\"web/app.css\");\n\n if (await fileExistsAsync(cssFile)) {\n console.log(`${colors.yellowBright(\"src/web/app.css\")} already exists, skipping...`);\n\n return false;\n }\n\n await ensureDirectoryAsync(srcPath(\"web\"));\n await putFileAsync(cssFile, appCssStub);\n console.log(`${colors.green(\"✓\")} Created src/web/app.css`);\n\n return true;\n}\n\n/**\n * Write `postcss.config.mjs`, unless the project already has a PostCSS config.\n *\n * An existing config is left completely alone. It is an app-owned build file\n * that may already register autoprefixer, nesting, or a CSS-modules pass, and\n * appending Tailwind to it by string surgery would be guessing at both its\n * module format and its plugin order.\n */\nasync function writePostcssConfig(): Promise<void> {\n for (const name of POSTCSS_CONFIG_FILES) {\n if (!(await fileExistsAsync(rootPath(name)))) continue;\n\n const current = await getFileAsync(rootPath(name)).catch(() => \"\");\n\n if (current.includes(\"@tailwindcss/postcss\")) {\n console.log(`${colors.yellowBright(name)} already registers Tailwind, skipping...`);\n } else {\n console.log(\n `${colors.yellowBright(\"!\")} ${colors.yellowBright(name)} already exists — add Tailwind to it yourself:\\n` +\n ` plugins: { \"@tailwindcss/postcss\": {} }\\n` +\n \" Two PostCSS configs in one project is undefined behaviour, so this feature did not write a second one.\",\n );\n }\n\n return;\n }\n\n await putFileAsync(rootPath(\"postcss.config.mjs\"), postcssConfigStub);\n console.log(`${colors.green(\"✓\")} Created postcss.config.mjs`);\n}\n\n/**\n * Import the stylesheet from `src/web/root.tsx`.\n *\n * The root is the one module every SSR page renders through, so importing the\n * stylesheet there is what puts it in the client bundle for every route.\n *\n * `root.tsx` is guaranteed to exist by the time this runs: `requires: [\"web\"]`\n * makes the add command resolve `web` ahead of `tailwind` and run its\n * `onExecuting` first, and that is what creates the file. The check below is\n * therefore for a root a human has since moved or deleted — a case worth a\n * printed instruction, not a failure.\n */\nasync function importStylesheetFromRoot(): Promise<void> {\n const rootFile = srcPath(\"web/root.tsx\");\n\n if (!(await fileExistsAsync(rootFile))) {\n console.log(\n `${colors.yellowBright(\"!\")} ${colors.yellowBright(\"src/web/root.tsx\")} not found — ` +\n `add ${colors.yellowBright(APP_CSS_IMPORT)} to your application root yourself.\\n` +\n \" Until the stylesheet is imported from a module the bundler reaches, no Tailwind CSS is emitted.\",\n );\n\n return;\n }\n\n const current = await getFileAsync(rootFile);\n\n // Matches the import whichever quote style and specifier the file uses, so a\n // re-run against a hand-edited root does not stack a second copy.\n if (/import\\s+[\"'].*app\\.css[\"']/.test(current)) {\n console.log(`${colors.yellowBright(\"src/web/root.tsx\")} already imports app.css, skipping...`);\n\n return;\n }\n\n // FIRST line, above the framework imports. A side-effect CSS import has no\n // binding to order against, and putting it at the top keeps it from being\n // swept away by an import sorter that only ranks module specifiers.\n await putFileAsync(rootFile, `${APP_CSS_IMPORT}\\n${current}`);\n console.log(`${colors.green(\"✓\")} Imported app.css in src/web/root.tsx`);\n}\n\n/**\n * Wire Tailwind v4 into the page layer.\n *\n * Three 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 Tailwind itself.\n */\nasync function completeTailwindInstallation(_options: CommandActionData) {\n await createStylesheet();\n await writePostcssConfig();\n await importStylesheetFromRoot();\n}\n\nexport const tailwindFeature: FeatureDefinition = {\n description:\n \"Installs Tailwind CSS v4 for the SSR page layer. Creates src/web/app.css (the CSS-first config — v4 has no tailwind.config.js), imports it from src/web/root.tsx, and registers the engine through postcss.config.mjs, which Vite loads in dev and build alike.\",\n // `web` owns root.tsx and the CSS pipeline that serves this stylesheet.\n // Requiring it also fixes the order: the add command resolves requirements\n // depth-first, so `web` scaffolds src/web before this feature writes into it.\n requires: [\"web\"],\n devDependencies: {\n // Build-time only, both of them — Tailwind compiles to a plain stylesheet\n // and nothing it ships is imported at runtime.\n tailwindcss: \"^4.1.16\",\n // The engine and its PostCSS adapter are separate packages in v4 and are\n // released in lockstep; keep these two ranges identical.\n \"@tailwindcss/postcss\": \"^4.1.16\",\n },\n onExecuting: completeTailwindInstallation,\n};\n"],"mappings":";;;;;;;;;;;;;;;;;AAsBA,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCnB,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;AAsB1B,MAAM,uBAAuB;CAC3B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;AAGA,MAAM,iBAAiB;;;;;;;;;AAUvB,eAAe,mBAAqC;CAClD,MAAM,UAAU,QAAQ,aAAa;CAErC,IAAI,MAAM,gBAAgB,OAAO,GAAG;EAClC,QAAQ,IAAI,GAAG,OAAO,aAAa,iBAAiB,EAAE,6BAA6B;EAEnF,OAAO;CACT;CAEA,MAAM,qBAAqB,QAAQ,KAAK,CAAC;CACzC,MAAM,aAAa,SAAS,UAAU;CACtC,QAAQ,IAAI,GAAG,OAAO,MAAM,GAAG,EAAE,yBAAyB;CAE1D,OAAO;AACT;;;;;;;;;AAUA,eAAe,qBAAoC;CACjD,KAAK,MAAM,QAAQ,sBAAsB;EACvC,IAAI,CAAE,MAAM,gBAAgB,SAAS,IAAI,CAAC,GAAI;EAI9C,KAAI,MAFkB,aAAa,SAAS,IAAI,CAAC,CAAC,CAAC,YAAY,EAAE,EAEtD,CAAC,SAAS,sBAAsB,GACzC,QAAQ,IAAI,GAAG,OAAO,aAAa,IAAI,EAAE,yCAAyC;OAElF,QAAQ,IACN,GAAG,OAAO,aAAa,GAAG,EAAE,GAAG,OAAO,aAAa,IAAI,EAAE,oMAG3D;EAGF;CACF;CAEA,MAAM,aAAa,SAAS,oBAAoB,GAAG,iBAAiB;CACpE,QAAQ,IAAI,GAAG,OAAO,MAAM,GAAG,EAAE,4BAA4B;AAC/D;;;;;;;;;;;;;AAcA,eAAe,2BAA0C;CACvD,MAAM,WAAW,QAAQ,cAAc;CAEvC,IAAI,CAAE,MAAM,gBAAgB,QAAQ,GAAI;EACtC,QAAQ,IACN,GAAG,OAAO,aAAa,GAAG,EAAE,GAAG,OAAO,aAAa,kBAAkB,EAAE,mBAC9D,OAAO,aAAa,cAAc,EAAE,uIAE/C;EAEA;CACF;CAEA,MAAM,UAAU,MAAM,aAAa,QAAQ;CAI3C,IAAI,8BAA8B,KAAK,OAAO,GAAG;EAC/C,QAAQ,IAAI,GAAG,OAAO,aAAa,kBAAkB,EAAE,sCAAsC;EAE7F;CACF;CAKA,MAAM,aAAa,UAAU,GAAG,eAAe,IAAI,SAAS;CAC5D,QAAQ,IAAI,GAAG,OAAO,MAAM,GAAG,EAAE,sCAAsC;AACzE;;;;;;;;;AAUA,eAAe,6BAA6B,UAA6B;CACvE,MAAM,iBAAiB;CACvB,MAAM,mBAAmB;CACzB,MAAM,yBAAyB;AACjC;AAEA,MAAa,kBAAqC;CAChD,aACE;CAIF,UAAU,CAAC,KAAK;CAChB,iBAAiB;EAGf,aAAa;EAGb,wBAAwB;CAC1B;CACA,aAAa;AACf"}
|
|
1
|
+
{"version":3,"file":"tailwind.feature.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/tailwind.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 * The stylesheet entry, and the whole of Tailwind's configuration.\n *\n * Tailwind v4 is CSS-first: there is no `tailwind.config.js` and no `content`\n * globs to keep in sync with the project layout — the engine discovers the\n * templates that reach it through the bundler graph, and everything a v3 config\n * held (`theme`, `plugins`, `darkMode`) is expressed in CSS beside the import.\n * So this one file is the config, which is why the feature writes it rather\n * than an `ejectConfig` entry: `ejectConfig` lands in `src/config/*.ts`, and a\n * stylesheet that must sit in the bundler's path does not belong there.\n */\nconst appCssStub = `@import \"tailwindcss\";\n\n/*\n Tailwind v4 configures itself from CSS — there is no tailwind.config.js.\n\n Design tokens go in an @theme block, and each one becomes both a CSS variable\n and a utility class:\n\n @theme {\n --color-brand: oklch(0.62 0.19 259);\n --font-display: \"Inter\", sans-serif;\n }\n\n gives you \\`bg-brand\\`, \\`text-brand\\`, \\`font-display\\` and \\`var(--color-brand)\\`.\n\n Plugins are imported here too — \\`@plugin \"@tailwindcss/typography\";\\` — and\n your own non-utility CSS can simply follow this comment.\n*/\n`;\n\n/**\n * PostCSS rather than a Vite config file, and deliberately at the project root.\n *\n * Vite discovers `postcss.config.mjs` from the project root on its own, in the\n * dev server and in `vite build` alike, without the application owning a Vite\n * config at all. That matters here: the only app-facing Vite plugin array in\n * this framework is `webConnector({ plugins })`, which is wired into the DEV\n * server exclusively — the client build composes its own plugin list from its\n * caller. Registering `@tailwindcss/vite` there would produce styles in `dev`\n * and silently drop them from a production build, which is a worse failure than\n * any config file, because it only shows up after deploy.\n *\n * `.mjs` because a Warlock project's `package.json` is not guaranteed to set\n * `\"type\": \"module\"`, and `export default` in a `.js` file would throw in a\n * CommonJS project.\n */\nconst postcssConfigStub = `/**\n * Tailwind v4 runs as a PostCSS plugin. Vite loads this file automatically —\n * in dev and in build — so no Vite configuration is required.\n *\n * The plugin lives in its own package in v4: \\`tailwindcss\\` is the engine,\n * \\`@tailwindcss/postcss\\` is the adapter. Naming \\`tailwindcss\\` here directly is\n * the v3 spelling and will not work.\n */\nexport default {\n plugins: {\n \"@tailwindcss/postcss\": {},\n },\n};\n`;\n\n/**\n * Every filename Vite/PostCSS will pick up from the project root.\n *\n * Checked as a set rather than just writing our own: two PostCSS configs in one\n * directory is not a merge, it is a coin toss over which one loads, so a project\n * that already has one gets instructions instead of a second file.\n */\nconst POSTCSS_CONFIG_FILES = [\n \"postcss.config.mjs\",\n \"postcss.config.js\",\n \"postcss.config.cjs\",\n \"postcss.config.ts\",\n \"postcss.config.json\",\n \".postcssrc\",\n \".postcssrc.js\",\n \".postcssrc.json\",\n];\n\n/** The side-effect import that pulls the stylesheet into the bundler graph. */\nconst APP_CSS_IMPORT = 'import \"./app.css\";';\n\n/**\n * Create `src/web/app.css` — the sentinel for \"this feature already ran\".\n *\n * Nothing in the project template creates this file (the scaffold ships a\n * static `public/home.css` instead), so its presence means `add tailwind` has\n * been here, and its contents may since have been edited into a real design\n * system. It is never rewritten.\n */\nasync function createStylesheet(): Promise<boolean> {\n const cssFile = srcPath(\"web/app.css\");\n\n if (await fileExistsAsync(cssFile)) {\n console.log(`${colors.yellowBright(\"src/web/app.css\")} already exists, skipping...`);\n\n return false;\n }\n\n await ensureDirectoryAsync(srcPath(\"web\"));\n await putFileAsync(cssFile, appCssStub);\n console.log(`${colors.green(\"✓\")} Created src/web/app.css`);\n\n return true;\n}\n\n/**\n * Write `postcss.config.mjs`, unless the project already has a PostCSS config.\n *\n * An existing config is left completely alone. It is an app-owned build file\n * that may already register autoprefixer, nesting, or a CSS-modules pass, and\n * appending Tailwind to it by string surgery would be guessing at both its\n * module format and its plugin order.\n */\nasync function writePostcssConfig(): Promise<void> {\n for (const name of POSTCSS_CONFIG_FILES) {\n if (!(await fileExistsAsync(rootPath(name)))) continue;\n\n const current = await getFileAsync(rootPath(name)).catch(() => \"\");\n\n if (current.includes(\"@tailwindcss/postcss\")) {\n console.log(`${colors.yellowBright(name)} already registers Tailwind, skipping...`);\n } else {\n console.log(\n `${colors.yellowBright(\"!\")} ${colors.yellowBright(name)} already exists — add Tailwind to it yourself:\\n` +\n ` plugins: { \"@tailwindcss/postcss\": {} }\\n` +\n \" Two PostCSS configs in one project is undefined behaviour, so this feature did not write a second one.\",\n );\n }\n\n return;\n }\n\n await putFileAsync(rootPath(\"postcss.config.mjs\"), postcssConfigStub);\n console.log(`${colors.green(\"✓\")} Created postcss.config.mjs`);\n}\n\n/**\n * Import the stylesheet from `src/web/root.tsx`.\n *\n * The root is the one module every SSR page renders through, so importing the\n * stylesheet there is what puts it in the client bundle for every route.\n *\n * `root.tsx` is guaranteed to exist by the time this runs: `requires: [\"web\"]`\n * makes the add command resolve `web` ahead of `tailwind` and run its\n * `onExecuting` first, and that is what creates the file. The check below is\n * therefore for a root a human has since moved or deleted — a case worth a\n * printed instruction, not a failure.\n */\nasync function importStylesheetFromRoot(): Promise<void> {\n const rootFile = srcPath(\"web/root.tsx\");\n\n if (!(await fileExistsAsync(rootFile))) {\n console.log(\n `${colors.yellowBright(\"!\")} ${colors.yellowBright(\"src/web/root.tsx\")} not found — ` +\n `add ${colors.yellowBright(APP_CSS_IMPORT)} to your application root yourself.\\n` +\n \" Until the stylesheet is imported from a module the bundler reaches, no Tailwind CSS is emitted.\",\n );\n\n return;\n }\n\n const current = await getFileAsync(rootFile);\n\n // Matches the import whichever quote style and specifier the file uses, so a\n // re-run against a hand-edited root does not stack a second copy.\n if (/import\\s+[\"'].*app\\.css[\"']/.test(current)) {\n console.log(`${colors.yellowBright(\"src/web/root.tsx\")} already imports app.css, skipping...`);\n\n return;\n }\n\n // FIRST line, above the framework imports. A side-effect CSS import has no\n // binding to order against, and putting it at the top keeps it from being\n // swept away by an import sorter that only ranks module specifiers.\n await putFileAsync(rootFile, `${APP_CSS_IMPORT}\\n${current}`);\n console.log(`${colors.green(\"✓\")} Imported app.css in src/web/root.tsx`);\n}\n\n/**\n * Wire Tailwind v4 into the page layer.\n *\n * Three 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 Tailwind itself.\n */\nasync function completeTailwindInstallation(_options: CommandActionData) {\n await createStylesheet();\n await writePostcssConfig();\n await importStylesheetFromRoot();\n}\n\nexport const tailwindFeature: FeatureDefinition = {\n description:\n \"Installs Tailwind CSS v4 for the SSR page layer. Creates src/web/app.css (the CSS-first config — v4 has no tailwind.config.js), imports it from src/web/root.tsx, and registers the engine through postcss.config.mjs, which Vite loads in dev and build alike.\",\n // `web` owns root.tsx and the CSS pipeline that serves this stylesheet.\n // Requiring it also fixes the order: the add command resolves requirements\n // depth-first, so `web` scaffolds src/web before this feature writes into it.\n requires: [\"web\"],\n devDependencies: {\n // Build-time only, both of them — Tailwind compiles to a plain stylesheet\n // and nothing it ships is imported at runtime.\n tailwindcss: \"^4.1.16\",\n // The engine and its PostCSS adapter are separate packages in v4 and are\n // released in lockstep; keep these two ranges identical.\n \"@tailwindcss/postcss\": \"^4.1.16\",\n },\n onExecuting: completeTailwindInstallation,\n};\n"],"mappings":";;;;;;;;;;;;;;;;;AAiBA,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCnB,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;AAsB1B,MAAM,uBAAuB;CAC3B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;AAGA,MAAM,iBAAiB;;;;;;;;;AAUvB,eAAe,mBAAqC;CAClD,MAAM,UAAU,QAAQ,aAAa;CAErC,IAAI,MAAM,gBAAgB,OAAO,GAAG;EAClC,QAAQ,IAAI,GAAG,OAAO,aAAa,iBAAiB,EAAE,6BAA6B;EAEnF,OAAO;CACT;CAEA,MAAM,qBAAqB,QAAQ,KAAK,CAAC;CACzC,MAAM,aAAa,SAAS,UAAU;CACtC,QAAQ,IAAI,GAAG,OAAO,MAAM,GAAG,EAAE,yBAAyB;CAE1D,OAAO;AACT;;;;;;;;;AAUA,eAAe,qBAAoC;CACjD,KAAK,MAAM,QAAQ,sBAAsB;EACvC,IAAI,CAAE,MAAM,gBAAgB,SAAS,IAAI,CAAC,GAAI;EAI9C,KAAI,MAFkB,aAAa,SAAS,IAAI,CAAC,CAAC,CAAC,YAAY,EAAE,EAEtD,CAAC,SAAS,sBAAsB,GACzC,QAAQ,IAAI,GAAG,OAAO,aAAa,IAAI,EAAE,yCAAyC;OAElF,QAAQ,IACN,GAAG,OAAO,aAAa,GAAG,EAAE,GAAG,OAAO,aAAa,IAAI,EAAE,oMAG3D;EAGF;CACF;CAEA,MAAM,aAAa,SAAS,oBAAoB,GAAG,iBAAiB;CACpE,QAAQ,IAAI,GAAG,OAAO,MAAM,GAAG,EAAE,4BAA4B;AAC/D;;;;;;;;;;;;;AAcA,eAAe,2BAA0C;CACvD,MAAM,WAAW,QAAQ,cAAc;CAEvC,IAAI,CAAE,MAAM,gBAAgB,QAAQ,GAAI;EACtC,QAAQ,IACN,GAAG,OAAO,aAAa,GAAG,EAAE,GAAG,OAAO,aAAa,kBAAkB,EAAE,mBAC9D,OAAO,aAAa,cAAc,EAAE,uIAE/C;EAEA;CACF;CAEA,MAAM,UAAU,MAAM,aAAa,QAAQ;CAI3C,IAAI,8BAA8B,KAAK,OAAO,GAAG;EAC/C,QAAQ,IAAI,GAAG,OAAO,aAAa,kBAAkB,EAAE,sCAAsC;EAE7F;CACF;CAKA,MAAM,aAAa,UAAU,GAAG,eAAe,IAAI,SAAS;CAC5D,QAAQ,IAAI,GAAG,OAAO,MAAM,GAAG,EAAE,sCAAsC;AACzE;;;;;;;;;AAUA,eAAe,6BAA6B,UAA6B;CACvE,MAAM,iBAAiB;CACvB,MAAM,mBAAmB;CACzB,MAAM,yBAAyB;AACjC;AAEA,MAAa,kBAAqC;CAChD,aACE;CAIF,UAAU,CAAC,KAAK;CAChB,iBAAiB;EAGf,aAAa;EAGb,wBAAwB;CAC1B;CACA,aAAa;AACf"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/types.ts"],"sourcesContent":["import type { CommandActionData } from \"../../commands/types\";\n\n/**\n * Internal placeholder for an @warlock.js dependency whose concrete version\n * comes from the installed Core package at command execution time.\n *\n * This is deliberately not a valid package-manager range. If a future code\n * path forgets to resolve it, installation fails loudly instead of silently\n * falling back to a stale framework major copied into a feature definition.\n */\nexport const INSTALLED_WARLOCK_VERSION = \"__INSTALLED_WARLOCK_VERSION__\";\n\r\n/**\r\n * One entry in the feature registry.\r\n *\r\n * Every feature owns a module of its own that exports a single value of this\r\n * shape, with its `onExecuting` body beside it; `features/index.ts` is only the\r\n * ordered index that collects them.\r\n */\r\nexport type FeatureDefinition = {\r\n dependencies?: Record<string, string>;\r\n devDependencies?: Record<string, string>;\r\n description: string;\r\n requires?: string[];\r\n script?: Record<string, string>;\r\n onExecuting?: (options: CommandActionData) => Promise<any>;\r\n ejectConfig?: {\r\n content: string;\r\n name: string;\r\n };\r\n};\r\n"],"mappings":";;;;;;;;;AAUA,MAAa,4BAA4B"}
|
|
1
|
+
{"version":3,"file":"types.mjs","names":[],"sources":["../../../../../../../../core/src/generations/features/types.ts"],"sourcesContent":["import type { CommandActionData } from \"../../commands/types\";\r\n\r\n/**\r\n * Internal placeholder for an @warlock.js dependency whose concrete version\r\n * comes from the installed Core package at command execution time.\r\n *\r\n * This is deliberately not a valid package-manager range. If a future code\r\n * path forgets to resolve it, installation fails loudly instead of silently\r\n * falling back to a stale framework major copied into a feature definition.\r\n */\r\nexport const INSTALLED_WARLOCK_VERSION = \"__INSTALLED_WARLOCK_VERSION__\";\r\n\r\n/**\r\n * One entry in the feature registry.\r\n *\r\n * Every feature owns a module of its own that exports a single value of this\r\n * shape, with its `onExecuting` body beside it; `features/index.ts` is only the\r\n * ordered index that collects them.\r\n */\r\nexport type FeatureDefinition = {\r\n dependencies?: Record<string, string>;\r\n devDependencies?: Record<string, string>;\r\n description: string;\r\n requires?: string[];\r\n script?: Record<string, string>;\r\n onExecuting?: (options: CommandActionData) => Promise<any>;\r\n ejectConfig?: {\r\n content: string;\r\n name: string;\r\n };\r\n};\r\n"],"mappings":";;;;;;;;;AAUA,MAAa,4BAA4B"}
|