@tailor-platform/sdk 1.36.0 → 1.37.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.
Files changed (76) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/dist/application-ILhZq_oW.mjs +4 -0
  3. package/dist/{application-BwboBFcU.mjs → application-qRGMV8Tr.mjs} +50 -20
  4. package/dist/application-qRGMV8Tr.mjs.map +1 -0
  5. package/dist/{brand-0SscafcY.mjs → brand-D-d15jx3.mjs} +1 -1
  6. package/dist/{brand-0SscafcY.mjs.map → brand-D-d15jx3.mjs.map} +1 -1
  7. package/dist/cli/index.mjs +11 -11
  8. package/dist/cli/index.mjs.map +1 -1
  9. package/dist/cli/lib.d.mts +6 -6
  10. package/dist/cli/lib.mjs +7 -7
  11. package/dist/{client-B6icVEv4.mjs → client-424n_3T9.mjs} +1 -1
  12. package/dist/{client-CN15WgW2.mjs → client-DllDLYmZ.mjs} +4 -4
  13. package/dist/{client-CN15WgW2.mjs.map → client-DllDLYmZ.mjs.map} +1 -1
  14. package/dist/configure/index.d.mts +5 -5
  15. package/dist/configure/index.mjs +4 -14
  16. package/dist/configure/index.mjs.map +1 -1
  17. package/dist/{crash-report-CdxPj_SW.mjs → crash-report-CDQ2JvgR.mjs} +4 -4
  18. package/dist/{crash-report-CdxPj_SW.mjs.map → crash-report-CDQ2JvgR.mjs.map} +1 -1
  19. package/dist/{crash-report-CB1UtT3O.mjs → crash-report-aHnky_xH.mjs} +1 -1
  20. package/dist/{enum-constants-DI85-fPE.mjs → enum-constants-Dx82rSjf.mjs} +1 -1
  21. package/dist/{enum-constants-DI85-fPE.mjs.map → enum-constants-Dx82rSjf.mjs.map} +1 -1
  22. package/dist/{env-_ce3IYbl.d.mts → env-04IQXqsl.d.mts} +2 -2
  23. package/dist/{file-utils-C4rXlOVt.mjs → file-utils-DeWpvq3T.mjs} +1 -1
  24. package/dist/{file-utils-C4rXlOVt.mjs.map → file-utils-DeWpvq3T.mjs.map} +1 -1
  25. package/dist/{index-C7vIBAg8.d.mts → index-BUT18Kak.d.mts} +2 -2
  26. package/dist/{index-CYaunQeL.d.mts → index-BVJQLjyN.d.mts} +27 -12
  27. package/dist/{index-DDCyefuU.d.mts → index-C3kcXHXJ.d.mts} +2 -2
  28. package/dist/{index-DZN1QFLM.d.mts → index-CeS4FA9o.d.mts} +2 -2
  29. package/dist/{index-CxSLivW7.d.mts → index-DnIg_LfT.d.mts} +2 -2
  30. package/dist/{interceptor-f7slMkCC.mjs → interceptor-dSNiQq71.mjs} +1 -1
  31. package/dist/{interceptor-f7slMkCC.mjs.map → interceptor-dSNiQq71.mjs.map} +1 -1
  32. package/dist/{job-CPKYCk_e.mjs → job-DkAklmE4.mjs} +2 -2
  33. package/dist/{job-CPKYCk_e.mjs.map → job-DkAklmE4.mjs.map} +1 -1
  34. package/dist/{kysely-type-DtnNdHn3.mjs → kysely-type-CwtvQuxh.mjs} +1 -1
  35. package/dist/{kysely-type-DtnNdHn3.mjs.map → kysely-type-CwtvQuxh.mjs.map} +1 -1
  36. package/dist/{logger-qz-Y4sBV.mjs → logger-C8qBDCKO.mjs} +1 -1
  37. package/dist/{logger-qz-Y4sBV.mjs.map → logger-C8qBDCKO.mjs.map} +1 -1
  38. package/dist/package-json--6dmp6-h.mjs +4 -0
  39. package/dist/{package-json-CfUqjJaQ.mjs → package-json-BHViVisJ.mjs} +1 -1
  40. package/dist/{package-json-CfUqjJaQ.mjs.map → package-json-BHViVisJ.mjs.map} +1 -1
  41. package/dist/plugin/builtin/enum-constants/index.d.mts +1 -1
  42. package/dist/plugin/builtin/enum-constants/index.mjs +1 -1
  43. package/dist/plugin/builtin/file-utils/index.d.mts +1 -1
  44. package/dist/plugin/builtin/file-utils/index.mjs +1 -1
  45. package/dist/plugin/builtin/kysely-type/index.d.mts +1 -1
  46. package/dist/plugin/builtin/kysely-type/index.mjs +1 -1
  47. package/dist/plugin/builtin/seed/index.d.mts +1 -1
  48. package/dist/plugin/builtin/seed/index.mjs +1 -1
  49. package/dist/plugin/index.d.mts +2 -2
  50. package/dist/{plugin-CiPUxkyN.d.mts → plugin-D6P4g_2L.d.mts} +14 -9
  51. package/dist/{runtime-C7RRDaB3.mjs → runtime-D9ejnCm6.mjs} +566 -95
  52. package/dist/runtime-D9ejnCm6.mjs.map +1 -0
  53. package/dist/{schema-D27cW0Ca.mjs → schema-CnwUqPyM.mjs} +4 -361
  54. package/dist/schema-CnwUqPyM.mjs.map +1 -0
  55. package/dist/{seed-BZIFDG27.mjs → seed-DrbB1VXd.mjs} +1 -1
  56. package/dist/{seed-BZIFDG27.mjs.map → seed-DrbB1VXd.mjs.map} +1 -1
  57. package/dist/telemetry-4IOPW6wE.mjs +4 -0
  58. package/dist/{telemetry-CREcGK8y.mjs → telemetry-DwHuiNiR.mjs} +2 -2
  59. package/dist/{telemetry-CREcGK8y.mjs.map → telemetry-DwHuiNiR.mjs.map} +1 -1
  60. package/dist/types-B9ZMosul.mjs +372 -0
  61. package/dist/types-B9ZMosul.mjs.map +1 -0
  62. package/dist/types-C45jRrCM.mjs +4 -0
  63. package/dist/utils/test/index.d.mts +2 -2
  64. package/dist/utils/test/index.mjs +1 -1
  65. package/dist/{workflow.generated-8BeGQsVU.d.mts → workflow.generated-Bj_DVqGh.d.mts} +2 -2
  66. package/docs/services/auth.md +6 -5
  67. package/docs/services/executor.md +5 -12
  68. package/docs/services/resolver.md +6 -13
  69. package/docs/services/workflow.md +4 -3
  70. package/package.json +4 -4
  71. package/dist/application-BB5TqXWY.mjs +0 -4
  72. package/dist/application-BwboBFcU.mjs.map +0 -1
  73. package/dist/package-json-D5Km1jjt.mjs +0 -4
  74. package/dist/runtime-C7RRDaB3.mjs.map +0 -1
  75. package/dist/schema-D27cW0Ca.mjs.map +0 -1
  76. package/dist/telemetry-C508zIi1.mjs +0 -4
@@ -1,10 +1,10 @@
1
1
 
