@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.
Files changed (27) hide show
  1. package/CHANGELOG.md +309 -0
  2. package/dist/codemods/ast-grep-helpers-CXtWn3RB.js +171 -0
  3. package/dist/codemods/v2/apply-to-deploy/scripts/transform.js +22 -4
  4. package/dist/codemods/v2/auth-attributes-rename/scripts/transform.js +213 -0
  5. package/dist/codemods/v2/auth-connection-token-helper/scripts/transform.js +243 -0
  6. package/dist/codemods/v2/auth-invoker-call-unwrap/scripts/transform.js +7 -0
  7. package/dist/codemods/v2/auth-invoker-unwrap/scripts/transform.js +108 -13
  8. package/dist/codemods/v2/cli-rename/scripts/transform.js +373 -14
  9. package/dist/codemods/v2/db-type-to-table/scripts/transform.js +383 -0
  10. package/dist/codemods/v2/env-var-rename/scripts/transform.js +88 -0
  11. package/dist/codemods/v2/erd-site-to-plugin/scripts/transform.js +195 -0
  12. package/dist/codemods/v2/exec-job-function-rename/scripts/transform.js +95 -0
  13. package/dist/codemods/v2/execute-script-arg/scripts/transform.js +60 -0
  14. package/dist/codemods/v2/forward-relation-name/scripts/transform.js +115 -0
  15. package/dist/codemods/v2/idp-publish-events-rename/scripts/transform.js +186 -0
  16. package/dist/codemods/v2/principal-unify/scripts/transform.js +1555 -44
  17. package/dist/codemods/v2/rename-bin/scripts/transform.js +1087 -0
  18. package/dist/codemods/v2/runtime-globals-opt-in/scripts/transform.js +103 -0
  19. package/dist/codemods/v2/runtime-subpath-namespace/scripts/transform.js +792 -0
  20. package/dist/codemods/v2/sdk-skills-shim/scripts/transform.js +3 -3
  21. package/dist/codemods/v2/seed-exec-to-cli-plugin/scripts/transform.js +115 -0
  22. package/dist/codemods/v2/tailor-output-ignore-dir/scripts/transform.js +14 -0
  23. package/dist/codemods/v2/tailordb-namespace/scripts/transform.js +5 -4
  24. package/dist/codemods/v2/wait-point-rename/scripts/transform.js +126 -0
  25. package/dist/codemods/v2/workflow-trigger-rename/scripts/transform.js +123 -0
  26. package/dist/index.js +1962 -51
  27. 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-sdk skills install";
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-sdk skills install`.
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-sdk` CLI instead.
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 deprecated capital-cased `Tailordb` ambient
10
- * namespace to the new lowercase `tailordb` namespace. The capital-cased
11
- * namespace was inherited from `@tailor-platform/function-types`; the SDK
12
- * keeps it as a `@deprecated` alias in v1 and removes it in v2.
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 };