@tailor-platform/sdk 0.12.4 → 0.14.0

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.
@@ -1,4 +1,4 @@
1
- import { WORKFLOW_JOB_BRAND, getDistDir, tailorUserMap } from "./job-wYkb6yMl.mjs";
1
+ import { WORKFLOW_JOB_BRAND, getDistDir, tailorUserMap } from "./job-inTmXxpa.mjs";
2
2
  import { createRequire } from "node:module";
3
3
  import { defineCommand } from "citty";
4
4
  import * as path from "node:path";
@@ -115,16 +115,180 @@ function loadFilesWithIgnores(config) {
115
115
  return files;
116
116
  }
117
117
 
118
+ //#endregion
119
+ //#region src/parser/service/auth/schema.ts
120
+ const AuthInvokerSchema = z.object({
121
+ namespace: z.string(),
122
+ machineUserName: z.string()
123
+ });
124
+ const secretValueSchema = z.object({
125
+ vaultName: z.string(),
126
+ secretKey: z.string()
127
+ });
128
+ const samlBaseSchema = z.object({
129
+ name: z.string(),
130
+ kind: z.literal("SAML"),
131
+ spCertBase64: secretValueSchema.optional(),
132
+ spKeyBase64: secretValueSchema.optional()
133
+ });
134
+ const OIDCSchema = z.object({
135
+ name: z.string(),
136
+ kind: z.literal("OIDC"),
137
+ clientID: z.string(),
138
+ clientSecret: secretValueSchema,
139
+ providerURL: z.string(),
140
+ issuerURL: z.string().optional(),
141
+ usernameClaim: z.string().optional()
142
+ });
143
+ const SAMLSchema = samlBaseSchema.extend({
144
+ metadataURL: z.string().optional(),
145
+ rawMetadata: z.string().optional()
146
+ }).refine((value) => {
147
+ const hasMetadata = value.metadataURL !== void 0;
148
+ const hasRaw = value.rawMetadata !== void 0;
149
+ return hasMetadata !== hasRaw;
150
+ }, "Provide either metadataURL or rawMetadata");
151
+ const IDTokenSchema = z.object({
152
+ name: z.string(),
153
+ kind: z.literal("IDToken"),
154
+ providerURL: z.string(),
155
+ issuerURL: z.string().optional(),
156
+ clientID: z.string(),
157
+ usernameClaim: z.string().optional()
158
+ });
159
+ const BuiltinIdPSchema = z.object({
160
+ name: z.string(),
161
+ kind: z.literal("BuiltInIdP"),
162
+ namespace: z.string(),
163
+ clientName: z.string()
164
+ });
165
+ const IdProviderSchema = z.discriminatedUnion("kind", [
166
+ OIDCSchema,
167
+ SAMLSchema,
168
+ IDTokenSchema,
169
+ BuiltinIdPSchema
170
+ ]);
171
+ const OAuth2ClientGrantTypeSchema = z.union([z.literal("authorization_code"), z.literal("refresh_token")]);
172
+ const OAuth2ClientSchema = z.object({
173
+ description: z.string().optional(),
174
+ grantTypes: z.array(OAuth2ClientGrantTypeSchema).default(["authorization_code", "refresh_token"]),
175
+ redirectURIs: z.array(z.union([
176
+ z.templateLiteral(["https://", z.string()]),
177
+ z.templateLiteral(["http://", z.string()]),
178
+ z.templateLiteral([z.string(), ":url"]),
179
+ z.templateLiteral([
180
+ z.string(),
181
+ ":url/",
182
+ z.string()
183
+ ])
184
+ ])),
185
+ clientType: z.union([
186
+ z.literal("confidential"),
187
+ z.literal("public"),
188
+ z.literal("browser")
189
+ ]).optional()
190
+ });
191
+ const SCIMAuthorizationSchema = z.object({
192
+ type: z.union([z.literal("oauth2"), z.literal("bearer")]),
193
+ bearerSecret: secretValueSchema.optional()
194
+ });
195
+ const SCIMAttributeTypeSchema = z.union([
196
+ z.literal("string"),
197
+ z.literal("number"),
198
+ z.literal("boolean"),
199
+ z.literal("datetime"),
200
+ z.literal("complex")
201
+ ]);
202
+ const SCIMAttributeSchema = z.object({
203
+ type: SCIMAttributeTypeSchema,
204
+ name: z.string(),
205
+ description: z.string().optional(),
206
+ mutability: z.union([
207
+ z.literal("readOnly"),
208
+ z.literal("readWrite"),
209
+ z.literal("writeOnly")
210
+ ]).optional(),
211
+ required: z.boolean().optional(),
212
+ multiValued: z.boolean().optional(),
213
+ uniqueness: z.union([
214
+ z.literal("none"),
215
+ z.literal("server"),
216
+ z.literal("global")
217
+ ]).optional(),
218
+ canonicalValues: z.array(z.string()).nullable().optional(),
219
+ get subAttributes() {
220
+ return z.array(SCIMAttributeSchema).nullable().optional();
221
+ }
222
+ });
223
+ const SCIMSchemaSchema = z.object({
224
+ name: z.string(),
225
+ attributes: z.array(SCIMAttributeSchema)
226
+ });
227
+ const SCIMAttributeMappingSchema = z.object({
228
+ tailorDBField: z.string(),
229
+ scimPath: z.string()
230
+ });
231
+ const SCIMResourceSchema = z.object({
232
+ name: z.string(),
233
+ tailorDBNamespace: z.string(),
234
+ tailorDBType: z.string(),
235
+ coreSchema: SCIMSchemaSchema,
236
+ attributeMapping: z.array(SCIMAttributeMappingSchema)
237
+ });
238
+ const SCIMSchema = z.object({
239
+ machineUserName: z.string(),
240
+ authorization: SCIMAuthorizationSchema,
241
+ resources: z.array(SCIMResourceSchema)
242
+ });
243
+ const TenantProviderSchema = z.object({
244
+ namespace: z.string(),
245
+ type: z.string(),
246
+ signatureField: z.string()
247
+ });
248
+ const UserProfileSchema = z.object({
249
+ type: z.object({
250
+ name: z.string(),
251
+ fields: z.any(),
252
+ metadata: z.any(),
253
+ hooks: z.any(),
254
+ validate: z.any(),
255
+ features: z.any(),
256
+ indexes: z.any(),
257
+ files: z.any(),
258
+ permission: z.any(),
259
+ gqlPermission: z.any(),
260
+ _output: z.any()
261
+ }),
262
+ usernameField: z.string(),
263
+ attributes: z.record(z.string(), z.literal(true)).optional(),
264
+ attributeList: z.array(z.string()).optional()
265
+ });
266
+ const ValueOperandSchema = z.union([
267
+ z.string(),
268
+ z.boolean(),
269
+ z.array(z.string()),
270
+ z.array(z.boolean())
271
+ ]);
272
+ const MachineUserSchema = z.object({
273
+ attributes: z.record(z.string(), ValueOperandSchema).optional(),
274
+ attributeList: z.array(z.uuid()).optional()
275
+ });
276
+ const AuthConfigSchema = z.object({
277
+ name: z.string(),
278
+ userProfile: UserProfileSchema.optional(),
279
+ machineUsers: z.record(z.string(), MachineUserSchema).optional(),
280
+ oauth2Clients: z.record(z.string(), OAuth2ClientSchema).optional(),
281
+ idProvider: IdProviderSchema.optional(),
282
+ scim: SCIMSchema.optional(),
283
+ tenantProvider: TenantProviderSchema.optional()
284
+ }).brand("AuthConfig");
285
+
118
286
  //#endregion
119
287
  //#region src/parser/service/common.ts
120
288
  const functionSchema = z.custom((val) => typeof val === "function");
121
289
 
122
290
  //#endregion
123
291
  //#region src/parser/service/executor/schema.ts
