@tailor-platform/sdk 1.25.4 → 1.27.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 (65) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/application-CBJFUKrU.mjs +4701 -0
  3. package/dist/application-CBJFUKrU.mjs.map +1 -0
  4. package/dist/application-WyZetOky.mjs +11 -0
  5. package/dist/cli/index.mjs +290 -32
  6. package/dist/cli/index.mjs.map +1 -1
  7. package/dist/cli/lib.d.mts +350 -8
  8. package/dist/cli/lib.mjs +10 -8
  9. package/dist/cli/lib.mjs.map +1 -1
  10. package/dist/client-C2_wgujH.mjs +6 -0
  11. package/dist/{application-91Th6tm6.mjs → client-bTbnbQbB.mjs} +24 -4795
  12. package/dist/client-bTbnbQbB.mjs.map +1 -0
  13. package/dist/configure/index.d.mts +5 -5
  14. package/dist/configure/index.mjs.map +1 -1
  15. package/dist/crash-report-Cot_9Esm.mjs +6 -0
  16. package/dist/crash-report-Ju8cQF-l.mjs +414 -0
  17. package/dist/crash-report-Ju8cQF-l.mjs.map +1 -0
  18. package/dist/{enum-constants-6uK0VI_s.mjs → enum-constants-D1nfn0qD.mjs} +1 -1
  19. package/dist/{enum-constants-6uK0VI_s.mjs.map → enum-constants-D1nfn0qD.mjs.map} +1 -1
  20. package/dist/{env-uBeVwE9B.d.mts → env-BuMbIknz.d.mts} +2 -2
  21. package/dist/{file-utils-2T9w20FP.mjs → file-utils-Bctuzn3x.mjs} +1 -1
  22. package/dist/{file-utils-2T9w20FP.mjs.map → file-utils-Bctuzn3x.mjs.map} +1 -1
  23. package/dist/{index-BD-K97-C.d.mts → index-B0Lrzywd.d.mts} +2 -2
  24. package/dist/{index-cZilKprY.d.mts → index-CbnLNm14.d.mts} +2 -2
  25. package/dist/{index-D1J5SfyK.d.mts → index-CyapgSFI.d.mts} +2 -2
  26. package/dist/{index-CT53egux.d.mts → index-D1AM_02Y.d.mts} +2 -2
  27. package/dist/{index-Bu12qy3m.d.mts → index-cD9sQLTh.d.mts} +15 -15
  28. package/dist/{interceptor-BPiIBTk_.mjs → interceptor-B0d_GrI5.mjs} +1 -1
  29. package/dist/{interceptor-BPiIBTk_.mjs.map → interceptor-B0d_GrI5.mjs.map} +1 -1
  30. package/dist/{kysely-type-cMNbsQ6k.mjs → kysely-type-B_IecdK9.mjs} +1 -1
  31. package/dist/{kysely-type-cMNbsQ6k.mjs.map → kysely-type-B_IecdK9.mjs.map} +1 -1
  32. package/dist/logger-CqezTedh.mjs +181 -0
  33. package/dist/logger-CqezTedh.mjs.map +1 -0
  34. package/dist/{package-json-CVUv8Y9T.mjs → package-json-D3x2nBPB.mjs} +1 -1
  35. package/dist/{package-json-CVUv8Y9T.mjs.map → package-json-D3x2nBPB.mjs.map} +1 -1
  36. package/dist/package-json-DHfTiUCS.mjs +4 -0
  37. package/dist/plugin/builtin/enum-constants/index.d.mts +1 -1
  38. package/dist/plugin/builtin/enum-constants/index.mjs +1 -1
  39. package/dist/plugin/builtin/file-utils/index.d.mts +1 -1
  40. package/dist/plugin/builtin/file-utils/index.mjs +1 -1
  41. package/dist/plugin/builtin/kysely-type/index.d.mts +1 -1
  42. package/dist/plugin/builtin/kysely-type/index.mjs +1 -1
  43. package/dist/plugin/builtin/seed/index.d.mts +1 -1
  44. package/dist/plugin/builtin/seed/index.mjs +1 -1
  45. package/dist/plugin/index.d.mts +2 -2
  46. package/dist/{plugin-zY5wvV82.d.mts → plugin-D3a0-qe0.d.mts} +20 -4
  47. package/dist/{query-kb_4EQp4.mjs → query-CgGbAmUg.mjs} +460 -361
  48. package/dist/query-CgGbAmUg.mjs.map +1 -0
  49. package/dist/{seed-CCVRLibh.mjs → seed-CWkIDWMb.mjs} +1 -1
  50. package/dist/{seed-CCVRLibh.mjs.map → seed-CWkIDWMb.mjs.map} +1 -1
  51. package/dist/{telemetry-DDQZRqHK.mjs → telemetry-BevrwWwF.mjs} +1 -1
  52. package/dist/{telemetry-0w8OupuQ.mjs → telemetry-VvNfsyEE.mjs} +2 -2
  53. package/dist/{telemetry-0w8OupuQ.mjs.map → telemetry-VvNfsyEE.mjs.map} +1 -1
  54. package/dist/utils/test/index.d.mts +2 -2
  55. package/dist/{workflow.generated-v1LXRuB6.d.mts → workflow.generated-BsgIlrH-.d.mts} +2 -2
  56. package/docs/cli/crash-report.md +107 -0
  57. package/docs/cli/setup.md +82 -0
  58. package/docs/cli-reference.md +19 -0
  59. package/docs/services/auth.md +33 -0
  60. package/docs/services/resolver.md +32 -0
  61. package/package.json +4 -4
  62. package/dist/application-91Th6tm6.mjs.map +0 -1
  63. package/dist/application-DegTCDd8.mjs +0 -9
  64. package/dist/package-json-Bj76LPsV.mjs +0 -4
  65. package/dist/query-kb_4EQp4.mjs.map +0 -1
@@ -1,7 +1,9 @@
1
1
  import { t as db } from "./schema-BePzTFBV.mjs";
