@warlock.js/core 5.35.0 → 5.37.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 +53 -0
- package/esm/cli/commands/migrate.command.mjs +10 -0
- package/esm/cli/commands/migrate.command.mjs.map +1 -1
- package/esm/cli/commands/release/release-command.action.mjs +245 -0
- package/esm/cli/commands/release/release-command.action.mjs.map +1 -0
- package/esm/cli/commands/release.command.mjs +66 -0
- package/esm/cli/commands/release.command.mjs.map +1 -0
- package/esm/cli/framework-cli-commands.mjs +2 -0
- package/esm/cli/framework-cli-commands.mjs.map +1 -1
- package/esm/database/migrate-action.mjs +24 -2
- package/esm/database/migrate-action.mjs.map +1 -1
- package/esm/dev-server/loader/load-hook.d.mts.map +1 -1
- package/esm/dev-server/loader/load-hook.mjs +6 -0
- package/esm/dev-server/loader/load-hook.mjs.map +1 -1
- package/esm/dev-server/module-loader.mjs +1 -1
- package/esm/dev-server/parse-imports.mjs +1 -1
- package/esm/dev-server/type-generator.mjs +1 -1
- package/esm/e2e/cdp.d.mts.map +1 -1
- package/esm/e2e/cdp.mjs +9 -2
- package/esm/e2e/cdp.mjs.map +1 -1
- package/esm/e2e/page.d.mts +9 -1
- package/esm/e2e/page.d.mts.map +1 -1
- package/esm/e2e/page.mjs +45 -7
- package/esm/e2e/page.mjs.map +1 -1
- package/esm/e2e/types.d.mts +18 -0
- package/esm/e2e/types.d.mts.map +1 -1
- package/esm/e2e/types.mjs.map +1 -1
- package/esm/errors/error-codes.d.mts +24 -0
- package/esm/errors/error-codes.d.mts.map +1 -1
- package/esm/errors/error-codes.mjs +24 -0
- package/esm/errors/error-codes.mjs.map +1 -1
- package/esm/generations/features/test.feature.mjs +7 -7
- package/esm/generations/features/test.feature.mjs.map +1 -1
- package/esm/generations/features/web.feature.mjs +3 -3
- package/esm/generations/features/web.feature.mjs.map +1 -1
- package/esm/index.d.mts +6 -3
- package/esm/index.mjs +4 -2
- package/esm/localization/index.mjs +3 -1
- package/esm/localization/translation-usage-graph.d.mts +49 -0
- package/esm/localization/translation-usage-graph.d.mts.map +1 -0
- package/esm/localization/translation-usage-graph.mjs +128 -0
- package/esm/localization/translation-usage-graph.mjs.map +1 -0
- package/esm/localization/translation-usage.d.mts +85 -0
- package/esm/localization/translation-usage.d.mts.map +1 -0
- package/esm/localization/translation-usage.mjs +307 -0
- package/esm/localization/translation-usage.mjs.map +1 -0
- package/esm/production/build-budgets.d.mts +18 -0
- package/esm/production/build-budgets.d.mts.map +1 -1
- package/esm/production/build-budgets.mjs.map +1 -1
- package/esm/production/route-registration-snapshot.mjs +12 -3
- package/esm/production/route-registration-snapshot.mjs.map +1 -1
- package/esm/release/activate.mjs +261 -0
- package/esm/release/activate.mjs.map +1 -0
- package/esm/release/candidate.mjs +107 -0
- package/esm/release/candidate.mjs.map +1 -0
- package/esm/release/cascade-migration-provider.mjs +32 -0
- package/esm/release/cascade-migration-provider.mjs.map +1 -0
- package/esm/release/config.mjs +60 -0
- package/esm/release/config.mjs.map +1 -0
- package/esm/release/gate.mjs +67 -0
- package/esm/release/gate.mjs.map +1 -0
- package/esm/release/migrations-proof.mjs +100 -0
- package/esm/release/migrations-proof.mjs.map +1 -0
- package/esm/release/prune.mjs +42 -0
- package/esm/release/prune.mjs.map +1 -0
- package/esm/release/report.mjs +25 -0
- package/esm/release/report.mjs.map +1 -0
- package/esm/release/runner.mjs +55 -0
- package/esm/release/runner.mjs.map +1 -0
- package/esm/release/semver.mjs +53 -0
- package/esm/release/semver.mjs.map +1 -0
- package/esm/release/stage.mjs +122 -0
- package/esm/release/stage.mjs.map +1 -0
- package/esm/release/store.mjs +131 -0
- package/esm/release/store.mjs.map +1 -0
- package/esm/release/types.d.mts +102 -0
- package/esm/release/types.d.mts.map +1 -0
- package/esm/release/verify.mjs +94 -0
- package/esm/release/verify.mjs.map +1 -0
- package/esm/router/index.d.mts +2 -2
- package/esm/router/index.mjs +2 -2
- package/esm/router/router.d.mts +4 -0
- package/esm/router/router.d.mts.map +1 -1
- package/esm/router/router.mjs +14 -3
- package/esm/router/router.mjs.map +1 -1
- package/esm/router/types.d.mts +45 -0
- package/esm/router/types.d.mts.map +1 -1
- package/esm/router/unguarded-route-diagnostics.d.mts +6 -0
- package/esm/router/unguarded-route-diagnostics.d.mts.map +1 -1
- package/esm/router/unguarded-route-diagnostics.mjs +10 -2
- package/esm/router/unguarded-route-diagnostics.mjs.map +1 -1
- package/esm/tooling/check/api-route-references.d.mts +16 -0
- package/esm/tooling/check/api-route-references.d.mts.map +1 -0
- package/esm/tooling/check/api-route-references.mjs +146 -0
- package/esm/tooling/check/api-route-references.mjs.map +1 -0
- package/esm/tooling/check/conventions-step.d.mts +6 -3
- package/esm/tooling/check/conventions-step.d.mts.map +1 -1
- package/esm/tooling/check/conventions-step.mjs +19 -10
- package/esm/tooling/check/conventions-step.mjs.map +1 -1
- package/esm/tooling/check/run-check.d.mts.map +1 -1
- package/esm/tooling/check/run-check.mjs +4 -2
- package/esm/tooling/check/run-check.mjs.map +1 -1
- package/esm/tooling/check/security-conventions.mjs +19 -1
- package/esm/tooling/check/security-conventions.mjs.map +1 -1
- package/esm/tooling/check/translation-conventions.mjs +32 -0
- package/esm/tooling/check/translation-conventions.mjs.map +1 -0
- package/esm/tooling/inspect/collect-files.mjs +1 -1
- package/esm/tooling/inspect/collect-routes.mjs +3 -2
- package/esm/tooling/inspect/collect-routes.mjs.map +1 -1
- package/esm/upgrade/codemods/index.mjs +2 -0
- package/esm/upgrade/codemods/index.mjs.map +1 -1
- package/esm/upgrade/codemods/vite-vitest.mjs +170 -0
- package/esm/upgrade/codemods/vite-vitest.mjs.map +1 -0
- package/esm/upgrade/notes.mjs +4 -0
- package/esm/upgrade/notes.mjs.map +1 -1
- package/esm/validation/init.mjs +11 -4
- package/esm/validation/init.mjs.map +1 -1
- package/esm/validation/translate-rule.mjs +63 -0
- package/esm/validation/translate-rule.mjs.map +1 -0
- package/esm/warlock-config/index.d.mts +3 -2
- package/esm/warlock-config/types.d.mts +8 -0
- package/esm/warlock-config/types.d.mts.map +1 -1
- package/llms-full.txt +221 -18
- package/llms.txt +1 -0
- package/package.json +13 -13
- package/skills/deploy-release/SKILL.md +76 -0
- package/skills/index.md +1 -1
- package/skills/register-route/SKILL.md +25 -0
- package/skills/test-service/SKILL.md +8 -8
- package/skills/upgrade-from-5-29/SKILL.md +91 -0
- package/skills/use-localization/SKILL.md +4 -0
- package/skills/warlock-check/SKILL.md +11 -8
- package/skills/warlock-conventions/SKILL.md +1 -1
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { manualAt, replaceNode } from "./helpers.mjs";
|
|
2
|
+
import { parseVersion } from "../version.mjs";
|
|
3
|
+
|
|
4
|
+
//#region ../../packages/core/src/upgrade/codemods/vite-vitest.ts
|
|
5
|
+
/**
|
|
6
|
+
* The newest releases that are at least two weeks old on the day 5.36 shipped;
|
|
7
|
+
* `warlock add test` and `warlock add web` install the same ones.
|
|
8
|
+
*/
|
|
9
|
+
const TARGETS = {
|
|
10
|
+
vite: {
|
|
11
|
+
major: 8,
|
|
12
|
+
version: "8.3.3"
|
|
13
|
+
},
|
|
14
|
+
vitest: {
|
|
15
|
+
major: 5,
|
|
16
|
+
version: "5.0.3"
|
|
17
|
+
},
|
|
18
|
+
"@vitest/coverage-v8": {
|
|
19
|
+
major: 5,
|
|
20
|
+
version: "5.0.3"
|
|
21
|
+
},
|
|
22
|
+
"@vitest/coverage-istanbul": {
|
|
23
|
+
major: 5,
|
|
24
|
+
version: "5.0.3"
|
|
25
|
+
},
|
|
26
|
+
"@vitest/ui": {
|
|
27
|
+
major: 5,
|
|
28
|
+
version: "5.0.3"
|
|
29
|
+
},
|
|
30
|
+
"@vitejs/plugin-react": {
|
|
31
|
+
major: 6,
|
|
32
|
+
version: "6.1.2"
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
const MIGRATION_GUIDES = "https://vite.dev/guide/migration and https://vitest.dev/guide/migration";
|
|
36
|
+
/** Version specs that are not a plain registry range and must not be rewritten. */
|
|
37
|
+
const NON_REGISTRY_SPEC = /^(workspace:|file:|link:|portal:|git|github:|https?:|npm:)|^\*$|^latest$/;
|
|
38
|
+
/** Entry points Vitest 5 removed. `vitest/runners` is handled separately. */
|
|
39
|
+
const REMOVED_ENTRY_POINTS = /* @__PURE__ */ new Set([
|
|
40
|
+
"vitest/coverage",
|
|
41
|
+
"vitest/reporters",
|
|
42
|
+
"vitest/environments",
|
|
43
|
+
"vitest/snapshot",
|
|
44
|
+
"vitest/suite",
|
|
45
|
+
"vitest/mocker",
|
|
46
|
+
"vitest/internal/module-runner"
|
|
47
|
+
]);
|
|
48
|
+
const VITE_CONFIG_FILE = /^vite(st)?\.config\.(ts|mts|cts|js|mjs|cjs)$/;
|
|
49
|
+
function serialize(original, value) {
|
|
50
|
+
const indent = /^[ \t]+(?=")/m.exec(original)?.[0];
|
|
51
|
+
return (indent ? JSON.stringify(value, null, indent) : JSON.stringify(value)) + (original.endsWith("\n") ? "\n" : "");
|
|
52
|
+
}
|
|
53
|
+
function lineOf(source, needle) {
|
|
54
|
+
const index = source.indexOf(needle);
|
|
55
|
+
return index < 0 ? 1 : source.slice(0, index).split("\n").length;
|
|
56
|
+
}
|
|
57
|
+
/** The spec moved to `target`, keeping its `^`/`~`/exact style; undefined when it already allows it. */
|
|
58
|
+
function upgradedSpec(spec, target) {
|
|
59
|
+
if (NON_REGISTRY_SPEC.test(spec)) return void 0;
|
|
60
|
+
const parsed = parseVersion(spec);
|
|
61
|
+
if (!parsed || parsed[0] >= target.major) return void 0;
|
|
62
|
+
return `${/^[\^~]/.exec(spec)?.[0] ?? (/^\d/.test(spec) ? "" : "^")}${target.version}`;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* 5.36: Vite 8 (Rolldown), Vitest 5 and `@vitejs/plugin-react` 6 are one
|
|
66
|
+
* family and must move together.
|
|
67
|
+
*
|
|
68
|
+
* - package.json: raises `vite`, `vitest`, `@vitest/*` and
|
|
69
|
+
* `@vitejs/plugin-react` (dependencies, devDependencies, `overrides` and
|
|
70
|
+
* `resolutions`) to the supported majors.
|
|
71
|
+
* - `src/test-runner.ts`-style runners: Vitest 5 has no `vitest/runners`; the
|
|
72
|
+
* runner class is `TestRunner`, exported from `vitest`, and
|
|
73
|
+
* `onCleanupWorkerContext` is an optional property.
|
|
74
|
+
* - Lists removed Vitest entry points, a top-level `esbuild` option, and object
|
|
75
|
+
* `manualChunks` in Vite configs for review.
|
|
76
|
+
*/
|
|
77
|
+
const viteVitest = {
|
|
78
|
+
id: "vite-8-vitest-5",
|
|
79
|
+
version: "5.36.0",
|
|
80
|
+
package: "@warlock.js/core",
|
|
81
|
+
title: "Move to Vite 8, Vitest 5 and @vitejs/plugin-react 6",
|
|
82
|
+
skill: "upgrade-from-5-29",
|
|
83
|
+
run(context) {
|
|
84
|
+
const { Node, SyntaxKind } = context.tsMorph;
|
|
85
|
+
const output = {
|
|
86
|
+
edits: [],
|
|
87
|
+
manual: []
|
|
88
|
+
};
|
|
89
|
+
const source = context.text.read("package.json");
|
|
90
|
+
if (source !== void 0) {
|
|
91
|
+
const pkg = JSON.parse(source);
|
|
92
|
+
const changes = [];
|
|
93
|
+
for (const field of [
|
|
94
|
+
"dependencies",
|
|
95
|
+
"devDependencies",
|
|
96
|
+
"overrides",
|
|
97
|
+
"resolutions"
|
|
98
|
+
]) {
|
|
99
|
+
const entries = pkg[field];
|
|
100
|
+
if (!entries) continue;
|
|
101
|
+
for (const [name, target] of Object.entries(TARGETS)) {
|
|
102
|
+
const spec = entries[name];
|
|
103
|
+
if (typeof spec !== "string") continue;
|
|
104
|
+
const next = upgradedSpec(spec, target);
|
|
105
|
+
if (!next) continue;
|
|
106
|
+
entries[name] = next;
|
|
107
|
+
changes.push({
|
|
108
|
+
name: field === "dependencies" || field === "devDependencies" ? name : `${field}.${name}`,
|
|
109
|
+
from: spec,
|
|
110
|
+
to: next
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const first = changes[0];
|
|
115
|
+
if (first) context.text.write("package.json", serialize(source, pkg), {
|
|
116
|
+
line: lineOf(source, `"${first.name.replace(/^[a-z]+\./, "")}"`),
|
|
117
|
+
description: changes.map((change) => `${change.name} ${change.from} → ${change.to}`).join(", ")
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
for (const sourceFile of context.sourceFiles) for (const declaration of sourceFile.getImportDeclarations()) {
|
|
121
|
+
const moduleName = declaration.getModuleSpecifierValue();
|
|
122
|
+
if (REMOVED_ENTRY_POINTS.has(moduleName)) {
|
|
123
|
+
output.manual.push(manualAt(context, declaration, `Vitest 5 removed the "${moduleName}" entry point. Import from "vitest" or "vitest/node" instead; see ${MIGRATION_GUIDES}.`));
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
if (moduleName !== "vitest/runners") continue;
|
|
127
|
+
const specifiers = declaration.getNamedImports();
|
|
128
|
+
const runner = specifiers.find((specifier) => specifier.getName() === "VitestTestRunner");
|
|
129
|
+
if (!runner || specifiers.length !== 1 || declaration.getDefaultImport()) {
|
|
130
|
+
output.manual.push(manualAt(context, declaration, "Vitest 5 removed \"vitest/runners\". Import the runner class as `TestRunner` from \"vitest\"; benchmark runners no longer exist."));
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
const alias = runner.getAliasNode()?.getText();
|
|
134
|
+
output.edits.push(replaceNode(context, declaration, alias ? `import { TestRunner as ${alias} } from "vitest";` : "import { TestRunner } from \"vitest\";", "imported the runner as TestRunner from \"vitest\""));
|
|
135
|
+
if (!alias) for (const identifier of sourceFile.getDescendantsOfKind(SyntaxKind.Identifier)) {
|
|
136
|
+
if (identifier.getText() !== "VitestTestRunner") continue;
|
|
137
|
+
if (identifier.getFirstAncestorByKind(SyntaxKind.ImportDeclaration)) continue;
|
|
138
|
+
output.edits.push(replaceNode(context, identifier, "TestRunner", "renamed VitestTestRunner to TestRunner"));
|
|
139
|
+
}
|
|
140
|
+
for (const call of sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {
|
|
141
|
+
const callee = call.getExpression();
|
|
142
|
+
if (!Node.isPropertyAccessExpression(callee) || callee.getName() !== "onCleanupWorkerContext" || call.getQuestionDotTokenNode()) continue;
|
|
143
|
+
output.edits.push(replaceNode(context, callee, `${callee.getText()}?.`, "onCleanupWorkerContext is optional in Vitest 5 (absent in vm pools)"));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
for (const file of context.text.list()) {
|
|
147
|
+
if (!VITE_CONFIG_FILE.test(file)) continue;
|
|
148
|
+
const config = context.text.read(file);
|
|
149
|
+
if (config === void 0) continue;
|
|
150
|
+
config.split("\n").forEach((text, index) => {
|
|
151
|
+
const line = index + 1;
|
|
152
|
+
if (/^\s*esbuild\s*:/.test(text)) output.manual.push({
|
|
153
|
+
file,
|
|
154
|
+
line,
|
|
155
|
+
hint: "Vite 8 transforms TypeScript and JSX with Oxc: move this `esbuild` option to `oxc` (jsx, define, include/exclude convert; `target` does not). Oxc does not lower native decorators; keep `lowerStage3Decorators()` in `plugins` for decorated modules."
|
|
156
|
+
});
|
|
157
|
+
if (/\bmanualChunks\s*:\s*\{/.test(text)) output.manual.push({
|
|
158
|
+
file,
|
|
159
|
+
line,
|
|
160
|
+
hint: "Vite 8 (Rolldown) removed the object form of `output.manualChunks`. Use `output.codeSplitting.groups` with `name` and `test`."
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
return output;
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
//#endregion
|
|
169
|
+
export { viteVitest };
|
|
170
|
+
//# sourceMappingURL=vite-vitest.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite-vitest.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/upgrade/codemods/vite-vitest.ts"],"sourcesContent":["import type { Codemod, CodemodOutput } from \"../types\";\nimport { parseVersion } from \"../version\";\nimport { manualAt, replaceNode } from \"./helpers\";\n\n/**\n * The newest releases that are at least two weeks old on the day 5.36 shipped;\n * `warlock add test` and `warlock add web` install the same ones.\n */\nconst TARGETS: Record<string, { major: number; version: string }> = {\n vite: { major: 8, version: \"8.3.3\" },\n vitest: { major: 5, version: \"5.0.3\" },\n \"@vitest/coverage-v8\": { major: 5, version: \"5.0.3\" },\n \"@vitest/coverage-istanbul\": { major: 5, version: \"5.0.3\" },\n \"@vitest/ui\": { major: 5, version: \"5.0.3\" },\n \"@vitejs/plugin-react\": { major: 6, version: \"6.1.2\" },\n};\n\nconst MIGRATION_GUIDES = \"https://vite.dev/guide/migration and https://vitest.dev/guide/migration\";\n\n/** Version specs that are not a plain registry range and must not be rewritten. */\nconst NON_REGISTRY_SPEC =\n /^(workspace:|file:|link:|portal:|git|github:|https?:|npm:)|^\\*$|^latest$/;\n\n/** Entry points Vitest 5 removed. `vitest/runners` is handled separately. */\nconst REMOVED_ENTRY_POINTS = new Set([\n \"vitest/coverage\",\n \"vitest/reporters\",\n \"vitest/environments\",\n \"vitest/snapshot\",\n \"vitest/suite\",\n \"vitest/mocker\",\n \"vitest/internal/module-runner\",\n]);\n\nconst VITE_CONFIG_FILE = /^vite(st)?\\.config\\.(ts|mts|cts|js|mjs|cjs)$/;\n\ntype PackageJson = Record<string, unknown> & {\n dependencies?: Record<string, unknown>;\n devDependencies?: Record<string, unknown>;\n overrides?: Record<string, unknown>;\n resolutions?: Record<string, unknown>;\n};\n\nfunction serialize(original: string, value: unknown): string {\n const indent = /^[ \\t]+(?=\")/m.exec(original)?.[0];\n const json = indent ? JSON.stringify(value, null, indent) : JSON.stringify(value);\n return json + (original.endsWith(\"\\n\") ? \"\\n\" : \"\");\n}\n\nfunction lineOf(source: string, needle: string): number {\n const index = source.indexOf(needle);\n return index < 0 ? 1 : source.slice(0, index).split(\"\\n\").length;\n}\n\n/** The spec moved to `target`, keeping its `^`/`~`/exact style; undefined when it already allows it. */\nfunction upgradedSpec(spec: string, target: { major: number; version: string }) {\n if (NON_REGISTRY_SPEC.test(spec)) return undefined;\n const parsed = parseVersion(spec);\n // `>=7.3.5 <8` and ranges like it are rewritten too: the lower bound is what is read.\n if (!parsed || parsed[0] >= target.major) return undefined;\n const prefix = /^[\\^~]/.exec(spec)?.[0] ?? (/^\\d/.test(spec) ? \"\" : \"^\");\n return `${prefix}${target.version}`;\n}\n\n/**\n * 5.36: Vite 8 (Rolldown), Vitest 5 and `@vitejs/plugin-react` 6 are one\n * family and must move together.\n *\n * - package.json: raises `vite`, `vitest`, `@vitest/*` and\n * `@vitejs/plugin-react` (dependencies, devDependencies, `overrides` and\n * `resolutions`) to the supported majors.\n * - `src/test-runner.ts`-style runners: Vitest 5 has no `vitest/runners`; the\n * runner class is `TestRunner`, exported from `vitest`, and\n * `onCleanupWorkerContext` is an optional property.\n * - Lists removed Vitest entry points, a top-level `esbuild` option, and object\n * `manualChunks` in Vite configs for review.\n */\nexport const viteVitest: Codemod = {\n id: \"vite-8-vitest-5\",\n version: \"5.36.0\",\n package: \"@warlock.js/core\",\n title: \"Move to Vite 8, Vitest 5 and @vitejs/plugin-react 6\",\n skill: \"upgrade-from-5-29\",\n run(context) {\n const { Node, SyntaxKind } = context.tsMorph;\n const output: CodemodOutput = { edits: [], manual: [] };\n\n const source = context.text.read(\"package.json\");\n if (source !== undefined) {\n const pkg = JSON.parse(source) as PackageJson;\n const changes: { name: string; from: string; to: string }[] = [];\n for (const field of [\n \"dependencies\",\n \"devDependencies\",\n \"overrides\",\n \"resolutions\",\n ] as const) {\n const entries = pkg[field];\n if (!entries) continue;\n for (const [name, target] of Object.entries(TARGETS)) {\n const spec = entries[name];\n if (typeof spec !== \"string\") continue;\n const next = upgradedSpec(spec, target);\n if (!next) continue;\n entries[name] = next;\n changes.push({\n name:\n field === \"dependencies\" || field === \"devDependencies\" ? name : `${field}.${name}`,\n from: spec,\n to: next,\n });\n }\n }\n const first = changes[0];\n if (first) {\n context.text.write(\"package.json\", serialize(source, pkg), {\n line: lineOf(source, `\"${first.name.replace(/^[a-z]+\\./, \"\")}\"`),\n description: changes\n .map((change) => `${change.name} ${change.from} → ${change.to}`)\n .join(\", \"),\n });\n }\n }\n\n for (const sourceFile of context.sourceFiles) {\n for (const declaration of sourceFile.getImportDeclarations()) {\n const moduleName = declaration.getModuleSpecifierValue();\n\n if (REMOVED_ENTRY_POINTS.has(moduleName)) {\n output.manual.push(\n manualAt(\n context,\n declaration,\n `Vitest 5 removed the \"${moduleName}\" entry point. Import from \"vitest\" or \"vitest/node\" instead; see ${MIGRATION_GUIDES}.`,\n ),\n );\n continue;\n }\n if (moduleName !== \"vitest/runners\") continue;\n\n const specifiers = declaration.getNamedImports();\n const runner = specifiers.find((specifier) => specifier.getName() === \"VitestTestRunner\");\n if (!runner || specifiers.length !== 1 || declaration.getDefaultImport()) {\n output.manual.push(\n manualAt(\n context,\n declaration,\n 'Vitest 5 removed \"vitest/runners\". Import the runner class as `TestRunner` from \"vitest\"; benchmark runners no longer exist.',\n ),\n );\n continue;\n }\n\n const alias = runner.getAliasNode()?.getText();\n output.edits.push(\n replaceNode(\n context,\n declaration,\n alias\n ? `import { TestRunner as ${alias} } from \"vitest\";`\n : 'import { TestRunner } from \"vitest\";',\n 'imported the runner as TestRunner from \"vitest\"',\n ),\n );\n\n if (!alias) {\n for (const identifier of sourceFile.getDescendantsOfKind(SyntaxKind.Identifier)) {\n if (identifier.getText() !== \"VitestTestRunner\") continue;\n if (identifier.getFirstAncestorByKind(SyntaxKind.ImportDeclaration)) continue;\n output.edits.push(\n replaceNode(\n context,\n identifier,\n \"TestRunner\",\n \"renamed VitestTestRunner to TestRunner\",\n ),\n );\n }\n }\n\n for (const call of sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {\n const callee = call.getExpression();\n if (\n !Node.isPropertyAccessExpression(callee) ||\n callee.getName() !== \"onCleanupWorkerContext\" ||\n call.getQuestionDotTokenNode()\n ) {\n continue;\n }\n output.edits.push(\n replaceNode(\n context,\n callee,\n `${callee.getText()}?.`,\n \"onCleanupWorkerContext is optional in Vitest 5 (absent in vm pools)\",\n ),\n );\n }\n }\n }\n\n for (const file of context.text.list()) {\n if (!VITE_CONFIG_FILE.test(file)) continue;\n const config = context.text.read(file);\n if (config === undefined) continue;\n const lines = config.split(\"\\n\");\n lines.forEach((text, index) => {\n const line = index + 1;\n if (/^\\s*esbuild\\s*:/.test(text)) {\n output.manual.push({\n file,\n line,\n hint: \"Vite 8 transforms TypeScript and JSX with Oxc: move this `esbuild` option to `oxc` (jsx, define, include/exclude convert; `target` does not). Oxc does not lower native decorators; keep `lowerStage3Decorators()` in `plugins` for decorated modules.\",\n });\n }\n if (/\\bmanualChunks\\s*:\\s*\\{/.test(text)) {\n output.manual.push({\n file,\n line,\n hint: \"Vite 8 (Rolldown) removed the object form of `output.manualChunks`. Use `output.codeSplitting.groups` with `name` and `test`.\",\n });\n }\n });\n }\n\n return output;\n },\n};\n"],"mappings":"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"}
|
package/esm/upgrade/notes.mjs
CHANGED
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
* operations. Printed so the rollout plans for them.
|
|
5
5
|
*/
|
|
6
6
|
const UPGRADE_NOTES = [
|
|
7
|
+
{
|
|
8
|
+
version: "5.36.0",
|
|
9
|
+
text: "Vitest 5 changes defaults: `clearMocks` is true, `json`/`junit` reporters write to `.vitest/` instead of stdout, `-t` joins names with ` > `, nested `vi.mock`/`vi.hoisted` calls throw and unawaited `resolves`/`rejects` assertions fail the test. Vite 8 needs Node.js 20.19 or 22.12+ and bundles with Rolldown."
|
|
10
|
+
},
|
|
7
11
|
{
|
|
8
12
|
version: "5.34.0",
|
|
9
13
|
text: "ESLint and Prettier support is gone in 5.34.0. Oxlint/Oxfmt is the only toolchain. The oxc-toolchain-removal step migrates remaining legacy configs/dependencies; review its manual findings. New/migrated templates enable correctness: error, which may report additional defects."
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notes.mjs","names":[],"sources":["../../../../../../../../packages/core/src/upgrade/notes.ts"],"sourcesContent":["import type { UpgradeNote } from \"./types\";\n\n/**\n * Breaking changes with nothing to edit in source: they change data or\n * operations. Printed so the rollout plans for them.\n */\nexport const UPGRADE_NOTES: readonly UpgradeNote[] = [\n {\n version: \"5.34.0\",\n text: \"ESLint and Prettier support is gone in 5.34.0. Oxlint/Oxfmt is the only toolchain. The oxc-toolchain-removal step migrates remaining legacy configs/dependencies; review its manual findings. New/migrated templates enable correctness: error, which may report additional defects.\",\n },\n {\n version: \"5.30.0\",\n text: \"Auth login throttle keys are hashed: counters and lockouts written by 5.29 are forgotten on upgrade (locked accounts and IPs unlock).\",\n },\n {\n version: \"5.30.0\",\n text: \"Image derivatives use a new cache layout: the first request re-renders each one. Delete the old two-level cache folders once the rollout is done.\",\n },\n];\n"],"mappings":";;;;;AAMA,MAAa,gBAAwC;CACnD;EACE,SAAS;EACT,MAAM;CACR;CACA;EACE,SAAS;EACT,MAAM;CACR;CACA;EACE,SAAS;EACT,MAAM;CACR;AACF"}
|
|
1
|
+
{"version":3,"file":"notes.mjs","names":[],"sources":["../../../../../../../../packages/core/src/upgrade/notes.ts"],"sourcesContent":["import type { UpgradeNote } from \"./types\";\n\n/**\n * Breaking changes with nothing to edit in source: they change data or\n * operations. Printed so the rollout plans for them.\n */\nexport const UPGRADE_NOTES: readonly UpgradeNote[] = [\n {\n version: \"5.36.0\",\n text: \"Vitest 5 changes defaults: `clearMocks` is true, `json`/`junit` reporters write to `.vitest/` instead of stdout, `-t` joins names with ` > `, nested `vi.mock`/`vi.hoisted` calls throw and unawaited `resolves`/`rejects` assertions fail the test. Vite 8 needs Node.js 20.19 or 22.12+ and bundles with Rolldown.\",\n },\n {\n version: \"5.34.0\",\n text: \"ESLint and Prettier support is gone in 5.34.0. Oxlint/Oxfmt is the only toolchain. The oxc-toolchain-removal step migrates remaining legacy configs/dependencies; review its manual findings. New/migrated templates enable correctness: error, which may report additional defects.\",\n },\n {\n version: \"5.30.0\",\n text: \"Auth login throttle keys are hashed: counters and lockouts written by 5.29 are forgotten on upgrade (locked accounts and IPs unlock).\",\n },\n {\n version: \"5.30.0\",\n text: \"Image derivatives use a new cache layout: the first request re-renders each one. Delete the old two-level cache folders once the rollout is done.\",\n },\n];\n"],"mappings":";;;;;AAMA,MAAa,gBAAwC;CACnD;EACE,SAAS;EACT,MAAM;CACR;CACA;EACE,SAAS;EACT,MAAM;CACR;CACA;EACE,SAAS;EACT,MAAM;CACR;CACA;EACE,SAAS;EACT,MAAM;CACR;AACF"}
|
package/esm/validation/init.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { config } from "../config/config-getter.mjs";
|
|
2
2
|
import "../config/index.mjs";
|
|
3
3
|
import { t } from "../http/middleware/inject-request-context.mjs";
|
|
4
|
+
import { translateSealRule } from "./translate-rule.mjs";
|
|
4
5
|
import { databasePlugin } from "./plugins/database-plugin.mjs";
|
|
5
6
|
import { filePlugin } from "./plugins/file-plugin.mjs";
|
|
6
7
|
import { localizedPlugin } from "./plugins/localized-plugin.mjs";
|
|
@@ -14,15 +15,21 @@ import { configureSeal, getSealConfig, registerPlugin } from "@warlock.js/seal";
|
|
|
14
15
|
* This file configures Seal to use Warlock's localization system
|
|
15
16
|
*/
|
|
16
17
|
configureSeal({
|
|
17
|
-
translateRule({ rule, attributes }) {
|
|
18
|
+
translateRule({ rule, attributes, context }) {
|
|
18
19
|
const translateRule = config.key("validation.translateRule");
|
|
19
20
|
if (translateRule) return translateRule({
|
|
20
21
|
rule,
|
|
21
|
-
attributes
|
|
22
|
+
attributes,
|
|
23
|
+
context
|
|
22
24
|
});
|
|
23
25
|
const translationKey = `${config.key("validation.translationGroup", "validation")}.${rule.name}`;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
return translateSealRule({
|
|
27
|
+
rule,
|
|
28
|
+
attributes,
|
|
29
|
+
context,
|
|
30
|
+
key: translationKey,
|
|
31
|
+
translate: t
|
|
32
|
+
});
|
|
26
33
|
},
|
|
27
34
|
translateAttribute({ attribute, context, rule }) {
|
|
28
35
|
const translateAttribute = config.key("validation.translateAttribute");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.mjs","names":[],"sources":["../../../../../../../../packages/core/src/validation/init.ts"],"sourcesContent":["/**\n * Initialize Seal with Warlock Framework Settings\n *\n * This file configures Seal to use Warlock's localization system\n */\n\n// Auto-register framework plugins\nimport { configureSeal, getSealConfig, registerPlugin } from \"@warlock.js/seal\";\nimport { config } from \"../config\";\nimport { t } from \"../http/middleware/inject-request-context\";\nimport { databasePlugin, filePlugin, localizedPlugin } from \"./plugins\";\n\n// Configure Seal to use Warlock's localization\nconfigureSeal({\n translateRule({ rule, attributes }) {\n const translateRule = config.key(\"validation.translateRule\");\n\n if (translateRule) {\n return translateRule({ rule, attributes });\n }\n\n const translationGroup = config.key(\"validation.translationGroup\", \"validation\");\n\n const translationKey = `${translationGroup}.${rule.name}`;\n
|
|
1
|
+
{"version":3,"file":"init.mjs","names":[],"sources":["../../../../../../../../packages/core/src/validation/init.ts"],"sourcesContent":["/**\n * Initialize Seal with Warlock Framework Settings\n *\n * This file configures Seal to use Warlock's localization system\n */\n\n// Auto-register framework plugins\nimport { configureSeal, getSealConfig, registerPlugin } from \"@warlock.js/seal\";\nimport { config } from \"../config\";\nimport { t } from \"../http/middleware/inject-request-context\";\nimport { translateSealRule } from \"./translate-rule\";\nimport { databasePlugin, filePlugin, localizedPlugin } from \"./plugins\";\n\n// Configure Seal to use Warlock's localization\nconfigureSeal({\n translateRule({ rule, attributes, context }) {\n const translateRule = config.key(\"validation.translateRule\");\n\n if (translateRule) {\n return translateRule({ rule, attributes, context });\n }\n\n const translationGroup = config.key(\"validation.translationGroup\", \"validation\");\n\n const translationKey = `${translationGroup}.${rule.name}`;\n\n return translateSealRule({ rule, attributes, context, key: translationKey, translate: t });\n },\n\n translateAttribute({ attribute, context, rule }) {\n const translateAttribute = config.key(\"validation.translateAttribute\");\n\n if (translateAttribute) {\n return translateAttribute({ attribute, context, rule });\n }\n\n const attributeGroup = config.key(\"validation.attributeGroup\") ?? \"attributes\";\n\n const translationKey = `${attributeGroup ? attributeGroup + \".\" : \"\"}${attribute}`;\n\n const output = t(translationKey, context.allValues);\n return output === translationKey ? attribute : output;\n },\n});\n\n// App config is not loaded yet when this module is imported, so resolve\n// firstErrorOnly lazily on every read instead of capturing it once.\nObject.defineProperty(getSealConfig(), \"firstErrorOnly\", {\n configurable: true,\n enumerable: true,\n get: () => config.key(\"validation.firstErrorOnly\", true),\n});\n\n// Register plugins to inject methods\nregisterPlugin(databasePlugin);\nregisterPlugin(filePlugin);\nregisterPlugin(localizedPlugin);\n"],"mappings":";;;;;;;;;;;;;;;;AAcA,cAAc;CACZ,cAAc,EAAE,MAAM,YAAY,WAAW;EAC3C,MAAM,gBAAgB,OAAO,IAAI,0BAA0B;EAE3D,IAAI,eACF,OAAO,cAAc;GAAE;GAAM;GAAY;EAAQ,CAAC;EAKpD,MAAM,iBAAiB,GAFE,OAAO,IAAI,+BAA+B,YAE1B,EAAE,GAAG,KAAK;EAEnD,OAAO,kBAAkB;GAAE;GAAM;GAAY;GAAS,KAAK;GAAgB,WAAW;EAAE,CAAC;CAC3F;CAEA,mBAAmB,EAAE,WAAW,SAAS,QAAQ;EAC/C,MAAM,qBAAqB,OAAO,IAAI,+BAA+B;EAErE,IAAI,oBACF,OAAO,mBAAmB;GAAE;GAAW;GAAS;EAAK,CAAC;EAGxD,MAAM,iBAAiB,OAAO,IAAI,2BAA2B,KAAK;EAElE,MAAM,iBAAiB,GAAG,iBAAiB,iBAAiB,MAAM,KAAK;EAEvE,MAAM,SAAS,EAAE,gBAAgB,QAAQ,SAAS;EAClD,OAAO,WAAW,iBAAiB,YAAY;CACjD;AACF,CAAC;AAID,OAAO,eAAe,cAAc,GAAG,kBAAkB;CACvD,cAAc;CACd,YAAY;CACZ,WAAW,OAAO,IAAI,6BAA6B,IAAI;AACzD,CAAC;AAGD,eAAe,cAAc;AAC7B,eAAe,UAAU;AACzB,eAAe,eAAe"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { environment } from "../utils/environment.mjs";
|
|
2
|
+
import { log } from "@warlock.js/logger";
|
|
3
|
+
|
|
4
|
+
//#region ../../packages/core/src/validation/translate-rule.ts
|
|
5
|
+
const PLACEHOLDER = /:([a-zA-Z_]+)/g;
|
|
6
|
+
/**
|
|
7
|
+
* Placeholder names other validation message sets use for the values seal
|
|
8
|
+
* exposes under a different name. Keyed by rule name; each entry maps the
|
|
9
|
+
* alias to the seal attribute it reads.
|
|
10
|
+
*/
|
|
11
|
+
const PLACEHOLDER_ALIASES = {
|
|
12
|
+
minLength: { length: "minLength" },
|
|
13
|
+
maxLength: { length: "maxLength" },
|
|
14
|
+
strongMinLength: { length: "minLength" },
|
|
15
|
+
match: { matchingInput: "field" }
|
|
16
|
+
};
|
|
17
|
+
const warnedKeys = /* @__PURE__ */ new Set();
|
|
18
|
+
/** Seal attributes plus the alias names other message sets use for them. */
|
|
19
|
+
function withPlaceholderAliases(ruleName, attributes) {
|
|
20
|
+
const aliases = PLACEHOLDER_ALIASES[ruleName];
|
|
21
|
+
if (!aliases) return attributes;
|
|
22
|
+
const output = { ...attributes };
|
|
23
|
+
for (const [alias, source] of Object.entries(aliases)) if (!(alias in output) && source in attributes) output[alias] = attributes[source];
|
|
24
|
+
return output;
|
|
25
|
+
}
|
|
26
|
+
/** The `:placeholder` names in a message that `attributes` cannot fill. */
|
|
27
|
+
function unfilledPlaceholders(message, attributes) {
|
|
28
|
+
const names = /* @__PURE__ */ new Set();
|
|
29
|
+
for (const match of message.matchAll(PLACEHOLDER)) {
|
|
30
|
+
const name = match[1];
|
|
31
|
+
if (name !== void 0 && !(name in attributes)) names.add(name);
|
|
32
|
+
}
|
|
33
|
+
return [...names];
|
|
34
|
+
}
|
|
35
|
+
function warnUnfilled(key, message, names) {
|
|
36
|
+
if (environment() !== "development" || warnedKeys.has(key)) return;
|
|
37
|
+
warnedKeys.add(key);
|
|
38
|
+
log.warn("validation", "placeholder", `Validation message for "${key}" still contains ${names.map((name) => `:${name}`).join(", ")} after interpolation: "${message}". Check the translation placeholders against the rule's parameters.`);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Seal's default rule translation for Warlock. A translation is used only when
|
|
42
|
+
* every `:placeholder` in it can be filled from the rule's parameters (after
|
|
43
|
+
* mapping the alias names other message sets use, such as `:length` for
|
|
44
|
+
* `:maxLength`). Otherwise seal's own message is used, so a message written
|
|
45
|
+
* for a different library (for example the browser form defaults that share the
|
|
46
|
+
* process-wide translation store) never reaches an API response with a raw
|
|
47
|
+
* placeholder in it. In development, the first time a key falls back this way
|
|
48
|
+
* a warning names the key.
|
|
49
|
+
*/
|
|
50
|
+
function translateSealRule({ rule, attributes, key, translate }) {
|
|
51
|
+
const own = rule.errorMessage || rule.defaultErrorMessage || key;
|
|
52
|
+
const aliased = withPlaceholderAliases(rule.name, attributes);
|
|
53
|
+
const translation = translate(key, aliased);
|
|
54
|
+
if (translation === key) return own;
|
|
55
|
+
const leftover = unfilledPlaceholders(translation, aliased);
|
|
56
|
+
if (leftover.length === 0) return translation;
|
|
57
|
+
warnUnfilled(key, translation, leftover);
|
|
58
|
+
return own;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
//#endregion
|
|
62
|
+
export { translateSealRule, withPlaceholderAliases };
|
|
63
|
+
//# sourceMappingURL=translate-rule.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translate-rule.mjs","names":[],"sources":["../../../../../../../../packages/core/src/validation/translate-rule.ts"],"sourcesContent":["import { log } from \"@warlock.js/logger\";\nimport type { RuleTranslation } from \"@warlock.js/seal\";\nimport { environment } from \"../utils/environment\";\n\nconst PLACEHOLDER = /:([a-zA-Z_]+)/g;\n\n/**\n * Placeholder names other validation message sets use for the values seal\n * exposes under a different name. Keyed by rule name; each entry maps the\n * alias to the seal attribute it reads.\n */\nconst PLACEHOLDER_ALIASES: Record<string, Record<string, string>> = {\n minLength: { length: \"minLength\" },\n maxLength: { length: \"maxLength\" },\n strongMinLength: { length: \"minLength\" },\n match: { matchingInput: \"field\" },\n};\n\nconst warnedKeys = new Set<string>();\n\n/** Test-only: forget which keys already produced a warning. */\nexport function resetUnfilledPlaceholderWarningsForTests(): void {\n warnedKeys.clear();\n}\n\n/** Seal attributes plus the alias names other message sets use for them. */\nexport function withPlaceholderAliases(\n ruleName: string,\n attributes: RuleTranslation[\"attributes\"],\n): RuleTranslation[\"attributes\"] {\n const aliases = PLACEHOLDER_ALIASES[ruleName];\n\n if (!aliases) return attributes;\n\n const output = { ...attributes };\n\n for (const [alias, source] of Object.entries(aliases)) {\n if (!(alias in output) && source in attributes) {\n output[alias] = attributes[source];\n }\n }\n\n return output;\n}\n\n/** The `:placeholder` names in a message that `attributes` cannot fill. */\nfunction unfilledPlaceholders(message: string, attributes: Record<string, unknown>): string[] {\n const names = new Set<string>();\n\n for (const match of message.matchAll(PLACEHOLDER)) {\n const name = match[1];\n\n if (name !== undefined && !(name in attributes)) names.add(name);\n }\n\n return [...names];\n}\n\nfunction warnUnfilled(key: string, message: string, names: string[]): void {\n if (environment() !== \"development\" || warnedKeys.has(key)) return;\n\n warnedKeys.add(key);\n\n log.warn(\n \"validation\",\n \"placeholder\",\n `Validation message for \"${key}\" still contains ${names\n .map((name) => `:${name}`)\n .join(\", \")} after interpolation: \"${message}\". ` +\n \"Check the translation placeholders against the rule's parameters.\",\n );\n}\n\nexport type TranslateSealRuleOptions = RuleTranslation & {\n /** Translation key to look up, e.g. `validation.maxLength`. */\n key: string;\n /** Translates the key with the given placeholders (the request-aware `t`). */\n translate: (key: string, placeholders: Record<string, unknown>) => string;\n};\n\n/**\n * Seal's default rule translation for Warlock. A translation is used only when\n * every `:placeholder` in it can be filled from the rule's parameters (after\n * mapping the alias names other message sets use, such as `:length` for\n * `:maxLength`). Otherwise seal's own message is used, so a message written\n * for a different library (for example the browser form defaults that share the\n * process-wide translation store) never reaches an API response with a raw\n * placeholder in it. In development, the first time a key falls back this way\n * a warning names the key.\n */\nexport function translateSealRule({\n rule,\n attributes,\n key,\n translate,\n}: TranslateSealRuleOptions): string {\n const own = rule.errorMessage || rule.defaultErrorMessage || key;\n const aliased = withPlaceholderAliases(rule.name, attributes);\n const translation = translate(key, aliased);\n\n if (translation === key) return own;\n\n const leftover = unfilledPlaceholders(translation, aliased);\n\n if (leftover.length === 0) return translation;\n\n warnUnfilled(key, translation, leftover);\n\n return own;\n}\n"],"mappings":";;;;AAIA,MAAM,cAAc;;;;;;AAOpB,MAAM,sBAA8D;CAClE,WAAW,EAAE,QAAQ,YAAY;CACjC,WAAW,EAAE,QAAQ,YAAY;CACjC,iBAAiB,EAAE,QAAQ,YAAY;CACvC,OAAO,EAAE,eAAe,QAAQ;AAClC;AAEA,MAAM,6BAAa,IAAI,IAAY;;AAQnC,SAAgB,uBACd,UACA,YAC+B;CAC/B,MAAM,UAAU,oBAAoB;CAEpC,IAAI,CAAC,SAAS,OAAO;CAErB,MAAM,SAAS,EAAE,GAAG,WAAW;CAE/B,KAAK,MAAM,CAAC,OAAO,WAAW,OAAO,QAAQ,OAAO,GAClD,IAAI,EAAE,SAAS,WAAW,UAAU,YAClC,OAAO,SAAS,WAAW;CAI/B,OAAO;AACT;;AAGA,SAAS,qBAAqB,SAAiB,YAA+C;CAC5F,MAAM,wBAAQ,IAAI,IAAY;CAE9B,KAAK,MAAM,SAAS,QAAQ,SAAS,WAAW,GAAG;EACjD,MAAM,OAAO,MAAM;EAEnB,IAAI,SAAS,UAAa,EAAE,QAAQ,aAAa,MAAM,IAAI,IAAI;CACjE;CAEA,OAAO,CAAC,GAAG,KAAK;AAClB;AAEA,SAAS,aAAa,KAAa,SAAiB,OAAuB;CACzE,IAAI,YAAY,MAAM,iBAAiB,WAAW,IAAI,GAAG,GAAG;CAE5D,WAAW,IAAI,GAAG;CAElB,IAAI,KACF,cACA,eACA,2BAA2B,IAAI,mBAAmB,MAC/C,KAAK,SAAS,IAAI,MAAM,CAAC,CACzB,KAAK,IAAI,EAAE,yBAAyB,QAAQ,qEAEjD;AACF;;;;;;;;;;;AAmBA,SAAgB,kBAAkB,EAChC,MACA,YACA,KACA,aACmC;CACnC,MAAM,MAAM,KAAK,gBAAgB,KAAK,uBAAuB;CAC7D,MAAM,UAAU,uBAAuB,KAAK,MAAM,UAAU;CAC5D,MAAM,cAAc,UAAU,KAAK,OAAO;CAE1C,IAAI,gBAAgB,KAAK,OAAO;CAEhC,MAAM,WAAW,qBAAqB,aAAa,OAAO;CAE1D,IAAI,SAAS,WAAW,GAAG,OAAO;CAElC,aAAa,KAAK,aAAa,QAAQ;CAEvC,OAAO;AACT"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { BudgetPage, BudgetViolation, BuildBudgetReport, BuildBudgets, PageBudget } from "../production/build-budgets.mjs";
|
|
1
|
+
import { BudgetPage, BudgetViolation, BuildBudgetReport, BuildBudgets, PageBudget, PayloadBudget } from "../production/build-budgets.mjs";
|
|
2
|
+
import { ReleaseCheck, ReleaseConfig } from "../release/types.mjs";
|
|
2
3
|
import { WarlockConfig } from "./types.mjs";
|
|
3
4
|
import { defineConfig } from "./define-config.mjs";
|
|
4
5
|
import { WarlockConfigManager, isUnknownTsExtensionError, isUnresolvedConfigImportError, isUnsupportedTypeStrippingError, warlockConfigManager } from "./warlock-config.manager.mjs";
|
|
5
|
-
export { type BudgetPage, type BudgetViolation, type BuildBudgetReport, type BuildBudgets, type PageBudget, WarlockConfig, WarlockConfigManager, defineConfig, isUnknownTsExtensionError, isUnresolvedConfigImportError, isUnsupportedTypeStrippingError, warlockConfigManager };
|
|
6
|
+
export { type BudgetPage, type BudgetViolation, type BuildBudgetReport, type BuildBudgets, type PageBudget, type PayloadBudget, type ReleaseCheck, type ReleaseConfig, WarlockConfig, WarlockConfigManager, defineConfig, isUnknownTsExtensionError, isUnresolvedConfigImportError, isUnsupportedTypeStrippingError, warlockConfigManager };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BuildBudgets } from "../production/build-budgets.mjs";
|
|
2
|
+
import { ReleaseConfig } from "../release/types.mjs";
|
|
2
3
|
import { BuiltInHealthCheckerOptions, FileHealthCheckerContract } from "../dev-server/health-checker/file-health-checker.contract.mjs";
|
|
3
4
|
import { Connector } from "../connectors/types.mjs";
|
|
4
5
|
import { CLICommand } from "../commands/cli-command.mjs";
|
|
@@ -270,6 +271,13 @@ export type WarlockConfig = {
|
|
|
270
271
|
*/
|
|
271
272
|
migrations?: Array<MigrationConstructor>;
|
|
272
273
|
};
|
|
274
|
+
/**
|
|
275
|
+
* Release pipeline settings for `warlock release`: candidate, gate,
|
|
276
|
+
* migration proof, stage, activate, verify and rollback.
|
|
277
|
+
*
|
|
278
|
+
* @see ReleaseConfig
|
|
279
|
+
*/
|
|
280
|
+
release?: ReleaseConfig;
|
|
273
281
|
/**
|
|
274
282
|
* Testing configuration
|
|
275
283
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.mts","names":[],"sources":["../../../../../../../../packages/core/src/warlock-config/types.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.d.mts","names":[],"sources":["../../../../../../../../packages/core/src/warlock-config/types.ts"],"mappings":";;;;;;;;;;;;;YAgBY;;;;EAIV;;;;IAIE;;;;IAIA;;;;IAIA;;;;;;;;;;;;;EAcF,QAAQ,KAAK;;;;;;IAMX;;IAEA,UAAU;;;;;;;;;;;IAWV;;;;;;IAMA;;;;;;;IAOA;;;;;;;;;;;;;;;;;IAiBA;;;;;;;;;;;;;;;;IAgBA;;;;;;IAMA;;;;;;IAMA;;;;;;;;;;;;;;;;;;;;;;;;;;EA2BF,aAAa;;;;EAKb;IACE,WAAW;;;;;EAMb;;;;IAIE;;;;;;MAME;;;;;;MAMA;;;;;;IAMF,iBAAiB,8BAA8B;;;;;IAK/C;;;;;;;;;IASA;;;;;;;;;IASA;;;;;;;;;;IAUA;;;;;;;;;;;;IAYA;;;;;;;;;;IAUA;;;;;;;;;IASA;;MAIM;;MAEA;;MAEA;;;;;;EAOR;;;;;;;;;;;;;;;;;;IAkBE,aAAa,MAAM;;;;;;;;EASrB,UAAU;;;;;;;EAQV;;;;;;;;;IASE;;;;;;;;IASA"}
|