@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 `.
|
|
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.
|
|
454
|
-
" payload keys such as tailor.workflow.
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
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
|
|
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.
|
|
793
|
-
after: "using wf = mockWorkflow();\nwf.setJobHandler((jobName) => (jobName === \"order-job\" ? { status: \"done\" } : null));\nconst result = await orderJob.
|
|
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 .
|
|
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
|
-
"
|
|
799
|
-
"full-chain local run; an unmocked
|
|
800
|
-
"
|
|
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:
|
|
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:
|
|
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:
|
|
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.
|
|
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.
|
|
35
|
+
"tsdown": "0.22.7",
|
|
36
36
|
"typescript": "6.0.3",
|
|
37
37
|
"vitest": "4.1.10"
|
|
38
38
|
},
|