@tailor-platform/sdk-codemod 0.3.8 → 0.4.0-next.10
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 +309 -0
- package/dist/codemods/ast-grep-helpers-CXtWn3RB.js +171 -0
- package/dist/codemods/v2/apply-to-deploy/scripts/transform.js +22 -4
- package/dist/codemods/v2/auth-attributes-rename/scripts/transform.js +213 -0
- package/dist/codemods/v2/auth-connection-token-helper/scripts/transform.js +243 -0
- package/dist/codemods/v2/auth-invoker-call-unwrap/scripts/transform.js +7 -0
- package/dist/codemods/v2/auth-invoker-unwrap/scripts/transform.js +108 -13
- package/dist/codemods/v2/cli-rename/scripts/transform.js +373 -14
- package/dist/codemods/v2/db-type-to-table/scripts/transform.js +383 -0
- package/dist/codemods/v2/env-var-rename/scripts/transform.js +88 -0
- package/dist/codemods/v2/erd-site-to-plugin/scripts/transform.js +195 -0
- package/dist/codemods/v2/exec-job-function-rename/scripts/transform.js +95 -0
- package/dist/codemods/v2/execute-script-arg/scripts/transform.js +60 -0
- package/dist/codemods/v2/forward-relation-name/scripts/transform.js +115 -0
- package/dist/codemods/v2/idp-publish-events-rename/scripts/transform.js +186 -0
- package/dist/codemods/v2/principal-unify/scripts/transform.js +1555 -44
- package/dist/codemods/v2/rename-bin/scripts/transform.js +1087 -0
- package/dist/codemods/v2/runtime-globals-opt-in/scripts/transform.js +103 -0
- package/dist/codemods/v2/runtime-subpath-namespace/scripts/transform.js +792 -0
- package/dist/codemods/v2/sdk-skills-shim/scripts/transform.js +3 -3
- package/dist/codemods/v2/seed-exec-to-cli-plugin/scripts/transform.js +115 -0
- package/dist/codemods/v2/tailor-output-ignore-dir/scripts/transform.js +14 -0
- package/dist/codemods/v2/tailordb-namespace/scripts/transform.js +5 -4
- package/dist/codemods/v2/wait-point-rename/scripts/transform.js +126 -0
- package/dist/codemods/v2/workflow-trigger-rename/scripts/transform.js +123 -0
- package/dist/index.js +1962 -51
- package/package.json +6 -5
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as path from "pathe";
|
|
2
2
|
//#region codemods/v2/sdk-skills-shim/scripts/transform.ts
|
|
3
3
|
const SHIM_PATTERN = /\btailor-sdk-skills(?:@[^\s'"`]+)?(?:[ \t]+install)?\b(?!-)/g;
|
|
4
|
-
const REPLACEMENT = "tailor
|
|
4
|
+
const REPLACEMENT = "tailor skills add";
|
|
5
5
|
function replaceShim(value) {
|
|
6
6
|
return value.replace(SHIM_PATTERN, REPLACEMENT);
|
|
7
7
|
}
|
|
@@ -34,10 +34,10 @@ function transformPackageJson(source) {
|
|
|
34
34
|
return JSON.stringify(parsed, null, 2) + trailing;
|
|
35
35
|
}
|
|
36
36
|
/**
|
|
37
|
-
* Replace `tailor-sdk-skills` invocations with `tailor
|
|
37
|
+
* Replace `tailor-sdk-skills` invocations with `tailor skills add`.
|
|
38
38
|
*
|
|
39
39
|
* The standalone `tailor-sdk-skills` binary is removed in v2; users must call
|
|
40
|
-
* the subcommand on the main `tailor
|
|
40
|
+
* the subcommand on the main `tailor` CLI instead.
|
|
41
41
|
* @param source - File contents
|
|
42
42
|
* @param filePath - Absolute path to the file (used to dispatch package.json vs text)
|
|
43
43
|
* @returns Transformed source or null when nothing matched.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import * as path from "pathe";
|
|
2
|
+
//#region codemods/v2/seed-exec-to-cli-plugin/scripts/transform.ts
|
|
3
|
+
const NODE_BINARY = "(?<![\\w.-])node(?![\\w-])";
|
|
4
|
+
const ARG_VALUE = `(?:[^\\s'"\`;&|]+|'[^']*'|"(?:(?:\\\\.)|[^"\\\\])*")`;
|
|
5
|
+
const SPACE = String.raw`(?:[^\S\n\r]|\\\r?\n)+`;
|
|
6
|
+
const VALUE_NODE_FLAG = "(?:--env-file|--env-file-if-exists|--import|--require|-r)";
|
|
7
|
+
const BOOLEAN_NODE_FLAG = "(?:--[^\\s'\"`;&|]+|-[^\\s'\"`;&|=-][^\\s'\"`;&|=]*)";
|
|
8
|
+
const NODE_FLAGS = `(?:(?:${SPACE}${VALUE_NODE_FLAG}(?:=${ARG_VALUE}|${SPACE}${ARG_VALUE}))|(?:${SPACE}${BOOLEAN_NODE_FLAG}))*`;
|
|
9
|
+
const RUNNER_PATH = `(?:[\\w.@~-]+/)+exec\\.mjs`;
|
|
10
|
+
const RUNNER_PATTERN = new RegExp(`${NODE_BINARY}(${NODE_FLAGS})${SPACE}(['"]?)(?:\\./)?${RUNNER_PATH}\\2(${SPACE}validate(?![-\\w=.:/]))?`, "g");
|
|
11
|
+
const FORK_PATTERN = /(?<![\w.$])fork\s*\(/;
|
|
12
|
+
const FORK_RUNNER_PATTERN = new RegExp(`${FORK_PATTERN.source}\\s*(['"\`])${RUNNER_PATH}\\1`, "gs");
|
|
13
|
+
const SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
14
|
+
".ts",
|
|
15
|
+
".tsx",
|
|
16
|
+
".mts",
|
|
17
|
+
".cts",
|
|
18
|
+
".js",
|
|
19
|
+
".jsx",
|
|
20
|
+
".mjs",
|
|
21
|
+
".cjs"
|
|
22
|
+
]);
|
|
23
|
+
const NODE_FLAG_PATTERN = new RegExp(`(${VALUE_NODE_FLAG})(?:=(${ARG_VALUE})|${SPACE}(${ARG_VALUE}))|(${BOOLEAN_NODE_FLAG})`, "g");
|
|
24
|
+
const ENV_FILE_FLAG = /^--env-file(?:-if-exists)?$/;
|
|
25
|
+
const RUNNER_PREFIX_PATTERN = new RegExp(`(?<![\\w.-])(?:pnpm|npm|yarn|bunx|bun|npx)(?:${SPACE}(?:run|exec|dlx))?${SPACE}$`);
|
|
26
|
+
const RUNNER_PREFIX_WINDOW = 64;
|
|
27
|
+
/** Translate the leading `node` flags into flags the CLI command accepts. */
|
|
28
|
+
function carriedOverFlags(nodeFlags) {
|
|
29
|
+
return [...nodeFlags.matchAll(NODE_FLAG_PATTERN)].filter((match) => match[1] != null && ENV_FILE_FLAG.test(match[1])).map((match) => ` ${match[1]} ${match[2] ?? match[3]}`).join("");
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Rewrite runner invocations. The runner spells validation as its first
|
|
33
|
+
* positional argument, so a consumed `validate` selects the subcommand.
|
|
34
|
+
*/
|
|
35
|
+
function rewrite(value, prefix = "") {
|
|
36
|
+
return value.replace(RUNNER_PATTERN, (_match, nodeFlags, _quote, validate, offset) => {
|
|
37
|
+
const command = validate ? "validate" : "apply";
|
|
38
|
+
const before = value.slice(Math.max(0, offset - RUNNER_PREFIX_WINDOW), offset);
|
|
39
|
+
return `${RUNNER_PREFIX_PATTERN.test(before) ? "" : prefix}tailor seed ${command}${carriedOverFlags(nodeFlags)}`;
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
function transformText(source) {
|
|
43
|
+
const updated = rewrite(source);
|
|
44
|
+
return updated === source ? null : updated;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Source files reach the plugin through a package runner rather than the bare
|
|
48
|
+
* binary, since the plugin executable is resolved from the project's
|
|
49
|
+
* node_modules rather than the PATH.
|
|
50
|
+
*/
|
|
51
|
+
function transformSourceText(source) {
|
|
52
|
+
const updated = rewrite(source, "npx ");
|
|
53
|
+
if (FORK_PATTERN.test(source) && updated.includes("exec.mjs")) return null;
|
|
54
|
+
return updated === source ? null : updated;
|
|
55
|
+
}
|
|
56
|
+
function transformPackageJson(source) {
|
|
57
|
+
let parsed;
|
|
58
|
+
try {
|
|
59
|
+
parsed = JSON.parse(source);
|
|
60
|
+
} catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
const scripts = parsed.scripts;
|
|
64
|
+
if (typeof scripts !== "object" || scripts == null || Array.isArray(scripts)) return null;
|
|
65
|
+
let modified = false;
|
|
66
|
+
for (const [name, value] of Object.entries(scripts)) {
|
|
67
|
+
if (typeof value !== "string") continue;
|
|
68
|
+
const updated = rewrite(value);
|
|
69
|
+
if (updated !== value) {
|
|
70
|
+
scripts[name] = updated;
|
|
71
|
+
modified = true;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (!modified) return null;
|
|
75
|
+
const trailing = source.endsWith("\n") ? "\n" : "";
|
|
76
|
+
return JSON.stringify(parsed, null, 2) + trailing;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Rewrite generated seed runner invocations to the `tailor seed` CLI plugin.
|
|
80
|
+
* @param source - File contents
|
|
81
|
+
* @param filePath - Absolute path to the file (used to dispatch package.json vs source vs text)
|
|
82
|
+
* @returns Transformed source or null when nothing matched.
|
|
83
|
+
*/
|
|
84
|
+
function transform(source, filePath) {
|
|
85
|
+
if (!source.includes("exec.mjs")) return null;
|
|
86
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
87
|
+
if (ext === ".json") return transformPackageJson(source);
|
|
88
|
+
if (SOURCE_EXTENSIONS.has(ext)) return transformSourceText(source);
|
|
89
|
+
return transformText(source);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Report `fork()` call sites the transform declined, so the migration surfaces
|
|
93
|
+
* the async-plumbing rewrite instead of silently leaving a stale invocation.
|
|
94
|
+
* @param source - Post-transform file contents
|
|
95
|
+
* @param filePath - Absolute path to the file
|
|
96
|
+
* @param relativePath - Path relative to the transformed project root
|
|
97
|
+
* @returns Locations that need manual migration.
|
|
98
|
+
*/
|
|
99
|
+
function reviewFindings(source, filePath, relativePath) {
|
|
100
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
101
|
+
if (!SOURCE_EXTENSIONS.has(ext)) return [];
|
|
102
|
+
if (!source.includes("exec.mjs") || !FORK_PATTERN.test(source)) return [];
|
|
103
|
+
const lines = source.split(/\r\n|\n|\r/);
|
|
104
|
+
return [...source.matchAll(FORK_RUNNER_PATTERN)].map((match) => {
|
|
105
|
+
const index = source.slice(0, match.index).split(/\r\n|\n|\r/).length - 1;
|
|
106
|
+
return {
|
|
107
|
+
file: relativePath,
|
|
108
|
+
line: index + 1,
|
|
109
|
+
message: "Replace the fork()-based seed runner call with execSync(\"npx tailor seed apply\"), forwarding env/stdio, and unwind the surrounding await/Promise plumbing.",
|
|
110
|
+
excerpt: lines[index].trim()
|
|
111
|
+
};
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
//#endregion
|
|
115
|
+
export { transform as default, reviewFindings };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//#region codemods/v2/tailor-output-ignore-dir/scripts/transform.ts
|
|
2
|
+
const GENERATED_DIR_IGNORE_ENTRY_RE = /^(!?\/?)\.tailor-sdk(\/?)([ \t]*)$/gm;
|
|
3
|
+
/**
|
|
4
|
+
* Rewrite exact ignore-file entries for the generated SDK output directory.
|
|
5
|
+
* @param source - File contents
|
|
6
|
+
* @returns Transformed source or null when nothing matched.
|
|
7
|
+
*/
|
|
8
|
+
function transform(source) {
|
|
9
|
+
if (!source.includes(".tailor-sdk")) return null;
|
|
10
|
+
const updated = source.replace(GENERATED_DIR_IGNORE_ENTRY_RE, (_match, prefix, slash, trailingWhitespace) => `${prefix}.tailor${slash}${trailingWhitespace}`);
|
|
11
|
+
return updated === source ? null : updated;
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
export { transform as default };
|
|
@@ -6,10 +6,11 @@ const MEMBER_GROUP = [
|
|
|
6
6
|
].join("|");
|
|
7
7
|
const PATTERN = new RegExp(String.raw`\bTailordb\.(${MEMBER_GROUP})\b`, "g");
|
|
8
8
|
/**
|
|
9
|
-
* Rewrite references to the
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* Rewrite references to the capital-cased `Tailordb` ambient namespace to the
|
|
10
|
+
* lowercase `tailordb` namespace. The capital-cased namespace was inherited
|
|
11
|
+
* from `@tailor-platform/function-types`; the SDK kept it as a `@deprecated`
|
|
12
|
+
* alias in v1 and removed it in v2, leaving only the lowercase `tailordb.*`
|
|
13
|
+
* namespace exposed by `@tailor-platform/sdk/runtime/globals`.
|
|
13
14
|
*
|
|
14
15
|
* Only the known type-only members (`QueryResult`, `CommandType`, `Client`)
|
|
15
16
|
* are rewritten so that unrelated user-defined symbols sharing the
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { Lang, parse } from "@ast-grep/napi";
|
|
2
|
+
//#region codemods/v2/wait-point-rename/scripts/transform.ts
|
|
3
|
+
const SDK_MODULE = "@tailor-platform/sdk";
|
|
4
|
+
const RENAMES = {
|
|
5
|
+
defineWaitPoint: "createWaitPoint",
|
|
6
|
+
defineWaitPoints: "createWaitPoints"
|
|
7
|
+
};
|
|
8
|
+
function isInsideImportStatement(node) {
|
|
9
|
+
let current = node.parent();
|
|
10
|
+
while (current) {
|
|
11
|
+
if (current.kind() === "import_statement") return true;
|
|
12
|
+
current = current.parent();
|
|
13
|
+
}
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Rename `defineWaitPoint` and `defineWaitPoints` imported from `@tailor-platform/sdk`
|
|
18
|
+
* to `createWaitPoint` and `createWaitPoints`, updating both the import specifiers
|
|
19
|
+
* and all usages in the file body.
|
|
20
|
+
* @param source - File contents
|
|
21
|
+
* @param filePath - Absolute path to the file (kept for the runner signature)
|
|
22
|
+
* @returns Transformed source or null when nothing matched.
|
|
23
|
+
*/
|
|
24
|
+
function transform(source, _filePath) {
|
|
25
|
+
if (!Object.keys(RENAMES).some((name) => source.includes(name))) return null;
|
|
26
|
+
if (!source.includes(SDK_MODULE)) return null;
|
|
27
|
+
const root = parse(source.includes("</") || source.includes("/>") ? Lang.Tsx : Lang.TypeScript, source).root();
|
|
28
|
+
const edits = [];
|
|
29
|
+
const needsBodyRename = /* @__PURE__ */ new Set();
|
|
30
|
+
const importStmts = root.findAll({ rule: {
|
|
31
|
+
kind: "import_statement",
|
|
32
|
+
has: {
|
|
33
|
+
kind: "string",
|
|
34
|
+
regex: `^["']${SDK_MODULE}["']$`
|
|
35
|
+
}
|
|
36
|
+
} });
|
|
37
|
+
for (const importStmt of importStmts) {
|
|
38
|
+
const specs = importStmt.findAll({ rule: { kind: "import_specifier" } });
|
|
39
|
+
for (const spec of specs) {
|
|
40
|
+
const idents = spec.children().filter((c) => c.kind() === "identifier");
|
|
41
|
+
if (idents.length === 0) continue;
|
|
42
|
+
const importedName = idents[0].text();
|
|
43
|
+
const newName = RENAMES[importedName];
|
|
44
|
+
if (!newName) continue;
|
|
45
|
+
const isAliased = idents.length > 1;
|
|
46
|
+
edits.push(idents[0].replace(newName));
|
|
47
|
+
if (!isAliased) needsBodyRename.add(importedName);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (edits.length === 0) return null;
|
|
51
|
+
if (needsBodyRename.size > 0) {
|
|
52
|
+
const shadowedRanges = /* @__PURE__ */ new Map();
|
|
53
|
+
const addShadowedRange = (name, scopeNode) => {
|
|
54
|
+
const r = scopeNode.range();
|
|
55
|
+
if (!shadowedRanges.has(name)) shadowedRanges.set(name, []);
|
|
56
|
+
shadowedRanges.get(name).push({
|
|
57
|
+
start: r.start.index,
|
|
58
|
+
end: r.end.index
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
const localDecls = root.findAll({ rule: { any: [{ kind: "function_declaration" }, { kind: "variable_declarator" }] } });
|
|
62
|
+
for (const decl of localDecls) {
|
|
63
|
+
if (isInsideImportStatement(decl)) continue;
|
|
64
|
+
const nameChild = decl.children().filter((c) => c.kind() === "identifier").find((c) => needsBodyRename.has(c.text())) ?? decl.children().find((c) => c.kind() === "object_pattern")?.children().find((c) => c.kind() === "shorthand_property_identifier_pattern" && needsBodyRename.has(c.text()));
|
|
65
|
+
if (!nameChild || !needsBodyRename.has(nameChild.text())) continue;
|
|
66
|
+
let scopeNode = root;
|
|
67
|
+
let p = decl.parent();
|
|
68
|
+
while (p) {
|
|
69
|
+
const k = p.kind();
|
|
70
|
+
if (k === "statement_block" || k === "program" || k === "for_statement" || k === "for_in_statement") {
|
|
71
|
+
scopeNode = p;
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
p = p.parent();
|
|
75
|
+
}
|
|
76
|
+
addShadowedRange(nameChild.text(), scopeNode);
|
|
77
|
+
}
|
|
78
|
+
const paramNodes = root.findAll({ rule: { any: [{ kind: "required_parameter" }, { kind: "optional_parameter" }] } });
|
|
79
|
+
for (const param of paramNodes) {
|
|
80
|
+
if (isInsideImportStatement(param)) continue;
|
|
81
|
+
const nameChild = param.children().flatMap((c) => c.kind() === "rest_pattern" ? c.children().filter((cc) => cc.kind() === "identifier") : c.kind() === "identifier" ? [c] : []).find((c) => needsBodyRename.has(c.text()));
|
|
82
|
+
if (!nameChild) continue;
|
|
83
|
+
let scopeNode = root;
|
|
84
|
+
let p = param.parent();
|
|
85
|
+
while (p) {
|
|
86
|
+
const k = p.kind();
|
|
87
|
+
if (k === "formal_parameters") {
|
|
88
|
+
p = p.parent();
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (k === "function_declaration" || k === "function_expression" || k === "arrow_function" || k === "method_definition") {
|
|
92
|
+
scopeNode = p;
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
addShadowedRange(nameChild.text(), scopeNode);
|
|
98
|
+
}
|
|
99
|
+
const forInStmts = root.findAll({ rule: { kind: "for_in_statement" } });
|
|
100
|
+
for (const stmt of forInStmts) {
|
|
101
|
+
const children = stmt.children();
|
|
102
|
+
const keywordIdx = children.findIndex((c) => c.kind() === "of" || c.kind() === "in");
|
|
103
|
+
if (keywordIdx < 0) continue;
|
|
104
|
+
for (let i = 0; i < keywordIdx; i++) {
|
|
105
|
+
const child = children[i];
|
|
106
|
+
if (child.kind() === "identifier" && needsBodyRename.has(child.text())) addShadowedRange(child.text(), stmt);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const renameNode = (node) => {
|
|
110
|
+
const name = node.text();
|
|
111
|
+
if (!needsBodyRename.has(name)) return;
|
|
112
|
+
if (isInsideImportStatement(node)) return;
|
|
113
|
+
const ranges = shadowedRanges.get(name);
|
|
114
|
+
if (ranges) {
|
|
115
|
+
const pos = node.range().start.index;
|
|
116
|
+
if (ranges.some((r) => pos >= r.start && pos < r.end)) return;
|
|
117
|
+
}
|
|
118
|
+
edits.push(node.replace(RENAMES[name]));
|
|
119
|
+
};
|
|
120
|
+
for (const ident of root.findAll({ rule: { kind: "identifier" } })) renameNode(ident);
|
|
121
|
+
for (const prop of root.findAll({ rule: { kind: "shorthand_property_identifier" } })) renameNode(prop);
|
|
122
|
+
}
|
|
123
|
+
return root.commitEdits(edits);
|
|
124
|
+
}
|
|
125
|
+
//#endregion
|
|
126
|
+
export { transform as default };
|
|
@@ -0,0 +1,123 @@
|
|
|
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: "execJobFunction",
|
|
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 `startWorkflow`/`execJobFunction`/
|
|
87
|
+
* `resumeWorkflowExecution` names,
|
|
88
|
+
* either on the ambient `tailor.workflow` global or on a `workflow` value
|
|
89
|
+
* imported from `@tailor-platform/sdk/runtime(/workflow)`.
|
|
90
|
+
* @param source - File contents
|
|
91
|
+
* @param filePath - Absolute path to the file
|
|
92
|
+
* @returns Transformed source or null when nothing matched.
|
|
93
|
+
*/
|
|
94
|
+
function transform(source, filePath) {
|
|
95
|
+
if (!quickFilter(source)) return null;
|
|
96
|
+
const root = parse(sourceLang(filePath ?? "", source), source).root();
|
|
97
|
+
const imports = findImportStatements(root);
|
|
98
|
+
const workflowLocals = collectWorkflowLocals(root, imports);
|
|
99
|
+
const tailorIsAmbient = tailorIsAmbientGlobal(root, imports);
|
|
100
|
+
const edits = [];
|
|
101
|
+
for (const member of root.findAll({ rule: { kind: "member_expression" } })) {
|
|
102
|
+
const property = member.field("property");
|
|
103
|
+
if (!property || property.kind() !== "property_identifier") continue;
|
|
104
|
+
const newName = RENAMES[property.text()];
|
|
105
|
+
if (!newName) continue;
|
|
106
|
+
const object = member.field("object");
|
|
107
|
+
if (!object) continue;
|
|
108
|
+
const isWorkflowImportReceiver = object.kind() === "identifier" && workflowLocals.has(object.text());
|
|
109
|
+
const isAmbientReceiver = tailorIsAmbient && isAmbientTailorWorkflow(object);
|
|
110
|
+
if (!isWorkflowImportReceiver && !isAmbientReceiver) continue;
|
|
111
|
+
if (newName === "startWorkflow") {
|
|
112
|
+
if (hasUnsafeThirdArgument(member)) continue;
|
|
113
|
+
edits.push(property.replace(newName));
|
|
114
|
+
const invokerEdit = findInvokerOptionEdit(member);
|
|
115
|
+
if (invokerEdit) edits.push(invokerEdit);
|
|
116
|
+
} else edits.push(property.replace(newName));
|
|
117
|
+
}
|
|
118
|
+
if (edits.length === 0) return null;
|
|
119
|
+
const result = root.commitEdits(edits);
|
|
120
|
+
return result === source ? null : result;
|
|
121
|
+
}
|
|
122
|
+
//#endregion
|
|
123
|
+
export { transform as default };
|