2
- import { $ as AuthSCIMAttribute_Uniqueness, A as userAgent, B as PipelineResolver_OperationType, C as fetchAll, D as initOperatorClient, F as TailorDBGQLPermission_Operator, G as ExecutorTargetType, H as FunctionExecution_Status, I as TailorDBGQLPermission_Permit, J as AuthInvokerSchema, K as ExecutorTriggerType, L as TailorDBType_Permission_Operator, M as WorkflowExecution_Status, N as WorkflowJobExecution_Status, O as platformBaseUrl, P as TailorDBGQLPermission_Action, Q as AuthSCIMAttribute_Type, R as TailorDBType_Permission_Permit, S as writePlatformConfig, V as IdPLang, W as ExecutorJobStatus, X as AuthOAuth2Client_GrantType, Y as AuthOAuth2Client_ClientType, Z as AuthSCIMAttribute_Mutability, _ as hashFile, a as loadConfig, at as ConditionSchema, b as loadWorkspaceId, ct as PageDirection, d as TailorDBTypeSchema, dt as CIPromptError, et as AuthSCIMConfig_AuthorizationType, f as stringifyFunction, ft as logger, g as getDistDir, h as createBundleCache, it as GetApplicationSchemaHealthResponse_ApplicationSchemaHealthStatus, j as WorkspacePlatformUserRole, k as resolveStaticWebsiteUrls, l as OAuth2ClientSchema, lt as ApplicationSchemaUpdateAttemptStatus, m as loadFilesWithIgnores, mt as symbols, n as generatePluginFilesIfNeeded, nt as TenantProviderConfig_TenantProviderType, ot as Condition_Operator, p as tailorUserMap, pt as styles, q as AuthIDPConfig_AuthType, r as loadApplication, rt as UserProfileProviderConfig_UserProfileProviderType, s as createExecutorService, st as FilterSchema, t as defineApplication, ut as Subgraph_ServiceType, w as fetchMachineUserToken, x as readPlatformConfig, y as loadAccessToken, z as TailorDBType_PermitAction } from "./application-91Th6tm6.mjs";
3
- import { t as readPackageJson } from "./package-json-CVUv8Y9T.mjs";
4
- import { r as withSpan } from "./telemetry-0w8OupuQ.mjs";
2
+ import { i as symbols, n as logger, r as styles, t as CIPromptError } from "./logger-CqezTedh.mjs";
3
+ import { A as AuthInvokerSchema, B as GetApplicationSchemaHealthResponse_ApplicationSchemaHealthStatus, C as FunctionExecution_Status, D as ExecutorTriggerType, E as ExecutorTargetType, F as AuthSCIMAttribute_Uniqueness, G as ApplicationSchemaUpdateAttemptStatus, H as Condition_Operator, I as AuthSCIMConfig_AuthorizationType, K as Subgraph_ServiceType, M as AuthOAuth2Client_GrantType, N as AuthSCIMAttribute_Mutability, O as AuthHookPoint, P as AuthSCIMAttribute_Type, R as TenantProviderConfig_TenantProviderType, S as IdPLang, T as ExecutorJobStatus, U as FilterSchema, V as ConditionSchema, W as PageDirection, _ as TailorDBGQLPermission_Permit, b as TailorDBType_PermitAction, d as userAgent, f as WorkspacePlatformUserRole, g as TailorDBGQLPermission_Operator, h as TailorDBGQLPermission_Action, j as AuthOAuth2Client_ClientType, k as AuthIDPConfig_AuthType, l as platformBaseUrl, m as WorkflowJobExecution_Status, n as fetchAll, p as WorkflowExecution_Status, r as fetchMachineUserToken, s as initOperatorClient, u as resolveStaticWebsiteUrls, v as TailorDBType_Permission_Operator, x as PipelineResolver_OperationType, y as TailorDBType_Permission_Permit, z as UserProfileProviderConfig_UserProfileProviderType } from "./client-bTbnbQbB.mjs";
4
+ import { t as readPackageJson } from "./package-json-D3x2nBPB.mjs";
5
+ import { S as writePlatformConfig, _ as hashFile, a as loadConfig, b as loadWorkspaceId, d as TailorDBTypeSchema, f as stringifyFunction, g as getDistDir, h as createBundleCache, l as OAuth2ClientSchema, m as loadFilesWithIgnores, n as generatePluginFilesIfNeeded, p as tailorUserMap, r as loadApplication, s as createExecutorService, t as defineApplication, x as readPlatformConfig, y as loadAccessToken } from "./application-CBJFUKrU.mjs";
6
+ import { r as withSpan } from "./telemetry-VvNfsyEE.mjs";
5
7
  import { arg, createDefineCommand, defineCommand, runCommand } from "politty";
6
8
  import { z } from "zod";
7
9
  import * as fs$1 from "node:fs";
@@ -19,11 +21,11 @@ import { findUpSync } from "find-up-simple";
19
21
  import ml from "multiline-ts";
20
22
  import * as crypto from "node:crypto";
21
23
  import { createHash } from "node:crypto";
22
- import { pathToFileURL } from "node:url";
23
- import * as inflection from "inflection";
24
24
  import * as rolldown from "rolldown";
25
25
  import * as fs from "node:fs/promises";
26
26
  import { glob } from "node:fs/promises";
27
+ import { pathToFileURL } from "node:url";
28
+ import * as inflection from "inflection";
27
29
  import { create, fromJson, toJson } from "@bufbuild/protobuf";
28
30
  import { ExitPromptError } from "@inquirer/core";
29
31
  import { confirm, input } from "@inquirer/prompts";
@@ -1073,6 +1075,273 @@ function protoSubgraph(subgraph) {
1073
1075
  };
1074
1076
  }
1075
1077
 