2
- import { A as ExecutorJobStatus, B as AuthSCIMAttribute_Type, C as TailorDBType_PermitAction, D as IdPPermissionPermit, E as IdPPermissionOperator, F as AuthIDPConfig_AuthType, G as UserProfileProviderConfig_UserProfileProviderType, H as AuthSCIMConfig_AuthorizationType, I as AuthInvokerSchema, J as Condition_Operator, K as GetApplicationSchemaHealthResponse_ApplicationSchemaHealthStatus, L as AuthOAuth2Client_ClientType, M as ExecutorTriggerType, N as AuthConnection_Type, O as FunctionExecution_Status, P as AuthHookPoint, Q as Subgraph_ServiceType, R as AuthOAuth2Client_GrantType, S as TailorDBType_Permission_Permit, T as IdPLang, V as AuthSCIMAttribute_Uniqueness, W as TenantProviderConfig_TenantProviderType, X as PageDirection, Y as FilterSchema, Z as ApplicationSchemaUpdateAttemptStatus, _ as WorkflowJobExecution_Status, a as fetchMachineUserToken, b as TailorDBGQLPermission_Permit, f as platformBaseUrl, g as WorkflowExecution_Status, h as WorkspacePlatformUserRole, i as fetchAll, j as ExecutorTargetType, m as userAgent, p as resolveStaticWebsiteUrls, q as ConditionSchema, u as initOperatorClient, v as TailorDBGQLPermission_Action, w as PipelineResolver_OperationType, x as TailorDBType_Permission_Operator, y as TailorDBGQLPermission_Operator, z as AuthSCIMAttribute_Mutability } from "./client-CN15WgW2.mjs";
3
- import { t as db } from "./schema-D27cW0Ca.mjs";
4
- import { i as symbols, n as logger, r as styles, t as CIPromptError } from "./logger-qz-Y4sBV.mjs";
5
- import { t as readPackageJson } from "./package-json-CfUqjJaQ.mjs";
6
- import { S as readPlatformConfig, T as writePlatformConfig, _ as hashFile, a as loadConfig, b as loadAccessToken, c as createExecutorService, d as TailorDBTypeSchema, f as stringifyFunction, g as getDistDir, h as createBundleCache, m as loadFilesWithIgnores, n as generatePluginFilesIfNeeded, p as tailorUserMap, r as loadApplication, t as defineApplication, u as OAuth2ClientSchema, x as loadWorkspaceId } from "./application-BwboBFcU.mjs";
7
- import { r as withSpan } from "./telemetry-CREcGK8y.mjs";
2
+ import { A as ExecutorJobStatus, B as AuthSCIMAttribute_Type, C as TailorDBType_PermitAction, D as IdPPermissionPermit, E as IdPPermissionOperator, F as AuthIDPConfig_AuthType, G as UserProfileProviderConfig_UserProfileProviderType, H as AuthSCIMConfig_AuthorizationType, I as AuthInvokerSchema, J as Condition_Operator, K as GetApplicationSchemaHealthResponse_ApplicationSchemaHealthStatus, L as AuthOAuth2Client_ClientType, M as ExecutorTriggerType, N as AuthConnection_Type, O as FunctionExecution_Status, P as AuthHookPoint, Q as Subgraph_ServiceType, R as AuthOAuth2Client_GrantType, S as TailorDBType_Permission_Permit, T as IdPLang, V as AuthSCIMAttribute_Uniqueness, W as TenantProviderConfig_TenantProviderType, X as PageDirection, Y as FilterSchema, Z as ApplicationSchemaUpdateAttemptStatus, _ as WorkflowJobExecution_Status, a as fetchMachineUserToken, b as TailorDBGQLPermission_Permit, f as platformBaseUrl, g as WorkflowExecution_Status, h as WorkspacePlatformUserRole, i as fetchAll, j as ExecutorTargetType, m as userAgent, p as resolveStaticWebsiteUrls, q as ConditionSchema, u as initOperatorClient, v as TailorDBGQLPermission_Action, w as PipelineResolver_OperationType, x as TailorDBType_Permission_Operator, y as TailorDBGQLPermission_Operator, z as AuthSCIMAttribute_Mutability } from "./client-DllDLYmZ.mjs";
3
+ import { t as db } from "./schema-CnwUqPyM.mjs";
4
+ import { i as symbols, n as logger, r as styles, t as CIPromptError } from "./logger-C8qBDCKO.mjs";
5
+ import { t as readPackageJson } from "./package-json-BHViVisJ.mjs";
6
+ import { S as readPlatformConfig, T as writePlatformConfig, _ as hashFile, a as loadConfig, b as loadAccessToken, c as createExecutorService, d as TailorDBTypeSchema, f as stringifyFunction, g as getDistDir, h as createBundleCache, m as loadFilesWithIgnores, n as generatePluginFilesIfNeeded, p as tailorUserMap, r as loadApplication, t as defineApplication, u as OAuth2ClientSchema, x as loadWorkspaceId } from "./application-qRGMV8Tr.mjs";
7
+ import { r as withSpan } from "./telemetry-DwHuiNiR.mjs";
8
8
  import { arg, createDefineCommand, defineCommand, runCommand } from "politty";
9
9
  import { z } from "zod";
10
10
  import * as fs$1 from "node:fs";
