@tailor-platform/sdk-codemod 0.3.0-next.6 → 0.3.0-next.8

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
@@ -1,5 +1,60 @@
1
1
  # @tailor-platform/sdk-codemod
2
2
 
3
+ ## 0.3.0-next.8
4
+
5
+ ### Patch Changes
6
+
7
+ - [#1811](https://github.com/tailor-platform/sdk/pull/1811) [`b2fc104`](https://github.com/tailor-platform/sdk/commit/b2fc104d9cdfc52e98c97bc18d80a9e2e9d5f4c2) Thanks [@toiroakr](https://github.com/toiroakr)! - Move the TailorDB `erdSite` setting out of the core config schema into the ERD plugin's own configuration. `db.<namespace>.erdSite` is no longer accepted in `tailor.config.ts`; configure the ERD deploy target on the plugin instead:
8
+
9
+ ```ts
10
+ import { definePlugins } from "@tailor-platform/sdk";
11
+ import { tailordbErdPlugin } from "@tailor-platform/sdk-plugin-tailordb-erd";
12
+
13
+ export const plugins = definePlugins(
14
+ // TailorDB namespace name → static website name
15
+ tailordbErdPlugin({ sites: { tailordb: "my-erd-site" } }),
16
+ );
17
+ ```
18
+
19
+ The `tailor tailordb erd` commands resolve deploy targets from `tailordbErdPlugin({ sites })` and now validate each namespace against `config.db` and each site name against `staticWebsites`, so typos surface when the config is loaded instead of at deploy time. The `v2/erd-site-to-plugin` codemod migrates existing configs automatically. For programmatic users, `loadTailorDBNamespaces()` additionally returns the config module's registered `plugins`, and namespace selector callbacks receive them as a second argument.
20
+
21
+ - [#1807](https://github.com/tailor-platform/sdk/pull/1807) [`817454f`](https://github.com/tailor-platform/sdk/commit/817454fff35e4093bce5fdcb9e1fcda8bbd1d7ef) Thanks [@dqn](https://github.com/dqn)! - `seedPlugin` no longer generates the `exec.mjs` seed runner. Seeding and validation move to the `tailor seed` commands provided by the `@tailor-platform/sdk-plugin-seed` CLI plugin: install it with `npm install -D @tailor-platform/sdk-plugin-seed`, replace `node <distPath>/exec.mjs` with `tailor seed apply` and `node <distPath>/exec.mjs validate` with `tailor seed validate`, and delete the stale generated `exec.mjs`. Seed data and schema generation (`data/*.jsonl`, `data/*.schema.ts`) is unchanged. Because the plugin reads the config at run time, `machineUserName` changes in seedPlugin options now take effect without regenerating. `@tailor-platform/sdk/cli` gains `loadSeedContext` (and `SeedContext` types) for this, `SeedData` is now JSON-typed, and `executeScript` accepts a plain object `invoker` (`ScriptInvoker`).
22
+
23
+ ## 0.3.0-next.7
24
+
25
+ ### Patch Changes
26
+
27
+ - [#1787](https://github.com/tailor-platform/sdk/pull/1787) [`898d0b0`](https://github.com/tailor-platform/sdk/commit/898d0b0f809d15ea883a32a78a247eda4ca7caa7) Thanks [@toiroakr](https://github.com/toiroakr)! - Fix `tailor upgrade` reporting zero codemods across several v2 prerelease boundaries. `v2/db-type-to-table` and `v2/runtime-subpath-namespace` now trigger at `2.0.0-next.4` (where they actually shipped) instead of `2.0.0-next.3`, and `v2/forward-relation-name`, `v2/tailordb-validate-simplify`, and `v2/tailordb-hook-redesign` now trigger at `2.0.0-next.5` instead of `2.0.0-next.4`.
28
+
29
+ - [#1787](https://github.com/tailor-platform/sdk/pull/1787) [`6653afd`](https://github.com/tailor-platform/sdk/commit/6653afd5a0be52f8c9a1dc6fa50e445f0f678dc0) Thanks [@toiroakr](https://github.com/toiroakr)! - Add a `V2_NEXT_PENDING` placeholder `prereleaseUntil` for codemods whose exact `2.0.0-next.N` release boundary isn't known yet at implementation time, plus a `pnpm codemod:resolve-pending` step (wired into the release workflow) that resolves it to the real version constant once the release PR bumps `@tailor-platform/sdk`'s version. Prevents codemod boundaries from drifting out of sync with the version they actually ship in, which previously required a manual follow-up fix after each release.
30
+
31
+ - [#1782](https://github.com/tailor-platform/sdk/pull/1782) [`c971797`](https://github.com/tailor-platform/sdk/commit/c971797c9bfa035a43771c46f2b1c3bd93f989a9) Thanks [@toiroakr](https://github.com/toiroakr)! - Rename `Workflow.trigger()` (returned by `createWorkflow()`) and `WorkflowJob.trigger()` (returned by `createWorkflowJob()`) to `.start()`, aligning the SDK's ergonomic verb with the platform's `start*` RPC vocabulary:
32
+
33
+ ```diff
34
+ const inventory = checkInventory.trigger({ orderId: input.orderId });
35
+ +const inventory = checkInventory.start({ orderId: input.orderId });
36
+
37
+ -const workflowRunId = await orderProcessingWorkflow.trigger(args, { invoker: "manager" });
38
+ +const workflowRunId = await orderProcessingWorkflow.start(args, { invoker: "manager" });
39
+ ```
40
+
41
+ `mockWorkflow()`'s `wf.job(definition)` / `wf.workflow(definition)` now return a mock of the `.start` method, and `wf.setTriggerHandler` / `wf.triggeredJobs` are renamed to `wf.setStartHandler` / `wf.startedJobs`. No codemod ships for the `.trigger()` → `.start()` call-site rename itself — see the `v2/workflow-start-rename` migration guide entry for manual migration steps.
42
+
43
+ - [#1782](https://github.com/tailor-platform/sdk/pull/1782) [`c971797`](https://github.com/tailor-platform/sdk/commit/c971797c9bfa035a43771c46f2b1c3bd93f989a9) Thanks [@toiroakr](https://github.com/toiroakr)! - Remove the pre-alignment `tailor.workflow` names `triggerWorkflow`, `triggerJobFunction`, and `resumeWorkflow` (and their `TriggerWorkflowOptions` / `TriggerJobFunctionOptions` option types) from `@tailor-platform/sdk/runtime`, the ambient `@tailor-platform/sdk/runtime/globals` types, and the `mockWorkflow()` test facade. Use the canonical names instead:
44
+
45
+ ```diff
46
+ import { workflow } from "@tailor-platform/sdk/runtime";
47
+
48
+ -await workflow.triggerWorkflow("myWorkflow", { data: "value" });
49
+ +await workflow.startWorkflow("myWorkflow", { data: "value" });
50
+ -workflow.triggerJobFunction("myJob", { data: "value" });
51
+ +workflow.startJobFunction("myJob", { data: "value" });
52
+ -await workflow.resumeWorkflow("execution-id");
53
+ +await workflow.resumeWorkflowExecution("execution-id");
54
+ ```
55
+
56
+ Run the `v2/workflow-trigger-rename` codemod to migrate call sites automatically.
57
+
3
58
  ## 0.3.0-next.6
4
59
 
5
60
  ### Patch Changes
@@ -0,0 +1,195 @@
1
+ import { Lang, parse } from "@ast-grep/napi";
2
+ //#region codemods/v2/erd-site-to-plugin/scripts/transform.ts
3
+ const PLUGIN_IMPORT = "import { tailordbErdPlugin } from \"@tailor-platform/sdk-plugin-tailordb-erd\";";
4
+ const DEFINE_PLUGINS_IMPORT = "import { definePlugins } from \"@tailor-platform/sdk\";";
5
+ const SDK_VALUE_IMPORT_REGEX = /(^|\n)import\s*\{[^}\n]*\}\s*from\s*["']@tailor-platform\/sdk["'];?/;
6
+ const FUNCTION_KINDS = /* @__PURE__ */ new Set([
7
+ "arrow_function",
8
+ "function_declaration",
9
+ "function_expression",
10
+ "generator_function",
11
+ "generator_function_declaration",
12
+ "method_definition"
13
+ ]);
14
+ function unquote(text) {
15
+ return text.replace(/^["']|["']$/g, "");
16
+ }
17
+ function propertyName(pair) {
18
+ const key = pair.field("key");
19
+ if (!key || key.kind() === "computed_property_name") return null;
20
+ return unquote(key.text());
21
+ }
22
+ function insideFunction(node) {
23
+ for (let current = node.parent(); current; current = current.parent()) if (FUNCTION_KINDS.has(current.kind())) return true;
24
+ return false;
25
+ }
26
+ /**
27
+ * Resolve the local binding name of `definePlugins` imported from
28
+ * `@tailor-platform/sdk`, honoring `import { definePlugins as alias }`.
29
+ * @param tree - Parsed source tree root.
30
+ * @returns Local binding name, or null when it is not imported.
31
+ */
32
+ function definePluginsLocalName(tree) {
33
+ const importStatements = tree.findAll({ rule: {
34
+ kind: "import_statement",
35
+ has: {
36
+ kind: "string",
37
+ regex: "^[\"']@tailor-platform/sdk[\"']$"
38
+ }
39
+ } });
40
+ for (const statement of importStatements) for (const specifier of statement.findAll({ rule: { kind: "import_specifier" } })) {
41
+ const name = specifier.field("name");
42
+ if (name?.text() !== "definePlugins") continue;
43
+ return (specifier.field("alias") ?? name).text();
44
+ }
45
+ return null;
46
+ }
47
+ /**
48
+ * Build an edit that removes a property pair from an object literal, cleaning
49
+ * up the separating comma, an inline line comment that documented the removed
50
+ * property, and the removed line's indentation.
51
+ * @param objectNode - Object literal containing the pair.
52
+ * @param pairNode - Property pair to remove.
53
+ * @returns Edit replacing the object literal with the pair removed.
54
+ */
55
+ function removePairEdit(objectNode, pairNode) {
56
+ const objText = objectNode.text();
57
+ const objStart = objectNode.range().start.index;
58
+ const start = pairNode.range().start.index - objStart;
59
+ const end = pairNode.range().end.index - objStart;
60
+ const before = objText.slice(0, start);
61
+ const after = objText.slice(end);
62
+ let removeFrom = start;
63
+ let removeTo = end;
64
+ const trailing = after.match(/^[ \t]*,[ \t]*(?:\/\/[^\n]*)?\n?/);
65
+ if (trailing) {
66
+ removeTo = end + trailing[0].length;
67
+ const indent = before.match(/\n[ \t]*$/);
68
+ if (indent) removeFrom = start - (indent[0].length - 1);
69
+ } else {
70
+ const leading = before.match(/,\s*$/);
71
+ if (leading) removeFrom = start - leading[0].length;
72
+ const inlineComment = after.match(/^[ \t]*\/\/[^\n]*/);
73
+ if (inlineComment) removeTo = end + inlineComment[0].length;
74
+ }
75
+ return objectNode.replace(objText.slice(0, removeFrom) + objText.slice(removeTo));
76
+ }
77
+ /**
78
+ * Build an edit that appends an argument to a call expression, preserving the
79
+ * call's single-line or multi-line formatting. Insertion points are derived
80
+ * from argument-list AST nodes so a trailing line comment after the last
81
+ * argument cannot swallow the separating comma.
82
+ * @param callNode - Call expression to extend.
83
+ * @param arg - Argument expression to append.
84
+ * @returns Edit replacing the call expression with the argument appended.
85
+ */
86
+ function appendArgEdit(callNode, arg) {
87
+ const callText = callNode.text();
88
+ const base = callNode.range().start.index;
89
+ const children = callNode.field("arguments").children();
90
+ const args = children.filter((child) => child.isNamed() && child.kind() !== "comment");
91
+ const closeOffset = children.at(-1).range().start.index - base;
92
+ const multiline = callText.includes("\n");
93
+ const closeIndent = callText.slice(0, closeOffset).match(/\n([ \t]*)$/)?.[1] ?? "";
94
+ const argIndent = `${closeIndent} `;
95
+ if (args.length === 0) {
96
+ const head = callText.slice(0, closeOffset).replace(/[ \t]*$/, "");
97
+ const rewritten = multiline ? `${head}${argIndent}${arg},\n${closeIndent})` : `${head}${arg})`;
98
+ return callNode.replace(rewritten);
99
+ }
100
+ const lastArg = args.at(-1);
101
+ const followers = children.slice(children.indexOf(lastArg) + 1);
102
+ const trailingComma = followers.find((child) => !child.isNamed() && child.text() === ",");
103
+ const anchor = trailingComma ?? lastArg;
104
+ if (!multiline) {
105
+ const insertAt = anchor.range().end.index - base;
106
+ const insertion = `${trailingComma ? "" : ","} ${arg}`;
107
+ return callNode.replace(callText.slice(0, insertAt) + insertion + callText.slice(insertAt));
108
+ }
109
+ const argInsertAt = (followers.findLast((child) => child.kind() === "comment" && child.range().start.index >= anchor.range().end.index && child.range().start.line === anchor.range().end.line) ?? anchor).range().end.index - base;
110
+ let rewritten = callText.slice(0, argInsertAt) + `\n${argIndent}${arg},` + callText.slice(argInsertAt);
111
+ if (!trailingComma) {
112
+ const commaAt = lastArg.range().end.index - base;
113
+ rewritten = rewritten.slice(0, commaAt) + "," + rewritten.slice(commaAt);
114
+ }
115
+ return callNode.replace(rewritten);
116
+ }
117
+ /**
118
+ * Add `definePlugins` to an existing single-line value import from
119
+ * `@tailor-platform/sdk`, or return null when no such import exists.
120
+ * @param source - Source code to modify.
121
+ * @returns Modified source, or null when a separate import line is needed.
122
+ */
123
+ function addDefinePluginsSpecifier(source) {
124
+ const match = source.match(SDK_VALUE_IMPORT_REGEX);
125
+ if (!match) return null;
126
+ const updated = match[0].replace(/,?\s*\}/, ", definePlugins }");
127
+ return source.replace(match[0], updated);
128
+ }
129
+ function insertImports(source, importLines) {
130
+ const sdkImportRegex = /^import\s+.*from\s+["']@tailor-platform\/sdk[^"']*["'];?$/gm;
131
+ let lastMatch = null;
132
+ for (let match = sdkImportRegex.exec(source); match; match = sdkImportRegex.exec(source)) lastMatch = match;
133
+ const block = importLines.join("\n");
134
+ if (lastMatch) {
135
+ const insertPos = lastMatch.index + lastMatch[0].length;
136
+ return source.slice(0, insertPos) + "\n" + block + source.slice(insertPos);
137
+ }
138
+ return block + "\n" + source;
139
+ }
140
+ /**
141
+ * Move `db.<namespace>.erdSite` entries in defineConfig() into a
142
+ * `tailordbErdPlugin({ sites })` argument of definePlugins():
143
+ *
144
+ * 1. Remove each `erdSite` property from `db.<namespace>` objects of
145
+ * top-level defineConfig() calls (factory-wrapped configs are left for
146
+ * manual review, since their erdSite values may reference local bindings)
147
+ * 2. Append `tailordbErdPlugin({ sites: { <namespace>: <value> } })` to the
148
+ * existing definePlugins() call (honoring an import alias), or add a
149
+ * `plugins` export when none exists
150
+ * 3. Add the plugin import (and a definePlugins import when newly needed)
151
+ * @param source - Source code to transform
152
+ * @returns Transformed source or null if no changes needed
153
+ */
154
+ function transform(source) {
155
+ if (!source.includes("erdSite") || !source.includes("@tailor-platform/sdk")) return null;
156
+ if (source.includes("tailordbErdPlugin")) return null;
157
+ const tree = parse(Lang.TypeScript, source).root();
158
+ const edits = [];
159
+ const siteEntries = [];
160
+ for (const call of tree.findAll({ rule: { pattern: "defineConfig($CONFIG)" } })) {
161
+ if (insideFunction(call)) continue;
162
+ const config = call.getMatch("CONFIG");
163
+ if (!config || config.kind() !== "object") continue;
164
+ const dbObject = config.children().find((child) => child.kind() === "pair" && propertyName(child) === "db")?.field("value");
165
+ if (!dbObject || dbObject.kind() !== "object") continue;
166
+ for (const nsPair of dbObject.children().filter((child) => child.kind() === "pair")) {
167
+ const nsKey = nsPair.field("key");
168
+ const nsObject = nsPair.field("value");
169
+ if (!nsKey || nsKey.kind() === "computed_property_name") continue;
170
+ if (!nsObject || nsObject.kind() !== "object") continue;
171
+ const erdPair = nsObject.children().find((child) => child.kind() === "pair" && propertyName(child) === "erdSite");
172
+ const valueNode = erdPair?.field("value");
173
+ if (!erdPair || !valueNode) continue;
174
+ siteEntries.push(`${nsKey.text()}: ${valueNode.text()}`);
175
+ edits.push(removePairEdit(nsObject, erdPair));
176
+ }
177
+ }
178
+ if (siteEntries.length === 0) return null;
179
+ const pluginExpr = `tailordbErdPlugin({ sites: { ${siteEntries.join(", ")} } })`;
180
+ const localDefinePlugins = definePluginsLocalName(tree);
181
+ const pluginsCall = tree.find({ rule: { pattern: `${localDefinePlugins ?? "definePlugins"}($$$ARGS)` } });
182
+ if (pluginsCall) edits.push(appendArgEdit(pluginsCall, pluginExpr));
183
+ let result = tree.commitEdits(edits);
184
+ const importLines = [PLUGIN_IMPORT];
185
+ if (!pluginsCall && !localDefinePlugins) {
186
+ const merged = addDefinePluginsSpecifier(result);
187
+ if (merged !== null) result = merged;
188
+ else importLines.push(DEFINE_PLUGINS_IMPORT);
189
+ }
190
+ result = insertImports(result, importLines);
191
+ if (!pluginsCall) result = result.replace(/\s*$/, "\n\n") + `export const plugins = ${localDefinePlugins ?? "definePlugins"}(\n ${pluginExpr},\n);\n`;
192
+ return result;
193
+ }
194
+ //#endregion
195
+ export { transform as default };
@@ -0,0 +1,122 @@
1
+ import { c as localDeclarationNames, i as importBindings, r as findImportStatements } from "../../../ast-grep-helpers-CXtWn3RB.js";
2
+ import { Lang, parse } from "@ast-grep/napi";
3
+ //#region codemods/v2/workflow-trigger-rename/scripts/transform.ts
4
+ const RENAMES = {
5
+ triggerWorkflow: "startWorkflow",
6
+ triggerJobFunction: "startJobFunction",
7
+ resumeWorkflow: "resumeWorkflowExecution"
8
+ };
9
+ const WORKFLOW_MODULE_SOURCES = /* @__PURE__ */ new Set(["@tailor-platform/sdk/runtime", "@tailor-platform/sdk/runtime/workflow"]);
10
+ function quickFilter(source) {
11
+ return Object.keys(RENAMES).some((name) => source.includes(name));
12
+ }
13
+ function sourceLang(filePath, source) {
14
+ const lower = filePath.toLowerCase();
15
+ if (lower.endsWith(".tsx") || lower.endsWith(".jsx")) return Lang.Tsx;
16
+ return source.includes("</") || source.includes("/>") ? Lang.Tsx : Lang.TypeScript;
17
+ }
18
+ function collectWorkflowLocals(root, imports) {
19
+ const locals = /* @__PURE__ */ new Set();
20
+ for (const importStmt of imports) for (const binding of importBindings(importStmt)) if (binding.importedName === "workflow" && WORKFLOW_MODULE_SOURCES.has(binding.source)) locals.add(binding.localName);
21
+ const declaredNames = localDeclarationNames(root);
22
+ for (const name of locals) if (declaredNames.has(name)) locals.delete(name);
23
+ return locals;
24
+ }
25
+ function tailorIsAmbientGlobal(root, imports) {
26
+ if (localDeclarationNames(root).has("tailor")) return false;
27
+ return !imports.some((stmt) => importBindings(stmt).some((b) => b.localName === "tailor"));
28
+ }
29
+ function isAmbientTailorWorkflow(object) {
30
+ if (!object || object.kind() !== "member_expression") return false;
31
+ const base = object.field("object");
32
+ const property = object.field("property");
33
+ return base?.kind() === "identifier" && base.text() === "tailor" && property?.text() === "workflow";
34
+ }
35
+ function expressionArguments(args) {
36
+ return args.children().filter((child) => ![
37
+ "(",
38
+ ")",
39
+ ","
40
+ ].includes(child.kind()));
41
+ }
42
+ function thirdArgument(member) {
43
+ const call = member.parent();
44
+ if (call?.kind() !== "call_expression") return null;
45
+ const args = call.field("arguments");
46
+ if (!args) return null;
47
+ return expressionArguments(args)[2] ?? null;
48
+ }
49
+ /**
50
+ * A `triggerWorkflow(name, args, options)` call whose third argument is not a
51
+ * literal object (e.g. a variable), or is an object literal that spreads one
52
+ * (`{ ...rest }`), cannot be safely renamed: we cannot tell whether it carries
53
+ * an `invoker` key that also needs renaming to `authInvoker`, and renaming
54
+ * just the method name would erase the `triggerWorkflow` token that flags the
55
+ * call for manual review, while the option silently stops working at
56
+ * runtime. Such calls are left entirely unrenamed.
57
+ * @param member - The `.triggerWorkflow` member-expression node being considered
58
+ * @returns Whether the call must be skipped
59
+ */
60
+ function hasUnsafeThirdArgument(member) {
61
+ const optionsArg = thirdArgument(member);
62
+ if (optionsArg === null) return false;
63
+ if (optionsArg.kind() !== "object") return true;
64
+ return optionsArg.children().some((child) => child.kind() === "spread_element");
65
+ }
66
+ /**
67
+ * `triggerWorkflow`'s removed SDK wrapper converted an `invoker` option to
68
+ * the platform's `authInvoker` shape before calling through; `startWorkflow`
69
+ * expects `authInvoker` directly. Build an edit renaming a literal `invoker`
70
+ * key (or shorthand) in the third argument of a `triggerWorkflow(...)` call
71
+ * being renamed to `startWorkflow`, so the option keeps working.
72
+ * @param member - The `.triggerWorkflow` member-expression node being renamed
73
+ * @returns An edit renaming the `invoker` key, or null when not applicable
74
+ */
75
+ function findInvokerOptionEdit(member) {
76
+ const optionsArg = thirdArgument(member);
77
+ if (!optionsArg || optionsArg.kind() !== "object") return null;
78
+ for (const child of optionsArg.children()) if (child.kind() === "pair") {
79
+ const key = child.field("key");
80
+ if (key?.kind() === "property_identifier" && key.text() === "invoker") return key.replace("authInvoker");
81
+ } else if (child.kind() === "shorthand_property_identifier" && child.text() === "invoker") return child.replace("authInvoker: invoker");
82
+ return null;
83
+ }
84
+ /**
85
+ * Rewrite `.triggerWorkflow(`, `.triggerJobFunction(`, and `.resumeWorkflow(`
86
+ * member accesses to their canonical `start*`/`resumeWorkflowExecution` names,
87
+ * either on the ambient `tailor.workflow` global or on a `workflow` value
88
+ * imported from `@tailor-platform/sdk/runtime(/workflow)`.
89
+ * @param source - File contents
90
+ * @param filePath - Absolute path to the file
91
+ * @returns Transformed source or null when nothing matched.
92
+ */
93
+ function transform(source, filePath) {
94
+ if (!quickFilter(source)) return null;
95
+ const root = parse(sourceLang(filePath ?? "", source), source).root();
96
+ const imports = findImportStatements(root);
97
+ const workflowLocals = collectWorkflowLocals(root, imports);
98
+ const tailorIsAmbient = tailorIsAmbientGlobal(root, imports);
99
+ const edits = [];
100
+ for (const member of root.findAll({ rule: { kind: "member_expression" } })) {
101
+ const property = member.field("property");
102
+ if (!property || property.kind() !== "property_identifier") continue;
103
+ const newName = RENAMES[property.text()];
104
+ if (!newName) continue;
105
+ const object = member.field("object");
106
+ if (!object) continue;
107
+ const isWorkflowImportReceiver = object.kind() === "identifier" && workflowLocals.has(object.text());
108
+ const isAmbientReceiver = tailorIsAmbient && isAmbientTailorWorkflow(object);
109
+ if (!isWorkflowImportReceiver && !isAmbientReceiver) continue;
110
+ if (newName === "startWorkflow") {
111
+ if (hasUnsafeThirdArgument(member)) continue;
112
+ edits.push(property.replace(newName));
113
+ const invokerEdit = findInvokerOptionEdit(member);
114
+ if (invokerEdit) edits.push(invokerEdit);
115
+ } else edits.push(property.replace(newName));
116
+ }
117
+ if (edits.length === 0) return null;
118
+ const result = root.commitEdits(edits);
119
+ return result === source ? null : result;
120
+ }
121
+ //#endregion
122
+ export { transform as default };
package/dist/index.js CHANGED
@@ -116,8 +116,11 @@ const RENAME_BIN_QUOTED_LEGACY_COMMAND_PATTERN = new RegExp([
116
116
  ].join(""));
117
117
  const V2_NEXT_1 = "2.0.0-next.1";
118
118
  const V2_NEXT_2 = "2.0.0-next.2";
119
- const V2_NEXT_3 = "2.0.0-next.3";
120
119
  const V2_NEXT_4 = "2.0.0-next.4";
120
+ const V2_NEXT_5 = "2.0.0-next.5";
121
+ const V2_NEXT_6 = "2.0.0-next.6";
122
+ const V2_NEXT_7 = "2.0.0-next.7";
123
+ const V2_NEXT_9 = "2.0.0-next.9";
121
124
  /** All registered codemods, in registration order. */
122
125
  const allCodemods = [
123
126
  {
@@ -417,7 +420,7 @@ const allCodemods = [
417
420
  {
418
421
  id: "v2/auth-invoker-unwrap",
419
422
  name: "auth.invoker(\"name\") → invoker: \"name\"",
420
- description: "Rename statically identified SDK `authInvoker` options to `invoker`, replace `auth.invoker(\"name\")` there with the bare `\"name\"` string, and drop the `auth` import when no other reference remains. Ambiguous workflow `.trigger()` calls are left for manual review. The `auth.invoker()` helper is removed in v2 because importing `auth` from `tailor.config.ts` into runtime files pulls Node-only modules into the bundle.",
423
+ description: "Rename statically identified SDK `authInvoker` options to `invoker`, replace `auth.invoker(\"name\")` there with the bare `\"name\"` string, and drop the `auth` import when no other reference remains. Ambiguous workflow `.start()` calls are left for manual review. The `auth.invoker()` helper is removed in v2 because importing `auth` from `tailor.config.ts` into runtime files pulls Node-only modules into the bundle.",
421
424
  since: "1.0.0",
422
425
  until: "2.0.0",
423
426
  prereleaseUntil: V2_NEXT_2,
@@ -450,8 +453,8 @@ const allCodemods = [
450
453
  " machine user name string; platform/runtime authInvoker payloads still expect",
451
454
  " the object form.",
452
455
  "2. Rename remaining authInvoker option keys to invoker only for SDK resolver,",
453
- " executor, workflow.trigger(), or startWorkflow() options. Keep platform/runtime",
454
- " payload keys such as tailor.workflow.triggerWorkflow(..., { authInvoker: ... }).",
456
+ " executor, workflow.start(), or startWorkflow() options. Keep platform/runtime",
457
+ " payload keys such as tailor.workflow.startWorkflow(..., { authInvoker: ... }).",
455
458
  "3. After removing every auth.invoker usage in a file, delete the now-unused auth",
456
459
  " import (keeping it pulls Node-only config modules into runtime bundles); leave",
457
460
  " the import if auth is still referenced elsewhere.",
@@ -502,7 +505,7 @@ const allCodemods = [
502
505
  description: "Rewrite `@tailor-platform/sdk/runtime/*` namespace-star and flat value imports to self-named namespace imports, and aggregate `file.deleteFile` calls to `file.delete`. `TailorContextAPI` and `TailorWorkflowAPI` now describe SDK wrappers; direct platform globals use `PlatformContextAPI` and `PlatformWorkflowAPI`.",
503
506
  since: "1.0.0",
504
507
  until: "2.0.0",
505
- prereleaseUntil: V2_NEXT_3,
508
+ prereleaseUntil: V2_NEXT_4,
506
509
  scriptPath: "v2/runtime-subpath-namespace/scripts/transform.js",
507
510
  filePatterns: ["**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}"],
508
511
  legacyPatterns: [
@@ -574,7 +577,7 @@ const allCodemods = [
574
577
  description: "Rename TailorDB schema builder calls from `db.type()` to `db.table()`. TailorDB schema definitions now use table terminology in SDK projects.",
575
578
  since: "1.0.0",
576
579
  until: "2.0.0",
577
- prereleaseUntil: V2_NEXT_3,
580
+ prereleaseUntil: V2_NEXT_4,
578
581
  scriptPath: "v2/db-type-to-table/scripts/transform.js",
579
582
  filePatterns: ["**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}"],
580
583
  legacyPatterns: ["db.type"],
@@ -600,7 +603,7 @@ const allCodemods = [
600
603
  description: "Review TailorDB relations that omit `toward.as`. Their forward GraphQL field names now derive from the relation field name with a trailing `ID`, `Id`, or `id` removed, instead of from the target table name.",
601
604
  since: "1.0.0",
602
605
  until: "2.0.0",
603
- prereleaseUntil: V2_NEXT_4,
606
+ prereleaseUntil: V2_NEXT_5,
604
607
  scriptPath: "v2/forward-relation-name/scripts/transform.js",
605
608
  filePatterns: ["**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}"],
606
609
  suspiciousPatterns: [
@@ -686,6 +689,54 @@ const allCodemods = [
686
689
  after: "import { createWaitPoints } from \"@tailor-platform/sdk\";\n\nexport const { approval } = createWaitPoints((define) => ({\n approval: define<{ message: string }, { approved: boolean }>(),\n}));"
687
690
  }]
688
691
  },
692
+ {
693
+ id: "v2/workflow-trigger-rename",
694
+ name: "workflow.triggerWorkflow/triggerJobFunction/resumeWorkflow → startWorkflow/startJobFunction/resumeWorkflowExecution",
695
+ description: "Rename tailor.workflow call sites from the pre-alignment triggerWorkflow/triggerJobFunction/resumeWorkflow names to the canonical startWorkflow/startJobFunction/resumeWorkflowExecution names, on both the ambient tailor.workflow global and a workflow value imported from @tailor-platform/sdk/runtime(/workflow). For a renamed triggerWorkflow call, also renames a literal `invoker` option key to `authInvoker` — startWorkflow's options expect the platform shape directly, unlike the removed triggerWorkflow wrapper, which converted invoker to authInvoker internally.",
696
+ since: "1.0.0",
697
+ until: "2.0.0",
698
+ prereleaseUntil: V2_NEXT_6,
699
+ scriptPath: "v2/workflow-trigger-rename/scripts/transform.js",
700
+ filePatterns: ["**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}"],
701
+ legacyPatterns: [
702
+ "triggerWorkflow",
703
+ "triggerJobFunction",
704
+ "resumeWorkflow"
705
+ ],
706
+ examples: [{
707
+ before: "import { workflow } from \"@tailor-platform/sdk/runtime\";\n\nawait workflow.triggerWorkflow(\"myWorkflow\", { data: \"value\" });",
708
+ after: "import { workflow } from \"@tailor-platform/sdk/runtime\";\n\nawait workflow.startWorkflow(\"myWorkflow\", { data: \"value\" });"
709
+ }, {
710
+ caption: "A literal invoker option is renamed to authInvoker:",
711
+ before: "await workflow.triggerWorkflow(\"myWorkflow\", { data: \"value\" }, { invoker: myInvoker });",
712
+ after: "await workflow.startWorkflow(\"myWorkflow\", { data: \"value\" }, { authInvoker: myInvoker });"
713
+ }],
714
+ prompt: [
715
+ "The pre-alignment tailor.workflow names triggerWorkflow, triggerJobFunction, and",
716
+ "resumeWorkflow are removed from the SDK's type surface in v2; use the canonical",
717
+ "startWorkflow, startJobFunction, and resumeWorkflowExecution names instead. The",
718
+ "codemod rewrites direct member-access call sites on the ambient tailor.workflow",
719
+ "global and on a workflow value imported from @tailor-platform/sdk/runtime or",
720
+ "@tailor-platform/sdk/runtime/workflow (including aliased imports). It skips a",
721
+ "file entirely when a local declaration shadows the workflow import or the",
722
+ "ambient tailor name, to avoid rewriting an unrelated same-named value — review",
723
+ "those manually.",
724
+ "",
725
+ "For a renamed triggerWorkflow call, the codemod also renames a literal invoker",
726
+ "option key (including shorthand { invoker }) to authInvoker, since startWorkflow",
727
+ "expects the platform's authInvoker shape directly while triggerWorkflow's removed",
728
+ "wrapper converted invoker to authInvoker internally.",
729
+ "",
730
+ "Also review, and migrate by hand:",
731
+ "- Destructured references (e.g. const { triggerWorkflow } = workflow) — the",
732
+ " codemod only rewrites direct member-access calls.",
733
+ "- Imported TriggerWorkflowOptions / TriggerJobFunctionOptions types — rename",
734
+ " them to StartWorkflowOptions / StartJobFunctionOptions.",
735
+ "- An invoker option passed via a variable or spread (not a literal object) —",
736
+ " the codemod only inspects literal object arguments; rename the invoker key",
737
+ " to authInvoker in the options object's own definition."
738
+ ].join("\n")
739
+ },
689
740
  {
690
741
  id: "v2/open-download-stream",
691
742
  name: "openDownloadStream → downloadStream",
@@ -781,23 +832,57 @@ const allCodemods = [
781
832
  },
782
833
  {
783
834
  id: "v2/workflow-trigger-dispatch",
784
- name: "Workflow .trigger() and trigger tests",
785
- description: "Workflow job `.trigger()` now aligns with the platform runtime: it returns the job result directly instead of a Promise wrapper, and tests no longer run job bodies locally. Mock trigger responses with `mockWorkflow()` (`setJobHandler` / `enqueueResult`, assert via `triggeredJobs`), or use `runWorkflowLocally()` for a full-chain local run.",
835
+ name: "Workflow job start() and start tests",
836
+ description: "Workflow job `.start()` (previously `.trigger()`) now aligns with the platform runtime: it returns the job result directly instead of a Promise wrapper, and tests no longer run job bodies locally. Mock start responses with `mockWorkflow()` (`setJobHandler` / `enqueueResult`, assert via `startedJobs`), or use `runWorkflowLocally()` for a full-chain local run.",
786
837
  since: "1.0.0",
787
838
  until: "2.0.0",
788
839
  prereleaseUntil: V2_NEXT_1,
789
840
  suspiciousPatterns: [".trigger("],
790
841
  examples: [{
791
842
  caption: "Tests must mock the workflow runtime instead of running bodies locally:",
792
- before: "const result = await orderJob.trigger({ id });\nexpect(result.status).toBe(\"done\");",
793
- after: "using wf = mockWorkflow();\nwf.setJobHandler((jobName) => (jobName === \"order-job\" ? { status: \"done\" } : null));\nconst result = await orderJob.trigger({ id });\nexpect(result.status).toBe(\"done\");"
843
+ before: "const result = await orderJob.start({ id });\nexpect(result.status).toBe(\"done\");",
844
+ after: "using wf = mockWorkflow();\nwf.setJobHandler((jobName) => (jobName === \"order-job\" ? { status: \"done\" } : null));\nconst result = await orderJob.start({ id });\nexpect(result.status).toBe(\"done\");"
794
845
  }],
795
846
  prompt: [
796
- "Workflow job .trigger() now uses the platform workflow runtime instead of running",
847
+ "Workflow job .start() now uses the platform workflow runtime instead of running",
797
848
  "the job body locally. In tests, acquire `using wf = mockWorkflow()` and provide",
798
- "trigger responses (setJobHandler / enqueueResult), or use runWorkflowLocally() for a",
799
- "full-chain local run; an unmocked trigger now throws. Outside tests, treat the",
800
- "trigger result as the job output directly (no Promise wrapper to unwrap)."
849
+ "start responses (setJobHandler / enqueueResult), or use runWorkflowLocally() for a",
850
+ "full-chain local run; an unmocked start now throws. Outside tests, treat the",
851
+ "start result as the job output directly (no Promise wrapper to unwrap)."
852
+ ].join("\n")
853
+ },
854
+ {
855
+ id: "v2/workflow-start-rename",
856
+ name: "Workflow.trigger()/WorkflowJob.trigger() → .start()",
857
+ description: "Rename `Workflow.trigger()` (returned by `createWorkflow()`) and `WorkflowJob.trigger()` (returned by `createWorkflowJob()`) to `.start()`, aligning the SDK's ergonomic verb with the platform's `start*` RPC vocabulary. No codemod ships for this rename: distinguishing a workflow/job `.trigger()` call from an unrelated object's own `.trigger()` method requires resolving the receiver back to a `createWorkflow`/`createWorkflowJob` result across files, which the SDK's own CLI bundler already does for build-time rewriting. Reusing that logic in a standalone script is a nontrivial lift, and — unlike the bundler, which fails loudly when it cannot rewrite a call — a codemod false positive would silently rewrite an unrelated `.trigger()` call with no error. For the call-site volume this rename typically involves, manual review guided by the prompt below is the safer trade-off.",
858
+ since: "1.0.0",
859
+ until: "2.0.0",
860
+ prereleaseUntil: V2_NEXT_7,
861
+ filePatterns: ["**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}"],
862
+ suspiciousPatterns: [".trigger("],
863
+ examples: [{
864
+ before: ["const inventory = checkInventory.trigger({ orderId: input.orderId });", "const workflowRunId = await orderProcessingWorkflow.trigger(args, { invoker: \"manager\" });"].join("\n"),
865
+ after: ["const inventory = checkInventory.start({ orderId: input.orderId });", "const workflowRunId = await orderProcessingWorkflow.start(args, { invoker: \"manager\" });"].join("\n")
866
+ }],
867
+ prompt: [
868
+ "In Tailor SDK v2, the ergonomic .trigger() method on a createWorkflow() or",
869
+ "createWorkflowJob() result is renamed to .start(). This is unrelated to the",
870
+ "separate tailor.workflow.triggerWorkflow/triggerJobFunction/resumeWorkflow removal",
871
+ "(see the workflow-trigger-rename codemod) — this rename targets the SDK's own",
872
+ "ergonomic wrapper, not the low-level platform call.",
873
+ "",
874
+ "For each flagged `.trigger(` call in these files:",
875
+ "1. Confirm the receiver is a workflow or job object — typically a local const",
876
+ " assigned from createWorkflow(...)/createWorkflowJob(...), a named import of one,",
877
+ " or the default import of a workflow module. Skip receivers that are unrelated",
878
+ " objects with their own .trigger() method (state machines, event emitters, etc.).",
879
+ "2. Rename the call from .trigger(...) to .start(...); the argument list is unchanged.",
880
+ "3. Update any mock/test code that reads WorkflowJob['trigger'] / Workflow['trigger']",
881
+ " as a type, or that mocks the ergonomic method via a wrapper — for example,",
882
+ " `wf.job(definition)` / `wf.workflow(definition)` from mockWorkflow() now return a",
883
+ " mock of the `.start` method.",
884
+ "4. Update prose/docs/comments that say \"trigger the workflow/job\" to \"start\" only",
885
+ " where they describe this SDK verb specifically, not unrelated event terminology."
801
886
  ].join("\n")
802
887
  },
803
888
  {
@@ -898,7 +983,7 @@ const allCodemods = [
898
983
  description: "Field-level `ValidateFn` is simplified from `(args: { value, data, invoker }) => boolean` to `(args: { value }) => string | void` — the function now returns the error message directly instead of a separate `[fn, message]` tuple. The `ValidateConfig` tuple form and `Validators<F>` record syntax on `db.type().validate()` are removed. Type-level validation uses `db.type().validate((args, issues) => void)` with `{ newRecord, oldRecord, invoker }` args and an `issues(field, message)` callback for cross-field rules.",
899
984
  since: "1.0.0",
900
985
  until: "2.0.0",
901
- prereleaseUntil: V2_NEXT_4,
986
+ prereleaseUntil: V2_NEXT_5,
902
987
  suspiciousPatterns: [
903
988
  "ValidateConfig",
904
989
  "Validators<",
@@ -944,7 +1029,7 @@ const allCodemods = [
944
1029
  description: "Field-level `HookFn` args change from `{ value, data, invoker }` to create `{ input, invoker, now }` / update `{ input, oldValue, invoker, now }` — `value` is renamed to `input`, matching the `input` arg on type-level hooks (same pre-hook data, narrowed to one field); `data` (the full record) is removed; `oldValue` (previous field value) is added for update hooks only; `now` (operation timestamp) is shared across all hooks. Type-level hooks on `db.type().hooks()` change from per-field mapping `{ fieldName: { create, update } }` (`Hooks<F>`) to a single `{ create, update }` object (`TypeHook<F>`) — create hooks take `{ input, invoker, now }`, update hooks take `{ input, oldRecord, invoker, now }` (oldRecord is always non-null). Both return partial field overrides.",
945
1030
  since: "1.0.0",
946
1031
  until: "2.0.0",
947
- prereleaseUntil: V2_NEXT_4,
1032
+ prereleaseUntil: V2_NEXT_5,
948
1033
  suspiciousPatterns: [
949
1034
  "Hooks<",
950
1035
  "HookFn<",
@@ -988,13 +1073,80 @@ const allCodemods = [
988
1073
  "3. Remove unused `Hooks<F>` / `HookFn<>` type imports"
989
1074
  ].join("\n")
990
1075
  },
1076
+ {
1077
+ id: "v2/erd-site-to-plugin",
1078
+ name: "`db.<namespace>.erdSite` → `tailordbErdPlugin({ sites })`",
1079
+ description: "Move the TailorDB `erdSite` setting from `db.<namespace>` in tailor.config.ts into `tailordbErdPlugin({ sites })` from `@tailor-platform/sdk-plugin-tailordb-erd`, registered via definePlugins(). The core config schema no longer accepts `erdSite`; the `tailor tailordb erd` commands read the target static website from the plugin configuration and validate each site name against `staticWebsites`. Install `@tailor-platform/sdk-plugin-tailordb-erd` as a dev dependency: the migrated config imports it, so config loading fails with a module-not-found error until it is installed.",
1080
+ since: "1.0.0",
1081
+ until: "2.0.0",
1082
+ prereleaseUntil: V2_NEXT_9,
1083
+ scriptPath: "v2/erd-site-to-plugin/scripts/transform.js",
1084
+ legacyPatterns: ["erdSite:"],
1085
+ sourceStringLegacyPatterns: ["erdSite"],
1086
+ suspiciousPatterns: [
1087
+ "erdSite:",
1088
+ /\berdSite\s*[,}]/,
1089
+ "tailordbErdPlugin"
1090
+ ],
1091
+ sourceStringSuspiciousPatterns: ["erdSite"],
1092
+ examples: [{
1093
+ before: [
1094
+ "export default defineConfig({",
1095
+ " db: {",
1096
+ " tailordb: {",
1097
+ " files: [\"./tailordb/*.ts\"],",
1098
+ " erdSite: \"my-erd-site\",",
1099
+ " },",
1100
+ " },",
1101
+ "});"
1102
+ ].join("\n"),
1103
+ after: [
1104
+ "import { tailordbErdPlugin } from \"@tailor-platform/sdk-plugin-tailordb-erd\";",
1105
+ "",
1106
+ "export default defineConfig({",
1107
+ " db: {",
1108
+ " tailordb: {",
1109
+ " files: [\"./tailordb/*.ts\"],",
1110
+ " },",
1111
+ " },",
1112
+ "});",
1113
+ "",
1114
+ "export const plugins = definePlugins(",
1115
+ " tailordbErdPlugin({ sites: { tailordb: \"my-erd-site\" } }),",
1116
+ ");"
1117
+ ].join("\n")
1118
+ }],
1119
+ prompt: [
1120
+ "In Tailor SDK v2 the TailorDB `erdSite` setting is removed from the core config",
1121
+ "schema; the ERD deploy target is configured on the ERD CLI plugin instead. The",
1122
+ "codemod rewrites literal `db.<namespace>.erdSite` entries inside top-level",
1123
+ "defineConfig() calls into a `tailordbErdPlugin({ sites: { <namespace>: <value> } })`",
1124
+ "argument of definePlugins(), importing it from @tailor-platform/sdk-plugin-tailordb-erd.",
1125
+ "",
1126
+ "First, for every config that now registers tailordbErdPlugin, make sure",
1127
+ "@tailor-platform/sdk-plugin-tailordb-erd is installed as a dev dependency — the",
1128
+ "migrated config imports it, so config loading fails with ERR_MODULE_NOT_FOUND",
1129
+ "until it is installed.",
1130
+ "",
1131
+ "For any remaining `erdSite` config keys the codemod did not rewrite — a db config",
1132
+ "built dynamically or passed via a variable, quoted or computed keys, spread",
1133
+ "properties, a defineConfig() call inside a factory function, or a file that",
1134
+ "already registers tailordbErdPlugin — move the namespace → static-website-name",
1135
+ "mapping into tailordbErdPlugin({ sites }) and delete the `erdSite` key. For",
1136
+ "factory-built configs, keep any referenced parameters or locals in scope when",
1137
+ "moving the value to the module-level definePlugins() export. Each site name",
1138
+ "must match a static website defined in staticWebsites. Leave unrelated",
1139
+ "identifiers that merely contain the name (e.g. a defineStaticWebSite variable",
1140
+ "named erdSite) unchanged."
1141
+ ].join("\n")
1142
+ },
991
1143
  {
992
1144
  id: "v2/generate-watch-flag",
993
1145
  name: "generate --watch flag removed",
994
1146
  description: "Review and remove `tailor generate --watch` / `-W` invocations and the `watch` option on `GenerateOptions`. The flag, its dependency watcher, and the self-restart-on-change logic are removed; `generate` now always performs a single generation pass.",
995
1147
  since: "1.0.0",
996
1148
  until: "2.0.0",
997
- prereleaseUntil: "2.0.0-next.6",
1149
+ prereleaseUntil: V2_NEXT_6,
998
1150
  filePatterns: [
999
1151
  "**/package.json",
1000
1152
  "**/*.{sh,bash,zsh,yml,yaml}",
@@ -1024,6 +1176,36 @@ const allCodemods = [
1024
1176
  "single generation pass and resolves once it completes."
1025
1177
  ].join("\n")
1026
1178
  },
1179
+ {
1180
+ id: "v2/seed-exec-to-cli-plugin",
1181
+ name: "Generated seed exec.mjs → tailor seed CLI plugin",
1182
+ description: "`seedPlugin` no longer generates the `exec.mjs` seed runner. Seeding and validation move to the `tailor seed` commands provided by the `@tailor-platform/sdk-plugin-seed` CLI plugin: install it as a devDependency, replace `node <distPath>/exec.mjs` invocations with `tailor seed apply` and `node <distPath>/exec.mjs validate` with `tailor seed validate`, and delete the stale generated `<distPath>/exec.mjs` file. Seed data and schema generation (`data/*.jsonl`, `data/*.schema.ts`) is unchanged, and the `tailor seed apply` options mirror the old script (`--machine-user`, `--namespace`, `--skip-idp`, `--truncate`, `--yes`, type-name arguments).",
1183
+ since: "1.0.0",
1184
+ until: "2.0.0",
1185
+ prereleaseUntil: V2_NEXT_9,
1186
+ filePatterns: ["**/package.json", "**/*.{sh,yml,yaml,md,mjs,ts}"],
1187
+ suspiciousPatterns: ["exec.mjs"],
1188
+ sourceStringSuspiciousPatterns: ["exec.mjs"],
1189
+ examples: [{
1190
+ before: "\"seed\": \"node ./seed/exec.mjs\",\n\"seed:validate\": \"node ./seed/exec.mjs validate\"",
1191
+ after: "\"seed\": \"tailor seed apply\",\n\"seed:validate\": \"tailor seed validate\"",
1192
+ lang: "jsonc"
1193
+ }],
1194
+ prompt: [
1195
+ "seedPlugin no longer generates the exec.mjs seed runner in v2. The tailor seed",
1196
+ "CLI plugin (@tailor-platform/sdk-plugin-seed) replaces it:",
1197
+ "",
1198
+ "- Install @tailor-platform/sdk-plugin-seed as a devDependency next to",
1199
+ " @tailor-platform/sdk.",
1200
+ "- Replace `node <distPath>/exec.mjs [options] [types...]` invocations with",
1201
+ " `tailor seed apply [options] [types...]` (same options: --machine-user/-m,",
1202
+ " --namespace/-n, --skip-idp, --truncate, --yes, and type-name arguments).",
1203
+ "- Replace `node <distPath>/exec.mjs validate [path]` with",
1204
+ " `tailor seed validate [path]`.",
1205
+ "- Delete the stale generated `<distPath>/exec.mjs` file; keep the data/",
1206
+ " directory (JSONL data and generated schemas) as-is."
1207
+ ].join("\n")
1208
+ },
1027
1209
  {
1028
1210
  id: "v2/node-minimum-22-15-0",
1029
1211
  name: "Node.js minimum version raised to 22.15.0",
@@ -1031,6 +1213,14 @@ const allCodemods = [
1031
1213
  since: "1.0.0",
1032
1214
  until: "2.0.0",
1033
1215
  notice: true
1216
+ },
1217
+ {
1218
+ id: "v2/remove-legacy-bundle-cleanup",
1219
+ name: "Legacy bundle artifact cleanup removed from deploy",
1220
+ description: "`tailor deploy` no longer deletes on-disk bundle artifacts (`.entry.js` files, workflow-job bundles, and the `hooks-validate-scripts/` directory) left in the SDK output directory (`.tailor` by default) by SDK versions that predate the current in-memory bundling approach. Current bundlers no longer write these files. No source change is required; if such stale files remain from a very old SDK version, delete only those specific files/directories manually — do not delete the output directory itself, since it also holds deploy state (e.g. `secrets-state/`, `*.context.json`) that existing secrets and Auth Connections depend on.",
1221
+ since: "1.0.0",
1222
+ until: "2.0.0",
1223
+ notice: true
1034
1224
  }
1035
1225
  ];
1036
1226
  /**
@@ -1043,12 +1233,13 @@ function resolveCodemodScript(scriptPath) {
1043
1233
  }
1044
1234
  function reachesCodemodBoundary(toVersion, codemod) {
1045
1235
  if (gte(toVersion, codemod.until)) return true;
1046
- if (codemod.prereleaseUntil === void 0 || !gte(toVersion, codemod.prereleaseUntil)) return false;
1236
+ if (codemod.prereleaseUntil === void 0 || codemod.prereleaseUntil === "pending" || !gte(toVersion, codemod.prereleaseUntil)) return false;
1047
1237
  const target = parse(toVersion);
1048
1238
  const boundary = parse(codemod.until);
1049
1239
  return target.prerelease.length > 0 && target.major === boundary.major && target.minor === boundary.minor && target.patch === boundary.patch;
1050
1240
  }
1051
1241
  function effectiveCodemodBoundary(codemod) {
1242
+ if (codemod.prereleaseUntil === "pending") return codemod.until;
1052
1243
  return codemod.prereleaseUntil ?? codemod.until;
1053
1244
  }
1054
1245
  function assertCodemodBoundaries(codemods) {
@@ -1056,7 +1247,7 @@ function assertCodemodBoundaries(codemods) {
1056
1247
  const boundary = parse(codemod.until);
1057
1248
  if (boundary === null) throw new Error(`Codemod ${codemod.id} until must be a valid semver version: ${codemod.until}`);
1058
1249
  if (boundary.prerelease.length > 0) throw new Error(`Codemod ${codemod.id} until must be a stable version: ${codemod.until}`);
1059
- if (codemod.prereleaseUntil === void 0) continue;
1250
+ if (codemod.prereleaseUntil === void 0 || codemod.prereleaseUntil === "pending") continue;
1060
1251
  const prereleaseBoundary = parse(codemod.prereleaseUntil);
1061
1252
  if (prereleaseBoundary === null) throw new Error(`Codemod ${codemod.id} prereleaseUntil must be a valid semver version: ${codemod.prereleaseUntil}`);
1062
1253
  if (prereleaseBoundary.prerelease.length === 0) throw new Error(`Codemod ${codemod.id} prereleaseUntil must be a prerelease version: ${codemod.prereleaseUntil}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tailor-platform/sdk-codemod",
3
- "version": "0.3.0-next.6",
3
+ "version": "0.3.0-next.8",
4
4
  "description": "Codemod runner for Tailor Platform SDK upgrades",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -32,7 +32,7 @@
32
32
  "@types/semver": "7.7.1",
33
33
  "eslint-plugin-zod": "4.7.0",
34
34
  "oxlint": "1.73.0",
35
- "tsdown": "0.22.5",
35
+ "tsdown": "0.22.7",
36
36
  "typescript": "6.0.3",
37
37
  "vitest": "4.1.10"
38
38
  },