1078
+ //#endregion
1079
+ //#region src/cli/commands/apply/function-registry.ts
1080
+ const CHUNK_SIZE = 64 * 1024;
1081
+ /**
1082
+ * Compute SHA-256 content hash for a script string.
1083
+ * @param content - Script content to hash
1084
+ * @returns Hex-encoded SHA-256 hash
1085
+ */
1086
+ function computeContentHash(content) {
1087
+ return crypto.createHash("sha256").update(content, "utf-8").digest("hex");
1088
+ }
1089
+ function functionRegistryTrn(workspaceId, name) {
1090
+ return `trn:v1:workspace:${workspaceId}:function_registry:${name}`;
1091
+ }
1092
+ /**
1093
+ * Build a function registry name for a resolver.
1094
+ * @param namespace - Resolver namespace
1095
+ * @param resolverName - Resolver name
1096
+ * @returns Function registry name
1097
+ */
1098
+ function resolverFunctionName(namespace, resolverName) {
1099
+ return `resolver--${namespace}--${resolverName}`;
1100
+ }
1101
+ /**
1102
+ * Build a function registry name for an executor.
1103
+ * @param executorName - Executor name
1104
+ * @returns Function registry name
1105
+ */
1106
+ function executorFunctionName(executorName) {
1107
+ return `executor--${executorName}`;
1108
+ }
1109
+ /**
1110
+ * Build a function registry name for a workflow job.
1111
+ * @param jobName - Workflow job name
1112
+ * @returns Function registry name
1113
+ */
1114
+ function workflowJobFunctionName(jobName) {
1115
+ return `workflow--${jobName}`;
1116
+ }
1117
+ /**
1118
+ * Build a function registry name for an auth hook.
1119
+ * @param authName - Auth namespace name
1120
+ * @param hookPoint - Hook point identifier (e.g. "before-login")
1121
+ * @returns Function registry name
1122
+ */
1123
+ function authHookFunctionName(authName, hookPoint) {
1124
+ return `auth-hook--${authName}--${hookPoint}`;
1125
+ }
1126
+ /**
1127
+ * Collect all function entries from bundled scripts for all services.
1128
+ * @param application - Application definition
1129
+ * @param workflowJobs - Collected workflow jobs from config
1130
+ * @returns Array of function entries to register
1131
+ */
1132
+ function collectFunctionEntries(application, workflowJobs) {
1133
+ const entries = [];
1134
+ const distDir = getDistDir();
1135
+ for (const app of application.applications) for (const pipeline of app.resolverServices) for (const resolver of Object.values(pipeline.resolvers)) {
1136
+ const scriptPath = path.join(distDir, "resolvers", `${resolver.name}.js`);
1137
+ try {
1138
+ const content = fs$1.readFileSync(scriptPath, "utf-8");
1139
+ entries.push({
1140
+ name: resolverFunctionName(pipeline.namespace, resolver.name),
1141
+ scriptContent: content,
1142
+ contentHash: computeContentHash(content),
1143
+ description: `Resolver: ${pipeline.namespace}/${resolver.name}`
1144
+ });
1145
+ } catch {
1146
+ logger.warn(`Function file not found: ${scriptPath}`);
1147
+ }
1148
+ }
1149
+ if (application.executorService) {
1150
+ const executors = application.executorService.executors;
1151
+ for (const executor of Object.values(executors)) if (executor.operation.kind === "function" || executor.operation.kind === "jobFunction") {
1152
+ const scriptPath = path.join(distDir, "executors", `${executor.name}.js`);
1153
+ try {
1154
+ const content = fs$1.readFileSync(scriptPath, "utf-8");
1155
+ entries.push({
1156
+ name: executorFunctionName(executor.name),
1157
+ scriptContent: content,
1158
+ contentHash: computeContentHash(content),
1159
+ description: `Executor: ${executor.name}`
1160
+ });
1161
+ } catch {
1162
+ logger.warn(`Function file not found: ${scriptPath}`);
1163
+ }
1164
+ }
1165
+ }
1166
+ for (const job of workflowJobs) {
1167
+ const scriptPath = path.join(distDir, "workflow-jobs", `${job.name}.js`);
1168
+ try {
1169
+ const content = fs$1.readFileSync(scriptPath, "utf-8");
1170
+ entries.push({
1171
+ name: workflowJobFunctionName(job.name),
1172
+ scriptContent: content,
1173
+ contentHash: computeContentHash(content),
1174
+ description: `Workflow job: ${job.name}`
1175
+ });
1176
+ } catch {
1177
+ logger.warn(`Function file not found: ${scriptPath}`);
1178
+ }
1179
+ }
1180
+ for (const app of application.applications) if (app.authService?.config.hooks?.beforeLogin) {
1181
+ const authName = app.authService.config.name;
1182
+ const funcName = authHookFunctionName(authName, "before-login");
1183
+ const scriptPath = path.join(distDir, "auth-hooks", `${funcName}.js`);
1184
+ try {
1185
+ const content = fs$1.readFileSync(scriptPath, "utf-8");
1186
+ entries.push({
1187
+ name: funcName,
1188
+ scriptContent: content,
1189
+ contentHash: computeContentHash(content),
1190
+ description: `Auth hook: ${authName}/before-login`
1191
+ });
1192
+ } catch {
1193
+ logger.warn(`Function file not found: ${scriptPath}`);
1194
+ }
1195
+ }
1196
+ return entries;
1197
+ }
1198
+ /**
1199
+ * Plan function registry changes based on current and desired state.
1200
+ * @param client - Operator client instance
1201
+ * @param workspaceId - Workspace ID
1202
+ * @param appName - Application name
1203
+ * @param entries - Desired function entries
1204
+ * @returns Planned changes
1205
+ */
1206
+ async function planFunctionRegistry(client, workspaceId, appName, entries) {
1207
+ const changeSet = createChangeSet("Function registry");
1208
+ const conflicts = [];
1209
+ const unmanaged = [];
1210
+ const resourceOwners = /* @__PURE__ */ new Set();
1211
+ const existingFunctions = await fetchAll(async (pageToken, maxPageSize) => {
1212
+ try {
1213
+ const response = await client.listFunctionRegistries({
1214
+ workspaceId,
1215
+ pageToken,
1216
+ pageSize: maxPageSize
1217
+ });
1218
+ return [response.functions.map((f) => ({
1219
+ name: f.name,
1220
+ contentHash: f.contentHash
1221
+ })), response.nextPageToken];
1222
+ } catch (error) {
1223
+ if (error instanceof ConnectError && error.code === Code.NotFound) return [[], ""];
1224
+ throw error;
1225
+ }
1226
+ });
1227
+ const existingMap = {};
1228
+ await Promise.all(existingFunctions.map(async (func) => {
1229
+ const { metadata } = await client.getMetadata({ trn: functionRegistryTrn(workspaceId, func.name) });
1230
+ existingMap[func.name] = {
1231
+ resource: func,
1232
+ label: metadata?.labels[sdkNameLabelKey]
1233
+ };
1234
+ }));
1235
+ for (const entry of entries) {
1236
+ const existing = existingMap[entry.name];
1237
+ const metaRequest = await buildMetaRequest(functionRegistryTrn(workspaceId, entry.name), appName);
1238
+ if (existing) {
1239
+ if (!existing.label) unmanaged.push({
1240
+ resourceType: "Function registry",
1241
+ resourceName: entry.name
1242
+ });
1243
+ else if (existing.label !== appName) conflicts.push({
1244
+ resourceType: "Function registry",
1245
+ resourceName: entry.name,
1246
+ currentOwner: existing.label
1247
+ });
1248
+ changeSet.updates.push({
1249
+ name: entry.name,
1250
+ entry,
1251
+ metaRequest
1252
+ });
1253
+ delete existingMap[entry.name];
1254
+ } else changeSet.creates.push({
1255
+ name: entry.name,
1256
+ entry,
1257
+ metaRequest
1258
+ });
1259
+ }
1260
+ for (const [name, existing] of Object.entries(existingMap)) {
1261
+ if (!existing) continue;
1262
+ const label = existing.label;
1263
+ if (label && label !== appName) resourceOwners.add(label);
1264
+ if (label === appName) changeSet.deletes.push({
1265
+ name,
1266
+ workspaceId
1267
+ });
1268
+ }
1269
+ changeSet.print();
1270
+ return {
1271
+ changeSet,
1272
+ conflicts,
1273
+ unmanaged,
1274
+ resourceOwners
1275
+ };
1276
+ }
1277
+ /**
1278
+ * Upload a function script to the function registry using client streaming.
1279
+ * @param client - Operator client instance
1280
+ * @param workspaceId - Workspace ID
1281
+ * @param entry - Function entry to upload
1282
+ * @param isCreate - Whether this is a create (true) or update (false)
1283
+ */
1284
+ async function uploadFunctionScript(client, workspaceId, entry, isCreate) {
1285
+ const buffer = Buffer.from(entry.scriptContent, "utf-8");
1286
+ const info = {
1287
+ workspaceId,
1288
+ name: entry.name,
1289
+ description: entry.description,
1290
+ sizeBytes: BigInt(buffer.length),
1291
+ contentHash: entry.contentHash
1292
+ };
1293
+ if (isCreate) {
1294
+ /** @yields {MessageInitShape<typeof CreateFunctionRegistryRequestSchema>} Create request messages (info header followed by content chunks) */
1295
+ async function* createStream() {
1296
+ yield { payload: {
1297
+ case: "info",
1298
+ value: info
1299
+ } };
1300
+ for (let i = 0; i < buffer.length; i += CHUNK_SIZE) yield { payload: {
1301
+ case: "chunk",
1302
+ value: buffer.subarray(i, Math.min(i + CHUNK_SIZE, buffer.length))
1303
+ } };
1304
+ }
1305
+ await client.createFunctionRegistry(createStream());
1306
+ } else {
1307
+ /** @yields {MessageInitShape<typeof UpdateFunctionRegistryRequestSchema>} Update request messages (info header followed by content chunks) */
1308
+ async function* updateStream() {
1309
+ yield { payload: {
1310
+ case: "info",
1311
+ value: info
1312
+ } };
1313
+ for (let i = 0; i < buffer.length; i += CHUNK_SIZE) yield { payload: {
1314
+ case: "chunk",
1315
+ value: buffer.subarray(i, Math.min(i + CHUNK_SIZE, buffer.length))
1316
+ } };
1317
+ }
1318
+ await client.updateFunctionRegistry(updateStream());
1319
+ }
1320
+ }
1321
+ /**
1322
+ * Apply function registry changes for the given phase.
1323
+ * @param client - Operator client instance
1324
+ * @param workspaceId - Workspace ID
1325
+ * @param result - Planned function registry changes
1326
+ * @param phase - Apply phase
1327
+ */
1328
+ async function applyFunctionRegistry(client, workspaceId, result, phase = "create-update") {
1329
+ const { changeSet } = result;
1330
+ if (phase === "create-update") {
1331
+ for (const create of changeSet.creates) {
1332
+ await uploadFunctionScript(client, workspaceId, create.entry, true);
1333
+ await client.setMetadata(create.metaRequest);
1334
+ }
1335
+ for (const update of changeSet.updates) {
1336
+ await uploadFunctionScript(client, workspaceId, update.entry, false);
1337
+ await client.setMetadata(update.metaRequest);
1338
+ }
1339
+ } else if (phase === "delete") await Promise.all(changeSet.deletes.map((del) => client.deleteFunctionRegistry({
1340
+ workspaceId: del.workspaceId,
1341
+ name: del.name
1342
+ })));
1343
+ }
1344
+
1076
1345
  //#endregion
1077
1346
  //#region src/cli/commands/apply/idp.ts