@@ -463,9 +463,10 @@ function extractAttributesFromConfig(config) {
463
463
  * @param attributeMap - Attribute map configuration
464
464
  * @param attributeList - Attribute list configuration
465
465
  * @param env - Environment configuration
466
+ * @param machineUserNames - Registered machine user names (used to narrow `authInvoker` strings)
466
467
  * @returns Generated type definition source
467
468
  */
468
- function generateTypeDefinition(attributeMap, attributeList, env) {
469
+ function generateTypeDefinition(attributeMap, attributeList, env, machineUserNames) {
469
470
  const mapFields = attributeMap ? Object.entries(attributeMap).map(([key, value]) => ` ${key}: ${value};`).join("\n") : "";
470
471
  const mapBody = !attributeMap || Object.keys(attributeMap).length === 0 ? "{}" : `{
471
472
  ${mapFields}
@@ -476,6 +477,11 @@ ${mapFields}
476
477
  const envFields = env ? Object.entries(env).map(([key, value]) => {
477
478
  return ` ${key}: ${typeof value === "string" ? `"${value}"` : String(value)};`;
478
479
  }).join("\n") : "";
480
+ const envBody = !env || Object.keys(env).length === 0 ? "{}" : `{
481
+ ${envFields}
482
+ }`;
483
+ const isValidIdentifier = (s) => /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(s);
484
+ const machineUserFields = machineUserNames?.length ? machineUserNames.map((name) => ` ${isValidIdentifier(name) ? name : JSON.stringify(name)}: true;`).join("\n") : "";
479
485
  return ml`
480
486
  // This file is auto-generated by @tailor-platform/sdk
481
487
  // Do not edit this file manually
@@ -484,8 +490,9 @@ ${mapFields}
484
490
  declare module "@tailor-platform/sdk" {
485
491
  interface AttributeMap ${mapBody}
486
492
  interface AttributeList ${listBody}
487
- interface Env ${!env || Object.keys(env).length === 0 ? "{}" : `{
488
- ${envFields}
493
+ interface Env ${envBody}
494
+ interface MachineUserNameRegistry ${!machineUserNames || machineUserNames.length === 0 ? "{}" : `{
495
+ ${machineUserFields}
489
496
  }`}
490
497
  }
491
498
 
@@ -496,6 +503,8 @@ export {};
496
503
  function collectAttributesFromConfig(config) {
497
504
  const auth = config.auth;
498
505
  if (!auth || typeof auth !== "object") return {};
506
+ const machineUsersObj = auth.machineUsers;
507
+ const machineUserNames = machineUsersObj && typeof machineUsersObj === "object" ? Object.keys(machineUsersObj) : void 0;
499
508
  const inferAttributeType = (field) => {
500
509
  const type = field?.type;
501
510
  const metadata = field?.metadata;
@@ -516,18 +525,22 @@ function collectAttributesFromConfig(config) {
516
525
  acc[key] = inferAttributeType(fields?.[key]);
517
526
  return acc;
518
527
  }, {}) : void 0,
519
- attributeList
528
+ attributeList,
529
+ machineUserNames
520
530
  };
521
531
  }
522
532
  if ("machineUserAttributes" in auth) {
523
533
  const machineUserAttributes = auth.machineUserAttributes;
524
- if (!machineUserAttributes) return {};
525
- return { attributeMap: Object.entries(machineUserAttributes).reduce((acc, [key, field]) => {
526
- acc[key] = inferAttributeType(field);
527
- return acc;
528
- }, {}) };
534
+ if (!machineUserAttributes) return { machineUserNames };
535
+ return {
536
+ attributeMap: Object.entries(machineUserAttributes).reduce((acc, [key, field]) => {
537
+ acc[key] = inferAttributeType(field);
538
+ return acc;
539
+ }, {}),
540
+ machineUserNames
541
+ };
529
542
  }
530
- return {};
543
+ return { machineUserNames };
531
544
  }
532
545
  /**
533
546
  * Resolve the output path for the generated type definition file.
@@ -545,13 +558,14 @@ function resolveTypeDefinitionPath(configPath) {
545
558
  async function generateUserTypes(options) {
546
559
  const { config, configPath } = options;
547
560
  try {
548
- const { attributeMap, attributeList } = extractAttributesFromConfig(config);
561
+ const { attributeMap, attributeList, machineUserNames } = extractAttributesFromConfig(config);
549
562
  if (!attributeMap && !attributeList) logger.info("No attributes found in configuration", { mode: "plain" });
550
563
  if (attributeMap) logger.debug(`Extracted AttributeMap: ${JSON.stringify(attributeMap)}`);
551
564
  if (attributeList) logger.debug(`Extracted AttributeList: ${JSON.stringify(attributeList)}`);
565
+ if (machineUserNames?.length) logger.debug(`Extracted MachineUserNames: ${JSON.stringify(machineUserNames)}`);
552
566
  const env = config.env;
553
567
  if (env) logger.debug(`Extracted Env: ${JSON.stringify(env)}`);
554
- const typeDefContent = generateTypeDefinition(attributeMap, attributeList, env);
568
+ const typeDefContent = generateTypeDefinition(attributeMap, attributeList, env, machineUserNames);
555
569
  const outputPath = resolveTypeDefinitionPath(configPath);
556
570
  fs$1.mkdirSync(path.dirname(outputPath), { recursive: true });
557
571
  fs$1.writeFileSync(outputPath, typeDefContent);
@@ -945,7 +959,6 @@ function formatPlanSummary(summary) {
945
959
  `${summary.delete} to delete`
946
960
  ];
947
961
  if (summary.replace > 0) parts.push(`${summary.replace} to replace`);
948
- parts.push(`${summary.unchanged} unchanged`);
949
962
  return `Plan: ${parts.join(", ")}`;
950
963
  }
951
964
 
@@ -1133,13 +1146,9 @@ async function planApplication(context) {
1133
1146
  applicationName: application.name
1134
1147
  }
1135
1148
  });
1136
- changeSet.print();
1137
- return changeSet;
1138
- }
1139
- if (application.subgraphs.length === 0) {
1140
- changeSet.print();
1141
1149
  return changeSet;
1142
1150
  }
1151
+ if (application.subgraphs.length === 0) return changeSet;
1143
1152
  let authNamespace;
1144
1153
  let authIdpConfigName;
1145
1154
  if (application.authService && application.authService.config) {
@@ -1191,7 +1200,6 @@ async function planApplication(context) {
1191
1200
  request,
1192
1201
  metaRequest
1193
1202
  });
1194
- changeSet.print();
1195
1203
  return changeSet;
1196
1204
  }
1197
1205
  function protoSubgraph(subgraph) {
@@ -1501,6 +1509,10 @@ function computeContentHash(content) {
1501
1509
  function functionRegistryTrn$1(workspaceId, name) {
1502
1510
  return `trn:v1:workspace:${workspaceId}:function_registry:${name}`;
1503
1511
  }
1512
+ const RESOLVER_PREFIX = "resolver--";
1513
+ const EXECUTOR_PREFIX = "executor--";
1514
+ const WORKFLOW_PREFIX = "workflow--";
1515
+ const AUTH_HOOK_PREFIX = "auth-hook--";
1504
1516
  /**
1505
1517
  * Build a function registry name for a resolver.
1506
1518
  * @param namespace - Resolver namespace
@@ -1508,7 +1520,7 @@ function functionRegistryTrn$1(workspaceId, name) {
1508
1520
  * @returns Function registry name
1509
1521
  */
1510
1522
  function resolverFunctionName(namespace, resolverName) {
1511
- return `resolver--${namespace}--${resolverName}`;
1523
+ return `${RESOLVER_PREFIX}${namespace}--${resolverName}`;
1512
1524
  }
1513
1525
  /**
1514
1526
  * Build a function registry name for an executor.
@@ -1516,7 +1528,7 @@ function resolverFunctionName(namespace, resolverName) {
1516
1528
  * @returns Function registry name
1517
1529
  */
1518
1530
  function executorFunctionName(executorName) {
1519
- return `executor--${executorName}`;
1531
+ return `${EXECUTOR_PREFIX}${executorName}`;
1520
1532
  }
1521
1533
  /**
1522
1534
  * Build a function registry name for a workflow job.
@@ -1524,7 +1536,50 @@ function executorFunctionName(executorName) {
1524
1536
  * @returns Function registry name
1525
1537
  */
1526
1538
  function workflowJobFunctionName(jobName) {
1527
- return `workflow--${jobName}`;
1539
+ return `${WORKFLOW_PREFIX}${jobName}`;
1540
+ }
1541
+ /**
1542
+ * Split function registry changes into grouped buckets by resource-name prefix.
1543
+ * @param changeSet - Function registry change set
1544
+ * @returns Grouped function registry changes by resource kind
1545
+ */
1546
+ function splitFunctionRegistryChanges(changeSet) {
1547
+ function partition(items) {
1548
+ const buckets = {
1549
+ workflowJob: [],
1550
+ resolver: [],
1551
+ executor: [],
1552
+ authHook: [],
1553
+ other: []
1554
+ };
1555
+ for (const item of items) if (item.name.startsWith("workflow--")) buckets.workflowJob.push(item);
1556
+ else if (item.name.startsWith("resolver--")) buckets.resolver.push(item);
1557
+ else if (item.name.startsWith("executor--")) buckets.executor.push(item);
1558
+ else if (item.name.startsWith("auth-hook--")) buckets.authHook.push(item);
1559
+ else buckets.other.push(item);
1560
+ return buckets;
1561
+ }
1562
+ const creates = partition(changeSet.creates);
1563
+ const updates = partition(changeSet.updates);
1564
+ const deletes = partition(changeSet.deletes);
1565
+ const replaces = partition(changeSet.replaces);
1566
+ const unchanged = partition(changeSet.unchanged);
1567
+ function collect(key) {
1568
+ return {
1569
+ creates: creates[key],
1570
+ updates: updates[key],
1571
+ deletes: deletes[key],
1572
+ replaces: replaces[key],
1573
+ unchanged: unchanged[key]
1574
+ };
1575
+ }
1576
+ return {
1577
+ workflowJobChanges: collect("workflowJob"),
1578
+ resolverFunctionChanges: collect("resolver"),
1579
+ executorFunctionChanges: collect("executor"),
1580
+ authHookFunctionChanges: collect("authHook"),
1581
+ otherChanges: collect("other")
1582
+ };
1528
1583
  }
1529
1584
  /**
1530
1585
  * Build a function registry name for an auth hook.
@@ -1687,9 +1742,13 @@ async function planFunctionRegistry(client, workspaceId, appName, entries) {
1687
1742
  workspaceId
1688
1743
  });
1689
1744
  }
1690
- changeSet.print();
1745
+ const { workflowJobChanges, resolverFunctionChanges, executorFunctionChanges, authHookFunctionChanges } = splitFunctionRegistryChanges(changeSet);
1691
1746
  return {
1692
1747
  changeSet,
1748
+ workflowJobChanges,
1749
+ resolverFunctionChanges,
1750
+ executorFunctionChanges,
1751
+ authHookFunctionChanges,
1693
1752
  conflicts,
1694
1753
  unmanaged,
1695
1754
  resourceOwners
@@ -1763,6 +1822,228 @@ async function applyFunctionRegistry(client, workspaceId, result, phase = "creat
1763
1822
  })));
1764
1823
  }
1765
1824
 
1825
+ //#endregion
1826
+ //#region src/cli/commands/apply/grouped-display.ts
1827
+ /**
1828
+ * Convert grouped function registry changes into mutable name sets.
1829
+ * @param changes - Grouped function registry changes
1830
+ * @returns Mutable name sets keyed by action
1831
+ */
1832
+ function createRelatedFunctionRegistryNameSets(changes) {
1833
+ return {
1834
+ creates: new Set(changes?.creates.map((item) => item.name) ?? []),
1835
+ updates: new Set(changes?.updates.map((item) => item.name) ?? []),
1836
+ deletes: new Set(changes?.deletes.map((item) => item.name) ?? []),
1837
+ replaces: new Set(changes?.replaces.map((item) => item.name) ?? [])
1838
+ };
1839
+ }
1840
+ const ACTION_SYMBOLS = {
1841
+ create: symbols.create,
1842
+ update: symbols.update,
1843
+ delete: symbols.delete,
1844
+ replace: symbols.replace
1845
+ };
1846
+ /**
1847
+ * Convert a plain change set into grouped display entries.
1848
+ * @param changeSet - Change set to convert
1849
+ * @param labels - Labels to attach to each entry
1850
+ * @param getNamespace - Optional callback to extract namespace from an item
1851
+ * @returns Display entries in CLI print order
1852
+ */
1853
+ function formatChangeSetEntries(changeSet, labels = [], getNamespace) {
1854
+ function toEntry(action, item) {
1855
+ return {
1856
+ action,
1857
+ symbol: ACTION_SYMBOLS[action],
1858
+ name: item.name,
1859
+ labels: [...labels],
1860
+ namespace: getNamespace?.(item)
1861
+ };
1862
+ }
1863
+ return [
1864
+ ...changeSet.creates.map((item) => toEntry("create", item)),
1865
+ ...changeSet.deletes.map((item) => toEntry("delete", item)),
1866
+ ...changeSet.updates.map((item) => toEntry("update", item)),
1867
+ ...changeSet.replaces.map((item) => toEntry("replace", item))
1868
+ ];
1869
+ }
1870
+ function formatGroupedDisplayLine(entry) {
1871
+ return entry.labels.length > 0 ? `${entry.symbol} ${entry.name} (${entry.labels.join(", ")})` : `${entry.symbol} ${entry.name}`;
1872
+ }
1873
+ function parseFunctionRegistryName(name) {
1874
+ if (name.startsWith("resolver--")) {
1875
+ const [, namespace, resolverName] = name.split("--");
1876
+ if (namespace && resolverName) return {
1877
+ displayName: resolverName,
1878
+ namespace
1879
+ };
1880
+ }
1881
+ if (name.startsWith("workflow--")) return { displayName: name.slice(WORKFLOW_PREFIX.length) };
1882
+ if (name.startsWith("executor--")) return { displayName: name.slice(EXECUTOR_PREFIX.length) };
1883
+ if (name.startsWith("auth-hook--")) {
1884
+ const [, namespace, hookPoint] = name.split("--");
1885
+ if (namespace && hookPoint) return {
1886
+ displayName: hookPoint,
1887
+ namespace
1888
+ };
1889
+ }
1890
+ return { displayName: name };
1891
+ }
1892
+ /**
1893
+ * Build function-registry-only entries that were not grouped with a parent resource.
1894
+ * @param names - Related function registry names keyed by action
1895
+ * @param consumed - Function registry names already grouped with parent resources
1896
+ * @returns Display entries for ungrouped function registry changes
1897
+ */
1898
+ function buildRemainingFunctionRegistryEntries(names, consumed = createRelatedFunctionRegistryNameSets()) {
1899
+ return [
1900
+ [
1901
+ "create",
1902
+ names.creates,
1903
+ consumed.creates
1904
+ ],
1905
+ [
1906
+ "delete",
1907
+ names.deletes,
1908
+ consumed.deletes
1909
+ ],
1910
+ [
1911
+ "update",
1912
+ names.updates,
1913
+ consumed.updates
1914
+ ],
1915
+ [
1916
+ "replace",
1917
+ names.replaces,
1918
+ consumed.replaces
1919
+ ]
1920
+ ].flatMap(([action, nameSet, consumedSet]) => [...nameSet].filter((name) => !consumedSet.has(name)).map((name) => {
1921
+ const { displayName, namespace } = parseFunctionRegistryName(name);
1922
+ return {
1923
+ action,
1924
+ symbol: ACTION_SYMBOLS[action],
1925
+ name: displayName,
1926
+ labels: ["function"],
1927
+ namespace
1928
+ };
1929
+ }));
1930
+ }
1931
+ /**
1932
+ * Format change set entries with function registry grouping.
1933
+ *
1934
+ * For each item in creates/updates/deletes, calls `getFunctionRegistryNames` to
1935
+ * derive zero or more function registry names. When a matching function registry
1936
+ * change exists for the same action, the item is displayed with both the resource
1937
+ * label and "functionRegistry". Ungrouped function registry changes are appended.
1938
+ * @param resourceLabel - Label for the resource kind (e.g. "executor", "resolver")
1939
+ * @param changeSet - Resource change set with creates/updates/deletes/replaces
1940
+ * @param changeSet.creates - Created resources
1941
+ * @param changeSet.updates - Updated resources
1942
+ * @param changeSet.deletes - Deleted resources
1943
+ * @param changeSet.replaces - Replaced resources
1944
+ * @param functionRegistryChanges - Related function registry changes
1945
+ * @param getFunctionRegistryNames - Derives function registry names from a resource item
1946
+ * @param options - Optional display callbacks
1947
+ * @param options.getNamespace - Extract namespace from an item for nested display
1948
+ * @param options.getDisplayName - Override display name for an item
1949
+ * @returns Display entries for CLI output
1950
+ */
1951
+ function formatChangeEntriesWithFunctionRegistry(resourceLabel, changeSet, functionRegistryChanges, getFunctionRegistryNames, options) {
1952
+ const { getNamespace, getDisplayName } = options ?? {};
1953
+ const functionNames = createRelatedFunctionRegistryNameSets(functionRegistryChanges);
1954
+ const consumed = createRelatedFunctionRegistryNameSets();
1955
+ function processItems(items, action, fnNameSet, consumedSet) {
1956
+ return items.map((item) => {
1957
+ const names = getFunctionRegistryNames(item, action);
1958
+ const hasMatch = names.some((name) => fnNameSet.has(name));
1959
+ if (hasMatch) {
1960
+ for (const name of names) if (fnNameSet.has(name)) consumedSet.add(name);
1961
+ }
1962
+ return {
1963
+ action,
1964
+ symbol: ACTION_SYMBOLS[action],
1965
+ name: getDisplayName?.(item) ?? item.name,
1966
+ labels: hasMatch ? [resourceLabel, "function"] : [resourceLabel],
1967
+ namespace: getNamespace?.(item)
1968
+ };
1969
+ });
1970
+ }
1971
+ return [
1972
+ ...processItems(changeSet.creates, "create", functionNames.creates, consumed.creates),
1973
+ ...processItems(changeSet.deletes, "delete", functionNames.deletes, consumed.deletes),
1974
+ ...processItems(changeSet.updates, "update", functionNames.updates, consumed.updates),
1975
+ ...changeSet.replaces.map((item) => ({
1976
+ action: "replace",
1977
+ symbol: ACTION_SYMBOLS["replace"],
1978
+ name: getDisplayName?.(item) ?? item.name,
1979
+ labels: [resourceLabel],
1980
+ namespace: getNamespace?.(item)
1981
+ })),
1982
+ ...buildRemainingFunctionRegistryEntries(functionNames, consumed)
1983
+ ];
1984
+ }
1985
+ /**
1986
+ * Extract service-level actions from a change set for namespace header display.
1987
+ * @param changeSet - Service change set
1988
+ * @returns Array of namespace actions
1989
+ */
1990
+ function extractServiceActions(changeSet) {
1991
+ return [
1992
+ ...changeSet.creates.map((item) => ({
1993
+ name: item.name,
1994
+ action: "create"
1995
+ })),
1996
+ ...changeSet.deletes.map((item) => ({
1997
+ name: item.name,
1998
+ action: "delete"
1999
+ })),
2000
+ ...changeSet.updates.map((item) => ({
2001
+ name: item.name,
2002
+ action: "update"
2003
+ })),
2004
+ ...changeSet.replaces.map((item) => ({
2005
+ name: item.name,
2006
+ action: "replace"
2007
+ }))
2008
+ ];
2009
+ }
2010
+ /**
2011
+ * Print a titled section of grouped display entries, nesting by namespace.
2012
+ * Service-level changes are shown as the namespace header symbol.
2013
+ * Services without child entries are shown as flat entries.
2014
+ * @param title - Section title
2015
+ * @param entries - Entries to print (should NOT include service entries)
2016
+ * @param serviceActions - Optional service-level actions to merge into namespace headers
2017
+ */
2018
+ function printGroupedDisplaySection(title, entries, serviceActions) {
2019
+ const serviceMap = /* @__PURE__ */ new Map();
2020
+ if (serviceActions) for (const sa of serviceActions) serviceMap.set(sa.name, sa.action);
2021
+ if (entries.length === 0 && serviceMap.size === 0) return;
2022
+ logger.log(styles.bold(`${title}:`));
2023
+ const namespaceOrder = [];
2024
+ const byNamespace = /* @__PURE__ */ new Map();
2025
+ for (const entry of entries) {
2026
+ const ns = entry.namespace;
2027
+ if (!byNamespace.has(ns)) {
2028
+ namespaceOrder.push(ns);
2029
+ byNamespace.set(ns, []);
2030
+ }
2031
+ byNamespace.get(ns).push(entry);
2032
+ }
2033
+ const printedServices = /* @__PURE__ */ new Set();
2034
+ for (const ns of namespaceOrder) {
2035
+ const group = byNamespace.get(ns);
2036
+ if (ns) {
2037
+ const svcAction = serviceMap.get(ns);
2038
+ const prefix = svcAction ? `${ACTION_SYMBOLS[svcAction]} ` : "";
2039
+ logger.log(` ${prefix}${styles.bold(`${ns}:`)}`);
2040
+ printedServices.add(ns);
2041
+ for (const entry of group) logger.log(` ${formatGroupedDisplayLine(entry)}`);
2042
+ } else for (const entry of group) logger.log(` ${formatGroupedDisplayLine(entry)}`);
2043
+ }
2044
+ for (const [name, action] of serviceMap) if (!printedServices.has(name)) logger.log(` ${ACTION_SYMBOLS[action]} ${name}`);
2045
+ }
2046
+
1766
2047
  //#endregion
1767
2048
  //#region src/parser/service/idp/permission.ts
1768
2049
  const operatorMap = {
@@ -1949,13 +2230,10 @@ async function planIdP(context) {
1949
2230
  const { client, workspaceId, application, forRemoval, forceApplyAll = false } = context;
1950
2231
  const idps = forRemoval ? [] : application.idpServices;
1951
2232
  const { changeSet: serviceChangeSet, conflicts, unmanaged, resourceOwners } = await planServices$3(client, workspaceId, application.name, idps);
1952
- const clientChangeSet = await planClients(client, workspaceId, idps, serviceChangeSet.deletes.map((del) => del.name), forceApplyAll);
1953
- serviceChangeSet.print();
1954
- clientChangeSet.print();
1955
2233
  return {
1956
2234
  changeSet: {
1957
2235
  service: serviceChangeSet,
1958
- client: clientChangeSet
2236
+ client: await planClients(client, workspaceId, idps, serviceChangeSet.deletes.map((del) => del.name), forceApplyAll)
1959
2237
  },
1960
2238
  conflicts,
1961
2239
  unmanaged,
@@ -2396,16 +2674,6 @@ async function planAuth(context) {
2396
2674
  planSCIMResources(client, workspaceId, auths, deletedServices),
2397
2675
  planAuthConnections(client, workspaceId, application.name, auths)
2398
2676
  ]);
2399
- serviceChangeSet.print();
2400
- idpConfigChangeSet.print();
2401
- userProfileConfigChangeSet.print();
2402
- tenantConfigChangeSet.print();
2403
- machineUserChangeSet.print();
2404
- authHookChangeSet.print();
2405
- oauth2ClientChangeSet.print();
2406
- scimChangeSet.print();
2407
- scimResourceChangeSet.print();
2408
- connectionResult.changeSet.print();
2409
2677
  return {
2410
2678
  changeSet: {
2411
2679
  service: serviceChangeSet,
@@ -3371,6 +3639,21 @@ function areAuthHooksEqual(existing, desired) {
3371
3639
  } : void 0
3372
3640
  });
3373
3641
  }
3642
+ /**
3643
+ * Format auth hook changes for grouped dry-run display.
3644
+ * @param changeSet - Auth hook changes
3645
+ * @param functionRegistryAuthHookChanges - Related function registry changes for auth hooks
3646
+ * @returns Display entries for auth hook output
3647
+ */
3648
+ function formatAuthHookChangeEntries(changeSet, functionRegistryAuthHookChanges) {
3649
+ return formatChangeEntriesWithFunctionRegistry("authHook", changeSet, functionRegistryAuthHookChanges, (item) => {
3650
+ const [namespace, hookPoint] = item.name.split("/");
3651
+ return namespace && hookPoint ? [authHookFunctionName(namespace, hookPoint)] : [];
3652
+ }, {
3653
+ getNamespace: (item) => item.name.split("/")[0],
3654
+ getDisplayName: (item) => item.name.split("/")[1] ?? item.name
3655
+ });
3656
+ }
3374
3657
  async function planAuthHooks(client, workspaceId, auths, deletedServices, forceApplyAll = false) {
3375
3658
  const changeSet = createChangeSet("Auth hooks");
3376
3659
  for (const auth of auths) {
@@ -3606,6 +3889,32 @@ function buildResolverOperationHookExpr(env) {
3606
3889
  return `({ ...context.pipeline, input: context.args, user: ${tailorUserMap}, env: ${JSON.stringify(env)} });`;
3607
3890
  }
3608
3891
 
3892
+ //#endregion
3893
+ //#region src/cli/commands/apply/auth-invoker.ts
3894
+ /**
3895
+ * Normalize an authInvoker value to the object form required by the proto payload.
3896
+ *
3897
+ * Accepts either:
3898
+ * - `undefined` — returns undefined
3899
+ * - a plain string (machine user name) — expands to `{ namespace, machineUserName }` using `authNamespace`
3900
+ * - an object `{ namespace, machineUserName }` — returned as-is
3901
+ * @param authInvoker - String machine user name or object form
3902
+ * @param authNamespace - Auth service namespace (required when authInvoker is a string)
3903
+ * @param context - Contextual label used in error messages (e.g. `resolver "foo"`)
3904
+ * @returns Object form of auth invoker, or undefined
3905
+ */
3906
+ function normalizeAuthInvoker(authInvoker, authNamespace, context) {
3907
+ if (authInvoker === void 0) return void 0;
3908
+ if (typeof authInvoker === "string") {
3909
+ if (!authNamespace) throw new Error(`${context} uses a string authInvoker ("${authInvoker}"), but no Auth service is configured. Configure an Auth service or use the object form { namespace, machineUserName }.`);
3910
+ return {
3911
+ namespace: authNamespace,
3912
+ machineUserName: authInvoker
3913
+ };
3914
+ }
3915
+ return authInvoker;
3916
+ }
3917
+
3609
3918
  //#endregion
3610
3919
  //#region src/cli/commands/apply/executor.ts
3611
3920
  /**
@@ -3707,7 +4016,6 @@ async function planExecutor(context) {
3707
4016
  }
3708
4017
  });
3709
4018
  });
3710
- changeSet.print();
3711
4019
  return {
3712
4020
  changeSet,
3713
4021
  conflicts,
@@ -3715,6 +4023,31 @@ async function planExecutor(context) {
3715
4023
  resourceOwners
3716
4024
  };
3717
4025
  }
4026
+ function isFunctionBackedExecutor(executor) {
4027
+ return executor?.targetType === ExecutorTargetType.FUNCTION || executor?.targetType === ExecutorTargetType.JOB_FUNCTION;
4028
+ }
4029
+ /**
4030
+ * Build desired executor configs keyed by executor name from create/update changes.
4031
+ * @param changeSet - Executor create/update changes
4032
+ * @returns Executor configs keyed by name
4033
+ */
4034
+ function buildPlannedExecutorsByName(changeSet) {
4035
+ return Object.fromEntries([...changeSet.creates, ...changeSet.updates].map((item) => [item.name, item.request.executor]));
4036
+ }
4037
+ /**
4038
+ * Format executor changes for grouped dry-run display.
4039
+ * @param changeSet - Executor changes
4040
+ * @param executors - Desired executor configs keyed by name
4041
+ * @param functionRegistryExecutorChanges - Related function registry changes for executors
4042
+ * @returns Display entries for executor output
4043
+ */
4044
+ function formatExecutorChangeEntries(changeSet, executors, functionRegistryExecutorChanges) {
4045
+ return formatChangeEntriesWithFunctionRegistry("executor", changeSet, functionRegistryExecutorChanges, (item, action) => {
4046
+ if (action === "delete") return [executorFunctionName(item.name)];
4047
+ const executor = executors[item.name];
4048
+ return executor && isFunctionBackedExecutor(executor) ? [executorFunctionName(item.name)] : [];
4049
+ });
4050
+ }
3718
4051
  function normalizeComparableExecutor(executor) {
3719
4052
  const normalized = normalizeProtoConfig(executor) ?? {};
3720
4053
  const webhookHeaders = normalized.targetConfig?.config?.case === "webhook" ? [...normalized.targetConfig.config.value.headers ?? []].sort((left, right) => (left.key ?? "").localeCompare(right.key ?? "")) : void 0;
@@ -3862,6 +4195,8 @@ function protoExecutor(application, executor) {
3862
4195
  const target = executor.operation;
3863
4196
  let targetType;
3864
4197
  let targetConfig;
4198
+ const authNamespace = application.authService?.parsedConfig.name;
4199
+ const invokerContext = `Executor "${executor.name}"`;
3865
4200
  switch (target.kind) {
3866
4201
  case "webhook":
3867
4202
  targetType = ExecutorTargetType.WEBHOOK;
@@ -3899,7 +4234,7 @@ function protoExecutor(application, executor) {
3899
4234
  appName: target.appName ?? appName,
3900
4235
  query: target.query,
3901
4236
  variables: target.variables ? { expr: `(${stringifyFunction(target.variables)})(${argsExpr})` } : void 0,
3902
- invoker: target.authInvoker ?? void 0
4237
+ invoker: normalizeAuthInvoker(target.authInvoker, authNamespace, invokerContext)
3903
4238
  }
3904
4239
  } };
3905
4240
  break;
@@ -3913,7 +4248,7 @@ function protoExecutor(application, executor) {
3913
4248
  name: "operation",
3914
4249
  scriptRef: executorFunctionName(executor.name),
3915
4250
  variables: { expr: argsExpr },
3916
- invoker: target.authInvoker ?? void 0
4251
+ invoker: normalizeAuthInvoker(target.authInvoker, authNamespace, invokerContext)
3917
4252
  }
3918
4253
  } };
3919
4254
  break;
@@ -3924,7 +4259,7 @@ function protoExecutor(application, executor) {
3924
4259
  value: {
3925
4260
  workflowName: target.workflowName,
3926
4261
  variables: target.args ? typeof target.args === "function" ? { expr: `(${stringifyFunction(target.args)})(${argsExpr})` } : { expr: JSON.stringify(target.args) } : void 0,
3927
- invoker: target.authInvoker ?? void 0
4262
+ invoker: normalizeAuthInvoker(target.authInvoker, authNamespace, invokerContext)
3928
4263
  }
3929
4264
  } };
3930
4265
  break;
@@ -4016,9 +4351,7 @@ async function planPipeline(context) {
4016
4351
  }
4017
4352
  const executors = forRemoval ? [] : Object.values(await application.executorService?.loadExecutors() ?? {});
4018
4353
  const { changeSet: serviceChangeSet, conflicts, unmanaged, resourceOwners } = await planServices$1(client, workspaceId, application.name, pipelines);
4019
- const resolverChangeSet = await planResolvers(client, workspaceId, pipelines, executors, serviceChangeSet.deletes.map((del) => del.name), application.env, forceApplyAll);
4020
- serviceChangeSet.print();
4021
- resolverChangeSet.print();
4354
+ const { changeSet: resolverChangeSet } = await planResolvers(client, workspaceId, pipelines, executors, serviceChangeSet.deletes.map((del) => del.name), application.env, application.authService?.config.name, forceApplyAll);
4022
4355
  return {
4023
4356
  changeSet: {
4024
4357
  service: serviceChangeSet,
@@ -4110,7 +4443,7 @@ async function planServices$1(client, workspaceId, appName, pipelines) {
4110
4443
  resourceOwners
4111
4444
  };
4112
4445
  }
4113
- async function planResolvers(client, workspaceId, pipelines, executors, deletedServices, env, forceApplyAll = false) {
4446
+ async function planResolvers(client, workspaceId, pipelines, executors, deletedServices, env, authNamespace, forceApplyAll = false) {
4114
4447
  const changeSet = createChangeSet("Pipeline resolvers");
4115
4448
  const fetchResolvers = (namespaceName) => {
4116
4449
  return fetchAll(async (pageToken, maxPageSize) => {
@@ -4135,7 +4468,7 @@ async function planResolvers(client, workspaceId, pipelines, executors, deletedS
4135
4468
  const existingResolvers = await fetchResolvers(pipeline.namespace);
4136
4469
  const existingResolversMap = new Map(existingResolvers.map((resolver) => [resolver.name, resolver]));
4137
4470
  for (const resolver of Object.values(pipeline.resolvers)) {
4138
- const desiredResolver = processResolver(pipeline.namespace, resolver, executorUsedResolvers, env);
4471
+ const desiredResolver = processResolver(pipeline.namespace, resolver, executorUsedResolvers, env, authNamespace);
4139
4472
  if (existingResolversMap.get(resolver.name)) {
4140
4473
  const { pipelineResolver: existingResolverDetail } = await client.getPipelineResolver({
4141
4474
  workspaceId,
@@ -4182,7 +4515,19 @@ async function planResolvers(client, workspaceId, pipelines, executors, deletedS
4182
4515
  }
4183
4516
  });
4184
4517
  });
4185
- return changeSet;
4518
+ return { changeSet };
4519
+ }
4520
+ /**
4521
+ * Format resolver changes for grouped dry-run display.
4522
+ * @param changeSet - Resolver changes
4523
+ * @param resolverFunctionChanges - Related function registry changes for resolvers
4524
+ * @returns Display entries for resolver output
4525
+ */
4526
+ function formatResolverChangeEntries(changeSet, resolverFunctionChanges) {
4527
+ return formatChangeEntriesWithFunctionRegistry("resolver", changeSet, resolverFunctionChanges, (item) => {
4528
+ const namespace = item.request.namespaceName;
4529
+ return namespace ? [resolverFunctionName(namespace, item.name)] : [];
4530
+ }, { getNamespace: (item) => item.request.namespaceName });
4186
4531
  }
4187
4532
  function normalizeComparableResolver(resolver) {
4188
4533
  const normalized = normalizeProtoConfig(resolver) ?? {};
@@ -4237,7 +4582,7 @@ function normalizeComparableType(type) {
4237
4582
  fields: (type.fields ?? []).map((field) => normalizeComparableField(field))
4238
4583
  };
4239
4584
  }
4240
- function processResolver(namespace, resolver, executorUsedResolvers, env) {
4585
+ function processResolver(namespace, resolver, executorUsedResolvers, env, authNamespace) {
4241
4586
  const pipelines = [{
4242
4587
  name: "body",
4243
4588
  operationName: "body",
@@ -4246,7 +4591,7 @@ function processResolver(namespace, resolver, executorUsedResolvers, env) {
4246
4591
  operationSourceRef: resolverFunctionName(namespace, resolver.name),
4247
4592
  operationHook: { expr: buildResolverOperationHookExpr(env) },
4248
4593
  postScript: `args.body`,
4249
- invoker: resolver.authInvoker
4594
+ invoker: normalizeAuthInvoker(resolver.authInvoker, authNamespace, `Resolver "${resolver.name}"`)
4250
4595
  }];
4251
4596
  const typeBaseName = inflection.camelize(resolver.name);
4252
4597
  const inputs = resolver.input ? protoFields(resolver.input, `${typeBaseName}Input`, true) : [];
@@ -4444,12 +4789,6 @@ async function planSecretManager(context) {
4444
4789
  });
4445
4790
  }
4446
4791
  }
4447
- vaultChangeSet.print();
4448
- secretChangeSet.print();
4449
- if (skippedSecrets.length > 0) {
4450
- logger.log(styles.bold("Secret Manager secrets (skipped - no value provided):"));
4451
- for (const name of skippedSecrets) logger.log(` ${styles.dim("○")} ${name}`);
4452
- }
4453
4792
  return {
4454
4793
  vaultChangeSet,
4455
4794
  secretChangeSet,
@@ -4650,7 +4989,6 @@ async function planStaticWebsite(context) {
4650
4989
  }
4651
4990
  });
4652
4991
  });
4653
- changeSet.print();
4654
4992
  return {
4655
4993
  changeSet,
4656
4994
  conflicts,
@@ -6771,9 +7109,6 @@ async function planTailorDB(context) {
6771
7109
  const { changeSet: serviceChangeSet, conflicts, unmanaged, resourceOwners } = await planServices(client, workspaceId, application.name, tailordbs);
6772
7110
  const deletedServices = serviceChangeSet.deletes.map((del) => del.name);
6773
7111
  const [typeChangeSet, gqlPermissionChangeSet] = await Promise.all([planTypes(client, workspaceId, tailordbs, executors, deletedServices, void 0, forceApplyAll), planGqlPermissions(client, workspaceId, tailordbs, deletedServices, forceApplyAll)]);
6774
- serviceChangeSet.print();
6775
- typeChangeSet.print();
6776
- gqlPermissionChangeSet.print();
6777
7112
  return {
6778
7113
  changeSet: {
6779
7114
  service: serviceChangeSet,
@@ -6791,6 +7126,42 @@ async function planTailorDB(context) {
6791
7126
  }
6792
7127
  };
6793
7128
  }
7129
+ function itemKey(item) {
7130
+ return `${item.request?.namespaceName ?? ""}/${item.name}`;
7131
+ }
7132
+ function collectTailorDBDisplayEntries(action, typeItems, gqlPermissionItems) {
7133
+ const typeKeys = new Set(typeItems.map(itemKey));
7134
+ const gqlPermissionKeys = new Set(gqlPermissionItems.map(itemKey));
7135
+ const typeEntries = typeItems.map((item) => ({
7136
+ action,
7137
+ symbol: ACTION_SYMBOLS[action],
7138
+ name: item.name,
7139
+ labels: gqlPermissionKeys.has(itemKey(item)) ? ["type", "gqlPermission"] : ["type"],
7140
+ namespace: item.request?.namespaceName
7141
+ }));
7142
+ const gqlPermissionOnlyEntries = gqlPermissionItems.filter((item) => !typeKeys.has(itemKey(item))).map((item) => ({
7143
+ action,
7144
+ symbol: ACTION_SYMBOLS[action],
7145
+ name: item.name,
7146
+ labels: ["gqlPermission"],
7147
+ namespace: item.request?.namespaceName
7148
+ }));
7149
+ return [...typeEntries, ...gqlPermissionOnlyEntries];
7150
+ }
7151
+ /**
7152
+ * Format TailorDB type and gqlPermission changes as grouped dry-run entries.
7153
+ * @param typeChangeSet - TailorDB type changes
7154
+ * @param gqlPermissionChangeSet - TailorDB gqlPermission changes
7155
+ * @returns Display entries for TailorDB resource output
7156
+ */
7157
+ function formatTailorDBResourceChangeEntries(typeChangeSet, gqlPermissionChangeSet) {
7158
+ return [
7159
+ ...collectTailorDBDisplayEntries("create", typeChangeSet.creates, gqlPermissionChangeSet.creates),
7160
+ ...collectTailorDBDisplayEntries("delete", typeChangeSet.deletes, gqlPermissionChangeSet.deletes),
7161
+ ...collectTailorDBDisplayEntries("update", typeChangeSet.updates, gqlPermissionChangeSet.updates),
7162
+ ...collectTailorDBDisplayEntries("replace", typeChangeSet.replaces, gqlPermissionChangeSet.replaces)
7163
+ ];
7164
+ }
6794
7165
  function trn(workspaceId, name) {
6795
7166
  return `${trnPrefix(workspaceId)}:tailordb:${name}`;
6796
7167
  }
@@ -7688,15 +8059,16 @@ async function planWorkflow(client, workspaceId, appName, workflows, mainJobDeps
7688
8059
  });
7689
8060
  }
7690
8061
  Object.values(existingWorkflows).forEach((existing) => {
7691
- const label = existing?.label;
8062
+ if (!existing) return;
8063
+ const label = existing.label;
7692
8064
  if (label && label !== appName) resourceOwners.add(label);
7693
8065
  if (label === appName) changeSet.deletes.push({
7694
8066
  name: existing.resource.name,
7695
8067
  workspaceId,
7696
- workflowId: existing.resource.id
8068
+ workflowId: existing.resource.id,
8069
+ usedJobNames: getExistingWorkflowJobNames(existing.resource)
7697
8070
  });
7698
8071
  });
7699
- changeSet.print();
7700
8072
  return {
7701
8073
  changeSet,
7702
8074
  conflicts,
@@ -7706,6 +8078,15 @@ async function planWorkflow(client, workspaceId, appName, workflows, mainJobDeps
7706
8078
  unchangedWorkflowJobNames
7707
8079
  };
7708
8080
  }
8081
+ /**
8082
+ * Format workflow changes for grouped dry-run display.
8083
+ * @param changeSet - Workflow changes
8084
+ * @param workflowJobFunctionChanges - Related function registry changes for workflow jobs
8085
+ * @returns Display entries for workflow output
8086
+ */
8087
+ function formatWorkflowChangeEntries(changeSet, workflowJobFunctionChanges) {
8088
+ return formatChangeEntriesWithFunctionRegistry("workflow", changeSet, workflowJobFunctionChanges, (item) => "usedJobNames" in item ? item.usedJobNames.map((jobName) => workflowJobFunctionName(jobName)) : []);
8089
+ }
7709
8090
  function canTreatWorkflowAsUnchanged(existing, workflow, usedJobNames, unchangedJobFunctions) {
7710
8091
  if (!usedJobNames.every((jobName) => unchangedJobFunctions.has(jobName))) return false;
7711
8092
  return areWorkflowsEqual(existing, workflow, usedJobNames);
@@ -7740,6 +8121,11 @@ function normalizeComparableWorkflowRetryPolicy(policy) {
7740
8121
  function normalizeComparableWorkflowJobNames(jobFunctions) {
7741
8122
  return Array.isArray(jobFunctions) ? [...jobFunctions].sort() : Object.keys(jobFunctions ?? {}).sort();
7742
8123
  }
8124
+ function getExistingWorkflowJobNames(existing) {
8125
+ const jobNames = new Set(Object.keys(existing.jobFunctions ?? {}));
8126
+ if (existing.mainJobFunctionName) jobNames.add(existing.mainJobFunctionName);
8127
+ return [...jobNames].sort();
8128
+ }
7743
8129
  function normalizeRetryPolicyForCompare(policy) {
7744
8130
  return {
7745
8131
  maxRetries: policy.maxRetries,
@@ -7826,34 +8212,95 @@ async function shouldForceApplyAll(client, workspaceId, application, functionEnt
7826
8212
  }
7827
8213
  return false;
7828
8214
  }
7829
- function printPlanSummary(results) {
7830
- const summary = summarizeChangeSets([
7831
- results.functionRegistry.changeSet,
7832
- results.tailorDB.changeSet.service,
7833
- results.tailorDB.changeSet.type,
7834
- results.tailorDB.changeSet.gqlPermission,
8215
+ function printPlanResults(results) {
8216
+ const executorEntries = formatExecutorChangeEntries(results.executor.changeSet, buildPlannedExecutorsByName(results.executor.changeSet), results.functionRegistry.executorFunctionChanges);
8217
+ const resolverEntries = formatResolverChangeEntries(results.pipeline.changeSet.resolver, results.functionRegistry.resolverFunctionChanges);
8218
+ const workflowEntries = formatWorkflowChangeEntries(results.workflow.changeSet, results.functionRegistry.workflowJobChanges);
8219
+ const authHookEntries = formatAuthHookChangeEntries(results.auth.changeSet.authHook, results.functionRegistry.authHookFunctionChanges);
8220
+ const tailorDBEntries = [...formatTailorDBResourceChangeEntries(results.tailorDB.changeSet.type, results.tailorDB.changeSet.gqlPermission)];
8221
+ const pipelineEntries = [...resolverEntries];
8222
+ const namespaceOf = (item) => {
8223
+ if ("request" in item && item.request && typeof item.request === "object" && "namespaceName" in item.request) return item.request.namespaceName;
8224
+ if ("namespaceName" in item) return item.namespaceName;
8225
+ };
8226
+ const authNamespaceOf = (item) => "request" in item && item.request && typeof item.request === "object" && "authNamespace" in item.request ? item.request.authNamespace : void 0;
8227
+ const idpEntries = [...formatChangeSetEntries(results.idp.changeSet.client, ["client"], namespaceOf)];
8228
+ const authEntries = [
8229
+ ...formatChangeSetEntries(results.auth.changeSet.idpConfig, ["idpConfig"], namespaceOf),
8230
+ ...formatChangeSetEntries(results.auth.changeSet.userProfileConfig, ["userProfileConfig"], namespaceOf),
8231
+ ...formatChangeSetEntries(results.auth.changeSet.tenantConfig, ["tenantConfig"], namespaceOf),
8232
+ ...formatChangeSetEntries(results.auth.changeSet.machineUser, ["machineUser"], authNamespaceOf),
8233
+ ...authHookEntries,
8234
+ ...formatChangeSetEntries(results.auth.changeSet.oauth2Client, ["oauth2Client"], namespaceOf),
8235
+ ...formatChangeSetEntries(results.auth.changeSet.scim, ["scimConfig"], namespaceOf),
8236
+ ...formatChangeSetEntries(results.auth.changeSet.scimResource, ["scimResource"], namespaceOf),
8237
+ ...results.auth.changeSet.connection ? formatChangeSetEntries(results.auth.changeSet.connection, ["connection"], namespaceOf) : []
8238
+ ];
8239
+ const { otherChanges: otherFunctionRegistryChanges } = splitFunctionRegistryChanges(results.functionRegistry.changeSet);
8240
+ printGroupedDisplaySection(results.functionRegistry.changeSet.title, formatChangeSetEntries(otherFunctionRegistryChanges));
8241
+ const tailorDBServiceActions = extractServiceActions(results.tailorDB.changeSet.service);
8242
+ const pipelineServiceActions = extractServiceActions(results.pipeline.changeSet.service);
8243
+ const idpServiceActions = extractServiceActions(results.idp.changeSet.service);
8244
+ const authServiceActions = extractServiceActions(results.auth.changeSet.service);
8245
+ results.staticWebsite.changeSet.print();
8246
+ results.app.print();
8247
+ printGroupedDisplaySection("TailorDB", tailorDBEntries, tailorDBServiceActions);
8248
+ printGroupedDisplaySection("Resolver", pipelineEntries, pipelineServiceActions);
8249
+ printGroupedDisplaySection("Executor", executorEntries);
8250
+ printGroupedDisplaySection("Workflow", workflowEntries);
8251
+ printGroupedDisplaySection("IdP", idpEntries, idpServiceActions);
8252
+ printGroupedDisplaySection("Auth", authEntries, authServiceActions);
8253
+ results.secretManager.vaultChangeSet.print();
8254
+ results.secretManager.secretChangeSet.print();
8255
+ if (results.secretManager.skippedSecrets.length > 0) {
8256
+ logger.log(styles.bold("Secret Manager secrets (skipped - no value provided):"));
8257
+ for (const name of results.secretManager.skippedSecrets) logger.log(` ${styles.dim("○")} ${name}`);
8258
+ }
8259
+ const summary = summarizePlanResults(results, [
8260
+ ...tailorDBEntries,
8261
+ ...pipelineEntries,
8262
+ ...executorEntries,
8263
+ ...workflowEntries,
8264
+ ...idpEntries,
8265
+ ...authEntries
8266
+ ], [
8267
+ ...tailorDBServiceActions,
8268
+ ...pipelineServiceActions,
8269
+ ...idpServiceActions,
8270
+ ...authServiceActions
8271
+ ]);
8272
+ logger.log(formatPlanSummary(summary));
8273
+ }
8274
+ /**
8275
+ * Summarize plan counts from display entries, service actions, and non-grouped changesets.
8276
+ * @param results - Planned apply results
8277
+ * @param displayEntries - All grouped display entries across sections
8278
+ * @param serviceActions - All service-level namespace actions
8279
+ * @returns Aggregated plan summary
8280
+ */
8281
+ function summarizePlanResults(results, displayEntries, serviceActions) {
8282
+ const summary = {
8283
+ create: 0,
8284
+ update: 0,
8285
+ delete: 0,
8286
+ replace: 0,
8287
+ unchanged: 0
8288
+ };
8289
+ for (const entry of displayEntries) summary[entry.action] += 1;
8290
+ for (const sa of serviceActions) summary[sa.action] += 1;
8291
+ const { otherChanges } = splitFunctionRegistryChanges(results.functionRegistry.changeSet);
8292
+ const nonGrouped = summarizeChangeSets([
8293
+ otherChanges,
7835
8294
  results.staticWebsite.changeSet,
7836
- results.idp.changeSet.service,
7837
- results.idp.changeSet.client,
7838
- results.auth.changeSet.service,
7839
- results.auth.changeSet.idpConfig,
7840
- results.auth.changeSet.userProfileConfig,
7841
- results.auth.changeSet.tenantConfig,
7842
- results.auth.changeSet.machineUser,
7843
- results.auth.changeSet.oauth2Client,
7844
- results.auth.changeSet.authHook,
7845
- results.auth.changeSet.scim,
7846
- results.auth.changeSet.scimResource,
7847
- ...results.auth.changeSet.connection ? [results.auth.changeSet.connection] : [],
7848
- results.pipeline.changeSet.service,
7849
- results.pipeline.changeSet.resolver,
7850
8295
  results.app,
7851
- results.executor.changeSet,
7852
- results.workflow.changeSet,
7853
8296
  results.secretManager.vaultChangeSet,
7854
8297
  results.secretManager.secretChangeSet
7855
8298
  ]);
7856
- logger.log(formatPlanSummary(summary));
8299
+ summary.create += nonGrouped.create;
8300
+ summary.update += nonGrouped.update;
8301
+ summary.delete += nonGrouped.delete;
8302
+ summary.replace += nonGrouped.replace;
8303
+ return summary;
7857
8304
  }
7858
8305
  /**
7859
8306
  * Apply the configured application to the Tailor platform.
@@ -7943,7 +8390,7 @@ async function apply(options) {
7943
8390
  forceApplyAll
7944
8391
  };
7945
8392
  const functionRegistry = await withSpan("plan.functionRegistry", () => planFunctionRegistry(client, workspaceId, application.name, functionEntries));
7946
- const unchangedWorkflowJobs = new Set(functionRegistry.changeSet.unchanged.map((entry) => entry.name).filter((name) => name.startsWith("workflow--")).map((name) => name.slice(10)));
8393
+ const unchangedWorkflowJobs = new Set(functionRegistry.changeSet.unchanged.filter((entry) => entry.name.startsWith(WORKFLOW_PREFIX)).map((entry) => entry.name.slice(WORKFLOW_PREFIX.length)));
7947
8394
  const [tailorDB, staticWebsite, idp, auth, pipeline, app, executor, workflow, secretManager] = await Promise.all([
7948
8395
  withSpan("plan.tailorDB", () => planTailorDB(ctx)),
7949
8396
  withSpan("plan.staticWebsite", () => planStaticWebsite(ctx)),
@@ -8038,7 +8485,7 @@ async function apply(options) {
8038
8485
  }
8039
8486
  });
8040
8487
  });
8041
- printPlanSummary({
8488
+ printPlanResults({
8042
8489
  functionRegistry,
8043
8490
  tailorDB,
8044
8491
  staticWebsite,
@@ -11436,6 +11883,30 @@ async function execRemove(client, workspaceId, application, config, confirm) {
11436
11883
  const workflow = await planWorkflow(client, workspaceId, application.name, {}, {});
11437
11884
  const functionRegistry = await planFunctionRegistry(client, workspaceId, application.name, []);
11438
11885
  const secretManager = await planSecretManager(ctx);
11886
+ functionRegistry.changeSet.print();
11887
+ staticWebsite.changeSet.print();
11888
+ app.print();
11889
+ tailorDB.changeSet.service.print();
11890
+ tailorDB.changeSet.type.print();
11891
+ tailorDB.changeSet.gqlPermission.print();
11892
+ pipeline.changeSet.service.print();
11893
+ pipeline.changeSet.resolver.print();
11894
+ executor.changeSet.print();
11895
+ workflow.changeSet.print();
11896
+ idp.changeSet.service.print();
11897
+ idp.changeSet.client.print();
11898
+ auth.changeSet.service.print();
11899
+ auth.changeSet.idpConfig.print();
11900
+ auth.changeSet.userProfileConfig.print();
11901
+ auth.changeSet.tenantConfig.print();
11902
+ auth.changeSet.machineUser.print();
11903
+ auth.changeSet.oauth2Client.print();
11904
+ auth.changeSet.authHook.print();
11905
+ auth.changeSet.scim.print();
11906
+ auth.changeSet.scimResource.print();
11907
+ auth.changeSet.connection?.print();
11908
+ secretManager.vaultChangeSet.print();
11909
+ secretManager.secretChangeSet.print();
11439
11910
  if (tailorDB.changeSet.service.deletes.length === 0 && staticWebsite.changeSet.deletes.length === 0 && idp.changeSet.service.deletes.length === 0 && auth.changeSet.service.deletes.length === 0 && pipeline.changeSet.service.deletes.length === 0 && app.deletes.length === 0 && executor.changeSet.deletes.length === 0 && workflow.changeSet.deletes.length === 0 && functionRegistry.changeSet.deletes.length === 0 && secretManager.vaultChangeSet.deletes.length === 0 && secretManager.secretChangeSet.deletes.length === 0) return;
11440
11911
  if (confirm) await confirm();
11441
11912
  await applyWorkflow(client, workflow, "delete");
@@ -12133,7 +12604,7 @@ async function generate(options) {
12133
12604
  if (options.init) await handleInitOption(namespacesWithMigrations, options.yes);
12134
12605
  let pluginManager;
12135
12606
  if (plugins.length > 0) pluginManager = new PluginManager(plugins);
12136
- const { defineApplication } = await import("./application-BB5TqXWY.mjs");
12607
+ const { defineApplication } = await import("./application-ILhZq_oW.mjs");
12137
12608
  const application = defineApplication({
12138
12609
  config,
12139
12610
  pluginManager
@@ -14336,4 +14807,4 @@ function isDeno() {
14336
14807
 
14337
14808
  //#endregion
14338
14809
  export { getFolder as $, getNextMigrationNumber as $t, listWorkflows as A, functionExecutionStatusToString as At, updateCommand$1 as B, DB_TYPES_FILE_NAME as Bt, listApps as C, startCommand as Ct, resumeCommand as D, executionsCommand as Dt, healthCommand as E, getWorkflow as Et, show as F, executeScript as Ft, listOrganizations as G, compareLocalTypesWithSnapshot as Gt, organizationTree as H, INITIAL_SCHEMA_NUMBER as Ht, showCommand as I, waitForExecution$1 as It, updateCommand$2 as J, formatMigrationNumber as Jt, getCommand$1 as K, compareSnapshots as Kt, logBetaWarning as L, MIGRATION_LABEL_KEY as Lt, truncateCommand as M, getCommand$5 as Mt, generate as N, getExecutor as Nt, resumeWorkflow as O, getWorkflowExecution as Ot, generateCommand as P, apply as Pt, getCommand$2 as Q, getMigrationFiles as Qt, remove as R, parseMigrationLabelNumber as Rt, createWorkspace as S, watchExecutorJob as St, getAppHealth as T, getCommand$4 as Tt, treeCommand as U, MIGRATE_FILE_NAME as Ut, updateOrganization as V, DIFF_FILE_NAME as Vt, listCommand$4 as W, SCHEMA_FILE_NAME as Wt, listCommand$5 as X, getMigrationDirPath as Xt, updateFolder as Y, getLatestMigrationNumber as Yt, listFolders as Z, getMigrationFilePath as Zt, getCommand as _, isVerbose as _n, listCommand$8 as _t, updateCommand as a, hasChanges as an, listOAuth2Clients as at, deleteWorkspace as b, jobsCommand as bt, removeUser as c, sdkNameLabelKey as cn, getMachineUserToken as ct, inviteCommand as d, apiCall as dn, listMachineUsers as dt, isValidMigrationNumber as en, deleteCommand$1 as et, inviteUser as f, apiCommand as fn, generate$1 as ft, listWorkspaces as g, deploymentArgs as gn, triggerExecutor as gt, listCommand$1 as h, confirmationArgs as hn, triggerCommand as ht, isCLIError as i, formatMigrationDiff as in, listCommand$6 as it, truncate as j, formatKeyValueTable as jt, listCommand$3 as k, listWorkflowExecutions as kt, listCommand as l, trnPrefix as ln, tokenCommand as lt, restoreWorkspace as m, commonArgs as mn, webhookCommand as mt, query as n, reconstructSnapshotFromMigrations as nn, createCommand$1 as nt, updateUser as o, getNamespacesWithMigrations as on, getCommand$3 as ot, restoreCommand as p, defineAppCommand as pn, listWebhookExecutors as pt, getOrganization as q, createSnapshotFromLocalTypes as qt, queryCommand as r, formatDiffSummary as rn, createFolder as rt, removeCommand as s, prompt as sn, getOAuth2Client as st, isNativeTypeScriptRuntime as t, loadDiff as tn, deleteFolder as tt, listUsers as u, generateUserTypes as un, listCommand$7 as ut, getWorkspace as v, workspaceArgs as vn, listExecutors as vt, listCommand$2 as w, startWorkflow as wt, createCommand as x, listExecutorJobs as xt, deleteCommand as y, getExecutorJob as yt, removeCommand$1 as z, bundleMigrationScript as zt };
14339
- //# sourceMappingURL=runtime-C7RRDaB3.mjs.map
14810
+ //# sourceMappingURL=runtime-D9ejnCm6.mjs.map