@zerotal/core 1.14.2 → 1.14.3

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 CHANGED
@@ -8,6 +8,21 @@ follows the Zerotal monorepo's unified versioning.
8
8
 
9
9
  ## [Unreleased]
10
10
 
11
+ ## [1.14.3] — 2026-09-01
12
+
13
+ ### Fixed
14
+
15
+ - **`zt upgrade` never offered the 1.13.0 migrations.** `deprecated-aliases` bundled two
16
+ retirements that shipped in 1.13.0 — `serve --dev` and `routes:types` — with the
17
+ `BaseModel` rename that has not shipped and is scheduled for 2.0. A codemod carries one
18
+ version, so it took the later one, and `zt upgrade --to 1.13.0` selected nothing at all.
19
+ The migration for the release that broke `serve --dev` was unreachable by every app
20
+ crossing it.
21
+
22
+ Split into `deprecated-aliases` at 1.13.0 and `base-model-rename` at 2.0.0. An app moving
23
+ to any 1.13+ release now gets the command rewrites, and one crossing to 2.0 no longer has
24
+ `BaseModel` renamed as a side effect of a command alias.
25
+
11
26
  ## [1.14.2] — 2026-09-01
12
27
 
13
28
  ### Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zerotal/core",
3
- "version": "1.14.2",
3
+ "version": "1.14.3",
4
4
  "license": "MIT",
5
5
  "maturity": "stable",
6
6
  "private": false,
@@ -1,24 +1,19 @@
1
1
  /**
2
2
  * Ledger #4 — retire the deprecated aliases.
3
3
  *
4
- * Three pairs, each a live alias somebody depends on, which is exactly why they
5
- * could not be dropped in a minor:
4
+ * Two codemods, not one, because they retire at different times and a codemod
5
+ * carries a single version:
6
6
  *
7
- * - `BaseModel` → `Model`. The same class object under its original name.
8
- * - `routes:types` → `route:types`. Registered as an alias in `CommandRunner`.
9
- * - `serve --dev` → `dev`. The flag still works; `dev` is the command it became.
7
+ * - {@link deprecatedAliases} at **1.13.0** — `routes:types` → `route:types` and
8
+ * `serve --dev` → `dev`. Both shipped; both fail loudly now.
9
+ * - {@link baseModelRename} at **2.0.0** — `BaseModel` → `Model`. Not shipped;
10
+ * sized on 1 Sep 2026 at 617 api-surface references and moved to 2.0.
10
11
  *
11
- * ## What this deliberately does not do
12
- *
13
- * **It does not touch `BaseModel` in a type position.** `Model` and `BaseModel`
14
- * are the same runtime class, so a value-position rename is cosmetic and safe.
15
- * In a generic bound — `<T extends BaseModel>` — the name may be load-bearing
16
- * for a reader even though it resolves identically, and in framework source it
17
- * genuinely is. Those are reported for a person instead.
18
- *
19
- * That asymmetry is the whole reason this is a codemod rather than a
20
- * find-and-replace: the mixin-composition script learned it the hard way in
21
- * 1.3.0, where a blind rename broke import specifiers it had not considered.
12
+ * They were one codemod, which forced one version, which took the later of the
13
+ * two — so `zt upgrade --to 1.13.0` selected nothing and offered no help
14
+ * migrating the very release that retired `serve --dev`, while an app crossing to
15
+ * 2.0 would have had its `BaseModel` renamed as a side effect of a command alias.
16
+ * A codemod nobody's upgrade reaches is a codemod that does not exist.
22
17
  */
23
18
  import type { Change, Codemod, CodemodResult, Manual, SourceFile } from "../types.ts";
24
19
 
@@ -52,10 +47,53 @@ const COMMAND_ALIASES: { find: RegExp; replace: string; label: string }[] = [
52
47
  },
53
48
  ];
54
49
 
50
+ /** The command aliases retired in 1.13.0. Both fail loudly rather than drifting. */
55
51
  export const deprecatedAliases: Codemod = {
56
- version: "2.0.0",
52
+ version: "1.13.0",
57
53
  name: "deprecated-aliases",
58
- description: "Retire BaseModel, routes:types and serve --dev in favour of their real names",
54
+ description: "Retire routes:types and serve --dev in favour of their real names",
55
+ ledger: 4,
56
+
57
+ run(files: SourceFile[]): CodemodResult {
58
+ const changes: Change[] = [];
59
+
60
+ for (const { file, contents } of files) {
61
+ let next = contents;
62
+ const notes: string[] = [];
63
+
64
+ for (const { find, replace, label } of COMMAND_ALIASES) {
65
+ const hits = next.match(find)?.length ?? 0;
66
+ if (hits === 0) continue;
67
+ next = next.replace(find, replace);
68
+ notes.push(`${hits} × ${label}`);
69
+ }
70
+
71
+ if (next !== contents) changes.push({ file, summary: notes.join(", "), contents: next });
72
+ }
73
+
74
+ return { changes, manual: [] };
75
+ },
76
+ };
77
+
78
+ /**
79
+ * `BaseModel` → `Model`, which has **not shipped** and is scheduled for 2.0.
80
+ *
81
+ * ## What this deliberately does not do
82
+ *
83
+ * **It does not touch `BaseModel` in a type position.** `Model` and `BaseModel`
84
+ * are the same runtime class, so a value-position rename is cosmetic and safe.
85
+ * In a generic bound — `<T extends BaseModel>` — the name may be load-bearing
86
+ * for a reader even though it resolves identically, and in framework source it
87
+ * genuinely is. Those are reported for a person instead.
88
+ *
89
+ * That asymmetry is the whole reason this is a codemod rather than a
90
+ * find-and-replace: the mixin-composition script learned it the hard way in
91
+ * 1.3.0, where a blind rename broke import specifiers it had not considered.
92
+ */
93
+ export const baseModelRename: Codemod = {
94
+ version: "2.0.0",
95
+ name: "base-model-rename",
96
+ description: "Rename BaseModel to Model at every declaration site",
59
97
  ledger: 4,
60
98
 
61
99
  run(files: SourceFile[]): CodemodResult {
@@ -88,13 +126,6 @@ export const deprecatedAliases: Codemod = {
88
126
  notes.push("import specifier updated");
89
127
  }
90
128
 
91
- for (const { find, replace, label } of COMMAND_ALIASES) {
92
- const hits = next.match(find)?.length ?? 0;
93
- if (hits === 0) continue;
94
- next = next.replace(find, replace);
95
- notes.push(`${hits} × ${label}`);
96
- }
97
-
98
129
  // Type positions are a handover, not a rewrite.
99
130
  contents.split("\n").forEach((line, i) => {
100
131
  if (!BASE_MODEL_TYPE.test(line)) return;
@@ -8,9 +8,9 @@
8
8
  * that rule fails is by everyone assuming there is time.
9
9
  */
10
10
  import type { Codemod } from "../types.ts";
11
- import { deprecatedAliases } from "./deprecated-aliases.ts";
11
+ import { deprecatedAliases, baseModelRename } from "./deprecated-aliases.ts";
12
12
  import { clientTaggedTemplate } from "./client-tagged-template.ts";
13
13
 
14
- export const CODEMODS: Codemod[] = [clientTaggedTemplate, deprecatedAliases];
14
+ export const CODEMODS: Codemod[] = [clientTaggedTemplate, deprecatedAliases, baseModelRename];
15
15
 
16
- export { deprecatedAliases, clientTaggedTemplate };
16
+ export { deprecatedAliases, baseModelRename, clientTaggedTemplate };