124
- const InvokerSchema = z.object({
125
- authName: z.string(),
126
- machineUser: z.string()
127
- });
128
292
  const RecordTriggerSchema = z.object({
129
293
  kind: z.enum([
130
294
  "recordCreated",
@@ -154,14 +318,14 @@ const TriggerSchema = z.discriminatedUnion("kind", [
154
318
  const FunctionOperationSchema = z.object({
155
319
  kind: z.enum(["function", "jobFunction"]),
156
320
  body: functionSchema,
157
- invoker: InvokerSchema.optional()
321
+ authInvoker: AuthInvokerSchema.optional()
158
322
  });
159
323
  const GqlOperationSchema = z.object({
160
324
  kind: z.literal("graphql"),
161
325
  appName: z.string().optional(),
162
326
  query: z.string(),
163
327
  variables: functionSchema.optional(),
164
- invoker: InvokerSchema.optional()
328
+ authInvoker: AuthInvokerSchema.optional()
165
329
  });
166
330
  const WebhookOperationSchema = z.object({
167
331
  kind: z.literal("webhook"),
@@ -587,9 +751,7 @@ function defineApplication(config) {
587
751
  //#region src/parser/service/workflow/schema.ts
588
752
  const WorkflowJobSchema = z.object({
589
753
  name: z.string(),
590
- get deps() {
591
- return z.array(WorkflowJobSchema).optional();
592
- },
754
+ trigger: functionSchema,
593
755
  body: functionSchema
594
756
  });
595
757
  const WorkflowSchema = z.object({
@@ -601,17 +763,16 @@ const WorkflowSchema = z.object({
601
763
  //#region src/cli/application/workflow/service.ts
602
764
  /**
603
765
  * Load workflow files and collect all jobs in a single pass.
766
+ * Dependencies are detected at bundle time via AST analysis.
604
767
  */
605
768
  async function loadAndCollectJobs(config) {
606
769
  const workflows = {};
607
770
  const workflowSources = [];
608
771
  const collectedJobs = [];
609
- let unusedJobs = [];
610
772
  if (!config.files || config.files.length === 0) return {
611
773
  workflows,
612
774
  workflowSources,
613
775
  jobs: collectedJobs,
614
- unusedJobs,
615
776
  fileCount: 0
616
777
  };
617
778
  const workflowFiles = loadFilesWithIgnores(config);
@@ -630,27 +791,13 @@ async function loadAndCollectJobs(config) {
630
791
  const existing = allJobsMap.get(job.name);
631
792
  if (existing) throw new Error(`Duplicate job name "${job.name}" found:\n - ${existing.sourceFile} (export: ${existing.exportName})\n - ${job.sourceFile} (export: ${job.exportName})\nEach job must have a unique name.`);
632
793
  allJobsMap.set(job.name, job);
794
+ collectedJobs.push(job);
633
795
  }
634
796
  }
635
- const tracedJobs = /* @__PURE__ */ new Map();
636
- for (const { workflow } of workflowSources) traceJobDependencies(workflow.mainJob, tracedJobs);
637
- const notExportedJobs = [];
638
- for (const jobName of tracedJobs.keys()) if (!allJobsMap.has(jobName)) notExportedJobs.push(jobName);
639
- if (notExportedJobs.length > 0) throw new Error(`The following workflow jobs are used but not exported:\n` + notExportedJobs.map((name) => ` - "${name}"`).join("\n") + "\n\nAll workflow jobs must be named exports. Example:\n export const myJob = createWorkflowJob({ name: \"my-job\", ... });\n\nAlso ensure that files containing job exports are included in the workflow.files glob pattern.");
640
- unusedJobs = Array.from(allJobsMap.keys()).filter((jobName) => !tracedJobs.has(jobName));
641
- for (const [jobName, job] of tracedJobs) {
642
- const exportedMetadata = allJobsMap.get(jobName);
643
- const depNames = job.deps?.map((dep) => dep.name);
644
- collectedJobs.push({
645
- ...exportedMetadata,
646
- deps: depNames
647
- });
648
- }
649
797
  return {
650
798
  workflows,
651
799
  workflowSources,
652
800
  jobs: collectedJobs,
653
- unusedJobs,
654
801
  fileCount
655
802
  };
656
803
  }
@@ -665,7 +812,6 @@ function printLoadedWorkflows(result) {
665
812
  const relativePath = path.relative(process.cwd(), sourceFile);
666
813
  console.log("Workflow:", styleText("greenBright", `"${workflow.name}"`), "loaded from", styleText("cyan", relativePath));
667
814
  }
668
- if (result.unusedJobs.length > 0) console.warn(`⚠️ Warning: Unused workflow jobs found: ${result.unusedJobs.join(", ")}`);
669
815
  }
670
816
  /**
671
817
  * Load a single file and extract jobs and workflow
@@ -691,29 +837,583 @@ async function loadFileContent(filePath) {
691
837
  }
692
838
  }
693
839
  } catch (error) {
694
- const relativePath = path.relative(process.cwd(), filePath);
695
- console.error(styleText("red", "Failed to load workflow from"), styleText("redBright", relativePath));
696
- console.error(error);
697
- throw error;
840
+ const relativePath = path.relative(process.cwd(), filePath);
841
+ console.error(styleText("red", "Failed to load workflow from"), styleText("redBright", relativePath));
842
+ console.error(error);
843
+ throw error;
844
+ }
845
+ return {
846
+ jobs,
847
+ workflow
848
+ };
849
+ }
850
+ /**
851
+ * Check if a value is a WorkflowJob by looking for the brand symbol
852
+ */
853
+ function isWorkflowJob(value) {
854
+ return value != null && typeof value === "object" && WORKFLOW_JOB_BRAND in value && value[WORKFLOW_JOB_BRAND] === true;
855
+ }
856
+
857
+ //#endregion
858
+ //#region src/cli/bundler/workflow/ast-utils.ts
859
+ /**
860
+ * Check if a module source is from \@tailor-platform/sdk (including subpaths)
861
+ */
862
+ function isTailorSdkSource(source) {
863
+ return /^@tailor-platform\/sdk(\/|$)/.test(source);
864
+ }
865
+ /**
866
+ * Get the source string from a dynamic import or require call
867
+ */
868
+ function getImportSource(node) {
869
+ if (!node) return null;
870
+ if (node.type === "ImportExpression") {
871
+ const source = node.source;
872
+ if (source.type === "Literal" && typeof source.value === "string") return source.value;
873
+ }
874
+ if (node.type === "CallExpression") {
875
+ const callExpr = node;
876
+ if (callExpr.callee.type === "Identifier" && callExpr.callee.name === "require") {
877
+ const arg = callExpr.arguments[0];
878
+ if (arg && "type" in arg && arg.type === "Literal" && "value" in arg && typeof arg.value === "string") return arg.value;
879
+ }
880
+ }
881
+ return null;
882
+ }
883
+ /**
884
+ * Unwrap AwaitExpression to get the inner expression
885
+ */
886
+ function unwrapAwait(node) {
887
+ if (node?.type === "AwaitExpression") return node.argument;
888
+ return node;
889
+ }
890
+ /**
891
+ * Check if a node is a string literal
892
+ */
893
+ function isStringLiteral(node) {
894
+ return node?.type === "Literal" && typeof node.value === "string";
895
+ }
896
+ /**
897
+ * Check if a node is a function expression (arrow or regular)
898
+ */
899
+ function isFunctionExpression(node) {
900
+ return node?.type === "ArrowFunctionExpression" || node?.type === "FunctionExpression";
901
+ }
902
+ /**
903
+ * Find a property in an object expression
904
+ */
905
+ function findProperty(properties, name) {
906
+ for (const prop of properties) if (prop.type === "Property") {
907
+ const objProp = prop;
908
+ if ((objProp.key.type === "Identifier" ? objProp.key.name : objProp.key.type === "Literal" ? objProp.key.value : null) === name) return {
909
+ key: objProp.key,
910
+ value: objProp.value,
911
+ start: objProp.start,
912
+ end: objProp.end
913
+ };
914
+ }
915
+ return null;
916
+ }
917
+ /**
918
+ * Apply string replacements to source code
919
+ * Replacements are applied from end to start to maintain positions
920
+ */
921
+ function applyReplacements(source, replacements) {
922
+ const sorted = [...replacements].sort((a, b) => b.start - a.start);
923
+ let result = source;
924
+ for (const r of sorted) result = result.slice(0, r.start) + r.text + result.slice(r.end);
925
+ return result;
926
+ }
927
+ /**
928
+ * Find the end of a statement including any trailing newline
929
+ */
930
+ function findStatementEnd(source, position) {
931
+ let i = position;
932
+ while (i < source.length && (source[i] === ";" || source[i] === " " || source[i] === " ")) i++;
933
+ if (i < source.length && source[i] === "\n") i++;
934
+ return i;
935
+ }
936
+ /**
937
+ * Resolve a relative path from a base directory
938
+ * Simple implementation that handles ./ and ../ prefixes
939
+ */
940
+ function resolvePath(baseDir, relativePath) {
941
+ const parts = relativePath.replace(/\\/g, "/").split("/");
942
+ const baseParts = baseDir.replace(/\\/g, "/").split("/");
943
+ for (const part of parts) if (part === ".") {} else if (part === "..") baseParts.pop();
944
+ else baseParts.push(part);
945
+ return baseParts.join("/");
946
+ }
947
+
948
+ //#endregion
949
+ //#region src/cli/bundler/workflow/sdk-binding-collector.ts
950
+ /**
951
+ * Collect all import bindings for a specific function from \@tailor-platform/sdk
952
+ * Returns a Set of local names that refer to the function
953
+ */
954
+ function collectSdkBindings(program, functionName) {
955
+ const bindings = /* @__PURE__ */ new Set();
956
+ function walk(node) {
957
+ if (!node || typeof node !== "object") return;
958
+ const nodeType = node.type;
959
+ if (nodeType === "ImportDeclaration") {
960
+ const importDecl = node;
961
+ const source = importDecl.source?.value;
962
+ if (typeof source === "string" && isTailorSdkSource(source)) {
963
+ for (const specifier of importDecl.specifiers || []) if (specifier.type === "ImportSpecifier") {
964
+ const importSpec = specifier;
965
+ const imported = importSpec.imported.type === "Identifier" ? importSpec.imported.name : importSpec.imported.value;
966
+ if (imported === functionName) bindings.add(importSpec.local?.name || imported);
967
+ } else if (specifier.type === "ImportDefaultSpecifier" || specifier.type === "ImportNamespaceSpecifier") {
968
+ const spec = specifier;
969
+ bindings.add(`__namespace__:${spec.local?.name}`);
970
+ }
971
+ }
972
+ }
973
+ if (nodeType === "VariableDeclaration") {
974
+ const varDecl = node;
975
+ for (const decl of varDecl.declarations || []) {
976
+ const init = unwrapAwait(decl.init);
977
+ const source = getImportSource(init);
978
+ if (source && isTailorSdkSource(source)) {
979
+ const id = decl.id;
980
+ if (id?.type === "Identifier") bindings.add(`__namespace__:${id.name}`);
981
+ else if (id?.type === "ObjectPattern") {
982
+ const objPattern = id;
983
+ for (const prop of objPattern.properties || []) if (prop.type === "Property") {
984
+ const bindingProp = prop;
985
+ const keyName = bindingProp.key.type === "Identifier" ? bindingProp.key.name : bindingProp.key.value;
986
+ if (keyName === functionName) {
987
+ const localName = bindingProp.value.type === "Identifier" ? bindingProp.value.name : keyName;
988
+ bindings.add(localName ?? "");
989
+ }
990
+ }
991
+ }
992
+ }
993
+ }
994
+ }
995
+ for (const key of Object.keys(node)) {
996
+ const child = node[key];
997
+ if (Array.isArray(child)) child.forEach((c) => walk(c));
998
+ else if (child && typeof child === "object") walk(child);
999
+ }
1000
+ }
1001
+ walk(program);
1002
+ return bindings;
1003
+ }
1004
+ /**
1005
+ * Check if a CallExpression is a call to a specific SDK function
1006
+ */
1007
+ function isSdkFunctionCall(node, bindings, functionName) {
1008
+ if (node.type !== "CallExpression") return false;
1009
+ const callee = node.callee;
1010
+ if (callee.type === "Identifier") {
1011
+ const identifier = callee;
1012
+ return bindings.has(identifier.name);
1013
+ }
1014
+ if (callee.type === "MemberExpression") {
1015
+ const memberExpr = callee;
1016
+ if (!memberExpr.computed) {
1017
+ const object = memberExpr.object;
1018
+ const property = memberExpr.property;
1019
+ if (object.type === "Identifier" && bindings.has(`__namespace__:${object.name}`) && property.name === functionName) return true;
1020
+ }
1021
+ }
1022
+ return false;
1023
+ }
1024
+
1025
+ //#endregion
1026
+ //#region src/cli/bundler/workflow/job-detector.ts
1027
+ /**
1028
+ * Find all workflow jobs by detecting createWorkflowJob calls from \@tailor-platform/sdk
1029
+ */
1030
+ function findAllJobs(program, _sourceText) {
1031
+ const jobs = [];
1032
+ const bindings = collectSdkBindings(program, "createWorkflowJob");
1033
+ function walk(node, parents = []) {
1034
+ if (!node || typeof node !== "object") return;
1035
+ if (isSdkFunctionCall(node, bindings, "createWorkflowJob")) {
1036
+ const args = node.arguments;
1037
+ if (args?.length >= 1 && args[0]?.type === "ObjectExpression") {
1038
+ const configObj = args[0];
1039
+ const nameProp = findProperty(configObj.properties, "name");
1040
+ const bodyProp = findProperty(configObj.properties, "body");
1041
+ if (nameProp && isStringLiteral(nameProp.value) && bodyProp && isFunctionExpression(bodyProp.value)) {
1042
+ let statementRange;
1043
+ let exportName;
1044
+ for (let i = parents.length - 1; i >= 0; i--) {
1045
+ const parent = parents[i];
1046
+ if (parent.type === "VariableDeclarator") {
1047
+ const declarator = parent;
1048
+ if (declarator.id?.type === "Identifier") exportName = declarator.id.name;
1049
+ }
1050
+ if (parent.type === "ExportNamedDeclaration" || parent.type === "VariableDeclaration") statementRange = {
1051
+ start: parent.start,
1052
+ end: parent.end
1053
+ };
1054
+ }
1055
+ jobs.push({
1056
+ name: nameProp.value.value,
1057
+ exportName,
1058
+ nameRange: {
1059
+ start: nameProp.start,
1060
+ end: nameProp.end
1061
+ },
1062
+ bodyValueRange: {
1063
+ start: bodyProp.value.start,
1064
+ end: bodyProp.value.end
1065
+ },
1066
+ statementRange
1067
+ });
1068
+ }
1069
+ }
1070
+ }
1071
+ const newParents = [...parents, node];
1072
+ for (const key of Object.keys(node)) {
1073
+ const child = node[key];
1074
+ if (Array.isArray(child)) child.forEach((c) => walk(c, newParents));
1075
+ else if (child && typeof child === "object") walk(child, newParents);
1076
+ }
1077
+ }
1078
+ walk(program);
1079
+ return jobs;
1080
+ }
1081
+ /**
1082
+ * Build a map from export name to job name from detected jobs
1083
+ */
1084
+ function buildJobNameMap(jobs) {
1085
+ const map = /* @__PURE__ */ new Map();
1086
+ for (const job of jobs) if (job.exportName) map.set(job.exportName, job.name);
1087
+ return map;
1088
+ }
1089
+ /**
1090
+ * Detect all .trigger() calls in the source code
1091
+ * Returns information about each trigger call for transformation
1092
+ */
1093
+ function detectTriggerCalls(program, sourceText) {
1094
+ const calls = [];
1095
+ function walk(node) {
1096
+ if (!node || typeof node !== "object") return;
1097
+ if (node.type === "CallExpression") {
1098
+ const callExpr = node;
1099
+ const callee = callExpr.callee;
1100
+ if (callee.type === "MemberExpression") {
1101
+ const memberExpr = callee;
1102
+ if (!memberExpr.computed && memberExpr.object.type === "Identifier" && memberExpr.property.name === "trigger") {
1103
+ const identifierName = memberExpr.object.name;
1104
+ let argsText = "";
1105
+ if (callExpr.arguments.length > 0) {
1106
+ const firstArg = callExpr.arguments[0];
1107
+ const lastArg = callExpr.arguments[callExpr.arguments.length - 1];
1108
+ if (firstArg && lastArg && "start" in firstArg && "end" in lastArg) argsText = sourceText.slice(firstArg.start, lastArg.end);
1109
+ }
1110
+ calls.push({
1111
+ identifierName,
1112
+ callRange: {
1113
+ start: callExpr.start,
1114
+ end: callExpr.end
1115
+ },
1116
+ argsText
1117
+ });
1118
+ }
1119
+ }
1120
+ }
1121
+ for (const key of Object.keys(node)) {
1122
+ const child = node[key];
1123
+ if (Array.isArray(child)) child.forEach((c) => walk(c));
1124
+ else if (child && typeof child === "object") walk(child);
1125
+ }
1126
+ }
1127
+ walk(program);
1128
+ return calls;
1129
+ }
1130
+
1131
+ //#endregion
1132
+ //#region src/cli/bundler/workflow/workflow-detector.ts
1133
+ /**
1134
+ * Find all workflows by detecting createWorkflow calls from \@tailor-platform/sdk
1135
+ */
1136
+ function findAllWorkflows(program, _sourceText) {
1137
+ const workflows = [];
1138
+ const bindings = collectSdkBindings(program, "createWorkflow");
1139
+ function walk(node, parents = []) {
1140
+ if (!node || typeof node !== "object") return;
1141
+ if (isSdkFunctionCall(node, bindings, "createWorkflow")) {
1142
+ const args = node.arguments;
1143
+ if (args?.length >= 1 && args[0]?.type === "ObjectExpression") {
1144
+ const configObj = args[0];
1145
+ const nameProp = findProperty(configObj.properties, "name");
1146
+ if (nameProp && isStringLiteral(nameProp.value)) {
1147
+ let exportName;
1148
+ let isDefaultExport = false;
1149
+ for (let i = parents.length - 1; i >= 0; i--) {
1150
+ const parent = parents[i];
1151
+ if (parent.type === "VariableDeclarator") {
1152
+ const declarator = parent;
1153
+ if (declarator.id?.type === "Identifier") {
1154
+ exportName = declarator.id.name;
1155
+ break;
1156
+ }
1157
+ }
1158
+ if (parent.type === "ExportDefaultDeclaration") isDefaultExport = true;
1159
+ }
1160
+ workflows.push({
1161
+ name: nameProp.value.value,
1162
+ exportName,
1163
+ isDefaultExport
1164
+ });
1165
+ }
1166
+ }
1167
+ }
1168
+ const newParents = [...parents, node];
1169
+ for (const key of Object.keys(node)) {
1170
+ const child = node[key];
1171
+ if (Array.isArray(child)) child.forEach((c) => walk(c, newParents));
1172
+ else if (child && typeof child === "object") walk(child, newParents);
1173
+ }
1174
+ }
1175
+ walk(program);
1176
+ return workflows;
1177
+ }
1178
+ /**
1179
+ * Build a map from export name to workflow name from detected workflows
1180
+ */
1181
+ function buildWorkflowNameMap(workflows) {
1182
+ const map = /* @__PURE__ */ new Map();
1183
+ for (const workflow of workflows) if (workflow.exportName) map.set(workflow.exportName, workflow.name);
1184
+ return map;
1185
+ }
1186
+ /**
1187
+ * Detect default imports in a source file and return a map from local name to import source
1188
+ */
1189
+ function detectDefaultImports(program) {
1190
+ const imports = /* @__PURE__ */ new Map();
1191
+ function walk(node) {
1192
+ if (!node || typeof node !== "object") return;
1193
+ if (node.type === "ImportDeclaration") {
1194
+ const importDecl = node;
1195
+ const source = importDecl.source?.value;
1196
+ if (typeof source === "string") {
1197
+ for (const specifier of importDecl.specifiers || []) if (specifier.type === "ImportDefaultSpecifier") {
1198
+ const spec = specifier;
1199
+ if (spec.local?.name) imports.set(spec.local.name, source);
1200
+ }
1201
+ }
1202
+ }
1203
+ for (const key of Object.keys(node)) {
1204
+ const child = node[key];
1205
+ if (Array.isArray(child)) child.forEach((c) => walk(c));
1206
+ else if (child && typeof child === "object") walk(child);
1207
+ }
1208
+ }
1209
+ walk(program);
1210
+ return imports;
1211
+ }
1212
+
1213
+ //#endregion
1214
+ //#region src/cli/bundler/workflow/trigger-transformer.ts
1215
+ /**
1216
+ * Extract authInvoker info from a config object expression
1217
+ * Returns the authInvoker value text and whether it's a shorthand property
1218
+ */
1219
+ function extractAuthInvokerInfo(configArg, sourceText) {
1220
+ if (!configArg || typeof configArg !== "object") return void 0;
1221
+ if (configArg.type !== "ObjectExpression") return void 0;
1222
+ const objExpr = configArg;
1223
+ for (const prop of objExpr.properties) {
1224
+ if (prop.type !== "Property") continue;
1225
+ const objProp = prop;
1226
+ if ((objProp.key.type === "Identifier" ? objProp.key.name : objProp.key.type === "Literal" ? objProp.key.value : null) === "authInvoker") {
1227
+ if (objProp.shorthand) return {
1228
+ isShorthand: true,
1229
+ valueText: "authInvoker"
1230
+ };
1231
+ return {
1232
+ isShorthand: false,
1233
+ valueText: sourceText.slice(objProp.value.start, objProp.value.end)
1234
+ };
1235
+ }
1236
+ }
1237
+ }
1238
+ /**
1239
+ * Detect .trigger() calls for known workflows and jobs
1240
+ * Only detects calls where the identifier is in workflowNames or jobNames
1241
+ *
1242
+ * @param program - The parsed AST program
1243
+ * @param sourceText - The source code text
1244
+ * @param workflowNames - Set of known workflow identifier names
1245
+ * @param jobNames - Set of known job identifier names
1246
+ */
1247
+ function detectExtendedTriggerCalls(program, sourceText, workflowNames, jobNames) {
1248
+ const calls = [];
1249
+ function walk(node) {
1250
+ if (!node || typeof node !== "object") return;
1251
+ if (node.type === "CallExpression") {
1252
+ const callExpr = node;
1253
+ const callee = callExpr.callee;
1254
+ if (callee.type === "MemberExpression") {
1255
+ const memberExpr = callee;
1256
+ if (!memberExpr.computed && memberExpr.object.type === "Identifier" && memberExpr.property.name === "trigger") {
1257
+ const identifierName = memberExpr.object.name;
1258
+ const isWorkflow = workflowNames.has(identifierName);
1259
+ const isJob = jobNames.has(identifierName);
1260
+ if (!isWorkflow && !isJob) return;
1261
+ const argCount = callExpr.arguments.length;
1262
+ let argsText = "";
1263
+ if (argCount > 0) {
1264
+ const firstArg = callExpr.arguments[0];
1265
+ if (firstArg && "start" in firstArg && "end" in firstArg) argsText = sourceText.slice(firstArg.start, firstArg.end);
1266
+ }
1267
+ if (isWorkflow && argCount >= 2) {
1268
+ const secondArg = callExpr.arguments[1];
1269
+ const authInvoker = extractAuthInvokerInfo(secondArg, sourceText);
1270
+ if (authInvoker) calls.push({
1271
+ kind: "workflow",
1272
+ identifierName,
1273
+ callRange: {
1274
+ start: callExpr.start,
1275
+ end: callExpr.end
1276
+ },
1277
+ argsText,
1278
+ authInvoker
1279
+ });
1280
+ } else if (isJob) calls.push({
1281
+ kind: "job",
1282
+ identifierName,
1283
+ callRange: {
1284
+ start: callExpr.start,
1285
+ end: callExpr.end
1286
+ },
1287
+ argsText
1288
+ });
1289
+ }
1290
+ }
1291
+ }
1292
+ for (const key of Object.keys(node)) {
1293
+ const child = node[key];
1294
+ if (Array.isArray(child)) child.forEach((c) => walk(c));
1295
+ else if (child && typeof child === "object") walk(child);
1296
+ }
1297
+ }
1298
+ walk(program);
1299
+ return calls;
1300
+ }
1301
+ /**
1302
+ * Transform trigger calls for resolver/executor/workflow functions
1303
+ * Handles both job.trigger() and workflow.trigger() calls
1304
+ *
1305
+ * @param source - The source code to transform
1306
+ * @param workflowNameMap - Map from variable name to workflow name
1307
+ * @param jobNameMap - Map from variable name to job name
1308
+ * @param workflowFileMap - Map from file path (without extension) to workflow name for default exports
1309
+ * @param currentFilePath - Path of the current file being transformed (for resolving relative imports)
1310
+ */
1311
+ function transformFunctionTriggers(source, workflowNameMap, jobNameMap, workflowFileMap, currentFilePath) {
1312
+ const { program } = parseSync("input.ts", source);
1313
+ const localWorkflowNameMap = new Map(workflowNameMap);
1314
+ if (workflowFileMap && currentFilePath) {
1315
+ const defaultImports = detectDefaultImports(program);
1316
+ const currentDir = currentFilePath.replace(/[/\\][^/\\]+$/, "");
1317
+ for (const [localName, importSource] of defaultImports) {
1318
+ if (!importSource.startsWith(".")) continue;
1319
+ const resolvedPath = resolvePath(currentDir, importSource);
1320
+ const workflowName = workflowFileMap.get(resolvedPath);
1321
+ if (workflowName) localWorkflowNameMap.set(localName, workflowName);
1322
+ }
1323
+ }
1324
+ const workflowNames = new Set(localWorkflowNameMap.keys());
1325
+ const jobNames = new Set(jobNameMap.keys());
1326
+ const triggerCalls = detectExtendedTriggerCalls(program, source, workflowNames, jobNames);
1327
+ const replacements = [];
1328
+ for (const call of triggerCalls) if (call.kind === "workflow" && call.authInvoker) {
1329
+ const workflowName = localWorkflowNameMap.get(call.identifierName);
1330
+ if (workflowName) {
1331
+ const authInvokerExpr = call.authInvoker.isShorthand ? "authInvoker" : call.authInvoker.valueText;
1332
+ const transformedCall = `tailor.workflow.triggerWorkflow("${workflowName}", ${call.argsText || "undefined"}, { authInvoker: ${authInvokerExpr} })`;
1333
+ replacements.push({
1334
+ start: call.callRange.start,
1335
+ end: call.callRange.end,
1336
+ text: transformedCall
1337
+ });
1338
+ }
1339
+ } else if (call.kind === "job") {
1340
+ const jobName = jobNameMap.get(call.identifierName);
1341
+ if (jobName) {
1342
+ const transformedCall = `tailor.workflow.triggerJobFunction("${jobName}", ${call.argsText || "undefined"})`;
1343
+ replacements.push({
1344
+ start: call.callRange.start,
1345
+ end: call.callRange.end,
1346
+ text: transformedCall
1347
+ });
1348
+ }
1349
+ }
1350
+ return applyReplacements(source, replacements);
1351
+ }
1352
+
1353
+ //#endregion
1354
+ //#region src/cli/bundler/trigger-context.ts
1355
+ /**
1356
+ * Normalize a file path by removing extension and resolving to absolute path
1357
+ */
1358
+ function normalizeFilePath(filePath) {
1359
+ const absolutePath = path.resolve(filePath);
1360
+ const ext = path.extname(absolutePath);
1361
+ return absolutePath.slice(0, -ext.length);
1362
+ }
1363
+ /**
1364
+ * Build trigger context from workflow configuration
1365
+ * Scans workflow files to collect workflow and job mappings
1366
+ */
1367
+ async function buildTriggerContext(workflowConfig) {
1368
+ const workflowNameMap = /* @__PURE__ */ new Map();
1369
+ const jobNameMap = /* @__PURE__ */ new Map();
1370
+ const workflowFileMap = /* @__PURE__ */ new Map();
1371
+ if (!workflowConfig) return {
1372
+ workflowNameMap,
1373
+ jobNameMap,
1374
+ workflowFileMap
1375
+ };
1376
+ const workflowFiles = loadFilesWithIgnores(workflowConfig);
1377
+ for (const file of workflowFiles) try {
1378
+ const source = await fs.promises.readFile(file, "utf-8");
1379
+ const { program } = parseSync("input.ts", source);
1380
+ const workflows = findAllWorkflows(program, source);
1381
+ const workflowMap = buildWorkflowNameMap(workflows);
1382
+ for (const [exportName, workflowName] of workflowMap) workflowNameMap.set(exportName, workflowName);
1383
+ for (const workflow of workflows) if (workflow.isDefaultExport) {
1384
+ const normalizedPath = normalizeFilePath(file);
1385
+ workflowFileMap.set(normalizedPath, workflow.name);
1386
+ }
1387
+ const jobs = findAllJobs(program, source);
1388
+ const jobMap = buildJobNameMap(jobs);
1389
+ for (const [exportName, jobName] of jobMap) jobNameMap.set(exportName, jobName);
1390
+ } catch (error) {
1391
+ const errorMessage = error instanceof Error ? error.message : String(error);
1392
+ console.warn(styleText("yellow", `Warning: Failed to process workflow file ${file}: ${errorMessage}`));
1393
+ continue;
698
1394
  }
699
1395
  return {
700
- jobs,
701
- workflow
1396
+ workflowNameMap,
1397
+ jobNameMap,
1398
+ workflowFileMap
702
1399
  };
703
1400
  }
704
1401
  /**
705
- * Check if a value is a WorkflowJob by looking for the brand symbol
706
- */
707
- function isWorkflowJob(value) {
708
- return value != null && typeof value === "object" && WORKFLOW_JOB_BRAND in value && value[WORKFLOW_JOB_BRAND] === true;
709
- }
710
- /**
711
- * Recursively trace all job dependencies
1402
+ * Create a rolldown plugin for transforming trigger calls
1403
+ * Returns undefined if no trigger context is provided
712
1404
  */
713
- function traceJobDependencies(job, visited) {
714
- if (visited.has(job.name)) return;
715
- visited.set(job.name, job);
716
- if (job.deps && Array.isArray(job.deps)) for (const dep of job.deps) traceJobDependencies(dep, visited);
1405
+ function createTriggerTransformPlugin(triggerContext) {
1406
+ if (!triggerContext) return;
1407
+ return {
1408
+ name: "trigger-transform",
1409
+ transform: {
1410
+ filter: { id: { include: [/\.ts$/, /\.js$/] } },
1411
+ handler(code, id) {
1412
+ if (!code.includes(".trigger(")) return null;
1413
+ return { code: transformFunctionTriggers(code, triggerContext.workflowNameMap, triggerContext.jobNameMap, triggerContext.workflowFileMap, id) };
1414
+ }
1415
+ }
1416
+ };
717
1417
  }
718
1418
 
719
1419
  //#endregion
@@ -734,7 +1434,7 @@ async function loadExecutor(executorFilePath) {
734
1434
  * 1. Creates entry file that extracts operation.body
735
1435
  * 2. Bundles in a single step with tree-shaking
736
1436
  */
737
- async function bundleExecutors(config) {
1437
+ async function bundleExecutors(config, triggerContext) {
738
1438
  const files = loadFilesWithIgnores(config);
739
1439
  if (files.length === 0) throw new Error(`No files found matching pattern: ${config.files?.join(", ")}`);
740
1440
  console.log("");
@@ -767,10 +1467,10 @@ async function bundleExecutors(config) {
767
1467
  } catch {
768
1468
  tsconfig = void 0;
769
1469
  }
770
- await Promise.all(executors.map((executor) => bundleSingleExecutor(executor, outputDir, tsconfig)));
1470
+ await Promise.all(executors.map((executor) => bundleSingleExecutor(executor, outputDir, tsconfig, triggerContext)));
771
1471
  console.log(styleText("green", "Bundled"), styleText("cyan", "\"executor\""));
772
1472
  }
773
- async function bundleSingleExecutor(executor, outputDir, tsconfig) {
1473
+ async function bundleSingleExecutor(executor, outputDir, tsconfig, triggerContext) {
774
1474
  const entryPath = path.join(outputDir, `${executor.name}.entry.js`);
775
1475
  const absoluteSourcePath = path.resolve(executor.sourceFile).replace(/\\/g, "/");
776
1476
  const entryContent = ml`
@@ -782,6 +1482,8 @@ async function bundleSingleExecutor(executor, outputDir, tsconfig) {
782
1482
  `;
783
1483
  fs.writeFileSync(entryPath, entryContent);
784
1484
  const outputPath = path.join(outputDir, `${executor.name}.js`);
1485
+ const triggerPlugin = createTriggerTransformPlugin(triggerContext);
1486
+ const plugins = triggerPlugin ? [triggerPlugin] : [];
785
1487
  await rolldown.build(rolldown.defineConfig({
786
1488
  input: entryPath,
787
1489
  output: {
@@ -792,6 +1494,7 @@ async function bundleSingleExecutor(executor, outputDir, tsconfig) {
792
1494
  inlineDynamicImports: true
793
1495
  },
794
1496
  tsconfig,
1497
+ plugins,
795
1498
  treeshake: {
796
1499
  moduleSideEffects: false,
797
1500
  annotations: true,
@@ -820,7 +1523,7 @@ async function loadResolver(resolverFilePath) {
820
1523
  * 2. Creates entry file
821
1524
  * 3. Bundles in a single step with tree-shaking
822
1525
  */
823
- async function bundleResolvers(namespace, config) {
1526
+ async function bundleResolvers(namespace, config, triggerContext) {
824
1527
  const files = loadFilesWithIgnores(config);
825
1528
  if (files.length === 0) throw new Error(`No files found matching pattern: ${config.files?.join(", ")}`);
826
1529
  console.log("");
@@ -845,10 +1548,10 @@ async function bundleResolvers(namespace, config) {
845
1548
  } catch {
846
1549
  tsconfig = void 0;
847
1550
  }
848
- await Promise.all(resolvers.map((resolver) => bundleSingleResolver(resolver, outputDir, tsconfig)));
1551
+ await Promise.all(resolvers.map((resolver) => bundleSingleResolver(resolver, outputDir, tsconfig, triggerContext)));
849
1552
  console.log(styleText("green", "Bundled"), styleText("cyan", `"${namespace}"`));
850
1553
  }
851
- async function bundleSingleResolver(resolver, outputDir, tsconfig) {
1554
+ async function bundleSingleResolver(resolver, outputDir, tsconfig, triggerContext) {
852
1555
  const entryPath = path.join(outputDir, `${resolver.name}.entry.js`);
853
1556
  const absoluteSourcePath = path.resolve(resolver.sourceFile).replace(/\\/g, "/");
854
1557
  const entryContent = ml`
@@ -881,6 +1584,8 @@ async function bundleSingleResolver(resolver, outputDir, tsconfig) {
881
1584
  `;
882
1585
  fs.writeFileSync(entryPath, entryContent);
883
1586
  const outputPath = path.join(outputDir, `${resolver.name}.js`);
1587
+ const triggerPlugin = createTriggerTransformPlugin(triggerContext);
1588
+ const plugins = triggerPlugin ? [triggerPlugin] : [];
884
1589
  await rolldown.build(rolldown.defineConfig({
885
1590
  input: entryPath,
886
1591
  output: {
@@ -891,6 +1596,7 @@ async function bundleSingleResolver(resolver, outputDir, tsconfig) {
891
1596
  inlineDynamicImports: true
892
1597
  },
893
1598
  tsconfig,
1599
+ plugins,
894
1600
  treeshake: {
895
1601
  moduleSideEffects: false,
896
1602
  annotations: true,
@@ -901,227 +1607,7 @@ async function bundleSingleResolver(resolver, outputDir, tsconfig) {
901
1607
  }
902
1608
 
903
1609
  //#endregion
904
- //#region src/cli/bundler/workflow/ast-transformer.ts
905
- /**
906
- * Check if a module source is from \@tailor-platform/sdk (including subpaths)
907
- */
908
- function isTailorSdkSource(source) {
909
- return /^@tailor-platform\/sdk(\/|$)/.test(source);
910
- }
911
- /**
912
- * Get the source string from a dynamic import or require call
913
- */
914
- function getImportSource(node) {
915
- if (!node) return null;
916
- if (node.type === "ImportExpression") {
917
- const source = node.source;
918
- if (source.type === "Literal" && typeof source.value === "string") return source.value;
919
- }
920
- if (node.type === "CallExpression") {
921
- const callExpr = node;
922
- if (callExpr.callee.type === "Identifier" && callExpr.callee.name === "require") {
923
- const arg = callExpr.arguments[0];
924
- if (arg && "type" in arg && arg.type === "Literal" && "value" in arg && typeof arg.value === "string") return arg.value;
925
- }
926
- }
927
- return null;
928
- }
929
- /**
930
- * Unwrap AwaitExpression to get the inner expression
931
- */
932
- function unwrapAwait(node) {
933
- if (node?.type === "AwaitExpression") return node.argument;
934
- return node;
935
- }
936
- /**
937
- * Collect all import bindings for createWorkflowJob from \@tailor-platform/sdk
938
- * Returns a Set of local names that refer to createWorkflowJob
939
- */
940
- function collectCreateWorkflowJobBindings(program) {
941
- const bindings = /* @__PURE__ */ new Set();
942
- function walk(node) {
943
- if (!node || typeof node !== "object") return;
944
- const nodeType = node.type;
945
- if (nodeType === "ImportDeclaration") {
946
- const importDecl = node;
947
- const source = importDecl.source?.value;
948
- if (typeof source === "string" && isTailorSdkSource(source)) {
949
- for (const specifier of importDecl.specifiers || []) if (specifier.type === "ImportSpecifier") {
950
- const importSpec = specifier;
951
- const imported = importSpec.imported.type === "Identifier" ? importSpec.imported.name : importSpec.imported.value;
952
- if (imported === "createWorkflowJob") bindings.add(importSpec.local?.name || imported);
953
- } else if (specifier.type === "ImportDefaultSpecifier" || specifier.type === "ImportNamespaceSpecifier") {
954
- const spec = specifier;
955
- bindings.add(`__namespace__:${spec.local?.name}`);
956
- }
957
- }
958
- }
959
- if (nodeType === "VariableDeclaration") {
960
- const varDecl = node;
961
- for (const decl of varDecl.declarations || []) {
962
- const init = unwrapAwait(decl.init);
963
- const source = getImportSource(init);
964
- if (source && isTailorSdkSource(source)) {
965
- const id = decl.id;
966
- if (id?.type === "Identifier") bindings.add(`__namespace__:${id.name}`);
967
- else if (id?.type === "ObjectPattern") {
968
- const objPattern = id;
969
- for (const prop of objPattern.properties || []) if (prop.type === "Property") {
970
- const bindingProp = prop;
971
- const keyName = bindingProp.key.type === "Identifier" ? bindingProp.key.name : bindingProp.key.value;
972
- if (keyName === "createWorkflowJob") {
973
- const localName = bindingProp.value.type === "Identifier" ? bindingProp.value.name : keyName;
974
- bindings.add(localName ?? "");
975
- }
976
- }
977
- }
978
- }
979
- }
980
- }
981
- for (const key of Object.keys(node)) {
982
- const child = node[key];
983
- if (Array.isArray(child)) child.forEach((c) => walk(c));
984
- else if (child && typeof child === "object") walk(child);
985
- }
986
- }
987
- walk(program);
988
- return bindings;
989
- }
990
- /**
991
- * Check if a CallExpression is a createWorkflowJob call
992
- */
993
- function isCreateWorkflowJobCall(node, bindings) {
994
- if (node.type !== "CallExpression") return false;
995
- const callee = node.callee;
996
- if (callee.type === "Identifier") {
997
- const identifier = callee;
998
- return bindings.has(identifier.name);
999
- }
1000
- if (callee.type === "MemberExpression") {
1001
- const memberExpr = callee;
1002
- if (!memberExpr.computed) {
1003
- const object = memberExpr.object;
1004
- const property = memberExpr.property;
1005
- if (object.type === "Identifier" && bindings.has(`__namespace__:${object.name}`) && property.name === "createWorkflowJob") return true;
1006
- }
1007
- }
1008
- return false;
1009
- }
1010
- /**
1011
- * Check if a node is a string literal
1012
- */
1013
- function isStringLiteral(node) {
1014
- return node?.type === "Literal" && typeof node.value === "string";
1015
- }
1016
- /**
1017
- * Check if a node is a function expression (arrow or regular)
1018
- */
1019
- function isFunctionExpression(node) {
1020
- return node?.type === "ArrowFunctionExpression" || node?.type === "FunctionExpression";
1021
- }
1022
- /**
1023
- * Find a property in an object expression
1024
- */
1025
- function findProperty(properties, name) {
1026
- for (const prop of properties) if (prop.type === "Property") {
1027
- const objProp = prop;
1028
- if ((objProp.key.type === "Identifier" ? objProp.key.name : objProp.key.type === "Literal" ? objProp.key.value : null) === name) return {
1029
- key: objProp.key,
1030
- value: objProp.value,
1031
- start: objProp.start,
1032
- end: objProp.end
1033
- };
1034
- }
1035
- return null;
1036
- }
1037
- /**
1038
- * Find all workflow jobs by detecting createWorkflowJob calls from \@tailor-platform/sdk
1039
- */
1040
- function findAllJobs(program, _sourceText) {
1041
- const jobs = [];
1042
- const bindings = collectCreateWorkflowJobBindings(program);
1043
- function walk(node, parents = []) {
1044
- if (!node || typeof node !== "object") return;
1045
- if (isCreateWorkflowJobCall(node, bindings)) {
1046
- const args = node.arguments;
1047
- if (args?.length >= 1 && args[0]?.type === "ObjectExpression") {
1048
- const configObj = args[0];
1049
- const nameProp = findProperty(configObj.properties, "name");
1050
- const bodyProp = findProperty(configObj.properties, "body");
1051
- const depsProp = findProperty(configObj.properties, "deps");
1052
- if (nameProp && isStringLiteral(nameProp.value) && bodyProp && isFunctionExpression(bodyProp.value)) {
1053
- let statementRange;
1054
- for (let i = 0; i < parents.length; i++) {
1055
- const parent = parents[i];
1056
- if (parent.type === "ExportNamedDeclaration" || parent.type === "VariableDeclaration") {
1057
- statementRange = {
1058
- start: parent.start,
1059
- end: parent.end
1060
- };
1061
- break;
1062
- }
1063
- }
1064
- jobs.push({
1065
- name: nameProp.value.value,
1066
- nameRange: {
1067
- start: nameProp.start,
1068
- end: nameProp.end
1069
- },
1070
- depsRange: depsProp ? {
1071
- start: depsProp.start,
1072
- end: depsProp.end
1073
- } : void 0,
1074
- bodyValueRange: {
1075
- start: bodyProp.value.start,
1076
- end: bodyProp.value.end
1077
- },
1078
- statementRange
1079
- });
1080
- }
1081
- }
1082
- }
1083
- const newParents = [...parents, node];
1084
- for (const key of Object.keys(node)) {
1085
- const child = node[key];
1086
- if (Array.isArray(child)) child.forEach((c) => walk(c, newParents));
1087
- else if (child && typeof child === "object") walk(child, newParents);
1088
- }
1089
- }
1090
- walk(program);
1091
- return jobs;
1092
- }
1093
- /**
1094
- * Apply string replacements to source code
1095
- * Replacements are applied from end to start to maintain positions
1096
- */
1097
- function applyReplacements(source, replacements) {
1098
- const sorted = [...replacements].sort((a, b) => b.start - a.start);
1099
- let result = source;
1100
- for (const r of sorted) result = result.slice(0, r.start) + r.text + result.slice(r.end);
1101
- return result;
1102
- }
1103
- /**
1104
- * Find the end position including trailing comma
1105
- */
1106
- function findTrailingCommaEnd(source, position) {
1107
- let i = position;
1108
- while (i < source.length) {
1109
- const char = source[i];
1110
- if (char === ",") return i + 1;
1111
- if (!/\s/.test(char)) break;
1112
- i++;
1113
- }
1114
- return position;
1115
- }
1116
- /**
1117
- * Find the end of a statement including any trailing newline
1118
- */
1119
- function findStatementEnd(source, position) {
1120
- let i = position;
1121
- while (i < source.length && (source[i] === ";" || source[i] === " " || source[i] === " ")) i++;
1122
- if (i < source.length && source[i] === "\n") i++;
1123
- return i;
1124
- }
1610
+ //#region src/cli/bundler/workflow/source-transformer.ts
1125
1611
  /**
1126
1612
  * Find variable declarations by export names
1127
1613
  * Returns a map of export name to statement range
@@ -1162,54 +1648,74 @@ function findVariableDeclarationsByName(program) {
1162
1648
  }
1163
1649
  /**
1164
1650
  * Transform workflow source code
1165
- * - Target job: remove deps
1651
+ * - Transform .trigger() calls to tailor.workflow.triggerJobFunction()
1166
1652
  * - Other jobs: remove entire variable declaration
1167
1653
  *
1168
1654
  * @param source - The source code to transform
1169
1655
  * @param targetJobName - The name of the target job (from job config)
1170
1656
  * @param targetJobExportName - The export name of the target job (optional, for enhanced detection)
1171
1657
  * @param otherJobExportNames - Export names of other jobs to remove (optional, for enhanced detection)
1658
+ * @param allJobsMap - Map from export name to job name for trigger transformation (optional)
1172
1659
  */
1173
- function transformWorkflowSource(source, targetJobName, targetJobExportName, otherJobExportNames) {
1660
+ function transformWorkflowSource(source, targetJobName, targetJobExportName, otherJobExportNames, allJobsMap) {
1174
1661
  const { program } = parseSync("input.ts", source);
1175
1662
  const detectedJobs = findAllJobs(program, source);
1663
+ const jobNameMap = allJobsMap ?? buildJobNameMap(detectedJobs);
1176
1664
  const allDeclarations = findVariableDeclarationsByName(program);
1665
+ const triggerCalls = detectTriggerCalls(program, source);
1177
1666
  const replacements = [];
1178
- const removedRanges = /* @__PURE__ */ new Set();
1179
- const markRemoved = (start, end) => {
1180
- removedRanges.add(`${start}-${end}`);
1667
+ const removedRanges = [];
1668
+ const isInsideRemovedRange = (pos) => {
1669
+ return removedRanges.some((r) => pos >= r.start && pos < r.end);
1181
1670
  };
1182
- const isRemoved = (start, end) => {
1183
- return removedRanges.has(`${start}-${end}`);
1671
+ const isAlreadyMarkedForRemoval = (start) => {
1672
+ return removedRanges.some((r) => r.start === start);
1184
1673
  };
1185
- for (const job of detectedJobs) if (job.name === targetJobName) {
1186
- if (job.depsRange) replacements.push({
1187
- start: job.depsRange.start,
1188
- end: findTrailingCommaEnd(source, job.depsRange.end),
1189
- text: ""
1190
- });
1191
- } else if (job.statementRange && !isRemoved(job.statementRange.start, job.statementRange.end)) {
1192
- replacements.push({
1193
- start: job.statementRange.start,
1194
- end: findStatementEnd(source, job.statementRange.end),
1195
- text: ""
1674
+ for (const job of detectedJobs) {
1675
+ if (job.name === targetJobName) continue;
1676
+ if (job.statementRange && !isAlreadyMarkedForRemoval(job.statementRange.start)) {
1677
+ const endPos = findStatementEnd(source, job.statementRange.end);
1678
+ removedRanges.push({
1679
+ start: job.statementRange.start,
1680
+ end: endPos
1681
+ });
1682
+ replacements.push({
1683
+ start: job.statementRange.start,
1684
+ end: endPos,
1685
+ text: ""
1686
+ });
1687
+ } else if (!job.statementRange) replacements.push({
1688
+ start: job.bodyValueRange.start,
1689
+ end: job.bodyValueRange.end,
1690
+ text: "() => {}"
1196
1691
  });
1197
- markRemoved(job.statementRange.start, job.statementRange.end);
1198
- } else if (!job.statementRange) replacements.push({
1199
- start: job.bodyValueRange.start,
1200
- end: job.bodyValueRange.end,
1201
- text: "() => {}"
1202
- });
1692
+ }
1203
1693
  if (otherJobExportNames) for (const exportName of otherJobExportNames) {
1204
1694
  if (exportName === targetJobExportName) continue;
1205
1695
  const declRange = allDeclarations.get(exportName);
1206
- if (declRange && !isRemoved(declRange.start, declRange.end)) {
1696
+ if (declRange && !isAlreadyMarkedForRemoval(declRange.start)) {
1697
+ const endPos = findStatementEnd(source, declRange.end);
1698
+ removedRanges.push({
1699
+ start: declRange.start,
1700
+ end: endPos
1701
+ });
1207
1702
  replacements.push({
1208
1703
  start: declRange.start,
1209
- end: findStatementEnd(source, declRange.end),
1704
+ end: endPos,
1210
1705
  text: ""
1211
1706
  });
1212
- markRemoved(declRange.start, declRange.end);
1707
+ }
1708
+ }
1709
+ for (const call of triggerCalls) {
1710
+ if (isInsideRemovedRange(call.callRange.start)) continue;
1711
+ const jobName = jobNameMap.get(call.identifierName);
1712
+ if (jobName) {
1713
+ const transformedCall = `tailor.workflow.triggerJobFunction("${jobName}", ${call.argsText || "undefined"})`;
1714
+ replacements.push({
1715
+ start: call.callRange.start,
1716
+ end: call.callRange.end,
1717
+ text: transformedCall
1718
+ });
1213
1719
  }
1214
1720
  }
1215
1721
  return applyReplacements(source, replacements);
@@ -1221,18 +1727,20 @@ function transformWorkflowSource(source, targetJobName, targetJobExportName, oth
1221
1727
  * Bundle workflow jobs
1222
1728
  *
1223
1729
  * This function:
1224
- * 1. Uses a transform plugin to remove deps during bundling (preserves module resolution)
1225
- * 2. Creates entry file
1226
- * 3. Bundles in a single step with tree-shaking
1730
+ * 1. Detects which jobs are actually used (mainJobs + their dependencies)
1731
+ * 2. Uses a transform plugin to transform trigger calls during bundling
1732
+ * 3. Creates entry file and bundles with tree-shaking
1227
1733
  */
1228
- async function bundleWorkflowJobs(allJobs) {
1734
+ async function bundleWorkflowJobs(allJobs, mainJobNames, env = {}, triggerContext) {
1229
1735
  if (allJobs.length === 0) {
1230
1736
  console.log(styleText("dim", "No workflow jobs to bundle"));
1231
1737
  return;
1232
1738
  }
1739
+ const usedJobs = await filterUsedJobs(allJobs, mainJobNames);
1233
1740
  console.log("");
1234
- console.log("Bundling", styleText("cyanBright", allJobs.length.toString()), "files for", styleText("cyan", "\"workflow-job\""));
1741
+ console.log("Bundling", styleText("cyanBright", usedJobs.length.toString()), "files for", styleText("cyan", "\"workflow-job\""));
1235
1742
  const outputDir = path.resolve(getDistDir(), "workflow-jobs");
1743
+ if (fs.existsSync(outputDir)) fs.rmSync(outputDir, { recursive: true });
1236
1744
  fs.mkdirSync(outputDir, { recursive: true });
1237
1745
  let tsconfig;
1238
1746
  try {
@@ -1240,28 +1748,81 @@ async function bundleWorkflowJobs(allJobs) {
1240
1748
  } catch {
1241
1749
  tsconfig = void 0;
1242
1750
  }
1243
- await Promise.all(allJobs.map((job) => bundleSingleJob(job, allJobs, outputDir, tsconfig)));
1751
+ await Promise.all(usedJobs.map((job) => bundleSingleJob(job, usedJobs, outputDir, tsconfig, env, triggerContext)));
1244
1752
  console.log(styleText("green", "Bundled"), styleText("cyan", "\"workflow-job\""));
1245
1753
  }
1246
- async function bundleSingleJob(job, allJobs, outputDir, tsconfig) {
1247
- const depsJobNames = findJobDeps(job.name, allJobs);
1248
- const jobsObject = generateJobsObject(depsJobNames);
1754
+ /**
1755
+ * Filter jobs to only include those that are actually used.
1756
+ * A job is "used" if:
1757
+ * - It's a mainJob of a workflow
1758
+ * - It's called via .trigger() from another used job (transitively)
1759
+ */
1760
+ async function filterUsedJobs(allJobs, mainJobNames) {
1761
+ if (allJobs.length === 0 || mainJobNames.length === 0) return [];
1762
+ const jobsBySourceFile = /* @__PURE__ */ new Map();
1763
+ for (const job of allJobs) {
1764
+ const existing = jobsBySourceFile.get(job.sourceFile) || [];
1765
+ existing.push(job);
1766
+ jobsBySourceFile.set(job.sourceFile, existing);
1767
+ }
1768
+ const exportNameToJobName = /* @__PURE__ */ new Map();
1769
+ for (const job of allJobs) exportNameToJobName.set(job.exportName, job.name);
1770
+ const dependencies = /* @__PURE__ */ new Map();
1771
+ const fileResults = await Promise.all(Array.from(jobsBySourceFile.entries()).map(async ([sourceFile, jobs]) => {
1772
+ try {
1773
+ const source = await fs.promises.readFile(sourceFile, "utf-8");
1774
+ const { program } = parseSync("input.ts", source);
1775
+ const detectedJobs = findAllJobs(program, source);
1776
+ const localExportNameToJobName = /* @__PURE__ */ new Map();
1777
+ for (const detected of detectedJobs) if (detected.exportName) localExportNameToJobName.set(detected.exportName, detected.name);
1778
+ const triggerCalls = detectTriggerCalls(program, source);
1779
+ const jobDependencies = [];
1780
+ for (const job of jobs) {
1781
+ const detectedJob = detectedJobs.find((d) => d.name === job.name);
1782
+ if (!detectedJob) continue;
1783
+ const jobDeps = /* @__PURE__ */ new Set();
1784
+ for (const call of triggerCalls) if (detectedJob.bodyValueRange && call.callRange.start >= detectedJob.bodyValueRange.start && call.callRange.end <= detectedJob.bodyValueRange.end) {
1785
+ const triggeredJobName = localExportNameToJobName.get(call.identifierName) || exportNameToJobName.get(call.identifierName);
1786
+ if (triggeredJobName) jobDeps.add(triggeredJobName);
1787
+ }
1788
+ if (jobDeps.size > 0) jobDependencies.push({
1789
+ jobName: job.name,
1790
+ deps: jobDeps
1791
+ });
1792
+ }
1793
+ return jobDependencies;
1794
+ } catch {
1795
+ return [];
1796
+ }
1797
+ }));
1798
+ for (const jobDependencies of fileResults) for (const { jobName, deps } of jobDependencies) dependencies.set(jobName, deps);
1799
+ const usedJobNames = /* @__PURE__ */ new Set();
1800
+ function markUsed(jobName) {
1801
+ if (usedJobNames.has(jobName)) return;
1802
+ usedJobNames.add(jobName);
1803
+ const deps = dependencies.get(jobName);
1804
+ if (deps) for (const dep of deps) markUsed(dep);
1805
+ }
1806
+ for (const mainJobName of mainJobNames) markUsed(mainJobName);
1807
+ return allJobs.filter((job) => usedJobNames.has(job.name));
1808
+ }
1809
+ async function bundleSingleJob(job, allJobs, outputDir, tsconfig, env, triggerContext) {
1249
1810
  const entryPath = path.join(outputDir, `${job.name}.entry.js`);
1250
1811
  const absoluteSourcePath = path.resolve(job.sourceFile).replace(/\\/g, "/");
1251
1812
  const entryContent = ml`
1252
1813
  import { ${job.exportName} } from "${absoluteSourcePath}";
1253
1814
 
1254
- const jobs = {
1255
- ${jobsObject}
1256
- };
1815
+ const env = ${JSON.stringify(env)};
1257
1816
 
1258
1817
  globalThis.main = async (input) => {
1259
- return await ${job.exportName}.body(input, jobs);
1818
+ return await ${job.exportName}.body(input, { env });
1260
1819
  };
1261
1820
  `;
1262
1821
  fs.writeFileSync(entryPath, entryContent);
1263
1822
  const outputPath = path.join(outputDir, `${job.name}.js`);
1264
1823
  const otherJobExportNames = allJobs.filter((j) => j.name !== job.name).map((j) => j.exportName);
1824
+ const allJobsMap = /* @__PURE__ */ new Map();
1825
+ for (const j of allJobs) allJobsMap.set(j.exportName, j.name);
1265
1826
  await rolldown.build(rolldown.defineConfig({
1266
1827
  input: entryPath,
1267
1828
  output: {
@@ -1276,9 +1837,11 @@ async function bundleSingleJob(job, allJobs, outputDir, tsconfig) {
1276
1837
  name: "workflow-transform",
1277
1838
  transform: {
1278
1839
  filter: { id: { include: [/\.ts$/, /\.js$/] } },
1279
- handler(code) {
1280
- if (!code.includes("createWorkflowJob")) return null;
1281
- return { code: transformWorkflowSource(code, job.name, job.exportName, otherJobExportNames) };
1840
+ handler(code, id) {
1841
+ if (!code.includes("createWorkflowJob") && !code.includes("createWorkflow") && !code.includes(".trigger(")) return null;
1842
+ let transformed = transformWorkflowSource(code, job.name, job.exportName, otherJobExportNames, allJobsMap);
1843
+ if (triggerContext && transformed.includes(".trigger(")) transformed = transformFunctionTriggers(transformed, triggerContext.workflowNameMap, triggerContext.jobNameMap, triggerContext.workflowFileMap, id);
1844
+ return { code: transformed };
1282
1845
  }
1283
1846
  }
1284
1847
  }],
@@ -1290,18 +1853,6 @@ async function bundleSingleJob(job, allJobs, outputDir, tsconfig) {
1290
1853
  logLevel: "silent"
1291
1854
  }));
1292
1855
  }
1293
- /**
1294
- * Find the dependencies of a specific job
1295
- */
1296
- function findJobDeps(targetJobName, allJobs) {
1297
- return allJobs.find((j) => j.name === targetJobName)?.deps ?? [];
1298
- }
1299
- function generateJobsObject(jobNames) {
1300
- if (jobNames.length === 0) return "";
1301
- return jobNames.map((jobName) => {
1302
- return `"${jobName.replace(/[-\s]/g, "_")}": (args) => tailor.workflow.triggerJobFunction("${jobName}", args)`;
1303
- }).join(",\n ");
1304
- }
1305
1856
 
1306
1857
  //#endregion
1307
1858
  //#region src/parser/generator-config/index.ts
@@ -3866,12 +4417,12 @@ function protoIdPConfig(idpConfig) {
3866
4417
  value: {
3867
4418
  ...idpConfig.metadataURL !== void 0 ? { metadataUrl: idpConfig.metadataURL } : { rawMetadata: idpConfig.rawMetadata },
3868
4419
  ...idpConfig.spCertBase64 && { spCertBase64: {
3869
- vaultName: idpConfig.spCertBase64.VaultName,
3870
- secretKey: idpConfig.spCertBase64.SecretKey
4420
+ vaultName: idpConfig.spCertBase64.vaultName,
4421
+ secretKey: idpConfig.spCertBase64.secretKey
3871
4422
  } },
3872
4423
  ...idpConfig.spKeyBase64 && { spKeyBase64: {
3873
- vaultName: idpConfig.spKeyBase64.VaultName,
3874
- secretKey: idpConfig.spKeyBase64.SecretKey
4424
+ vaultName: idpConfig.spKeyBase64.vaultName,
4425
+ secretKey: idpConfig.spKeyBase64.secretKey
3875
4426
  } }
3876
4427
  }
3877
4428
  } }
@@ -3884,8 +4435,8 @@ function protoIdPConfig(idpConfig) {
3884
4435
  value: {
3885
4436
  clientIdKey: idpConfig.clientID,
3886
4437
  clientSecretKey: {
3887
- vaultName: idpConfig.clientSecret.VaultName,
3888
- secretKey: idpConfig.clientSecret.SecretKey
4438
+ vaultName: idpConfig.clientSecret.vaultName,
4439
+ secretKey: idpConfig.clientSecret.secretKey
3889
4440
  },
3890
4441
  providerUrl: idpConfig.providerURL,
3891
4442
  issuerUrl: idpConfig.issuerURL,
@@ -4321,8 +4872,8 @@ function protoSCIMConfig(scimConfig) {
4321
4872
  authorizationConfig: {
4322
4873
  case: "bearerSecret",
4323
4874
  value: {
4324
- vaultName: scimConfig.authorization.bearerSecret?.VaultName,
4325
- secretKey: scimConfig.authorization.bearerSecret?.SecretKey
4875
+ vaultName: scimConfig.authorization.bearerSecret?.vaultName,
4876
+ secretKey: scimConfig.authorization.bearerSecret?.secretKey
4326
4877
  }
4327
4878
  }
4328
4879
  };
@@ -4709,10 +5260,7 @@ function protoExecutor(appName, executor, env) {
4709
5260
  appName: target.appName ?? appName,
4710
5261
  query: target.query,
4711
5262
  variables: target.variables ? { expr: `(${target.variables.toString()})(args)` } : void 0,
4712
- invoker: target.invoker ? {
4713
- namespace: target.invoker.authName,
4714
- machineUserName: target.invoker.machineUser
4715
- } : void 0
5263
+ invoker: target.authInvoker ?? void 0
4716
5264
  }
4717
5265
  } };
4718
5266
  break;
@@ -4728,10 +5276,7 @@ function protoExecutor(appName, executor, env) {
4728
5276
  name: `${executor.name}__target`,
4729
5277
  script,
4730
5278
  variables: { expr: `({ ...args, appNamespace: args.namespaceName, env: ${JSON.stringify(env)} })` },
4731
- invoker: target.invoker ? {
4732
- namespace: target.invoker.authName,
4733
- machineUserName: target.invoker.machineUser
4734
- } : void 0
5279
+ invoker: target.authInvoker ?? void 0
4735
5280
  }
4736
5281
  } };
4737
5282
  break;
@@ -5685,59 +6230,56 @@ function protoGqlOperand(operand) {
5685
6230
  async function applyWorkflow(client, result, phase = "create-update") {
5686
6231
  const { changeSet } = result;
5687
6232
  if (phase === "create-update") {
5688
- await Promise.all(changeSet.creates.map(async (create) => {
5689
- const jobFunctions = {};
5690
- for (const [jobName, script] of create.scripts.entries()) {
5691
- const response = await client.createWorkflowJobFunction({
5692
- workspaceId: create.workspaceId,
5693
- jobFunctionName: jobName,
5694
- script
5695
- });
5696
- if (response.jobFunction) jobFunctions[jobName] = response.jobFunction.version;
5697
- }
6233
+ const jobFunctionVersions = await registerJobFunctions(client, changeSet);
6234
+ await Promise.all([...changeSet.creates.map(async (create) => {
5698
6235
  await client.createWorkflow({
5699
6236
  workspaceId: create.workspaceId,
5700
6237
  workflowName: create.workflow.name,
5701
6238
  mainJobFunctionName: create.workflow.mainJob.name,
5702
- jobFunctions
6239
+ jobFunctions: jobFunctionVersions
5703
6240
  });
5704
6241
  await client.setMetadata(create.metaRequest);
5705
- }));
5706
- await Promise.all(changeSet.updates.map(async (update) => {
5707
- const jobFunctions = {};
5708
- for (const [jobName, script] of update.scripts.entries()) {
5709
- const response = await client.updateWorkflowJobFunction({
5710
- workspaceId: update.workspaceId,
5711
- jobFunctionName: jobName,
5712
- script
5713
- });
5714
- if (response.jobFunction) jobFunctions[jobName] = response.jobFunction.version;
5715
- }
6242
+ }), ...changeSet.updates.map(async (update) => {
5716
6243
  await client.updateWorkflow({
5717
6244
  workspaceId: update.workspaceId,
5718
6245
  workflowName: update.workflow.name,
5719
6246
  mainJobFunctionName: update.workflow.mainJob.name,
5720
- jobFunctions
6247
+ jobFunctions: jobFunctionVersions
5721
6248
  });
5722
6249
  await client.setMetadata(update.metaRequest);
5723
- }));
6250
+ })]);
5724
6251
  } else if (phase === "delete") await Promise.all(changeSet.deletes.map((del) => client.deleteWorkflow({
5725
6252
  workspaceId: del.workspaceId,
5726
6253
  workflowId: del.workflowId
5727
6254
  })));
5728
6255
  }
5729
6256
  /**
5730
- * Recursively collect all job names from a workflow's mainJob and its dependencies
6257
+ * Register all job functions once, returns a map of job name to version.
6258
+ * Uses update for existing workflows, create for new workflows.
5731
6259
  */
5732
- function collectJobNamesFromWorkflow(workflow) {
5733
- const jobNames = /* @__PURE__ */ new Set();
5734
- const collectFromJob = (job) => {
5735
- if (!job || jobNames.has(job.name)) return;
5736
- jobNames.add(job.name);
5737
- if (job.deps && Array.isArray(job.deps)) for (const dep of job.deps) collectFromJob(dep);
5738
- };
5739
- collectFromJob(workflow.mainJob);
5740
- return jobNames;
6260
+ async function registerJobFunctions(client, changeSet) {
6261
+ const jobFunctionVersions = {};
6262
+ const firstWorkflow = changeSet.creates[0] || changeSet.updates[0];
6263
+ if (!firstWorkflow) return jobFunctionVersions;
6264
+ const { workspaceId, scripts } = firstWorkflow;
6265
+ const hasExistingWorkflows = changeSet.updates.length > 0;
6266
+ const results = await Promise.all(Array.from(scripts.entries()).map(async ([jobName, script]) => {
6267
+ const response = hasExistingWorkflows ? await client.updateWorkflowJobFunction({
6268
+ workspaceId,
6269
+ jobFunctionName: jobName,
6270
+ script
6271
+ }) : await client.createWorkflowJobFunction({
6272
+ workspaceId,
6273
+ jobFunctionName: jobName,
6274
+ script
6275
+ });
6276
+ return {
6277
+ jobName,
6278
+ version: response.jobFunction?.version
6279
+ };
6280
+ }));
6281
+ for (const { jobName, version } of results) if (version) jobFunctionVersions[jobName] = version;
6282
+ return jobFunctionVersions;
5741
6283
  }
5742
6284
  function trn(workspaceId, name) {
5743
6285
  return `trn:v1:workspace:${workspaceId}:workflow:${name}`;
@@ -5767,13 +6309,6 @@ async function planWorkflow(client, workspaceId, appName, workflows) {
5767
6309
  }));
5768
6310
  const allScripts = await loadWorkflowScripts();
5769
6311
  for (const workflow of Object.values(workflows)) {
5770
- const requiredJobNames = collectJobNamesFromWorkflow(workflow);
5771
- const scripts = /* @__PURE__ */ new Map();
5772
- for (const jobName of requiredJobNames) {
5773
- const script = allScripts.get(jobName);
5774
- if (script) scripts.set(jobName, script);
5775
- else console.warn(`Warning: Script for job "${jobName}" not found in workflow "${workflow.name}"`);
5776
- }
5777
6312
  const existing = existingWorkflows[workflow.name];
5778
6313
  const metaRequest = await buildMetaRequest(trn(workspaceId, workflow.name), appName);
5779
6314
  if (existing) {
@@ -5790,7 +6325,7 @@ async function planWorkflow(client, workspaceId, appName, workflows) {
5790
6325
  name: workflow.name,
5791
6326
  workspaceId,
5792
6327
  workflow,
5793
- scripts,
6328
+ scripts: allScripts,
5794
6329
  metaRequest
5795
6330
  });
5796
6331
  delete existingWorkflows[workflow.name];
@@ -5798,7 +6333,7 @@ async function planWorkflow(client, workspaceId, appName, workflows) {
5798
6333
  name: workflow.name,
5799
6334
  workspaceId,
5800
6335
  workflow,
5801
- scripts,
6336
+ scripts: allScripts,
5802
6337
  metaRequest
5803
6338
  });
5804
6339
  }
@@ -5824,7 +6359,7 @@ async function loadWorkflowScripts() {
5824
6359
  const jobsDir = path.join(getDistDir(), "workflow-jobs");
5825
6360
  if (!fs.existsSync(jobsDir)) return scripts;
5826
6361
  const files = fs.readdirSync(jobsDir);
5827
- for (const file of files) if (file.endsWith(".js") && !file.endsWith(".base.js") && !file.endsWith(".transformed.js") && !file.endsWith(".map")) {
6362
+ for (const file of files) if (/^[^.]+\.js$/.test(file)) {
5828
6363
  const jobName = file.replace(/\.js$/, "");
5829
6364
  const scriptPath = path.join(jobsDir, file);
5830
6365
  const script = fs.readFileSync(scriptPath, "utf-8");
@@ -5844,9 +6379,13 @@ async function apply(options) {
5844
6379
  const application = defineApplication(config);
5845
6380
  let workflowResult;
5846
6381
  if (application.workflowConfig) workflowResult = await loadAndCollectJobs(application.workflowConfig);
5847
- for (const app$1 of application.applications) for (const pipeline$1 of app$1.resolverServices) await buildPipeline(pipeline$1.namespace, pipeline$1.config);
5848
- if (application.executorService) await buildExecutor(application.executorService.config);
5849
- if (workflowResult && workflowResult.jobs.length > 0) await buildWorkflow(workflowResult.jobs);
6382
+ const triggerContext = await buildTriggerContext(application.workflowConfig);
6383
+ for (const app$1 of application.applications) for (const pipeline$1 of app$1.resolverServices) await buildPipeline(pipeline$1.namespace, pipeline$1.config, triggerContext);
6384
+ if (application.executorService) await buildExecutor(application.executorService.config, triggerContext);
6385
+ if (workflowResult && workflowResult.jobs.length > 0) {
6386
+ const mainJobNames = workflowResult.workflowSources.map((ws) => ws.workflow.mainJob.name);
6387
+ await buildWorkflow(workflowResult.jobs, mainJobNames, application.env, triggerContext);
6388
+ }
5850
6389
  if (buildOnly) return;
5851
6390
  const accessToken = await loadAccessToken({
5852
6391
  useProfile: true,
@@ -5945,14 +6484,14 @@ async function apply(options) {
5945
6484
  await applyTailorDB(client, tailorDB, "delete");
5946
6485
  console.log("Successfully applied changes.");
5947
6486
  }
5948
- async function buildPipeline(namespace, config) {
5949
- await bundleResolvers(namespace, config);
6487
+ async function buildPipeline(namespace, config, triggerContext) {
6488
+ await bundleResolvers(namespace, config, triggerContext);
5950
6489
  }
5951
- async function buildExecutor(config) {
5952
- await bundleExecutors(config);
6490
+ async function buildExecutor(config, triggerContext) {
6491
+ await bundleExecutors(config, triggerContext);
5953
6492
  }
5954
- async function buildWorkflow(collectedJobs) {
5955
- await bundleWorkflowJobs(collectedJobs);
6493
+ async function buildWorkflow(collectedJobs, mainJobNames, env, triggerContext) {
6494
+ await bundleWorkflowJobs(collectedJobs, mainJobNames, env, triggerContext);
5956
6495
  }
5957
6496
  const applyCommand = defineCommand({
5958
6497
  meta: {
@@ -7384,4 +7923,4 @@ const listCommand = defineCommand({
7384
7923
 
7385
7924
  //#endregion
7386
7925
  export { PATScope, apply, applyCommand, commonArgs, createCommand, deleteCommand, fetchAll, fetchLatestToken, fetchUserInfo, formatArgs, generate, generateCommand, generateUserTypes, getCommand, initOAuth2Client, initOperatorClient, listCommand, listCommand$1, listCommand$2, loadAccessToken, loadConfig, loadWorkspaceId, machineUserList, machineUserToken, oauth2ClientGet, oauth2ClientList, parseFormat, printWithFormat, readPackageJson, readPlatformConfig, remove, removeCommand, show, showCommand, tokenCommand, withCommonArgs, workspaceCreate, workspaceDelete, workspaceList, writePlatformConfig };
7387
- //# sourceMappingURL=list-4T6XN_zi.mjs.map
7926
+ //# sourceMappingURL=list-B8Uv3SPW.mjs.map