1078
1347
  /**
@@ -1400,6 +1669,7 @@ async function applyAuth(client, result, phase = "create-update") {
1400
1669
  await Promise.all([...changeSet.userProfileConfig.creates.map((create) => client.createUserProfileConfig(create.request)), ...changeSet.userProfileConfig.updates.map((update) => client.updateUserProfileConfig(update.request))]);
1401
1670
  await Promise.all([...changeSet.tenantConfig.creates.map((create) => client.createTenantConfig(create.request)), ...changeSet.tenantConfig.updates.map((update) => client.updateTenantConfig(update.request))]);
1402
1671
  await Promise.all([...changeSet.machineUser.creates.map((create) => client.createAuthMachineUser(create.request)), ...changeSet.machineUser.updates.map((update) => client.updateAuthMachineUser(update.request))]);
1672
+ await Promise.all([...changeSet.authHook.creates.map((create) => client.createAuthHook(create.request)), ...changeSet.authHook.updates.map((update) => client.updateAuthHook(update.request))]);
1403
1673
  await Promise.all([...changeSet.oauth2Client.creates.map(async (create) => {
1404
1674
  create.request.oauth2Client.redirectUris = await resolveStaticWebsiteUrls(client, create.request.workspaceId, create.request.oauth2Client.redirectUris, "OAuth2 redirect URIs");
1405
1675
  return client.createAuthOAuth2Client(create.request);
@@ -1418,6 +1688,7 @@ async function applyAuth(client, result, phase = "create-update") {
1418
1688
  await Promise.all(changeSet.scimResource.deletes.map((del) => client.deleteAuthSCIMResource(del.request)));
1419
1689
  await Promise.all(changeSet.scim.deletes.map((del) => client.deleteAuthSCIMConfig(del.request)));
1420
1690
  await Promise.all(changeSet.oauth2Client.deletes.map((del) => client.deleteAuthOAuth2Client(del.request)));
1691
+ await Promise.all(changeSet.authHook.deletes.map((del) => client.deleteAuthHook(del.request)));
1421
1692
  await Promise.all(changeSet.machineUser.deletes.map((del) => client.deleteAuthMachineUser(del.request)));
1422
1693
  await Promise.all(changeSet.tenantConfig.deletes.map((del) => client.deleteTenantConfig(del.request)));
1423
1694
  await Promise.all(changeSet.userProfileConfig.deletes.map((del) => client.deleteUserProfileConfig(del.request)));
@@ -1438,11 +1709,12 @@ async function planAuth(context) {
1438
1709
  }
1439
1710
  const { changeSet: serviceChangeSet, conflicts, unmanaged, resourceOwners } = await planServices$2(client, workspaceId, application.name, auths);
1440
1711
  const deletedServices = serviceChangeSet.deletes.map((del) => del.name);
1441
- const [idpConfigChangeSet, userProfileConfigChangeSet, tenantConfigChangeSet, machineUserChangeSet, oauth2ClientChangeSet, scimChangeSet, scimResourceChangeSet] = await Promise.all([
1712
+ const [idpConfigChangeSet, userProfileConfigChangeSet, tenantConfigChangeSet, machineUserChangeSet, authHookChangeSet, oauth2ClientChangeSet, scimChangeSet, scimResourceChangeSet] = await Promise.all([
1442
1713
  planIdPConfigs(client, workspaceId, auths, deletedServices),
1443
1714
  planUserProfileConfigs(client, workspaceId, auths, deletedServices),
1444
1715
  planTenantConfigs(client, workspaceId, auths, deletedServices),
1445
1716
  planMachineUsers(client, workspaceId, auths, deletedServices),
1717
+ planAuthHooks(client, workspaceId, auths, deletedServices),
1446
1718
  planOAuth2Clients(client, workspaceId, auths, deletedServices),
1447
1719
  planSCIMConfigs(client, workspaceId, auths, deletedServices),
1448
1720
  planSCIMResources(client, workspaceId, auths, deletedServices)
@@ -1452,6 +1724,7 @@ async function planAuth(context) {
1452
1724
  userProfileConfigChangeSet.print();
1453
1725
  tenantConfigChangeSet.print();
1454
1726
  machineUserChangeSet.print();
1727
+ authHookChangeSet.print();
1455
1728
  oauth2ClientChangeSet.print();
1456
1729
  scimChangeSet.print();
1457
1730
  scimResourceChangeSet.print();
@@ -1462,6 +1735,7 @@ async function planAuth(context) {
1462
1735
  userProfileConfig: userProfileConfigChangeSet,
1463
1736
  tenantConfig: tenantConfigChangeSet,
1464
1737
  machineUser: machineUserChangeSet,
1738
+ authHook: authHookChangeSet,
1465
1739
  oauth2Client: oauth2ClientChangeSet,
1466
1740
  scim: scimChangeSet,
1467
1741
  scimResource: scimResourceChangeSet
@@ -2261,6 +2535,72 @@ function protoSCIMAttribute(attr) {
2261
2535
  subAttributes: attr.subAttributes?.map((attr) => protoSCIMAttribute(attr))
2262
2536
  };
2263
2537
  }
2538
+ async function planAuthHooks(client, workspaceId, auths, deletedServices) {
2539
+ const changeSet = createChangeSet("Auth hooks");
2540
+ for (const auth of auths) {
2541
+ const { parsedConfig: config } = auth;
2542
+ const beforeLogin = config.hooks?.beforeLogin;
2543
+ let existingHook;
2544
+ try {
2545
+ await client.getAuthHook({
2546
+ workspaceId,
2547
+ namespaceName: config.name,
2548
+ hookPoint: AuthHookPoint.BEFORE_LOGIN
2549
+ });
2550
+ existingHook = true;
2551
+ } catch (error) {
2552
+ if (error instanceof ConnectError && error.code === Code.NotFound) existingHook = false;
2553
+ else throw error;
2554
+ }
2555
+ if (beforeLogin) {
2556
+ const hookRequest = {
2557
+ workspaceId,
2558
+ namespaceName: config.name,
2559
+ hook: {
2560
+ hookPoint: AuthHookPoint.BEFORE_LOGIN,
2561
+ scriptRef: authHookFunctionName(config.name, "before-login"),
2562
+ invoker: {
2563
+ namespace: config.name,
2564
+ machineUserName: beforeLogin.invoker
2565
+ }
2566
+ }
2567
+ };
2568
+ if (existingHook) changeSet.updates.push({
2569
+ name: `${config.name}/before-login`,
2570
+ request: hookRequest
2571
+ });
2572
+ else changeSet.creates.push({
2573
+ name: `${config.name}/before-login`,
2574
+ request: hookRequest
2575
+ });
2576
+ } else if (existingHook) changeSet.deletes.push({
2577
+ name: `${config.name}/before-login`,
2578
+ request: {
2579
+ workspaceId,
2580
+ namespaceName: config.name,
2581
+ hookPoint: AuthHookPoint.BEFORE_LOGIN
2582
+ }
2583
+ });
2584
+ }
2585
+ for (const namespaceName of deletedServices) try {
2586
+ await client.getAuthHook({
2587
+ workspaceId,
2588
+ namespaceName,
2589
+ hookPoint: AuthHookPoint.BEFORE_LOGIN
2590
+ });
2591
+ changeSet.deletes.push({
2592
+ name: `${namespaceName}/before-login`,
2593
+ request: {
2594
+ workspaceId,
2595
+ namespaceName,
2596
+ hookPoint: AuthHookPoint.BEFORE_LOGIN
2597
+ }
2598
+ });
2599
+ } catch (error) {
2600
+ if (error instanceof ConnectError && error.code === Code.NotFound) {} else throw error;
2601
+ }
2602
+ return changeSet;
2603
+ }
2264
2604
 
2265
2605
  //#endregion
2266
2606
  //#region src/cli/shared/prompt.ts
@@ -2325,358 +2665,116 @@ async function confirmOwnerConflict(conflicts, appName, yes) {
2325
2665
  */
