@tailor-platform/sdk-codemod 0.3.0-next.5 → 0.3.0-next.7

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,46 @@
1
1
  # @tailor-platform/sdk-codemod
2
2
 
3
+ ## 0.3.0-next.7
4
+
5
+ ### Patch Changes
6
+
7
+ - [#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`.
8
+
9
+ - [#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.
10
+
11
+ - [#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:
12
+
13
+ ```diff
14
+ const inventory = checkInventory.trigger({ orderId: input.orderId });
15
+ +const inventory = checkInventory.start({ orderId: input.orderId });
16
+
17
+ -const workflowRunId = await orderProcessingWorkflow.trigger(args, { invoker: "manager" });
18
+ +const workflowRunId = await orderProcessingWorkflow.start(args, { invoker: "manager" });
19
+ ```
20
+
21
+ `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.
22
+
23
+ - [#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:
24
+
25
+ ```diff
26
+ import { workflow } from "@tailor-platform/sdk/runtime";
27
+
28
+ -await workflow.triggerWorkflow("myWorkflow", { data: "value" });
29
+ +await workflow.startWorkflow("myWorkflow", { data: "value" });
30
+ -workflow.triggerJobFunction("myJob", { data: "value" });
31
+ +workflow.startJobFunction("myJob", { data: "value" });
32
+ -await workflow.resumeWorkflow("execution-id");
33
+ +await workflow.resumeWorkflowExecution("execution-id");
34
+ ```
35
+
36
+ Run the `v2/workflow-trigger-rename` codemod to migrate call sites automatically.
37
+
38
+ ## 0.3.0-next.6
39
+
40
+ ### Patch Changes
41
+
42
+ - [#1789](https://github.com/tailor-platform/sdk/pull/1789) [`e4db171`](https://github.com/tailor-platform/sdk/commit/e4db171e2a0138ea1a0ba1a972bf895fd0616a28) Thanks [@toiroakr](https://github.com/toiroakr)! - Flag `tailor generate --watch` / `-W` invocations and programmatic `generate({ watch })` usage for manual review as part of the v2 migration (the flag and its dependency watcher are removed in `@tailor-platform/sdk` v2).
43
+
3
44
  ## 0.3.0-next.5
4
45
 
5
46
  ### Patch Changes
@@ -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,9 @@ 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";
121
122
  /** All registered codemods, in registration order. */
122
123
  const allCodemods = [
123
124
  {
@@ -417,7 +418,7 @@ const allCodemods = [
417
418
  {
418
419
  id: "v2/auth-invoker-unwrap",
419
420
  name: "auth.invoker(\"name\") → invoker: \"name\"",
420
- description: "Rename statically identified SDK `authInvoker` options to `invoker`, replace `auth.invoker(\"name\")` there with the bare `\"name\"` string, and drop the `auth` import when no other reference remains. Ambiguous workflow `.trigger()` calls are left for manual review. The `auth.invoker()` helper is removed in v2 because importing `auth` from `tailor.config.ts` into runtime files pulls Node-only modules into the bundle.",
421
+ 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
422
  since: "1.0.0",
422
423
  until: "2.0.0",
423
424
  prereleaseUntil: V2_NEXT_2,
@@ -450,8 +451,8 @@ const allCodemods = [
450
451
  " machine user name string; platform/runtime authInvoker payloads still expect",
451
452
  " the object form.",
452
453
  "2. Rename remaining authInvoker option keys to invoker only for SDK resolver,",
453
- " executor, workflow.trigger(), or startWorkflow() options. Keep platform/runtime",
454
- " payload keys such as tailor.workflow.triggerWorkflow(..., { authInvoker: ... }).",
454
+ " executor, workflow.start(), or startWorkflow() options. Keep platform/runtime",
455
+ " payload keys such as tailor.workflow.startWorkflow(..., { authInvoker: ... }).",
455
456
  "3. After removing every auth.invoker usage in a file, delete the now-unused auth",
456
457
  " import (keeping it pulls Node-only config modules into runtime bundles); leave",
457
458
  " the import if auth is still referenced elsewhere.",
@@ -502,7 +503,7 @@ const allCodemods = [
502
503
  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
504
  since: "1.0.0",
504
505
  until: "2.0.0",
505
- prereleaseUntil: V2_NEXT_3,
506
+ prereleaseUntil: V2_NEXT_4,
506
507
  scriptPath: "v2/runtime-subpath-namespace/scripts/transform.js",
507
508
  filePatterns: ["**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}"],
508
509
  legacyPatterns: [
@@ -574,7 +575,7 @@ const allCodemods = [
574
575
  description: "Rename TailorDB schema builder calls from `db.type()` to `db.table()`. TailorDB schema definitions now use table terminology in SDK projects.",
575
576
  since: "1.0.0",
576
577
  until: "2.0.0",
577
- prereleaseUntil: V2_NEXT_3,
578
+ prereleaseUntil: V2_NEXT_4,
578
579
  scriptPath: "v2/db-type-to-table/scripts/transform.js",
579
580
  filePatterns: ["**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}"],
580
581
  legacyPatterns: ["db.type"],
@@ -600,7 +601,7 @@ const allCodemods = [
600
601
  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
602
  since: "1.0.0",
602
603
  until: "2.0.0",
603
- prereleaseUntil: V2_NEXT_4,
604
+ prereleaseUntil: V2_NEXT_5,
604
605
  scriptPath: "v2/forward-relation-name/scripts/transform.js",
605
606
  filePatterns: ["**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}"],
606
607
  suspiciousPatterns: [
@@ -686,6 +687,54 @@ const allCodemods = [
686
687
  after: "import { createWaitPoints } from \"@tailor-platform/sdk\";\n\nexport const { approval } = createWaitPoints((define) => ({\n approval: define<{ message: string }, { approved: boolean }>(),\n}));"
687
688
  }]
688
689
  },
690
+ {
691
+ id: "v2/workflow-trigger-rename",
692
+ name: "workflow.triggerWorkflow/triggerJobFunction/resumeWorkflow → startWorkflow/startJobFunction/resumeWorkflowExecution",
693
+ 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.",
694
+ since: "1.0.0",
695
+ until: "2.0.0",
696
+ prereleaseUntil: V2_NEXT_6,
697
+ scriptPath: "v2/workflow-trigger-rename/scripts/transform.js",
698
+ filePatterns: ["**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}"],
699
+ legacyPatterns: [
700
+ "triggerWorkflow",
701
+ "triggerJobFunction",
702
+ "resumeWorkflow"
703
+ ],
704
+ examples: [{
705
+ before: "import { workflow } from \"@tailor-platform/sdk/runtime\";\n\nawait workflow.triggerWorkflow(\"myWorkflow\", { data: \"value\" });",
706
+ after: "import { workflow } from \"@tailor-platform/sdk/runtime\";\n\nawait workflow.startWorkflow(\"myWorkflow\", { data: \"value\" });"
707
+ }, {
708
+ caption: "A literal invoker option is renamed to authInvoker:",
709
+ before: "await workflow.triggerWorkflow(\"myWorkflow\", { data: \"value\" }, { invoker: myInvoker });",
710
+ after: "await workflow.startWorkflow(\"myWorkflow\", { data: \"value\" }, { authInvoker: myInvoker });"
711
+ }],
712
+ prompt: [
713
+ "The pre-alignment tailor.workflow names triggerWorkflow, triggerJobFunction, and",
714
+ "resumeWorkflow are removed from the SDK's type surface in v2; use the canonical",
715
+ "startWorkflow, startJobFunction, and resumeWorkflowExecution names instead. The",
716
+ "codemod rewrites direct member-access call sites on the ambient tailor.workflow",
717
+ "global and on a workflow value imported from @tailor-platform/sdk/runtime or",
718
+ "@tailor-platform/sdk/runtime/workflow (including aliased imports). It skips a",
719
+ "file entirely when a local declaration shadows the workflow import or the",
720
+ "ambient tailor name, to avoid rewriting an unrelated same-named value — review",
721
+ "those manually.",
722
+ "",
723
+ "For a renamed triggerWorkflow call, the codemod also renames a literal invoker",
724
+ "option key (including shorthand { invoker }) to authInvoker, since startWorkflow",
725
+ "expects the platform's authInvoker shape directly while triggerWorkflow's removed",
726
+ "wrapper converted invoker to authInvoker internally.",
727
+ "",
728
+ "Also review, and migrate by hand:",
729
+ "- Destructured references (e.g. const { triggerWorkflow } = workflow) — the",
730
+ " codemod only rewrites direct member-access calls.",
731
+ "- Imported TriggerWorkflowOptions / TriggerJobFunctionOptions types — rename",
732
+ " them to StartWorkflowOptions / StartJobFunctionOptions.",
733
+ "- An invoker option passed via a variable or spread (not a literal object) —",
734
+ " the codemod only inspects literal object arguments; rename the invoker key",
735
+ " to authInvoker in the options object's own definition."
736
+ ].join("\n")
737
+ },
689
738
  {
690
739
  id: "v2/open-download-stream",
691
740
  name: "openDownloadStream → downloadStream",
@@ -781,23 +830,57 @@ const allCodemods = [
781
830
  },
782
831
  {
783
832
  id: "v2/workflow-trigger-dispatch",
784
- name: "Workflow .trigger() and trigger tests",
785
- description: "Workflow job `.trigger()` now aligns with the platform runtime: it returns the job result directly instead of a Promise wrapper, and tests no longer run job bodies locally. Mock trigger responses with `mockWorkflow()` (`setJobHandler` / `enqueueResult`, assert via `triggeredJobs`), or use `runWorkflowLocally()` for a full-chain local run.",
833
+ name: "Workflow job start() and start tests",
834
+ 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
835
  since: "1.0.0",
787
836
  until: "2.0.0",
788
837
  prereleaseUntil: V2_NEXT_1,
789
838
  suspiciousPatterns: [".trigger("],
790
839
  examples: [{
791
840
  caption: "Tests must mock the workflow runtime instead of running bodies locally:",
792
- before: "const result = await orderJob.trigger({ id });\nexpect(result.status).toBe(\"done\");",
793
- after: "using wf = mockWorkflow();\nwf.setJobHandler((jobName) => (jobName === \"order-job\" ? { status: \"done\" } : null));\nconst result = await orderJob.trigger({ id });\nexpect(result.status).toBe(\"done\");"
841
+ before: "const result = await orderJob.start({ id });\nexpect(result.status).toBe(\"done\");",
842
+ 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
843
  }],
795
844
  prompt: [
796
- "Workflow job .trigger() now uses the platform workflow runtime instead of running",
845
+ "Workflow job .start() now uses the platform workflow runtime instead of running",
797
846
  "the job body locally. In tests, acquire `using wf = mockWorkflow()` and provide",
798
- "trigger responses (setJobHandler / enqueueResult), or use runWorkflowLocally() for a",
799
- "full-chain local run; an unmocked trigger now throws. Outside tests, treat the",
800
- "trigger result as the job output directly (no Promise wrapper to unwrap)."
847
+ "start responses (setJobHandler / enqueueResult), or use runWorkflowLocally() for a",
848
+ "full-chain local run; an unmocked start now throws. Outside tests, treat the",
849
+ "start result as the job output directly (no Promise wrapper to unwrap)."
850
+ ].join("\n")
851
+ },
852
+ {
853
+ id: "v2/workflow-start-rename",
854
+ name: "Workflow.trigger()/WorkflowJob.trigger() → .start()",
855
+ 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.",
856
+ since: "1.0.0",
857
+ until: "2.0.0",
858
+ prereleaseUntil: "2.0.0-next.7",
859
+ filePatterns: ["**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}"],
860
+ suspiciousPatterns: [".trigger("],
861
+ examples: [{
862
+ before: ["const inventory = checkInventory.trigger({ orderId: input.orderId });", "const workflowRunId = await orderProcessingWorkflow.trigger(args, { invoker: \"manager\" });"].join("\n"),
863
+ after: ["const inventory = checkInventory.start({ orderId: input.orderId });", "const workflowRunId = await orderProcessingWorkflow.start(args, { invoker: \"manager\" });"].join("\n")
864
+ }],
865
+ prompt: [
866
+ "In Tailor SDK v2, the ergonomic .trigger() method on a createWorkflow() or",
867
+ "createWorkflowJob() result is renamed to .start(). This is unrelated to the",
868
+ "separate tailor.workflow.triggerWorkflow/triggerJobFunction/resumeWorkflow removal",
869
+ "(see the workflow-trigger-rename codemod) — this rename targets the SDK's own",
870
+ "ergonomic wrapper, not the low-level platform call.",
871
+ "",
872
+ "For each flagged `.trigger(` call in these files:",
873
+ "1. Confirm the receiver is a workflow or job object — typically a local const",
874
+ " assigned from createWorkflow(...)/createWorkflowJob(...), a named import of one,",
875
+ " or the default import of a workflow module. Skip receivers that are unrelated",
876
+ " objects with their own .trigger() method (state machines, event emitters, etc.).",
877
+ "2. Rename the call from .trigger(...) to .start(...); the argument list is unchanged.",
878
+ "3. Update any mock/test code that reads WorkflowJob['trigger'] / Workflow['trigger']",
879
+ " as a type, or that mocks the ergonomic method via a wrapper — for example,",
880
+ " `wf.job(definition)` / `wf.workflow(definition)` from mockWorkflow() now return a",
881
+ " mock of the `.start` method.",
882
+ "4. Update prose/docs/comments that say \"trigger the workflow/job\" to \"start\" only",
883
+ " where they describe this SDK verb specifically, not unrelated event terminology."
801
884
  ].join("\n")
802
885
  },
803
886
  {
@@ -898,7 +981,7 @@ const allCodemods = [
898
981
  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
982
  since: "1.0.0",
900
983
  until: "2.0.0",
901
- prereleaseUntil: V2_NEXT_4,
984
+ prereleaseUntil: V2_NEXT_5,
902
985
  suspiciousPatterns: [
903
986
  "ValidateConfig",
904
987
  "Validators<",
@@ -944,7 +1027,7 @@ const allCodemods = [
944
1027
  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
1028
  since: "1.0.0",
946
1029
  until: "2.0.0",
947
- prereleaseUntil: V2_NEXT_4,
1030
+ prereleaseUntil: V2_NEXT_5,
948
1031
  suspiciousPatterns: [
949
1032
  "Hooks<",
950
1033
  "HookFn<",
@@ -988,6 +1071,42 @@ const allCodemods = [
988
1071
  "3. Remove unused `Hooks<F>` / `HookFn<>` type imports"
989
1072
  ].join("\n")
990
1073
  },
1074
+ {
1075
+ id: "v2/generate-watch-flag",
1076
+ name: "generate --watch flag removed",
1077
+ 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.",
1078
+ since: "1.0.0",
1079
+ until: "2.0.0",
1080
+ prereleaseUntil: V2_NEXT_6,
1081
+ filePatterns: [
1082
+ "**/package.json",
1083
+ "**/*.{sh,bash,zsh,yml,yaml}",
1084
+ "**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}",
1085
+ "**/*.md"
1086
+ ],
1087
+ suspiciousPatterns: [/\bgenerate\b[^\n]*(?:--watch\b|\s-W\b)/, [/\bgenerate\s*\(/, /\bwatch\s*:/]],
1088
+ examples: [{
1089
+ lang: "sh",
1090
+ caption: "The --watch/-W flag no longer exists; re-run generate after each change:",
1091
+ before: "tailor generate --watch",
1092
+ after: "tailor generate"
1093
+ }],
1094
+ prompt: [
1095
+ "Tailor SDK v2 removes the `generate --watch` (`-W`) flag along with the",
1096
+ "dependency watcher and self-restart logic that powered it. `tailor generate`",
1097
+ "now always runs a single generation pass and exits.",
1098
+ "",
1099
+ "For each flagged `tailor generate ... --watch` / `-W` invocation (package.json",
1100
+ "scripts, shell scripts, CI configs, or docs), drop the flag and re-run",
1101
+ "`tailor generate` after each change instead. If automatic regeneration on file",
1102
+ "change is still needed, wrap the command with a general-purpose file watcher",
1103
+ "(e.g. `chokidar-cli`, `nodemon`) at the project level.",
1104
+ "",
1105
+ "For programmatic use of `generate()` from `@tailor-platform/sdk/cli`, remove the",
1106
+ "`watch` field from the `GenerateOptions` argument — the function now performs a",
1107
+ "single generation pass and resolves once it completes."
1108
+ ].join("\n")
1109
+ },
991
1110
  {
992
1111
  id: "v2/node-minimum-22-15-0",
993
1112
  name: "Node.js minimum version raised to 22.15.0",
@@ -995,6 +1114,14 @@ const allCodemods = [
995
1114
  since: "1.0.0",
996
1115
  until: "2.0.0",
997
1116
  notice: true
1117
+ },
1118
+ {
1119
+ id: "v2/remove-legacy-bundle-cleanup",
1120
+ name: "Legacy bundle artifact cleanup removed from deploy",
1121
+ 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.",
1122
+ since: "1.0.0",
1123
+ until: "2.0.0",
1124
+ notice: true
998
1125
  }
999
1126
  ];
1000
1127
  /**
@@ -1007,12 +1134,13 @@ function resolveCodemodScript(scriptPath) {
1007
1134
  }
1008
1135
  function reachesCodemodBoundary(toVersion, codemod) {
1009
1136
  if (gte(toVersion, codemod.until)) return true;
1010
- if (codemod.prereleaseUntil === void 0 || !gte(toVersion, codemod.prereleaseUntil)) return false;
1137
+ if (codemod.prereleaseUntil === void 0 || codemod.prereleaseUntil === "pending" || !gte(toVersion, codemod.prereleaseUntil)) return false;
1011
1138
  const target = parse(toVersion);
1012
1139
  const boundary = parse(codemod.until);
1013
1140
  return target.prerelease.length > 0 && target.major === boundary.major && target.minor === boundary.minor && target.patch === boundary.patch;
1014
1141
  }
1015
1142
  function effectiveCodemodBoundary(codemod) {
1143
+ if (codemod.prereleaseUntil === "pending") return codemod.until;
1016
1144
  return codemod.prereleaseUntil ?? codemod.until;
1017
1145
  }
1018
1146
  function assertCodemodBoundaries(codemods) {
@@ -1020,7 +1148,7 @@ function assertCodemodBoundaries(codemods) {
1020
1148
  const boundary = parse(codemod.until);
1021
1149
  if (boundary === null) throw new Error(`Codemod ${codemod.id} until must be a valid semver version: ${codemod.until}`);
1022
1150
  if (boundary.prerelease.length > 0) throw new Error(`Codemod ${codemod.id} until must be a stable version: ${codemod.until}`);
1023
- if (codemod.prereleaseUntil === void 0) continue;
1151
+ if (codemod.prereleaseUntil === void 0 || codemod.prereleaseUntil === "pending") continue;
1024
1152
  const prereleaseBoundary = parse(codemod.prereleaseUntil);
1025
1153
  if (prereleaseBoundary === null) throw new Error(`Codemod ${codemod.id} prereleaseUntil must be a valid semver version: ${codemod.prereleaseUntil}`);
1026
1154
  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.5",
3
+ "version": "0.3.0-next.7",
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.4",
35
+ "tsdown": "0.22.7",
36
36
  "typescript": "6.0.3",
37
37
  "vitest": "4.1.10"
38
38
  },