2326
2666
  async function confirmUnmanagedResources(resources, appName, yes) {
2327
2667
  if (resources.length === 0) return;
2328
- logger.warn("Existing resources not tracked by tailor-sdk were found:");
2329
- logger.log(` ${styles.info("Resources")}:`);
2330
- for (const r of resources) logger.log(` • ${styles.bold(r.resourceType)} ${styles.info(`"${r.resourceName}"`)}`);
2331
- logger.newline();
2332
- logger.log(" These resources may have been created by older SDK versions, Terraform, or CUE.");
2333
- logger.log(" To continue, confirm that tailor-sdk should manage them.");
2334
- logger.log(" If they are managed by another tool (e.g., Terraform), cancel and manage them there instead.");
2335
- if (yes) {
2336
- logger.success(`Adding to "${appName}" (--yes flag specified)...`, { mode: "plain" });
2337
- return;
2338
- }
2339
- if (!await prompt.confirm({
2340
- message: `Allow tailor-sdk to manage these resources for "${appName}"?`,
2341
- default: false
2342
- })) throw new Error(ml`
2343
- Apply cancelled. Resources remain unmanaged.
2344
- To override, run again and confirm, or use --yes flag.
2345
- `);
2346
- }
2347
- /**
2348
- * Confirm deletion of important resources.
2349
- * @param resources - Resources scheduled for deletion
2350
- * @param yes - Whether to auto-confirm without prompting
2351
- * @returns Promise that resolves when confirmation completes
2352
- */
2353
- async function confirmImportantResourceDeletion(resources, yes) {
2354
- if (resources.length === 0) return;
2355
- logger.warn("The following resources will be deleted:");
2356
- logger.log(` ${styles.info("Resources")}:`);
2357
- for (const r of resources) logger.log(` • ${styles.bold(r.resourceType)} ${styles.error(`"${r.resourceName}"`)}`);
2358
- logger.newline();
2359
- logger.log(styles.warning(" Deleting these resources will permanently remove all associated data."));
2360
- if (yes) {
2361
- logger.success("Deleting resources (--yes flag specified)...", { mode: "plain" });
2362
- return;
2363
- }
2364
- if (!await prompt.confirm({
2365
- message: "Are you sure you want to delete these resources?",
2366
- default: false
2367
- })) throw new Error(ml`
2368
- Apply cancelled. Resources will not be deleted.
2369
- To override, run again and confirm, or use --yes flag.
2370
- `);
2371
- }
2372
-
2373
- //#endregion
2374
- //#region src/cli/shared/runtime-args.ts
2375
- /**
2376
- * Runtime args transformation for all services.
2377
- *
2378
- * Each service transforms server-side args/context into SDK-friendly format:
2379
- * - Executor: server-side expression evaluated by platform before calling function
2380
- * - Resolver: operationHook expression evaluated by platform before calling function
2381
- *
2382
- * The user field mapping (server → SDK) shared across services is defined in
2383
- * `@/parser/service/tailordb` as `tailorUserMap`.
2384
- */
2385
- /**
2386
- * Actor field transformation expression.
2387
- *
2388
- * Transforms the server's actor object to match the SDK's TailorActor type:
2389
- * server `attributeMap` → SDK `attributes`
2390
- * server `attributes` → SDK `attributeList`
2391
- * other fields → passed through
2392
- * null/undefined actor → null
2393
- */
2394
- const ACTOR_TRANSFORM_EXPR = "actor: args.actor ? (({ attributeMap, attributes: attrList, ...rest }) => ({ ...rest, attributes: attributeMap, attributeList: attrList }))(args.actor) : null";
2395
- /**
2396
- * Build the JavaScript expression that transforms server-format executor event
2397
- * args into SDK-format args at runtime.
2398
- *
2399
- * The Tailor Platform server delivers event args with server-side field names.
2400
- * The SDK exposes different field names to user code. This function produces a
2401
- * JavaScript expression string that performs the mapping when evaluated
2402
- * server-side.
2403
- * @param triggerKind - The trigger kind discriminant from the parsed executor
2404
- * @param env - Application env record to embed in the expression
2405
- * @returns A JavaScript expression string, e.g. `({ ...args, ... })`
2406
- */
2407
- function buildExecutorArgsExpr(triggerKind, env) {
2408
- const envExpr = `env: ${JSON.stringify(env)}`;
2409
- switch (triggerKind) {
2410
- case "schedule":
2411
- case "recordCreated":
2412
- case "recordUpdated":
2413
- case "recordDeleted":
2414
- case "idpUserCreated":
2415
- case "idpUserUpdated":
2416
- case "idpUserDeleted":
2417
- case "authAccessTokenIssued":
2418
- case "authAccessTokenRefreshed":
2419
- case "authAccessTokenRevoked": return `({ ...args, appNamespace: args.namespaceName, ${ACTOR_TRANSFORM_EXPR}, ${envExpr} })`;
2420
- case "resolverExecuted": return `({ ...args, appNamespace: args.namespaceName, ${ACTOR_TRANSFORM_EXPR}, success: !!args.succeeded, result: args.succeeded?.result.resolver, error: args.failed?.error, ${envExpr} })`;
2421
- case "incomingWebhook": return `({ ...args, appNamespace: args.namespaceName, rawBody: args.raw_body, ${envExpr} })`;
2422
- default: throw new Error(`Unknown trigger kind for args expression: ${triggerKind}`);
2423
- }
2424
- }
2425
- /**
2426
- * Build the operationHook expression for resolver pipelines.
2427
- *
2428
- * Transforms server context to SDK resolver context:
2429
- * context.args → input
2430
- * context.pipeline → spread into result
2431
- * user (global var) → TailorUser (via tailorUserMap: workspace_id→workspaceId, attribute_map→attributes, attributes→attributeList)
2432
- * env → injected as JSON
2433
- * @param env - Application env record to embed in the expression
2434
- * @returns A JavaScript expression string for the operationHook
2435
- */
2436
- function buildResolverOperationHookExpr(env) {
2437
- return `({ ...context.pipeline, input: context.args, user: ${tailorUserMap}, env: ${JSON.stringify(env)} });`;
2438
- }
2439
-
2440
- //#endregion
2441
- //#region src/cli/commands/apply/function-registry.ts
2442
- const CHUNK_SIZE = 64 * 1024;
2443
- /**
2444
- * Compute SHA-256 content hash for a script string.
2445
- * @param content - Script content to hash
2446
- * @returns Hex-encoded SHA-256 hash
2447
- */
2448
- function computeContentHash(content) {
2449
- return crypto.createHash("sha256").update(content, "utf-8").digest("hex");
2450
- }
2451
- function functionRegistryTrn(workspaceId, name) {
2452
- return `trn:v1:workspace:${workspaceId}:function_registry:${name}`;
2453
- }
2454
- /**
2455
- * Build a function registry name for a resolver.
2456
- * @param namespace - Resolver namespace
2457
- * @param resolverName - Resolver name
2458
- * @returns Function registry name
2459
- */
2460
- function resolverFunctionName(namespace, resolverName) {
2461
- return `resolver--${namespace}--${resolverName}`;
2462
- }
2463
- /**
2464
- * Build a function registry name for an executor.
2465
- * @param executorName - Executor name
2466
- * @returns Function registry name
2467
- */
2468
- function executorFunctionName(executorName) {
2469
- return `executor--${executorName}`;
2470
- }
2471
- /**
2472
- * Build a function registry name for a workflow job.
2473
- * @param jobName - Workflow job name
2474
- * @returns Function registry name
2475
- */
2476
- function workflowJobFunctionName(jobName) {
2477
- return `workflow--${jobName}`;
2478
- }
2479
- /**
2480
- * Collect all function entries from bundled scripts for all services.
2481
- * @param application - Application definition
2482
- * @param workflowJobs - Collected workflow jobs from config
2483
- * @returns Array of function entries to register
2484
- */
2485
- function collectFunctionEntries(application, workflowJobs) {
2486
- const entries = [];
2487
- const distDir = getDistDir();
2488
- for (const app of application.applications) for (const pipeline of app.resolverServices) for (const resolver of Object.values(pipeline.resolvers)) {
2489
- const scriptPath = path.join(distDir, "resolvers", `${resolver.name}.js`);
2490
- try {
2491
- const content = fs$1.readFileSync(scriptPath, "utf-8");
2492
- entries.push({
2493
- name: resolverFunctionName(pipeline.namespace, resolver.name),
2494
- scriptContent: content,
2495
- contentHash: computeContentHash(content),
2496
- description: `Resolver: ${pipeline.namespace}/${resolver.name}`
2497
- });
2498
- } catch {
2499
- logger.warn(`Function file not found: ${scriptPath}`);
2500
- }
2501
- }
2502
- if (application.executorService) {
2503
- const executors = application.executorService.executors;
2504
- for (const executor of Object.values(executors)) if (executor.operation.kind === "function" || executor.operation.kind === "jobFunction") {
2505
- const scriptPath = path.join(distDir, "executors", `${executor.name}.js`);
2506
- try {
2507
- const content = fs$1.readFileSync(scriptPath, "utf-8");
2508
- entries.push({
2509
- name: executorFunctionName(executor.name),
2510
- scriptContent: content,
2511
- contentHash: computeContentHash(content),
2512
- description: `Executor: ${executor.name}`
2513
- });
2514
- } catch {
2515
- logger.warn(`Function file not found: ${scriptPath}`);
2516
- }
2517
- }
2518
- }
2519
- for (const job of workflowJobs) {
2520
- const scriptPath = path.join(distDir, "workflow-jobs", `${job.name}.js`);
2521
- try {
2522
- const content = fs$1.readFileSync(scriptPath, "utf-8");
2523
- entries.push({
2524
- name: workflowJobFunctionName(job.name),
2525
- scriptContent: content,
2526
- contentHash: computeContentHash(content),
2527
- description: `Workflow job: ${job.name}`
2528
- });
2529
- } catch {
2530
- logger.warn(`Function file not found: ${scriptPath}`);
2531
- }
2668
+ logger.warn("Existing resources not tracked by tailor-sdk were found:");
2669
+ logger.log(` ${styles.info("Resources")}:`);
2670
+ for (const r of resources) logger.log(` • ${styles.bold(r.resourceType)} ${styles.info(`"${r.resourceName}"`)}`);
2671
+ logger.newline();
2672
+ logger.log(" These resources may have been created by older SDK versions, Terraform, or CUE.");
2673
+ logger.log(" To continue, confirm that tailor-sdk should manage them.");
2674
+ logger.log(" If they are managed by another tool (e.g., Terraform), cancel and manage them there instead.");
2675
+ if (yes) {
2676
+ logger.success(`Adding to "${appName}" (--yes flag specified)...`, { mode: "plain" });
2677
+ return;
2532
2678
  }
2533
- return entries;
2679
+ if (!await prompt.confirm({
2680
+ message: `Allow tailor-sdk to manage these resources for "${appName}"?`,
2681
+ default: false
2682
+ })) throw new Error(ml`
2683
+ Apply cancelled. Resources remain unmanaged.
2684
+ To override, run again and confirm, or use --yes flag.
2685
+ `);
2534
2686
  }
2535
2687
  /**
2536
- * Plan function registry changes based on current and desired state.
2537
- * @param client - Operator client instance
2538
- * @param workspaceId - Workspace ID
2539
- * @param appName - Application name
2540
- * @param entries - Desired function entries
2541
- * @returns Planned changes
2688
+ * Confirm deletion of important resources.
2689
+ * @param resources - Resources scheduled for deletion
2690
+ * @param yes - Whether to auto-confirm without prompting
2691
+ * @returns Promise that resolves when confirmation completes
2542
2692
  */
2543
- async function planFunctionRegistry(client, workspaceId, appName, entries) {
2544
- const changeSet = createChangeSet("Function registry");
2545
- const conflicts = [];
2546
- const unmanaged = [];
2547
- const resourceOwners = /* @__PURE__ */ new Set();
2548
- const existingFunctions = await fetchAll(async (pageToken, maxPageSize) => {
2549
- try {
2550
- const response = await client.listFunctionRegistries({
2551
- workspaceId,
2552
- pageToken,
2553
- pageSize: maxPageSize
2554
- });
2555
- return [response.functions.map((f) => ({
2556
- name: f.name,
2557
- contentHash: f.contentHash
2558
- })), response.nextPageToken];
2559
- } catch (error) {
2560
- if (error instanceof ConnectError && error.code === Code.NotFound) return [[], ""];
2561
- throw error;
2562
- }
2563
- });
2564
- const existingMap = {};
2565
- await Promise.all(existingFunctions.map(async (func) => {
2566
- const { metadata } = await client.getMetadata({ trn: functionRegistryTrn(workspaceId, func.name) });
2567
- existingMap[func.name] = {
2568
- resource: func,
2569
- label: metadata?.labels[sdkNameLabelKey]
2570
- };
2571
- }));
2572
- for (const entry of entries) {
2573
- const existing = existingMap[entry.name];
2574
- const metaRequest = await buildMetaRequest(functionRegistryTrn(workspaceId, entry.name), appName);
2575
- if (existing) {
2576
- if (!existing.label) unmanaged.push({
2577
- resourceType: "Function registry",
2578
- resourceName: entry.name
2579
- });
2580
- else if (existing.label !== appName) conflicts.push({
2581
- resourceType: "Function registry",
2582
- resourceName: entry.name,
2583
- currentOwner: existing.label
2584
- });
2585
- changeSet.updates.push({
2586
- name: entry.name,
2587
- entry,
2588
- metaRequest
2589
- });
2590
- delete existingMap[entry.name];
2591
- } else changeSet.creates.push({
2592
- name: entry.name,
2593
- entry,
2594
- metaRequest
2595
- });
2596
- }
2597
- for (const [name, existing] of Object.entries(existingMap)) {
2598
- if (!existing) continue;
2599
- const label = existing.label;
2600
- if (label && label !== appName) resourceOwners.add(label);
2601
- if (label === appName) changeSet.deletes.push({
2602
- name,
2603
- workspaceId
2604
- });
2693
+ async function confirmImportantResourceDeletion(resources, yes) {
2694
+ if (resources.length === 0) return;
2695
+ logger.warn("The following resources will be deleted:");
2696
+ logger.log(` ${styles.info("Resources")}:`);
2697
+ for (const r of resources) logger.log(` • ${styles.bold(r.resourceType)} ${styles.error(`"${r.resourceName}"`)}`);
2698
+ logger.newline();
2699
+ logger.log(styles.warning(" Deleting these resources will permanently remove all associated data."));
2700
+ if (yes) {
2701
+ logger.success("Deleting resources (--yes flag specified)...", { mode: "plain" });
2702
+ return;
2605
2703
  }
2606
- changeSet.print();
2607
- return {
2608
- changeSet,
2609
- conflicts,
2610
- unmanaged,
2611
- resourceOwners
2612
- };
2704
+ if (!await prompt.confirm({
2705
+ message: "Are you sure you want to delete these resources?",
2706
+ default: false
2707
+ })) throw new Error(ml`
2708
+ Apply cancelled. Resources will not be deleted.
2709
+ To override, run again and confirm, or use --yes flag.
2710
+ `);
2613
2711
  }
2712
+
2713
+ //#endregion
2714
+ //#region src/cli/shared/runtime-args.ts
2614
2715
  /**
2615
- * Upload a function script to the function registry using client streaming.
2616
- * @param client - Operator client instance
2617
- * @param workspaceId - Workspace ID
2618
- * @param entry - Function entry to upload
2619
- * @param isCreate - Whether this is a create (true) or update (false)
2716
+ * Runtime args transformation for all services.
2717
+ *
2718
+ * Each service transforms server-side args/context into SDK-friendly format:
2719
+ * - Executor: server-side expression evaluated by platform before calling function
2720
+ * - Resolver: operationHook expression evaluated by platform before calling function
2721
+ *
2722
+ * The user field mapping (server → SDK) shared across services is defined in
2723
+ * `@/parser/service/tailordb` as `tailorUserMap`.
2620
2724
  */
2621
- async function uploadFunctionScript(client, workspaceId, entry, isCreate) {
2622
- const buffer = Buffer.from(entry.scriptContent, "utf-8");
2623
- const info = {
2624
- workspaceId,
2625
- name: entry.name,
2626
- description: entry.description,
2627
- sizeBytes: BigInt(buffer.length),
2628
- contentHash: entry.contentHash
2629
- };
2630
- if (isCreate) {
2631
- /** @yields {MessageInitShape<typeof CreateFunctionRegistryRequestSchema>} Create request messages (info header followed by content chunks) */
2632
- async function* createStream() {
2633
- yield { payload: {
2634
- case: "info",
2635
- value: info
2636
- } };
2637
- for (let i = 0; i < buffer.length; i += CHUNK_SIZE) yield { payload: {
2638
- case: "chunk",
2639
- value: buffer.subarray(i, Math.min(i + CHUNK_SIZE, buffer.length))
2640
- } };
2641
- }
2642
- await client.createFunctionRegistry(createStream());
2643
- } else {
2644
- /** @yields {MessageInitShape<typeof UpdateFunctionRegistryRequestSchema>} Update request messages (info header followed by content chunks) */
2645
- async function* updateStream() {
2646
- yield { payload: {
2647
- case: "info",
2648
- value: info
2649
- } };
2650
- for (let i = 0; i < buffer.length; i += CHUNK_SIZE) yield { payload: {
2651
- case: "chunk",
2652
- value: buffer.subarray(i, Math.min(i + CHUNK_SIZE, buffer.length))
2653
- } };
2654
- }
2655
- await client.updateFunctionRegistry(updateStream());
2725
+ /**
2726
+ * Actor field transformation expression.
2727
+ *
2728
+ * Transforms the server's actor object to match the SDK's TailorActor type:
2729
+ * server `attributeMap` → SDK `attributes`
2730
+ * server `attributes` → SDK `attributeList`
2731
+ * other fields → passed through
2732
+ * null/undefined actor → null
2733
+ */
2734
+ const ACTOR_TRANSFORM_EXPR = "actor: args.actor ? (({ attributeMap, attributes: attrList, ...rest }) => ({ ...rest, attributes: attributeMap, attributeList: attrList }))(args.actor) : null";
2735
+ /**
2736
+ * Build the JavaScript expression that transforms server-format executor event
2737
+ * args into SDK-format args at runtime.
2738
+ *
2739
+ * The Tailor Platform server delivers event args with server-side field names.
2740
+ * The SDK exposes different field names to user code. This function produces a
2741
+ * JavaScript expression string that performs the mapping when evaluated
2742
+ * server-side.
2743
+ * @param triggerKind - The trigger kind discriminant from the parsed executor
2744
+ * @param env - Application env record to embed in the expression
2745
+ * @returns A JavaScript expression string, e.g. `({ ...args, ... })`
2746
+ */
2747
+ function buildExecutorArgsExpr(triggerKind, env) {
2748
+ const envExpr = `env: ${JSON.stringify(env)}`;
2749
+ switch (triggerKind) {
2750
+ case "schedule":
2751
+ case "recordCreated":
2752
+ case "recordUpdated":
2753
+ case "recordDeleted":
2754
+ case "idpUserCreated":
2755
+ case "idpUserUpdated":
2756
+ case "idpUserDeleted":
2757
+ case "authAccessTokenIssued":
2758
+ case "authAccessTokenRefreshed":
2759
+ case "authAccessTokenRevoked": return `({ ...args, appNamespace: args.namespaceName, ${ACTOR_TRANSFORM_EXPR}, ${envExpr} })`;
2760
+ case "resolverExecuted": return `({ ...args, appNamespace: args.namespaceName, ${ACTOR_TRANSFORM_EXPR}, success: !!args.succeeded, result: args.succeeded?.result.resolver, error: args.failed?.error, ${envExpr} })`;
2761
+ case "incomingWebhook": return `({ ...args, appNamespace: args.namespaceName, rawBody: args.raw_body, ${envExpr} })`;
2762
+ default: throw new Error(`Unknown trigger kind for args expression: ${triggerKind}`);
2656
2763
  }
2657
2764
  }
2658
2765
  /**
2659
- * Apply function registry changes for the given phase.
2660
- * @param client - Operator client instance
2661
- * @param workspaceId - Workspace ID
2662
- * @param result - Planned function registry changes
2663
- * @param phase - Apply phase
2766
+ * Build the operationHook expression for resolver pipelines.
2767
+ *
2768
+ * Transforms server context to SDK resolver context:
2769
+ * context.args → input
2770
+ * context.pipeline → spread into result
2771
+ * user (global var) → TailorUser (via tailorUserMap: workspace_id→workspaceId, attribute_map→attributes, attributes→attributeList)
2772
+ * env → injected as JSON
2773
+ * @param env - Application env record to embed in the expression
2774
+ * @returns A JavaScript expression string for the operationHook
2664
2775
  */
2665
- async function applyFunctionRegistry(client, workspaceId, result, phase = "create-update") {
2666
- const { changeSet } = result;
2667
- if (phase === "create-update") {
2668
- for (const create of changeSet.creates) {
2669
- await uploadFunctionScript(client, workspaceId, create.entry, true);
2670
- await client.setMetadata(create.metaRequest);
2671
- }
2672
- for (const update of changeSet.updates) {
2673
- await uploadFunctionScript(client, workspaceId, update.entry, false);
2674
- await client.setMetadata(update.metaRequest);
2675
- }
2676
- } else if (phase === "delete") await Promise.all(changeSet.deletes.map((del) => client.deleteFunctionRegistry({
2677
- workspaceId: del.workspaceId,
2678
- name: del.name
2679
- })));
2776
+ function buildResolverOperationHookExpr(env) {
2777
+ return `({ ...context.pipeline, input: context.args, user: ${tailorUserMap}, env: ${JSON.stringify(env)} });`;
2680
2778
  }
2681
2779
 
2682
2780
  //#endregion
@@ -3181,7 +3279,8 @@ function processResolver(namespace, resolver, executorUsedResolvers, env) {
3181
3279
  operationType: PipelineResolver_OperationType.FUNCTION,
3182
3280
  operationSourceRef: resolverFunctionName(namespace, resolver.name),
3183
3281
  operationHook: { expr: buildResolverOperationHookExpr(env) },
3184
- postScript: `args.body`
3282
+ postScript: `args.body`,
3283
+ invoker: resolver.authInvoker
3185
3284
  }];
3186
3285
  const typeBaseName = inflection.camelize(resolver.name);
3187
3286
  const inputs = resolver.input ? protoFields(resolver.input, `${typeBaseName}Input`, true) : [];
@@ -9685,6 +9784,17 @@ const removeCommand$1 = defineAppCommand({
9685
9784
  }
9686
9785
  });
9687
9786
 
9787
+ //#endregion
9788
+ //#region src/cli/shared/beta.ts
9789
+ /**
9790
+ * Warn that a feature is in beta.
9791
+ * @param {string} featureName - Name of the beta feature (e.g., "tailordb erd", "tailordb migration")
9792
+ */
9793
+ function logBetaWarning(featureName) {
9794
+ logger.warn(`The '${featureName}' command is a beta feature and may introduce breaking changes in future releases.`);
9795
+ logger.newline();
9796
+ }
9797
+
9688
9798
  //#endregion
9689
9799
  //#region src/cli/commands/show.ts
9690
9800
  function applicationInfo(app) {
@@ -9742,17 +9852,6 @@ const showCommand = defineAppCommand({
9742
9852
  }
9743
9853
  });
9744
9854
 
9745
- //#endregion
9746
- //#region src/cli/shared/beta.ts
9747
- /**
9748
- * Warn that a feature is in beta.
9749
- * @param {string} featureName - Name of the beta feature (e.g., "tailordb erd", "tailordb migration")
9750
- */
9751
- function logBetaWarning(featureName) {
9752
- logger.warn(`The '${featureName}' command is a beta feature and may introduce breaking changes in future releases.`);
9753
- logger.newline();
9754
- }
9755
-
9756
9855
  //#endregion
9757
9856
  //#region src/cli/shared/editor.ts
9758
9857
  const DEFAULT_EDITOR = "editor";
@@ -10326,7 +10425,7 @@ async function generate(options) {
10326
10425
  if (options.init) await handleInitOption(namespacesWithMigrations, options.yes);
10327
10426
  let pluginManager;
10328
10427
  if (plugins.length > 0) pluginManager = new PluginManager(plugins);
10329
- const { defineApplication } = await import("./application-DegTCDd8.mjs");
10428
+ const { defineApplication } = await import("./application-WyZetOky.mjs");
10330
10429
  const application = defineApplication({
10331
10430
  config,
10332
10431
  pluginManager
@@ -12504,5 +12603,5 @@ function printGqlResult(result, options = {}) {
12504
12603
  }
12505
12604
 
12506
12605
  //#endregion
12507
- export { listExecutors as $, truncate as A, getLatestMigrationNumber as At, listOAuth2Clients as B, hasChanges as Bt, listCommand$2 as C, INITIAL_SCHEMA_NUMBER as Ct, resumeWorkflow as D, compareSnapshots as Dt, resumeCommand as E, compareLocalTypesWithSnapshot as Et, show as F, isValidMigrationNumber as Ft, listCommand$5 as G, apiCall as Gt, getOAuth2Client as H, prompt as Ht, showCommand as I, loadDiff as It, listWebhookExecutors as J, commonArgs as Jt, listMachineUsers as K, apiCommand as Kt, remove as L, reconstructSnapshotFromMigrations as Lt, generate as M, getMigrationFilePath as Mt, generateCommand as N, getMigrationFiles as Nt, listCommand$3 as O, createSnapshotFromLocalTypes as Ot, logBetaWarning as P, getNextMigrationNumber as Pt, listCommand$6 as Q, workspaceArgs as Qt, removeCommand$1 as R, formatDiffSummary as Rt, listApps as S, DIFF_FILE_NAME as St, healthCommand as T, SCHEMA_FILE_NAME as Tt, getMachineUserToken as U, trnPrefix as Ut, getCommand$1 as V, getNamespacesWithMigrations as Vt, tokenCommand as W, generateUserTypes as Wt, triggerCommand as X, deploymentArgs as Xt, webhookCommand as Y, confirmationArgs as Yt, triggerExecutor as Z, isVerbose as Zt, getWorkspace as _, waitForExecution$1 as _t, updateUser as a, startWorkflow as at, createCommand as b, bundleMigrationScript as bt, listCommand as c, executionsCommand as ct, inviteUser as d, functionExecutionStatusToString as dt, getExecutorJob as et, restoreCommand as f, formatKeyValueTable as ft, getCommand as g, executeScript as gt, listWorkspaces as h, apply as ht, updateCommand as i, startCommand as it, truncateCommand as j, getMigrationDirPath as jt, listWorkflows as k, formatMigrationNumber as kt, listUsers as l, getWorkflowExecution as lt, listCommand$1 as m, getExecutor as mt, queryCommand as n, listExecutorJobs as nt, removeCommand as o, getCommand$2 as ot, restoreWorkspace as p, getCommand$3 as pt, generate$1 as q, defineAppCommand as qt, isCLIError as r, watchExecutorJob as rt, removeUser as s, getWorkflow as st, query as t, jobsCommand as tt, inviteCommand as u, listWorkflowExecutions as ut, deleteCommand as v, MIGRATION_LABEL_KEY as vt, getAppHealth as w, MIGRATE_FILE_NAME as wt, createWorkspace as x, DB_TYPES_FILE_NAME as xt, deleteWorkspace as y, parseMigrationLabelNumber as yt, listCommand$4 as z, formatMigrationDiff as zt };
12508
- //# sourceMappingURL=query-kb_4EQp4.mjs.map
12606
+ export { listExecutors as $, truncate as A, getLatestMigrationNumber as At, listOAuth2Clients as B, hasChanges as Bt, listCommand$2 as C, INITIAL_SCHEMA_NUMBER as Ct, resumeWorkflow as D, compareSnapshots as Dt, resumeCommand as E, compareLocalTypesWithSnapshot as Et, showCommand as F, isValidMigrationNumber as Ft, listCommand$5 as G, apiCall as Gt, getOAuth2Client as H, prompt as Ht, logBetaWarning as I, loadDiff as It, listWebhookExecutors as J, commonArgs as Jt, listMachineUsers as K, apiCommand as Kt, remove as L, reconstructSnapshotFromMigrations as Lt, generate as M, getMigrationFilePath as Mt, generateCommand as N, getMigrationFiles as Nt, listCommand$3 as O, createSnapshotFromLocalTypes as Ot, show as P, getNextMigrationNumber as Pt, listCommand$6 as Q, workspaceArgs as Qt, removeCommand$1 as R, formatDiffSummary as Rt, listApps as S, DIFF_FILE_NAME as St, healthCommand as T, SCHEMA_FILE_NAME as Tt, getMachineUserToken as U, trnPrefix as Ut, getCommand$1 as V, getNamespacesWithMigrations as Vt, tokenCommand as W, generateUserTypes as Wt, triggerCommand as X, deploymentArgs as Xt, webhookCommand as Y, confirmationArgs as Yt, triggerExecutor as Z, isVerbose as Zt, getWorkspace as _, waitForExecution$1 as _t, updateUser as a, startWorkflow as at, createCommand as b, bundleMigrationScript as bt, listCommand as c, executionsCommand as ct, inviteUser as d, functionExecutionStatusToString as dt, getExecutorJob as et, restoreCommand as f, formatKeyValueTable as ft, getCommand as g, executeScript as gt, listWorkspaces as h, apply as ht, updateCommand as i, startCommand as it, truncateCommand as j, getMigrationDirPath as jt, listWorkflows as k, formatMigrationNumber as kt, listUsers as l, getWorkflowExecution as lt, listCommand$1 as m, getExecutor as mt, queryCommand as n, listExecutorJobs as nt, removeCommand as o, getCommand$2 as ot, restoreWorkspace as p, getCommand$3 as pt, generate$1 as q, defineAppCommand as qt, isCLIError as r, watchExecutorJob as rt, removeUser as s, getWorkflow as st, query as t, jobsCommand as tt, inviteCommand as u, listWorkflowExecutions as ut, deleteCommand as v, MIGRATION_LABEL_KEY as vt, getAppHealth as w, MIGRATE_FILE_NAME as wt, createWorkspace as x, DB_TYPES_FILE_NAME as xt, deleteWorkspace as y, parseMigrationLabelNumber as yt, listCommand$4 as z, formatMigrationDiff as zt };
12607
+ //# sourceMappingURL=query-CgGbAmUg.mjs.map