@tailor-platform/sdk 1.47.1 → 1.49.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.
- package/CHANGELOG.md +95 -0
- package/dist/{actor-jk4-f0yp.d.mts → actor-BeIEiPYM.d.mts} +2 -2
- package/dist/{application-C7H7y0hS.mjs → application-CZMzt9jL.mjs} +82 -18
- package/dist/application-CZMzt9jL.mjs.map +1 -0
- package/dist/application-v_E2W-Fz.mjs +4 -0
- package/dist/brand-D-d15jx3.mjs.map +1 -1
- package/dist/cli/index.mjs +55 -31
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/lib.d.mts +6 -6
- package/dist/cli/lib.mjs +6 -6
- package/dist/cli/lib.mjs.map +1 -1
- package/dist/cli/skills.mjs.map +1 -1
- package/dist/{client-DCqdtFte.mjs → client-CPW1N1Rs.mjs} +1 -1
- package/dist/{client-DbyKSN1F.mjs → client-_kHh0Pip.mjs} +2 -2
- package/dist/{client-DbyKSN1F.mjs.map → client-_kHh0Pip.mjs.map} +1 -1
- package/dist/configure/index.d.mts +4 -4
- package/dist/configure/index.mjs +51 -3
- package/dist/configure/index.mjs.map +1 -1
- package/dist/{crashreport-CNSw_BrJ.mjs → crashreport-CvmdFs4i.mjs} +5 -5
- package/dist/crashreport-CvmdFs4i.mjs.map +1 -0
- package/dist/{crashreport-DXGFd16F.mjs → crashreport-DHJuSmUc.mjs} +1 -1
- package/dist/enum-constants-C3KSpsYj.mjs.map +1 -1
- package/dist/{errors-wNQxQQBH.mjs → errors-pMPXghkO.mjs} +1 -1
- package/dist/{errors-wNQxQQBH.mjs.map → errors-pMPXghkO.mjs.map} +1 -1
- package/dist/field-DLSIuMTu.mjs.map +1 -1
- package/dist/file-utils-DjNi_3U_.mjs.map +1 -1
- package/dist/index-BQ4oi0AI.d.mts +48 -0
- package/dist/{index-BbOTbZFf.d.mts → index-BjXN1SdY.d.mts} +2 -2
- package/dist/{index-DB8EapT-.d.mts → index-C--7W0UO.d.mts} +5 -5
- package/dist/{index-BRvNi5q9.d.mts → index-VJW98BSy.d.mts} +2 -2
- package/dist/{index-iy-hNfGp.d.mts → index-nV4ZC_Ve.d.mts} +2 -2
- package/dist/{interceptor-CBsqEWDK.mjs → interceptor-DTNS0EtF.mjs} +1 -1
- package/dist/{interceptor-CBsqEWDK.mjs.map → interceptor-DTNS0EtF.mjs.map} +1 -1
- package/dist/{job-R5C2Hfcc.mjs → job-M3Avv_SV.mjs} +4 -3
- package/dist/{job-R5C2Hfcc.mjs.map → job-M3Avv_SV.mjs.map} +1 -1
- package/dist/kysely/index.mjs.map +1 -1
- package/dist/kysely-type-B8aRz_oC.mjs.map +1 -1
- package/dist/logger-DTNAMYGy.mjs.map +1 -1
- package/dist/{mock-BP-9O5On.mjs → mock-BfL09ULZ.mjs} +1 -1
- package/dist/{mock-BP-9O5On.mjs.map → mock-BfL09ULZ.mjs.map} +1 -1
- package/dist/multiline-e3IpANmS.mjs.map +1 -1
- package/dist/package-json-6Px8bDpG.mjs.map +1 -1
- package/dist/plugin/builtin/enum-constants/index.d.mts +1 -1
- package/dist/plugin/builtin/file-utils/index.d.mts +1 -1
- package/dist/plugin/builtin/kysely-type/index.d.mts +1 -1
- package/dist/plugin/builtin/seed/index.d.mts +1 -1
- package/dist/plugin/builtin/seed/index.mjs +1 -1
- package/dist/plugin/index.d.mts +2 -2
- package/dist/plugin/index.mjs.map +1 -1
- package/dist/{repl-editor-CZpLlOBj.mjs → repl-editor-jZ493eQI.mjs} +1 -1
- package/dist/{repl-editor-CZpLlOBj.mjs.map → repl-editor-jZ493eQI.mjs.map} +1 -1
- package/dist/{runtime-XjP6JMmP.mjs → runtime-oZgK353r.mjs} +484 -132
- package/dist/runtime-oZgK353r.mjs.map +1 -0
- package/dist/{tailordb-DjlNUV6u.mjs → schema-C5QjYEc-.mjs} +2 -42
- package/dist/schema-C5QjYEc-.mjs.map +1 -0
- package/dist/secret-file-BHpxGyNf.mjs +65 -0
- package/dist/secret-file-BHpxGyNf.mjs.map +1 -0
- package/dist/seed/index.mjs.map +1 -1
- package/dist/{seed-DrKY5yIF.mjs → seed-DjfAn0BC.mjs} +44 -19
- package/dist/seed-DjfAn0BC.mjs.map +1 -0
- package/dist/{service-obEU5gSM.mjs → service-DCgJxdg1.mjs} +2 -2
- package/dist/{service-obEU5gSM.mjs.map → service-DCgJxdg1.mjs.map} +1 -1
- package/dist/{tailor-db-field-Bn8ZC5lK.d.mts → tailor-db-field-4bMLe25-.d.mts} +5 -1
- package/dist/telemetry-C13VIFpT.mjs +4 -0
- package/dist/{telemetry-DcL8Fsm_.mjs → telemetry-C1Y56L5E.mjs} +1 -1
- package/dist/{telemetry-DcL8Fsm_.mjs.map → telemetry-C1Y56L5E.mjs.map} +1 -1
- package/dist/types-sir9UPht.mjs.map +1 -1
- package/dist/utils/test/index.d.mts +3 -3
- package/dist/utils/test/index.mjs +1 -1
- package/dist/utils/test/index.mjs.map +1 -1
- package/dist/vitest/environment.mjs +1 -1
- package/dist/vitest/environment.mjs.map +1 -1
- package/dist/vitest/index.mjs +1 -1
- package/dist/vitest/index.mjs.map +1 -1
- package/dist/vitest/setup.mjs +1 -1
- package/dist/vitest/setup.mjs.map +1 -1
- package/dist/{workflow.generated-i7PK4fg-.d.mts → workflow.generated-OYAu_6zX.d.mts} +12 -2
- package/docs/cli/application.md +4 -0
- package/docs/cli/workspace.md +20 -17
- package/docs/configuration.md +4 -0
- package/docs/generator/builtin.md +35 -4
- package/package.json +16 -16
- package/postinstall.mjs +1 -1
- package/dist/application-C7H7y0hS.mjs.map +0 -1
- package/dist/application-Csq5jxYP.mjs +0 -4
- package/dist/crashreport-CNSw_BrJ.mjs.map +0 -1
- package/dist/index-BXyS7xKC.d.mts +0 -21
- package/dist/runtime-XjP6JMmP.mjs.map +0 -1
- package/dist/seed-DrKY5yIF.mjs.map +0 -1
- package/dist/tailordb-DjlNUV6u.mjs.map +0 -1
- package/dist/telemetry-21afNV9_.mjs +0 -4
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
|
|
2
|
-
import {
|
|
3
|
-
import { $ as FilterSchema, A as FunctionExecution_Status, B as AuthOAuth2Client_GrantType, C as TailorDBType_Permission_Operator, D as IdPLang, E as PipelineResolver_OperationType, F as AuthConnection_Type, H as AuthSCIMAttribute_Type, I as AuthHookPoint, J as GetApplicationSchemaHealthResponse_ApplicationSchemaHealthStatus, K as TenantProviderConfig_TenantProviderType, L as AuthIDPConfig_AuthType, M as ExecutorJobStatus, N as ExecutorTargetType, O as IdPPermissionOperator, P as ExecutorTriggerType, Q as Condition_Operator, R as AuthInvokerSchema, S as TailorDBGQLPermission_Permit, T as TailorDBType_PermitAction, U as AuthSCIMAttribute_Uniqueness, V as AuthSCIMAttribute_Mutability, W as AuthSCIMConfig_AuthorizationType, X as Subgraph_ServiceType, Y as ApplicationSchemaUpdateAttemptStatus, Z as ConditionSchema, _ as WorkspacePlatformUserRole, a as fetchMachineUserToken, b as TailorDBGQLPermission_Action, d as initOperatorClient, et as PageDirection, g as OperatorService, h as userAgent, i as fetchAll, k as IdPPermissionPermit, m as resolveStaticWebsiteUrls, o as fetchPaged, p as platformBaseUrl, q as UserProfileProviderConfig_UserProfileProviderType, v as WorkflowExecution_Status, w as TailorDBType_Permission_Permit, x as TailorDBGQLPermission_Operator, y as WorkflowJobExecution_Status, z as AuthOAuth2Client_ClientType } from "./client-
|
|
2
|
+
import { t as db } from "./schema-C5QjYEc-.mjs";
|
|
3
|
+
import { $ as FilterSchema, A as FunctionExecution_Status, B as AuthOAuth2Client_GrantType, C as TailorDBType_Permission_Operator, D as IdPLang, E as PipelineResolver_OperationType, F as AuthConnection_Type, H as AuthSCIMAttribute_Type, I as AuthHookPoint, J as GetApplicationSchemaHealthResponse_ApplicationSchemaHealthStatus, K as TenantProviderConfig_TenantProviderType, L as AuthIDPConfig_AuthType, M as ExecutorJobStatus, N as ExecutorTargetType, O as IdPPermissionOperator, P as ExecutorTriggerType, Q as Condition_Operator, R as AuthInvokerSchema, S as TailorDBGQLPermission_Permit, T as TailorDBType_PermitAction, U as AuthSCIMAttribute_Uniqueness, V as AuthSCIMAttribute_Mutability, W as AuthSCIMConfig_AuthorizationType, X as Subgraph_ServiceType, Y as ApplicationSchemaUpdateAttemptStatus, Z as ConditionSchema, _ as WorkspacePlatformUserRole, a as fetchMachineUserToken, b as TailorDBGQLPermission_Action, d as initOperatorClient, et as PageDirection, g as OperatorService, h as userAgent, i as fetchAll, k as IdPPermissionPermit, m as resolveStaticWebsiteUrls, o as fetchPaged, p as platformBaseUrl, q as UserProfileProviderConfig_UserProfileProviderType, v as WorkflowExecution_Status, w as TailorDBType_Permission_Permit, x as TailorDBGQLPermission_Operator, y as WorkflowJobExecution_Status, z as AuthOAuth2Client_ClientType } from "./client-_kHh0Pip.mjs";
|
|
4
4
|
import { a as parseBoolean, i as symbols, n as logger, r as styles, t as CIPromptError } from "./logger-DTNAMYGy.mjs";
|
|
5
|
-
import { C as
|
|
5
|
+
import { C as loadConfigPath, O as writePlatformConfig, S as loadAccessToken, T as readPlatformConfig, _ as getDistDir, d as buildResolverOperationHookExpr, f as OAuth2ClientSchema, g as createBundleCache, h as loadFilesWithIgnores, m as stringifyFunction, n as generatePluginFilesIfNeeded, p as TailorDBTypeSchema, r as loadApplication, s as createExecutorService, t as defineApplication, u as buildExecutorArgsExpr, v as hashFile, w as loadWorkspaceId, y as loadConfig } from "./application-CZMzt9jL.mjs";
|
|
6
6
|
import { t as multiline } from "./multiline-e3IpANmS.mjs";
|
|
7
7
|
import { t as readPackageJson } from "./package-json-6Px8bDpG.mjs";
|
|
8
|
-
import { n as isCLIError, t as createCLIError } from "./errors-
|
|
9
|
-
import { r as withSpan } from "./telemetry-
|
|
8
|
+
import { n as isCLIError, t as createCLIError } from "./errors-pMPXghkO.mjs";
|
|
9
|
+
import { r as withSpan } from "./telemetry-C1Y56L5E.mjs";
|
|
10
10
|
import { arg, createDefineCommand, defineCommand, runCommand } from "politty";
|
|
11
11
|
import { z } from "zod";
|
|
12
12
|
import * as fs$1 from "node:fs";
|
|
@@ -23,7 +23,7 @@ import { xdgConfig } from "xdg-basedir";
|
|
|
23
23
|
import { Code, ConnectError } from "@connectrpc/connect";
|
|
24
24
|
import { resolveTSConfig } from "pkg-types";
|
|
25
25
|
import { ScalarType, create, fromJson, toJson } from "@bufbuild/protobuf";
|
|
26
|
-
import * as crypto from "node:crypto";
|
|
26
|
+
import * as crypto$1 from "node:crypto";
|
|
27
27
|
import { createHash } from "node:crypto";
|
|
28
28
|
import { ExitPromptError } from "@inquirer/core";
|
|
29
29
|
import { confirm, input, password } from "@inquirer/prompts";
|
|
@@ -31,6 +31,7 @@ import { isCI } from "std-env";
|
|
|
31
31
|
import * as rolldown from "rolldown";
|
|
32
32
|
import * as fs from "node:fs/promises";
|
|
33
33
|
import { glob } from "node:fs/promises";
|
|
34
|
+
import { parseSync } from "oxc-parser";
|
|
34
35
|
import * as inflection from "inflection";
|
|
35
36
|
import { setTimeout as setTimeout$1 } from "timers/promises";
|
|
36
37
|
import { spawn } from "node:child_process";
|
|
@@ -266,6 +267,38 @@ function isVerbose() {
|
|
|
266
267
|
*/
|
|
267
268
|
const defineAppCommand = createDefineCommand();
|
|
268
269
|
|
|
270
|
+
//#endregion
|
|
271
|
+
//#region src/cli/shared/readonly-guard.ts
|
|
272
|
+
/**
|
|
273
|
+
* Throw a CLIError if the active profile has `readonly: true`.
|
|
274
|
+
*
|
|
275
|
+
* Resolves the active profile in this order:
|
|
276
|
+
* 1. `opts.profile` (CLI flag)
|
|
277
|
+
* 2. `process.env.TAILOR_PLATFORM_PROFILE`
|
|
278
|
+
*
|
|
279
|
+
* If neither is set, no profile is in scope so the call is allowed. This is
|
|
280
|
+
* intentional: `TAILOR_PLATFORM_TOKEN` direct access (CI / machine user) and
|
|
281
|
+
* `--workspace-id` without a profile are out-of-band paths whose authorization
|
|
282
|
+
* is governed by the bearer token itself, not by the local profile flag.
|
|
283
|
+
*
|
|
284
|
+
* If the resolved profile cannot be found in the config, this function returns
|
|
285
|
+
* silently and lets downstream loaders surface the not-found error.
|
|
286
|
+
* @param opts - Options
|
|
287
|
+
* @param opts.profile - Optional explicit profile name from command args
|
|
288
|
+
*/
|
|
289
|
+
async function assertWritable(opts) {
|
|
290
|
+
const profileName = opts?.profile || process.env.TAILOR_PLATFORM_PROFILE;
|
|
291
|
+
if (!profileName) return;
|
|
292
|
+
const profile = (await readPlatformConfig()).profiles[profileName];
|
|
293
|
+
if (!profile || profile.readonly !== true) return;
|
|
294
|
+
throw createCLIError({
|
|
295
|
+
code: "PROFILE_READONLY",
|
|
296
|
+
message: `Profile "${profileName}" is read-only.`,
|
|
297
|
+
details: "This profile blocks platform-state mutations (apply, create/update/delete, deploy, etc.). Application-data operations remain available because their permissions are governed by the machine user.",
|
|
298
|
+
suggestion: `Use a different profile, unset TAILOR_PLATFORM_PROFILE, or run 'tailor-sdk profile update ${profileName} --permission write'.`
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
|
|
269
302
|
//#endregion
|
|
270
303
|
//#region src/cli/commands/api/api-call.ts
|
|
271
304
|
/**
|
|
@@ -567,6 +600,7 @@ Values already present in \`--body\` are never overridden. If a value cannot be
|
|
|
567
600
|
})
|
|
568
601
|
}).strict(),
|
|
569
602
|
run: async (args) => {
|
|
603
|
+
await assertWritable({ profile: args.profile });
|
|
570
604
|
const methodName = extractMethodName(args.endpoint);
|
|
571
605
|
const method = getMethodDescriptor(methodName);
|
|
572
606
|
const parsedBody = parseBodyAsObject(args.body);
|
|
@@ -1443,6 +1477,11 @@ function trnPrefix(workspaceId) {
|
|
|
1443
1477
|
}
|
|
1444
1478
|
const sdkNameLabelKey = "sdk-name";
|
|
1445
1479
|
const sdkVersionLabelKey = "sdk-version";
|
|
1480
|
+
const sdkAppIdLabelKey = "sdk-app-id";
|
|
1481
|
+
const appIdLabelPrefix = "app-";
|
|
1482
|
+
function toAppIdLabelValue(appId) {
|
|
1483
|
+
return `${appIdLabelPrefix}${appId}`;
|
|
1484
|
+
}
|
|
1446
1485
|
/**
|
|
1447
1486
|
* Check whether existing metadata was produced by the current SDK version.
|
|
1448
1487
|
* @param existingLabels - Labels currently stored on the remote resource
|
|
@@ -1453,13 +1492,33 @@ function hasMatchingSdkVersion(existingLabels, desiredLabels) {
|
|
|
1453
1492
|
return existingLabels?.[sdkVersionLabelKey] === desiredLabels?.[sdkVersionLabelKey];
|
|
1454
1493
|
}
|
|
1455
1494
|
/**
|
|
1495
|
+
* Determine whether a remote resource is owned by the given application.
|
|
1496
|
+
* When the resource carries an `sdk-app-id`, ownership is decided strictly
|
|
1497
|
+
* by id match — a resource explicitly tagged with another app's id is
|
|
1498
|
+
* NOT ours even if the legacy sdk-name happens to match. Resources without
|
|
1499
|
+
* `sdk-app-id` (legacy) fall back to sdk-name comparison.
|
|
1500
|
+
* @param labels - Labels currently stored on the remote resource
|
|
1501
|
+
* @param appName - Application name from the local config
|
|
1502
|
+
* @param appId - Stable application id from the local config (when present)
|
|
1503
|
+
* @returns True when the resource is owned by the application
|
|
1504
|
+
*/
|
|
1505
|
+
function isOwnedByApp(labels, appName, appId) {
|
|
1506
|
+
if (!labels) return false;
|
|
1507
|
+
const labelAppId = labels[sdkAppIdLabelKey];
|
|
1508
|
+
if (labelAppId) return appId !== void 0 && labelAppId === toAppIdLabelValue(appId);
|
|
1509
|
+
return labels[sdkNameLabelKey] === appName;
|
|
1510
|
+
}
|
|
1511
|
+
/**
|
|
1456
1512
|
* Build metadata request with SDK labels.
|
|
1457
|
-
* @param
|
|
1458
|
-
* @param
|
|
1459
|
-
* @param
|
|
1513
|
+
* @param params - Parameters for building the metadata request
|
|
1514
|
+
* @param params.trn - Target TRN
|
|
1515
|
+
* @param params.appName - Application name label
|
|
1516
|
+
* @param params.appId - Stable application id label (when managed by SDK)
|
|
1517
|
+
* @param params.existingLabels - Existing labels to preserve (optional)
|
|
1460
1518
|
* @returns Metadata request
|
|
1461
1519
|
*/
|
|
1462
|
-
async function buildMetaRequest(
|
|
1520
|
+
async function buildMetaRequest(params) {
|
|
1521
|
+
const { trn, appName, appId, existingLabels } = params;
|
|
1463
1522
|
const packageJson = await readPackageJson();
|
|
1464
1523
|
const sdkVersion = packageJson.version ? `v${packageJson.version.replace(/\./g, "-")}` : "unknown";
|
|
1465
1524
|
return {
|
|
@@ -1467,7 +1526,8 @@ async function buildMetaRequest(trn, appName, existingLabels) {
|
|
|
1467
1526
|
labels: {
|
|
1468
1527
|
...existingLabels ?? {},
|
|
1469
1528
|
[sdkNameLabelKey]: appName,
|
|
1470
|
-
[sdkVersionLabelKey]: sdkVersion
|
|
1529
|
+
[sdkVersionLabelKey]: sdkVersion,
|
|
1530
|
+
...appId ? { [sdkAppIdLabelKey]: toAppIdLabelValue(appId) } : {}
|
|
1471
1531
|
}
|
|
1472
1532
|
};
|
|
1473
1533
|
}
|
|
@@ -1568,11 +1628,20 @@ async function planApplication(context) {
|
|
|
1568
1628
|
}
|
|
1569
1629
|
});
|
|
1570
1630
|
if (forRemoval) {
|
|
1571
|
-
|
|
1572
|
-
|
|
1631
|
+
const ownedAppNames = /* @__PURE__ */ new Set();
|
|
1632
|
+
if (existingApplications.some((app) => app.name === application.name)) ownedAppNames.add(application.name);
|
|
1633
|
+
if (application.id) {
|
|
1634
|
+
const others = existingApplications.filter((app) => !ownedAppNames.has(app.name));
|
|
1635
|
+
const owned = await Promise.all(others.map(async (app) => {
|
|
1636
|
+
return isOwnedByApp(await fetchAppLabels(client, workspaceId, app.name), application.name, application.id) ? app.name : null;
|
|
1637
|
+
}));
|
|
1638
|
+
for (const name of owned) if (name) ownedAppNames.add(name);
|
|
1639
|
+
}
|
|
1640
|
+
for (const name of ownedAppNames) changeSet.deletes.push({
|
|
1641
|
+
name,
|
|
1573
1642
|
request: {
|
|
1574
1643
|
workspaceId,
|
|
1575
|
-
applicationName:
|
|
1644
|
+
applicationName: name
|
|
1576
1645
|
}
|
|
1577
1646
|
});
|
|
1578
1647
|
return changeSet;
|
|
@@ -1602,7 +1671,11 @@ async function planApplication(context) {
|
|
|
1602
1671
|
});
|
|
1603
1672
|
if (idpConfigs.length > 0) authIdpConfigName = idpConfigs[0].name;
|
|
1604
1673
|
}
|
|
1605
|
-
const metaRequest = await buildMetaRequest(
|
|
1674
|
+
const metaRequest = await buildMetaRequest({
|
|
1675
|
+
trn: trn$6(workspaceId, application.name),
|
|
1676
|
+
appName: application.name,
|
|
1677
|
+
appId: application.id
|
|
1678
|
+
});
|
|
1606
1679
|
const expectedLocalWebsites = new Set(application.staticWebsiteServices.map((website) => website.name));
|
|
1607
1680
|
const resolvedCors = await resolveStaticWebsiteUrls(client, workspaceId, application.config.cors, "CORS", { expectedLocalNames: expectedLocalWebsites });
|
|
1608
1681
|
const desired = normalizeComparableApplication(application, authNamespace, authIdpConfigName, resolvedCors);
|
|
@@ -1617,9 +1690,22 @@ async function planApplication(context) {
|
|
|
1617
1690
|
disableIntrospection: application.config.disableIntrospection
|
|
1618
1691
|
};
|
|
1619
1692
|
const existing = existingApplications.find((app) => app.name === application.name);
|
|
1693
|
+
if (application.id) {
|
|
1694
|
+
const otherApps = existingApplications.filter((app) => app.name !== application.name);
|
|
1695
|
+
const renamedAway = await Promise.all(otherApps.map(async (app) => {
|
|
1696
|
+
return isOwnedByApp(await fetchAppLabels(client, workspaceId, app.name), application.name, application.id) ? app.name : null;
|
|
1697
|
+
}));
|
|
1698
|
+
for (const name of renamedAway) if (name) changeSet.deletes.push({
|
|
1699
|
+
name,
|
|
1700
|
+
request: {
|
|
1701
|
+
workspaceId,
|
|
1702
|
+
applicationName: name
|
|
1703
|
+
}
|
|
1704
|
+
});
|
|
1705
|
+
}
|
|
1620
1706
|
if (existing) {
|
|
1621
|
-
const
|
|
1622
|
-
if (
|
|
1707
|
+
const labels = await fetchAppLabels(client, workspaceId, application.name);
|
|
1708
|
+
if (isOwnedByApp(labels, application.name, application.id) && hasMatchingSdkVersion(labels, metaRequest.labels) && areApplicationsEqual(existing, desired)) changeSet.unchanged.push({ name: application.name });
|
|
1623
1709
|
else changeSet.updates.push({
|
|
1624
1710
|
name: application.name,
|
|
1625
1711
|
request,
|
|
@@ -1632,6 +1718,15 @@ async function planApplication(context) {
|
|
|
1632
1718
|
});
|
|
1633
1719
|
return changeSet;
|
|
1634
1720
|
}
|
|
1721
|
+
async function fetchAppLabels(client, workspaceId, appName) {
|
|
1722
|
+
try {
|
|
1723
|
+
const { metadata } = await client.getMetadata({ trn: trn$6(workspaceId, appName) });
|
|
1724
|
+
return metadata?.labels;
|
|
1725
|
+
} catch (error) {
|
|
1726
|
+
if (error instanceof ConnectError && error.code === Code.NotFound) return;
|
|
1727
|
+
throw error;
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1635
1730
|
function protoSubgraph(subgraph) {
|
|
1636
1731
|
let serviceType;
|
|
1637
1732
|
switch (subgraph.Type) {
|
|
@@ -1757,10 +1852,11 @@ function hasNonSecretFieldChanged(existing, desired) {
|
|
|
1757
1852
|
* @param client - Operator client instance
|
|
1758
1853
|
* @param workspaceId - Workspace ID
|
|
1759
1854
|
* @param appName - Application name for ownership
|
|
1855
|
+
* @param appId - Stable application id (when managed by SDK)
|
|
1760
1856
|
* @param auths - Auth services with connection configs
|
|
1761
1857
|
* @returns Planned changes for auth connections
|
|
1762
1858
|
*/
|
|
1763
|
-
async function planAuthConnections(client, workspaceId, appName, auths) {
|
|
1859
|
+
async function planAuthConnections(client, workspaceId, appName, appId, auths) {
|
|
1764
1860
|
const changeSet = createChangeSet("Auth connections");
|
|
1765
1861
|
const conflicts = [];
|
|
1766
1862
|
const unmanaged = [];
|
|
@@ -1787,7 +1883,8 @@ async function planAuthConnections(client, workspaceId, appName, auths) {
|
|
|
1787
1883
|
const { metadata } = await client.getMetadata({ trn: connectionTrn(workspaceId, resource.name) });
|
|
1788
1884
|
existingConnections[resource.name] = {
|
|
1789
1885
|
resource,
|
|
1790
|
-
label: metadata?.labels[sdkNameLabelKey]
|
|
1886
|
+
label: metadata?.labels[sdkNameLabelKey],
|
|
1887
|
+
allLabels: metadata?.labels
|
|
1791
1888
|
};
|
|
1792
1889
|
} catch (error) {
|
|
1793
1890
|
if (error instanceof ConnectError && error.code === Code.InvalidArgument) {
|
|
@@ -1802,17 +1899,23 @@ async function planAuthConnections(client, workspaceId, appName, auths) {
|
|
|
1802
1899
|
const state = loadSecretsState();
|
|
1803
1900
|
for (const [name, config] of Object.entries(desiredConnections)) {
|
|
1804
1901
|
const existing = existingConnections[name];
|
|
1805
|
-
const metaRequest = metadataSupported ? await buildMetaRequest(
|
|
1902
|
+
const metaRequest = metadataSupported ? await buildMetaRequest({
|
|
1903
|
+
trn: connectionTrn(workspaceId, name),
|
|
1904
|
+
appName,
|
|
1905
|
+
appId
|
|
1906
|
+
}) : void 0;
|
|
1806
1907
|
if (existing) {
|
|
1807
|
-
if (
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1908
|
+
if (!isOwnedByApp(existing.allLabels, appName, appId)) {
|
|
1909
|
+
if (metadataSupported && !existing.label) unmanaged.push({
|
|
1910
|
+
resourceType: "Auth connection",
|
|
1911
|
+
resourceName: name
|
|
1912
|
+
});
|
|
1913
|
+
else if (existing.label) conflicts.push({
|
|
1914
|
+
resourceType: "Auth connection",
|
|
1915
|
+
resourceName: name,
|
|
1916
|
+
currentOwner: existing.label
|
|
1917
|
+
});
|
|
1918
|
+
}
|
|
1816
1919
|
const currentHash = hashConnectionConfig(config);
|
|
1817
1920
|
const storedHash = state.connections?.[name];
|
|
1818
1921
|
if (hasNonSecretFieldChanged(existing.resource, config) || currentHash !== storedHash) changeSet.replaces.push({
|
|
@@ -1834,11 +1937,12 @@ async function planAuthConnections(client, workspaceId, appName, auths) {
|
|
|
1834
1937
|
}
|
|
1835
1938
|
for (const [name, entry] of Object.entries(existingConnections)) {
|
|
1836
1939
|
if (!entry) continue;
|
|
1837
|
-
|
|
1940
|
+
const owned = isOwnedByApp(entry.allLabels, appName, appId);
|
|
1941
|
+
if (entry.label && !owned) {
|
|
1838
1942
|
resourceOwners.add(entry.label);
|
|
1839
1943
|
continue;
|
|
1840
1944
|
}
|
|
1841
|
-
if (
|
|
1945
|
+
if (owned || !metadataSupported) changeSet.deletes.push({
|
|
1842
1946
|
name,
|
|
1843
1947
|
request: {
|
|
1844
1948
|
workspaceId,
|
|
@@ -1934,7 +2038,7 @@ const CHUNK_SIZE = 64 * 1024;
|
|
|
1934
2038
|
* @returns Hex-encoded SHA-256 hash
|
|
1935
2039
|
*/
|
|
1936
2040
|
function computeContentHash(content) {
|
|
1937
|
-
return crypto.createHash("sha256").update(content, "utf-8").digest("hex");
|
|
2041
|
+
return crypto$1.createHash("sha256").update(content, "utf-8").digest("hex");
|
|
1938
2042
|
}
|
|
1939
2043
|
function functionRegistryTrn$1(workspaceId, name) {
|
|
1940
2044
|
return `trn:v1:workspace:${workspaceId}:function_registry:${name}`;
|
|
@@ -2103,10 +2207,11 @@ function filterBundledWorkflowJobs(jobs, usedJobNames) {
|
|
|
2103
2207
|
* @param client - Operator client instance
|
|
2104
2208
|
* @param workspaceId - Workspace ID
|
|
2105
2209
|
* @param appName - Application name
|
|
2210
|
+
* @param appId - Stable application id (when managed by SDK)
|
|
2106
2211
|
* @param entries - Desired function entries
|
|
2107
2212
|
* @returns Planned changes
|
|
2108
2213
|
*/
|
|
2109
|
-
async function planFunctionRegistry(client, workspaceId, appName, entries) {
|
|
2214
|
+
async function planFunctionRegistry(client, workspaceId, appName, appId, entries) {
|
|
2110
2215
|
const changeSet = createChangeSet("Function registry");
|
|
2111
2216
|
const conflicts = [];
|
|
2112
2217
|
const unmanaged = [];
|
|
@@ -2138,19 +2243,23 @@ async function planFunctionRegistry(client, workspaceId, appName, entries) {
|
|
|
2138
2243
|
}));
|
|
2139
2244
|
for (const entry of entries) {
|
|
2140
2245
|
const existing = existingMap[entry.name];
|
|
2141
|
-
const metaRequest = await buildMetaRequest(
|
|
2246
|
+
const metaRequest = await buildMetaRequest({
|
|
2247
|
+
trn: functionRegistryTrn$1(workspaceId, entry.name),
|
|
2248
|
+
appName,
|
|
2249
|
+
appId
|
|
2250
|
+
});
|
|
2142
2251
|
if (existing) {
|
|
2143
|
-
const
|
|
2144
|
-
if (!existing.label) unmanaged.push({
|
|
2252
|
+
const owned = isOwnedByApp(existing.allLabels, appName, appId);
|
|
2253
|
+
if (!owned) if (!existing.label) unmanaged.push({
|
|
2145
2254
|
resourceType: "Function registry",
|
|
2146
2255
|
resourceName: entry.name
|
|
2147
2256
|
});
|
|
2148
|
-
else
|
|
2257
|
+
else conflicts.push({
|
|
2149
2258
|
resourceType: "Function registry",
|
|
2150
2259
|
resourceName: entry.name,
|
|
2151
2260
|
currentOwner: existing.label
|
|
2152
2261
|
});
|
|
2153
|
-
if (existing.resource.contentHash === entry.contentHash &&
|
|
2262
|
+
if (existing.resource.contentHash === entry.contentHash && owned && hasMatchingSdkVersion(existing.allLabels, metaRequest.labels)) changeSet.unchanged.push({ name: entry.name });
|
|
2154
2263
|
else changeSet.updates.push({
|
|
2155
2264
|
name: entry.name,
|
|
2156
2265
|
entry,
|
|
@@ -2166,8 +2275,9 @@ async function planFunctionRegistry(client, workspaceId, appName, entries) {
|
|
|
2166
2275
|
for (const [name, existing] of Object.entries(existingMap)) {
|
|
2167
2276
|
if (!existing) continue;
|
|
2168
2277
|
const label = existing.label;
|
|
2169
|
-
|
|
2170
|
-
if (label
|
|
2278
|
+
const owned = isOwnedByApp(existing.allLabels, appName, appId);
|
|
2279
|
+
if (label && !owned) resourceOwners.add(label);
|
|
2280
|
+
if (owned) changeSet.deletes.push({
|
|
2171
2281
|
name,
|
|
2172
2282
|
workspaceId
|
|
2173
2283
|
});
|
|
@@ -2659,7 +2769,7 @@ async function applyIdP(client, result, phase = "create-update") {
|
|
|
2659
2769
|
async function planIdP(context) {
|
|
2660
2770
|
const { client, workspaceId, application, forRemoval, forceApplyAll = false, idpUserTriggerTargets } = context;
|
|
2661
2771
|
const idps = forRemoval ? [] : application.idpServices;
|
|
2662
|
-
const { changeSet: serviceChangeSet, conflicts, unmanaged, resourceOwners } = await planServices$3(client, workspaceId, application.name, idps, idpUserTriggerTargets ?? /* @__PURE__ */ new Set());
|
|
2772
|
+
const { changeSet: serviceChangeSet, conflicts, unmanaged, resourceOwners } = await planServices$3(client, workspaceId, application.name, application.id, idps, idpUserTriggerTargets ?? /* @__PURE__ */ new Set());
|
|
2663
2773
|
return {
|
|
2664
2774
|
changeSet: {
|
|
2665
2775
|
service: serviceChangeSet,
|
|
@@ -2746,7 +2856,7 @@ function areIdPServicesEqual(existing, desired) {
|
|
|
2746
2856
|
permission: normalizeComparablePermission(existing.permission)
|
|
2747
2857
|
}), desired);
|
|
2748
2858
|
}
|
|
2749
|
-
async function planServices$3(client, workspaceId, appName, idps, idpUserTriggerTargets) {
|
|
2859
|
+
async function planServices$3(client, workspaceId, appName, appId, idps, idpUserTriggerTargets) {
|
|
2750
2860
|
const changeSet = createChangeSet("IdP services");
|
|
2751
2861
|
const conflicts = [];
|
|
2752
2862
|
const unmanaged = [];
|
|
@@ -2777,7 +2887,11 @@ async function planServices$3(client, workspaceId, appName, idps, idpUserTrigger
|
|
|
2777
2887
|
for (const idp of idps) {
|
|
2778
2888
|
const namespaceName = idp.name;
|
|
2779
2889
|
const existing = existingServices[namespaceName];
|
|
2780
|
-
const metaRequest = await buildMetaRequest(
|
|
2890
|
+
const metaRequest = await buildMetaRequest({
|
|
2891
|
+
trn: trn$5(workspaceId, namespaceName),
|
|
2892
|
+
appName,
|
|
2893
|
+
appId
|
|
2894
|
+
});
|
|
2781
2895
|
let authorization;
|
|
2782
2896
|
switch (idp.authorization) {
|
|
2783
2897
|
case "insecure":
|
|
@@ -2823,17 +2937,17 @@ async function planServices$3(client, workspaceId, appName, idps, idpUserTrigger
|
|
|
2823
2937
|
permission: protoPermission
|
|
2824
2938
|
};
|
|
2825
2939
|
if (existing) {
|
|
2826
|
-
const
|
|
2827
|
-
if (!existing.label) unmanaged.push({
|
|
2940
|
+
const owned = isOwnedByApp(existing.allLabels, appName, appId);
|
|
2941
|
+
if (!owned) if (!existing.label) unmanaged.push({
|
|
2828
2942
|
resourceType: "IdP service",
|
|
2829
2943
|
resourceName: idp.name
|
|
2830
2944
|
});
|
|
2831
|
-
else
|
|
2945
|
+
else conflicts.push({
|
|
2832
2946
|
resourceType: "IdP service",
|
|
2833
2947
|
resourceName: idp.name,
|
|
2834
2948
|
currentOwner: existing.label
|
|
2835
2949
|
});
|
|
2836
|
-
if (
|
|
2950
|
+
if (owned && hasMatchingSdkVersion(existing.allLabels, metaRequest.labels) && areIdPServicesEqual(existing.resource, desired)) changeSet.unchanged.push({ name: namespaceName });
|
|
2837
2951
|
else changeSet.updates.push({
|
|
2838
2952
|
name: namespaceName,
|
|
2839
2953
|
request,
|
|
@@ -2847,9 +2961,11 @@ async function planServices$3(client, workspaceId, appName, idps, idpUserTrigger
|
|
|
2847
2961
|
});
|
|
2848
2962
|
}
|
|
2849
2963
|
Object.entries(existingServices).forEach(([namespaceName]) => {
|
|
2850
|
-
const
|
|
2851
|
-
|
|
2852
|
-
|
|
2964
|
+
const entry = existingServices[namespaceName];
|
|
2965
|
+
const label = entry?.label;
|
|
2966
|
+
const owned = isOwnedByApp(entry?.allLabels, appName, appId);
|
|
2967
|
+
if (label && !owned) resourceOwners.add(label);
|
|
2968
|
+
if (owned) changeSet.deletes.push({
|
|
2853
2969
|
name: namespaceName,
|
|
2854
2970
|
request: {
|
|
2855
2971
|
workspaceId,
|
|
@@ -3097,7 +3213,7 @@ async function planAuth(context) {
|
|
|
3097
3213
|
await application.authService.resolveNamespaces();
|
|
3098
3214
|
auths.push(application.authService);
|
|
3099
3215
|
}
|
|
3100
|
-
const { changeSet: serviceChangeSet, conflicts, unmanaged, resourceOwners } = await planServices$2(client, workspaceId, application.name, auths, forceApplyAll);
|
|
3216
|
+
const { changeSet: serviceChangeSet, conflicts, unmanaged, resourceOwners } = await planServices$2(client, workspaceId, application.name, application.id, auths, forceApplyAll);
|
|
3101
3217
|
const deletedServices = serviceChangeSet.deletes.map((del) => del.name);
|
|
3102
3218
|
const expectedLocalWebsites = new Set(application.staticWebsiteServices.map((website) => website.name));
|
|
3103
3219
|
const [idpConfigChangeSet, userProfileConfigChangeSet, tenantConfigChangeSet, machineUserChangeSet, authHookChangeSet, oauth2ClientChangeSet, scimChangeSet, scimResourceChangeSet, connectionResult] = await Promise.all([
|
|
@@ -3109,7 +3225,7 @@ async function planAuth(context) {
|
|
|
3109
3225
|
planOAuth2Clients(client, workspaceId, auths, deletedServices, expectedLocalWebsites, forceApplyAll),
|
|
3110
3226
|
planSCIMConfigs(client, workspaceId, auths, deletedServices),
|
|
3111
3227
|
planSCIMResources(client, workspaceId, auths, deletedServices),
|
|
3112
|
-
planAuthConnections(client, workspaceId, application.name, auths)
|
|
3228
|
+
planAuthConnections(client, workspaceId, application.name, application.id, auths)
|
|
3113
3229
|
]);
|
|
3114
3230
|
return {
|
|
3115
3231
|
changeSet: {
|
|
@@ -3132,7 +3248,7 @@ async function planAuth(context) {
|
|
|
3132
3248
|
function trn$4(workspaceId, name) {
|
|
3133
3249
|
return `trn:v1:workspace:${workspaceId}:auth:${name}`;
|
|
3134
3250
|
}
|
|
3135
|
-
async function planServices$2(client, workspaceId, appName, auths, forceApplyAll = false) {
|
|
3251
|
+
async function planServices$2(client, workspaceId, appName, appId, auths, forceApplyAll = false) {
|
|
3136
3252
|
const changeSet = createChangeSet("Auth services");
|
|
3137
3253
|
const conflicts = [];
|
|
3138
3254
|
const unmanaged = [];
|
|
@@ -3156,30 +3272,35 @@ async function planServices$2(client, workspaceId, appName, auths, forceApplyAll
|
|
|
3156
3272
|
const { metadata } = await client.getMetadata({ trn: trn$4(workspaceId, resource.namespace.name) });
|
|
3157
3273
|
existingServices[resource.namespace.name] = {
|
|
3158
3274
|
resource,
|
|
3159
|
-
label: metadata?.labels[sdkNameLabelKey]
|
|
3275
|
+
label: metadata?.labels[sdkNameLabelKey],
|
|
3276
|
+
allLabels: metadata?.labels
|
|
3160
3277
|
};
|
|
3161
3278
|
}));
|
|
3162
3279
|
for (const auth of auths) {
|
|
3163
3280
|
const { parsedConfig: config } = auth;
|
|
3164
3281
|
const existing = existingServices[config.name];
|
|
3165
|
-
const metaRequest = await buildMetaRequest(
|
|
3282
|
+
const metaRequest = await buildMetaRequest({
|
|
3283
|
+
trn: trn$4(workspaceId, config.name),
|
|
3284
|
+
appName,
|
|
3285
|
+
appId
|
|
3286
|
+
});
|
|
3166
3287
|
const request = {
|
|
3167
3288
|
workspaceId,
|
|
3168
3289
|
namespaceName: config.name,
|
|
3169
3290
|
publishSessionEvents: config.publishSessionEvents
|
|
3170
3291
|
};
|
|
3171
3292
|
if (existing) {
|
|
3172
|
-
const
|
|
3173
|
-
if (!existing.label) unmanaged.push({
|
|
3293
|
+
const owned = isOwnedByApp(existing.allLabels, appName, appId);
|
|
3294
|
+
if (!owned) if (!existing.label) unmanaged.push({
|
|
3174
3295
|
resourceType: "Auth service",
|
|
3175
3296
|
resourceName: config.name
|
|
3176
3297
|
});
|
|
3177
|
-
else
|
|
3298
|
+
else conflicts.push({
|
|
3178
3299
|
resourceType: "Auth service",
|
|
3179
3300
|
resourceName: config.name,
|
|
3180
3301
|
currentOwner: existing.label
|
|
3181
3302
|
});
|
|
3182
|
-
if (!forceApplyAll && existing.resource.publishSessionEvents === (config.publishSessionEvents ?? false) &&
|
|
3303
|
+
if (!forceApplyAll && existing.resource.publishSessionEvents === (config.publishSessionEvents ?? false) && owned) changeSet.unchanged.push({ name: config.name });
|
|
3183
3304
|
else changeSet.updates.push({
|
|
3184
3305
|
name: config.name,
|
|
3185
3306
|
request,
|
|
@@ -3193,9 +3314,11 @@ async function planServices$2(client, workspaceId, appName, auths, forceApplyAll
|
|
|
3193
3314
|
});
|
|
3194
3315
|
}
|
|
3195
3316
|
Object.entries(existingServices).forEach(([namespaceName]) => {
|
|
3196
|
-
const
|
|
3197
|
-
|
|
3198
|
-
|
|
3317
|
+
const entry = existingServices[namespaceName];
|
|
3318
|
+
const label = entry?.label;
|
|
3319
|
+
const owned = isOwnedByApp(entry?.allLabels, appName, appId);
|
|
3320
|
+
if (label && !owned) resourceOwners.add(label);
|
|
3321
|
+
if (owned) changeSet.deletes.push({
|
|
3199
3322
|
name: namespaceName,
|
|
3200
3323
|
request: {
|
|
3201
3324
|
workspaceId,
|
|
@@ -4159,10 +4282,99 @@ async function planAuthHooks(client, workspaceId, auths, deletedServices, forceA
|
|
|
4159
4282
|
return changeSet;
|
|
4160
4283
|
}
|
|
4161
4284
|
|
|
4285
|
+
//#endregion
|
|
4286
|
+
//#region src/cli/commands/deploy/config-id-injector.ts
|
|
4287
|
+
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
4288
|
+
function findDefineConfigCalls(node, results) {
|
|
4289
|
+
if (!node || typeof node !== "object") return;
|
|
4290
|
+
const n = node;
|
|
4291
|
+
if (n.type === "CallExpression") {
|
|
4292
|
+
const ce = n;
|
|
4293
|
+
if (ce.callee.type === "Identifier" && ce.callee.name === "defineConfig") {
|
|
4294
|
+
const arg = ce.arguments[0];
|
|
4295
|
+
const configObj = arg && arg.type === "ObjectExpression" ? arg : null;
|
|
4296
|
+
results.push({
|
|
4297
|
+
callExpr: ce,
|
|
4298
|
+
configObj
|
|
4299
|
+
});
|
|
4300
|
+
}
|
|
4301
|
+
}
|
|
4302
|
+
for (const key of Object.keys(n)) {
|
|
4303
|
+
const child = n[key];
|
|
4304
|
+
if (Array.isArray(child)) for (const c of child) findDefineConfigCalls(c, results);
|
|
4305
|
+
else if (child && typeof child === "object") findDefineConfigCalls(child, results);
|
|
4306
|
+
}
|
|
4307
|
+
}
|
|
4308
|
+
function findIdProperty(obj) {
|
|
4309
|
+
for (const prop of obj.properties) {
|
|
4310
|
+
if (prop.type !== "Property") continue;
|
|
4311
|
+
if ((prop.key.type === "Identifier" ? prop.key.name : prop.key.type === "Literal" ? prop.key.value : null) === "id") return prop;
|
|
4312
|
+
}
|
|
4313
|
+
return null;
|
|
4314
|
+
}
|
|
4315
|
+
/**
|
|
4316
|
+
* Ensure `tailor.config.ts` has an `id` property on the `defineConfig({...})`
|
|
4317
|
+
* argument. Generates a UUID when missing and writes it back to the file.
|
|
4318
|
+
* Returns null when the file does not contain a `defineConfig()` call (e.g.
|
|
4319
|
+
* a wrapper that re-exports another config).
|
|
4320
|
+
* @param configPath - Absolute path to the config file
|
|
4321
|
+
* @returns Resolved id and whether it was newly injected, or null if skipped
|
|
4322
|
+
*/
|
|
4323
|
+
async function ensureConfigId(configPath) {
|
|
4324
|
+
const source = await fs$1.promises.readFile(configPath, "utf-8");
|
|
4325
|
+
const { program } = parseSync(configPath, source);
|
|
4326
|
+
const calls = [];
|
|
4327
|
+
findDefineConfigCalls(program, calls);
|
|
4328
|
+
if (calls.length === 0) return null;
|
|
4329
|
+
if (calls.length > 1) throw new Error(`Multiple defineConfig() calls found in ${configPath}. Only one is supported.`);
|
|
4330
|
+
const { configObj } = calls[0];
|
|
4331
|
+
if (!configObj) throw new Error(`defineConfig() argument must be an inline object literal in ${configPath} so the SDK can manage the 'id' field.`);
|
|
4332
|
+
const idProp = findIdProperty(configObj);
|
|
4333
|
+
if (idProp) {
|
|
4334
|
+
const value = idProp.value;
|
|
4335
|
+
if (value.type !== "Literal") throw new Error(`'id' field in ${configPath} must be a string literal. To use this config for a separate app, delete it.`);
|
|
4336
|
+
const literalValue = value.value;
|
|
4337
|
+
if (typeof literalValue !== "string" || literalValue === "") throw new Error(`'id' field in ${configPath} must be a non-empty string literal. To use this config for a separate app, delete it.`);
|
|
4338
|
+
if (!uuidRegex.test(literalValue)) throw new Error(`'id' field in ${configPath} must be a UUID. To use this config for a separate app, delete it.`);
|
|
4339
|
+
return {
|
|
4340
|
+
id: literalValue,
|
|
4341
|
+
injected: false
|
|
4342
|
+
};
|
|
4343
|
+
}
|
|
4344
|
+
const id = crypto.randomUUID();
|
|
4345
|
+
const newSource = insertIdProperty(source, configObj, id);
|
|
4346
|
+
await fs$1.promises.writeFile(configPath, newSource, "utf-8");
|
|
4347
|
+
logger.info(`Generated app id and wrote to ${configPath}: ${id}`);
|
|
4348
|
+
return {
|
|
4349
|
+
id,
|
|
4350
|
+
injected: true
|
|
4351
|
+
};
|
|
4352
|
+
}
|
|
4353
|
+
const idComment = "// SDK-managed app id — do not edit, except when copying this config to a separate app.";
|
|
4354
|
+
function insertIdProperty(source, configObj, id) {
|
|
4355
|
+
const idLiteral = `id: ${JSON.stringify(id)}`;
|
|
4356
|
+
if (configObj.properties.length > 0) {
|
|
4357
|
+
const firstProp = configObj.properties[0];
|
|
4358
|
+
const lineStart = source.lastIndexOf("\n", firstProp.start - 1) + 1;
|
|
4359
|
+
const indent = source.slice(lineStart, firstProp.start);
|
|
4360
|
+
const insertion = `${idComment}\n${indent}${idLiteral},\n${indent}`;
|
|
4361
|
+
return source.slice(0, firstProp.start) + insertion + source.slice(firstProp.start);
|
|
4362
|
+
}
|
|
4363
|
+
const openBracePos = configObj.start + 1;
|
|
4364
|
+
const braceLineStart = source.lastIndexOf("\n", configObj.start) + 1;
|
|
4365
|
+
const baseIndent = source.slice(braceLineStart).match(/^[\t ]*/)?.[0] ?? "";
|
|
4366
|
+
const innerIndent = `${baseIndent} `;
|
|
4367
|
+
const insertion = `\n${innerIndent}${idComment}\n${innerIndent}${idLiteral},\n${baseIndent}`;
|
|
4368
|
+
return source.slice(0, openBracePos) + insertion + source.slice(openBracePos);
|
|
4369
|
+
}
|
|
4370
|
+
|
|
4162
4371
|
//#endregion
|
|
4163
4372
|
//#region src/cli/commands/deploy/confirm.ts
|
|
4164
4373
|
/**
|
|
4165
4374
|
* Confirm reassignment of resources when owner conflicts are detected.
|
|
4375
|
+
* Splits into two scenarios: id regeneration (same sdk-name, different
|
|
4376
|
+
* sdk-app-id) and name mismatch (different sdk-name). Each gets its own
|
|
4377
|
+
* prompt because the user-facing meaning is different.
|
|
4166
4378
|
* @param conflicts - Detected owner conflicts
|
|
4167
4379
|
* @param appName - Target application name
|
|
4168
4380
|
* @param yes - Whether to auto-confirm without prompting
|
|
@@ -4170,6 +4382,30 @@ async function planAuthHooks(client, workspaceId, auths, deletedServices, forceA
|
|
|
4170
4382
|
*/
|
|
4171
4383
|
async function confirmOwnerConflict(conflicts, appName, yes) {
|
|
4172
4384
|
if (conflicts.length === 0) return;
|
|
4385
|
+
const idRegenerated = conflicts.filter((c) => c.currentOwner === appName);
|
|
4386
|
+
const nameMismatches = conflicts.filter((c) => c.currentOwner !== appName);
|
|
4387
|
+
if (idRegenerated.length > 0) await confirmIdRegeneration(idRegenerated, appName, yes);
|
|
4388
|
+
if (nameMismatches.length > 0) await confirmNameMismatch(nameMismatches, appName, yes);
|
|
4389
|
+
}
|
|
4390
|
+
async function confirmIdRegeneration(conflicts, appName, yes) {
|
|
4391
|
+
logger.warn(`Application id was regenerated for "${appName}":`);
|
|
4392
|
+
logger.log(" These resources still carry the previous id.");
|
|
4393
|
+
logger.newline();
|
|
4394
|
+
logger.log(` ${styles.info("Resources")}:`);
|
|
4395
|
+
for (const c of conflicts) logger.log(` • ${styles.bold(c.resourceType)} ${styles.info(`"${c.resourceName}"`)}`);
|
|
4396
|
+
if (yes) {
|
|
4397
|
+
logger.success("Re-tagging resources with the new id (--yes flag specified)...", { mode: "plain" });
|
|
4398
|
+
return;
|
|
4399
|
+
}
|
|
4400
|
+
if (!await prompt.confirm({
|
|
4401
|
+
message: `Re-tag these resources with the new id for "${appName}"?\n${styles.dim("(The id in tailor.config.ts was removed since the previous deploy, so a new one was generated)")}`,
|
|
4402
|
+
default: false
|
|
4403
|
+
})) throw new Error(multiline`
|
|
4404
|
+
Apply cancelled. Resources remain tagged with the previous id.
|
|
4405
|
+
To override, run again and confirm, or use --yes flag.
|
|
4406
|
+
`);
|
|
4407
|
+
}
|
|
4408
|
+
async function confirmNameMismatch(conflicts, appName, yes) {
|
|
4173
4409
|
const currentOwners = [...new Set(conflicts.map((c) => c.currentOwner))];
|
|
4174
4410
|
logger.warn("Application name mismatch detected:");
|
|
4175
4411
|
logger.log(` ${styles.warning("Current application(s)")}: ${currentOwners.map((o) => styles.bold(`"${o}"`)).join(", ")}`);
|
|
@@ -4329,19 +4565,24 @@ async function planExecutor(context) {
|
|
|
4329
4565
|
const executors = forRemoval ? {} : await application.executorService?.loadExecutors() ?? {};
|
|
4330
4566
|
for (const executor of Object.values(executors)) {
|
|
4331
4567
|
const existing = existingExecutors[executor.name];
|
|
4332
|
-
const metaRequest = await buildMetaRequest(
|
|
4568
|
+
const metaRequest = await buildMetaRequest({
|
|
4569
|
+
trn: trn$3(workspaceId, executor.name),
|
|
4570
|
+
appName: application.name,
|
|
4571
|
+
appId: application.id
|
|
4572
|
+
});
|
|
4333
4573
|
const desiredExecutor = protoExecutor(application, executor);
|
|
4334
4574
|
if (existing) {
|
|
4335
|
-
|
|
4575
|
+
const owned = isOwnedByApp(existing.allLabels, application.name, application.id);
|
|
4576
|
+
if (!owned) if (!existing.label) unmanaged.push({
|
|
4336
4577
|
resourceType: "Executor",
|
|
4337
4578
|
resourceName: executor.name
|
|
4338
4579
|
});
|
|
4339
|
-
else
|
|
4580
|
+
else conflicts.push({
|
|
4340
4581
|
resourceType: "Executor",
|
|
4341
4582
|
resourceName: executor.name,
|
|
4342
4583
|
currentOwner: existing.label
|
|
4343
4584
|
});
|
|
4344
|
-
if (
|
|
4585
|
+
if (owned && hasMatchingSdkVersion(existing.allLabels, metaRequest.labels) && areExecutorsEqual(existing.resource, desiredExecutor)) changeSet.unchanged.push({ name: executor.name });
|
|
4345
4586
|
else changeSet.updates.push({
|
|
4346
4587
|
name: executor.name,
|
|
4347
4588
|
request: {
|
|
@@ -4361,9 +4602,11 @@ async function planExecutor(context) {
|
|
|
4361
4602
|
});
|
|
4362
4603
|
}
|
|
4363
4604
|
Object.entries(existingExecutors).forEach(([name]) => {
|
|
4364
|
-
const
|
|
4365
|
-
|
|
4366
|
-
|
|
4605
|
+
const entry = existingExecutors[name];
|
|
4606
|
+
const label = entry?.label;
|
|
4607
|
+
const owned = isOwnedByApp(entry?.allLabels, application.name, application.id);
|
|
4608
|
+
if (label && !owned) resourceOwners.add(label);
|
|
4609
|
+
if (owned) changeSet.deletes.push({
|
|
4367
4610
|
name,
|
|
4368
4611
|
request: {
|
|
4369
4612
|
workspaceId,
|
|
@@ -4722,7 +4965,7 @@ async function planPipeline(context) {
|
|
|
4722
4965
|
pipelines.push(pipeline);
|
|
4723
4966
|
}
|
|
4724
4967
|
const executors = forRemoval ? [] : Object.values(await application.executorService?.loadExecutors() ?? {});
|
|
4725
|
-
const { changeSet: serviceChangeSet, conflicts, unmanaged, resourceOwners } = await planServices$1(client, workspaceId, application.name, pipelines);
|
|
4968
|
+
const { changeSet: serviceChangeSet, conflicts, unmanaged, resourceOwners } = await planServices$1(client, workspaceId, application.name, application.id, pipelines);
|
|
4726
4969
|
const { changeSet: resolverChangeSet } = await planResolvers(client, workspaceId, pipelines, executors, serviceChangeSet.deletes.map((del) => del.name), application.env, application.authService?.config.name, forceApplyAll);
|
|
4727
4970
|
return {
|
|
4728
4971
|
changeSet: {
|
|
@@ -4737,7 +4980,7 @@ async function planPipeline(context) {
|
|
|
4737
4980
|
function trn$2(workspaceId, name) {
|
|
4738
4981
|
return `trn:v1:workspace:${workspaceId}:pipeline:${name}`;
|
|
4739
4982
|
}
|
|
4740
|
-
async function planServices$1(client, workspaceId, appName, pipelines) {
|
|
4983
|
+
async function planServices$1(client, workspaceId, appName, appId, pipelines) {
|
|
4741
4984
|
const changeSet = createChangeSet("Pipeline services");
|
|
4742
4985
|
const conflicts = [];
|
|
4743
4986
|
const unmanaged = [];
|
|
@@ -4767,18 +5010,23 @@ async function planServices$1(client, workspaceId, appName, pipelines) {
|
|
|
4767
5010
|
}));
|
|
4768
5011
|
for (const pipeline of pipelines) {
|
|
4769
5012
|
const existing = existingServices[pipeline.namespace];
|
|
4770
|
-
const metaRequest = await buildMetaRequest(
|
|
5013
|
+
const metaRequest = await buildMetaRequest({
|
|
5014
|
+
trn: trn$2(workspaceId, pipeline.namespace),
|
|
5015
|
+
appName,
|
|
5016
|
+
appId
|
|
5017
|
+
});
|
|
4771
5018
|
if (existing) {
|
|
4772
|
-
|
|
5019
|
+
const owned = isOwnedByApp(existing.allLabels, appName, appId);
|
|
5020
|
+
if (!owned) if (!existing.label) unmanaged.push({
|
|
4773
5021
|
resourceType: "Pipeline service",
|
|
4774
5022
|
resourceName: pipeline.namespace
|
|
4775
5023
|
});
|
|
4776
|
-
else
|
|
5024
|
+
else conflicts.push({
|
|
4777
5025
|
resourceType: "Pipeline service",
|
|
4778
5026
|
resourceName: pipeline.namespace,
|
|
4779
5027
|
currentOwner: existing.label
|
|
4780
5028
|
});
|
|
4781
|
-
if (
|
|
5029
|
+
if (owned && hasMatchingSdkVersion(existing.allLabels, metaRequest.labels)) changeSet.unchanged.push({ name: pipeline.namespace });
|
|
4782
5030
|
else changeSet.updates.push({
|
|
4783
5031
|
name: pipeline.namespace,
|
|
4784
5032
|
request: {
|
|
@@ -4798,9 +5046,11 @@ async function planServices$1(client, workspaceId, appName, pipelines) {
|
|
|
4798
5046
|
});
|
|
4799
5047
|
}
|
|
4800
5048
|
Object.entries(existingServices).forEach(([namespaceName]) => {
|
|
4801
|
-
const
|
|
4802
|
-
|
|
4803
|
-
|
|
5049
|
+
const entry = existingServices[namespaceName];
|
|
5050
|
+
const label = entry?.label;
|
|
5051
|
+
const owned = isOwnedByApp(entry?.allLabels, appName, appId);
|
|
5052
|
+
if (label && !owned) resourceOwners.add(label);
|
|
5053
|
+
if (owned) changeSet.deletes.push({
|
|
4804
5054
|
name: namespaceName,
|
|
4805
5055
|
request: {
|
|
4806
5056
|
workspaceId,
|
|
@@ -5062,17 +5312,22 @@ async function planSecretManager(context) {
|
|
|
5062
5312
|
const vaultName = vault.vaultName;
|
|
5063
5313
|
const existing = existingVaults[vaultName];
|
|
5064
5314
|
if (existing) {
|
|
5065
|
-
const metaRequest = await buildMetaRequest(
|
|
5066
|
-
|
|
5315
|
+
const metaRequest = await buildMetaRequest({
|
|
5316
|
+
trn: vaultTrn$1(workspaceId, vaultName),
|
|
5317
|
+
appName: application.name,
|
|
5318
|
+
appId: application.id
|
|
5319
|
+
});
|
|
5320
|
+
const owned = isOwnedByApp(existing.allLabels, application.name, application.id);
|
|
5321
|
+
if (!owned) if (!existing.label) unmanaged.push({
|
|
5067
5322
|
resourceType: "Secret Manager vault",
|
|
5068
5323
|
resourceName: vaultName
|
|
5069
5324
|
});
|
|
5070
|
-
else
|
|
5325
|
+
else conflicts.push({
|
|
5071
5326
|
resourceType: "Secret Manager vault",
|
|
5072
5327
|
resourceName: vaultName,
|
|
5073
5328
|
currentOwner: existing.label
|
|
5074
5329
|
});
|
|
5075
|
-
if (
|
|
5330
|
+
if (owned && hasMatchingSdkVersion(existing.allLabels, metaRequest.labels)) vaultChangeSet.unchanged.push({ name: vaultName });
|
|
5076
5331
|
else vaultChangeSet.updates.push({
|
|
5077
5332
|
name: vaultName,
|
|
5078
5333
|
workspaceId
|
|
@@ -5133,8 +5388,9 @@ async function planSecretManager(context) {
|
|
|
5133
5388
|
for (const [name, entry] of Object.entries(existingVaults)) {
|
|
5134
5389
|
if (!entry) continue;
|
|
5135
5390
|
const label = entry.label;
|
|
5136
|
-
|
|
5137
|
-
if (label
|
|
5391
|
+
const owned = isOwnedByApp(entry.allLabels, application.name, application.id);
|
|
5392
|
+
if (label && !owned) resourceOwners.add(label);
|
|
5393
|
+
if (owned) {
|
|
5138
5394
|
const secrets = await fetchAll(async (pageToken, maxPageSize) => {
|
|
5139
5395
|
try {
|
|
5140
5396
|
const { secrets, nextPageToken } = await client.listSecretManagerSecrets({
|
|
@@ -5190,12 +5446,20 @@ async function applySecretManager(client, result, phase = "create-update", appli
|
|
|
5190
5446
|
secretmanagerVaultName: create.name
|
|
5191
5447
|
});
|
|
5192
5448
|
if (application) {
|
|
5193
|
-
const metaRequest = await buildMetaRequest(
|
|
5449
|
+
const metaRequest = await buildMetaRequest({
|
|
5450
|
+
trn: vaultTrn$1(create.workspaceId, create.name),
|
|
5451
|
+
appName: application.name,
|
|
5452
|
+
appId: application.id
|
|
5453
|
+
});
|
|
5194
5454
|
await client.setMetadata(metaRequest);
|
|
5195
5455
|
}
|
|
5196
5456
|
}));
|
|
5197
5457
|
if (application) await Promise.all(vaultChangeSet.updates.map(async (update) => {
|
|
5198
|
-
const metaRequest = await buildMetaRequest(
|
|
5458
|
+
const metaRequest = await buildMetaRequest({
|
|
5459
|
+
trn: vaultTrn$1(update.workspaceId, update.name),
|
|
5460
|
+
appName: application.name,
|
|
5461
|
+
appId: application.id
|
|
5462
|
+
});
|
|
5199
5463
|
await client.setMetadata(metaRequest);
|
|
5200
5464
|
}));
|
|
5201
5465
|
await Promise.all(secretChangeSet.creates.map((create) => client.createSecretManagerSecret({
|
|
@@ -5316,7 +5580,11 @@ async function planStaticWebsite(context) {
|
|
|
5316
5580
|
const config = websiteService;
|
|
5317
5581
|
const name = websiteService.name;
|
|
5318
5582
|
const existing = existingWebsites[name];
|
|
5319
|
-
const metaRequest = await buildMetaRequest(
|
|
5583
|
+
const metaRequest = await buildMetaRequest({
|
|
5584
|
+
trn: trn$1(workspaceId, name),
|
|
5585
|
+
appName: application.name,
|
|
5586
|
+
appId: application.id
|
|
5587
|
+
});
|
|
5320
5588
|
const desired = normalizeComparableStaticWebsite(config);
|
|
5321
5589
|
const request = {
|
|
5322
5590
|
workspaceId,
|
|
@@ -5327,17 +5595,17 @@ async function planStaticWebsite(context) {
|
|
|
5327
5595
|
}
|
|
5328
5596
|
};
|
|
5329
5597
|
if (existing) {
|
|
5330
|
-
const
|
|
5331
|
-
if (!existing.label) unmanaged.push({
|
|
5598
|
+
const owned = isOwnedByApp(existing.allLabels, application.name, application.id);
|
|
5599
|
+
if (!owned) if (!existing.label) unmanaged.push({
|
|
5332
5600
|
resourceType: "StaticWebsite",
|
|
5333
5601
|
resourceName: name
|
|
5334
5602
|
});
|
|
5335
|
-
else
|
|
5603
|
+
else conflicts.push({
|
|
5336
5604
|
resourceType: "StaticWebsite",
|
|
5337
5605
|
resourceName: name,
|
|
5338
5606
|
currentOwner: existing.label
|
|
5339
5607
|
});
|
|
5340
|
-
if (
|
|
5608
|
+
if (owned && hasMatchingSdkVersion(existing.allLabels, metaRequest.labels) && areStaticWebsitesEqual(existing.resource, desired)) changeSet.unchanged.push({ name });
|
|
5341
5609
|
else changeSet.updates.push({
|
|
5342
5610
|
name,
|
|
5343
5611
|
request,
|
|
@@ -5351,9 +5619,11 @@ async function planStaticWebsite(context) {
|
|
|
5351
5619
|
});
|
|
5352
5620
|
}
|
|
5353
5621
|
Object.entries(existingWebsites).forEach(([name]) => {
|
|
5354
|
-
const
|
|
5355
|
-
|
|
5356
|
-
|
|
5622
|
+
const entry = existingWebsites[name];
|
|
5623
|
+
const label = entry?.label;
|
|
5624
|
+
const owned = isOwnedByApp(entry?.allLabels, application.name, application.id);
|
|
5625
|
+
if (label && !owned) resourceOwners.add(label);
|
|
5626
|
+
if (owned) changeSet.deletes.push({
|
|
5357
5627
|
name,
|
|
5358
5628
|
request: {
|
|
5359
5629
|
workspaceId,
|
|
@@ -7286,7 +7556,7 @@ function formatRemoteVerificationResults(results) {
|
|
|
7286
7556
|
* @param {ReadonlyMap<string, Record<string, TailorDBType>>} typesByNamespace - Types by namespace
|
|
7287
7557
|
* @param {LoadedConfig} config - Loaded application config (includes path)
|
|
7288
7558
|
* @param {boolean} noSchemaCheck - Whether to skip schema diff check
|
|
7289
|
-
* @returns {Promise<
|
|
7559
|
+
* @returns {Promise<ValidateAndDetectResult>} Pending migrations and namespaces that have migration directories configured
|
|
7290
7560
|
*/
|
|
7291
7561
|
async function validateAndDetectMigrations(client, workspaceId, typesByNamespace, config, noSchemaCheck) {
|
|
7292
7562
|
const namespacesWithMigrations = getNamespacesWithMigrations(config, path.dirname(config.path));
|
|
@@ -7327,7 +7597,38 @@ async function validateAndDetectMigrations(client, workspaceId, typesByNamespace
|
|
|
7327
7597
|
if (withScripts.length > 0) logger.info(` • ${withScripts.length} data migration(s) (requires migration script execution)`, { mode: "plain" });
|
|
7328
7598
|
}
|
|
7329
7599
|
}
|
|
7330
|
-
return
|
|
7600
|
+
return {
|
|
7601
|
+
pendingMigrations,
|
|
7602
|
+
namespacesWithMigrations
|
|
7603
|
+
};
|
|
7604
|
+
}
|
|
7605
|
+
/**
|
|
7606
|
+
* Reconcile the on-remote migration label with the working tree's latest
|
|
7607
|
+
* migration number for each namespace.
|
|
7608
|
+
*
|
|
7609
|
+
* Used after a `--no-schema-check` apply: that flag skips the local/remote
|
|
7610
|
+
* snapshot drift checks, but if it also leaves the label untouched the remote
|
|
7611
|
+
* label can drift past the working tree's latest migration (e.g. when
|
|
7612
|
+
* checking out an older revision and re-deploying). A subsequent run would
|
|
7613
|
+
* then reconstruct the expected snapshot at a label that no longer exists in
|
|
7614
|
+
* the working tree, triggering a false drift error.
|
|
7615
|
+
*
|
|
7616
|
+
* Always force `label = working_tree_max` regardless of the previous label so
|
|
7617
|
+
* the invariant `label <= working_tree_max` is preserved.
|
|
7618
|
+
* @param client - Operator client instance
|
|
7619
|
+
* @param workspaceId - Workspace ID
|
|
7620
|
+
* @param namespacesWithMigrations - Namespaces that have migration directories configured
|
|
7621
|
+
*/
|
|
7622
|
+
async function reconcileMigrationLabels(client, workspaceId, namespacesWithMigrations) {
|
|
7623
|
+
for (const { namespace, migrationsDir } of namespacesWithMigrations) {
|
|
7624
|
+
const targetVersion = getLatestMigrationNumber(migrationsDir);
|
|
7625
|
+
const currentVersion = await getRemoteMigrationNumber(client, workspaceId, namespace);
|
|
7626
|
+
await updateMigrationLabel(client, workspaceId, namespace, targetVersion);
|
|
7627
|
+
if (currentVersion !== targetVersion) {
|
|
7628
|
+
const from = currentVersion === null ? "<unset>" : formatMigrationNumber(currentVersion);
|
|
7629
|
+
logger.info(`Migration label for namespace ${namespace} reconciled: ${from} → ${formatMigrationNumber(targetVersion)}.`);
|
|
7630
|
+
}
|
|
7631
|
+
}
|
|
7331
7632
|
}
|
|
7332
7633
|
/**
|
|
7333
7634
|
* Build migration execution context for script-based migrations.
|
|
@@ -7363,7 +7664,7 @@ async function applyTailorDB(client, result, phase = "create-update") {
|
|
|
7363
7664
|
const types = tailordb.types;
|
|
7364
7665
|
if (types) typesByNamespace.set(tailordb.namespace, types);
|
|
7365
7666
|
}
|
|
7366
|
-
const pendingMigrations = await validateAndDetectMigrations(client, migrationContext.workspaceId, typesByNamespace, migrationContext.config, migrationContext.noSchemaCheck);
|
|
7667
|
+
const { pendingMigrations, namespacesWithMigrations } = await validateAndDetectMigrations(client, migrationContext.workspaceId, typesByNamespace, migrationContext.config, migrationContext.noSchemaCheck);
|
|
7367
7668
|
if (pendingMigrations.length > 0) {
|
|
7368
7669
|
processedTypes.reset();
|
|
7369
7670
|
deletedResources.reset();
|
|
@@ -7403,6 +7704,7 @@ async function applyTailorDB(client, result, phase = "create-update") {
|
|
|
7403
7704
|
await Promise.all(changeSet.gqlPermission.deletes.map((del) => client.deleteTailorDBGQLPermission(del.request)));
|
|
7404
7705
|
await Promise.all(changeSet.type.deletes.map((del) => client.deleteTailorDBType(del.request)));
|
|
7405
7706
|
}
|
|
7707
|
+
if (migrationContext.noSchemaCheck && namespacesWithMigrations.length > 0) await reconcileMigrationLabels(client, migrationContext.workspaceId, namespacesWithMigrations);
|
|
7406
7708
|
} else if (phase === "delete-resources") {
|
|
7407
7709
|
await Promise.all(changeSet.gqlPermission.deletes.map((del) => client.deleteTailorDBGQLPermission(del.request)));
|
|
7408
7710
|
await Promise.all(changeSet.type.deletes.map((del) => client.deleteTailorDBType(del.request)));
|
|
@@ -7657,7 +7959,7 @@ async function planTailorDB(context) {
|
|
|
7657
7959
|
tailordbs.push(tailordb);
|
|
7658
7960
|
}
|
|
7659
7961
|
const executors = forRemoval ? [] : Object.values(await application.executorService?.loadExecutors() ?? {});
|
|
7660
|
-
const { changeSet: serviceChangeSet, conflicts, unmanaged, resourceOwners } = await planServices(client, workspaceId, application.name, tailordbs);
|
|
7962
|
+
const { changeSet: serviceChangeSet, conflicts, unmanaged, resourceOwners } = await planServices(client, workspaceId, application.name, application.id, tailordbs);
|
|
7661
7963
|
const deletedServices = serviceChangeSet.deletes.map((del) => del.name);
|
|
7662
7964
|
const [typeChangeSet, gqlPermissionChangeSet] = await Promise.all([planTypes(client, workspaceId, tailordbs, executors, deletedServices, void 0, forceApplyAll), planGqlPermissions(client, workspaceId, tailordbs, deletedServices, forceApplyAll)]);
|
|
7663
7965
|
return {
|
|
@@ -7731,7 +8033,7 @@ function areTailorDBServicesEqual(existing, desired) {
|
|
|
7731
8033
|
defaultTimezone: "UTC"
|
|
7732
8034
|
}));
|
|
7733
8035
|
}
|
|
7734
|
-
async function planServices(client, workspaceId, appName, tailordbs) {
|
|
8036
|
+
async function planServices(client, workspaceId, appName, appId, tailordbs) {
|
|
7735
8037
|
const changeSet = createChangeSet("TailorDB services");
|
|
7736
8038
|
const conflicts = [];
|
|
7737
8039
|
const unmanaged = [];
|
|
@@ -7761,18 +8063,24 @@ async function planServices(client, workspaceId, appName, tailordbs) {
|
|
|
7761
8063
|
}));
|
|
7762
8064
|
for (const tailordb of tailordbs) {
|
|
7763
8065
|
const existing = existingServices[tailordb.namespace];
|
|
7764
|
-
const metaRequest = await buildMetaRequest(
|
|
8066
|
+
const metaRequest = await buildMetaRequest({
|
|
8067
|
+
trn: trn(workspaceId, tailordb.namespace),
|
|
8068
|
+
appName,
|
|
8069
|
+
appId,
|
|
8070
|
+
existingLabels: existing?.allLabels
|
|
8071
|
+
});
|
|
7765
8072
|
if (existing) {
|
|
7766
|
-
|
|
8073
|
+
const owned = isOwnedByApp(existing.allLabels, appName, appId);
|
|
8074
|
+
if (!owned) if (!existing.label) unmanaged.push({
|
|
7767
8075
|
resourceType: "TailorDB service",
|
|
7768
8076
|
resourceName: tailordb.namespace
|
|
7769
8077
|
});
|
|
7770
|
-
else
|
|
8078
|
+
else conflicts.push({
|
|
7771
8079
|
resourceType: "TailorDB service",
|
|
7772
8080
|
resourceName: tailordb.namespace,
|
|
7773
8081
|
currentOwner: existing.label
|
|
7774
8082
|
});
|
|
7775
|
-
if (
|
|
8083
|
+
if (owned && hasMatchingSdkVersion(existing.allLabels, metaRequest.labels) && areTailorDBServicesEqual(existing.resource, tailordb)) changeSet.unchanged.push({ name: tailordb.namespace });
|
|
7776
8084
|
else changeSet.updates.push({
|
|
7777
8085
|
name: tailordb.namespace,
|
|
7778
8086
|
metaRequest
|
|
@@ -7789,9 +8097,11 @@ async function planServices(client, workspaceId, appName, tailordbs) {
|
|
|
7789
8097
|
});
|
|
7790
8098
|
}
|
|
7791
8099
|
Object.entries(existingServices).forEach(([namespaceName]) => {
|
|
7792
|
-
const
|
|
7793
|
-
|
|
7794
|
-
|
|
8100
|
+
const entry = existingServices[namespaceName];
|
|
8101
|
+
const label = entry?.label;
|
|
8102
|
+
const owned = isOwnedByApp(entry?.allLabels, appName, appId);
|
|
8103
|
+
if (label && !owned) resourceOwners.add(label);
|
|
8104
|
+
if (owned) changeSet.deletes.push({
|
|
7795
8105
|
name: namespaceName,
|
|
7796
8106
|
request: {
|
|
7797
8107
|
workspaceId,
|
|
@@ -8434,9 +8744,9 @@ function formatMigrationCheckResults(results) {
|
|
|
8434
8744
|
* @returns Promise that resolves when workflows are applied
|
|
8435
8745
|
*/
|
|
8436
8746
|
async function applyWorkflow(client, result, phase = "create-update") {
|
|
8437
|
-
const { changeSet, appName } = result;
|
|
8747
|
+
const { changeSet, appName, appId } = result;
|
|
8438
8748
|
if (phase === "create-update") {
|
|
8439
|
-
const jobFunctionVersions = await registerJobFunctions(client, changeSet, appName, result.unchangedWorkflowJobNames);
|
|
8749
|
+
const jobFunctionVersions = await registerJobFunctions(client, changeSet, appName, appId, result.unchangedWorkflowJobNames);
|
|
8440
8750
|
await Promise.all([...changeSet.creates.map(async (create) => {
|
|
8441
8751
|
const filteredVersions = filterJobFunctionVersions(jobFunctionVersions, create.usedJobNames);
|
|
8442
8752
|
await client.createWorkflow({
|
|
@@ -8484,10 +8794,11 @@ function filterJobFunctionVersions(allVersions, usedJobNames) {
|
|
|
8484
8794
|
* @param client - Operator client instance
|
|
8485
8795
|
* @param changeSet - Workflow change set
|
|
8486
8796
|
* @param appName - Application name
|
|
8797
|
+
* @param appId
|
|
8487
8798
|
* @param unchangedWorkflowJobNames - Job function names used by unchanged workflows
|
|
8488
8799
|
* @returns Map of job function names to versions
|
|
8489
8800
|
*/
|
|
8490
|
-
async function registerJobFunctions(client, changeSet, appName, unchangedWorkflowJobNames = /* @__PURE__ */ new Set()) {
|
|
8801
|
+
async function registerJobFunctions(client, changeSet, appName, appId, unchangedWorkflowJobNames = /* @__PURE__ */ new Set()) {
|
|
8491
8802
|
const jobFunctionVersions = {};
|
|
8492
8803
|
const firstWorkflow = changeSet.creates[0] || changeSet.updates[0] || changeSet.deletes[0];
|
|
8493
8804
|
if (!firstWorkflow) return jobFunctionVersions;
|
|
@@ -8515,7 +8826,11 @@ async function registerJobFunctions(client, changeSet, appName, unchangedWorkflo
|
|
|
8515
8826
|
jobFunctionName: jobName,
|
|
8516
8827
|
scriptRef: workflowJobFunctionName(jobName)
|
|
8517
8828
|
});
|
|
8518
|
-
await client.setMetadata(await buildMetaRequest(
|
|
8829
|
+
await client.setMetadata(await buildMetaRequest({
|
|
8830
|
+
trn: jobFunctionTrn(workspaceId, jobName),
|
|
8831
|
+
appName,
|
|
8832
|
+
appId
|
|
8833
|
+
}));
|
|
8519
8834
|
return {
|
|
8520
8835
|
jobName,
|
|
8521
8836
|
version: response.jobFunction?.version
|
|
@@ -8526,7 +8841,7 @@ async function registerJobFunctions(client, changeSet, appName, unchangedWorkflo
|
|
|
8526
8841
|
const unusedJobFunctions = existingJobFunctions.filter((jobName) => !allUsedJobNames.has(jobName));
|
|
8527
8842
|
await Promise.all(unusedJobFunctions.map(async (jobName) => {
|
|
8528
8843
|
const { metadata } = await client.getMetadata({ trn: jobFunctionTrn(workspaceId, jobName) });
|
|
8529
|
-
if (metadata?.labels
|
|
8844
|
+
if (isOwnedByApp(metadata?.labels, appName, appId)) await client.setMetadata({
|
|
8530
8845
|
trn: jobFunctionTrn(workspaceId, jobName),
|
|
8531
8846
|
labels: { [sdkNameLabelKey]: "" }
|
|
8532
8847
|
});
|
|
@@ -8564,12 +8879,13 @@ function jobFunctionTrn(workspaceId, name) {
|
|
|
8564
8879
|
* @param client - Operator client instance
|
|
8565
8880
|
* @param workspaceId - Workspace ID
|
|
8566
8881
|
* @param appName - Application name
|
|
8882
|
+
* @param appId
|
|
8567
8883
|
* @param workflows - Parsed workflows
|
|
8568
8884
|
* @param mainJobDeps - Main job dependencies by workflow
|
|
8569
8885
|
* @param unchangedJobFunctions - Job functions already proven unchanged by function registry plan
|
|
8570
8886
|
* @returns Planned workflow changes
|
|
8571
8887
|
*/
|
|
8572
|
-
async function planWorkflow(client, workspaceId, appName, workflows, mainJobDeps, unchangedJobFunctions = /* @__PURE__ */ new Set()) {
|
|
8888
|
+
async function planWorkflow(client, workspaceId, appName, appId, workflows, mainJobDeps, unchangedJobFunctions = /* @__PURE__ */ new Set()) {
|
|
8573
8889
|
const changeSet = createChangeSet("Workflows");
|
|
8574
8890
|
const conflicts = [];
|
|
8575
8891
|
const unmanaged = [];
|
|
@@ -8594,20 +8910,25 @@ async function planWorkflow(client, workspaceId, appName, workflows, mainJobDeps
|
|
|
8594
8910
|
}));
|
|
8595
8911
|
for (const workflow of Object.values(workflows)) {
|
|
8596
8912
|
const existing = existingWorkflows[workflow.name];
|
|
8597
|
-
const metaRequest = await buildMetaRequest(
|
|
8913
|
+
const metaRequest = await buildMetaRequest({
|
|
8914
|
+
trn: workflowTrn$1(workspaceId, workflow.name),
|
|
8915
|
+
appName,
|
|
8916
|
+
appId
|
|
8917
|
+
});
|
|
8598
8918
|
const usedJobNames = mainJobDeps[workflow.mainJob.name];
|
|
8599
8919
|
if (!usedJobNames) throw new Error(`Job "${workflow.mainJob.name}" (mainJob of workflow "${workflow.name}") was not found.\n\nPossible causes:\n - The job is not exported as a named export\n - The file containing the job is not included in workflow.files glob pattern\n\nSolution:\n export const ${workflow.mainJob.name} = createWorkflowJob({ name: "${workflow.mainJob.name}", ... })`);
|
|
8600
8920
|
if (existing) {
|
|
8601
|
-
|
|
8921
|
+
const owned = isOwnedByApp(existing.allLabels, appName, appId);
|
|
8922
|
+
if (!owned) if (!existing.label) unmanaged.push({
|
|
8602
8923
|
resourceType: "Workflow",
|
|
8603
8924
|
resourceName: workflow.name
|
|
8604
8925
|
});
|
|
8605
|
-
else
|
|
8926
|
+
else conflicts.push({
|
|
8606
8927
|
resourceType: "Workflow",
|
|
8607
8928
|
resourceName: workflow.name,
|
|
8608
8929
|
currentOwner: existing.label
|
|
8609
8930
|
});
|
|
8610
|
-
if (
|
|
8931
|
+
if (owned && hasMatchingSdkVersion(existing.allLabels, metaRequest.labels) && canTreatWorkflowAsUnchanged(existing.resource, workflow, usedJobNames, unchangedJobFunctions)) {
|
|
8611
8932
|
changeSet.unchanged.push({ name: workflow.name });
|
|
8612
8933
|
for (const jobName of usedJobNames) unchangedWorkflowJobNames.add(jobName);
|
|
8613
8934
|
} else changeSet.updates.push({
|
|
@@ -8629,8 +8950,9 @@ async function planWorkflow(client, workspaceId, appName, workflows, mainJobDeps
|
|
|
8629
8950
|
Object.values(existingWorkflows).forEach((existing) => {
|
|
8630
8951
|
if (!existing) return;
|
|
8631
8952
|
const label = existing.label;
|
|
8632
|
-
|
|
8633
|
-
if (label
|
|
8953
|
+
const owned = isOwnedByApp(existing.allLabels, appName, appId);
|
|
8954
|
+
if (label && !owned) resourceOwners.add(label);
|
|
8955
|
+
if (owned) changeSet.deletes.push({
|
|
8634
8956
|
name: existing.resource.name,
|
|
8635
8957
|
workspaceId,
|
|
8636
8958
|
workflowId: existing.resource.id,
|
|
@@ -8643,6 +8965,7 @@ async function planWorkflow(client, workspaceId, appName, workflows, mainJobDeps
|
|
|
8643
8965
|
unmanaged,
|
|
8644
8966
|
resourceOwners,
|
|
8645
8967
|
appName,
|
|
8968
|
+
appId,
|
|
8646
8969
|
unchangedWorkflowJobNames
|
|
8647
8970
|
};
|
|
8648
8971
|
}
|
|
@@ -8765,7 +9088,11 @@ function collectIdpUserTriggerTargets(application) {
|
|
|
8765
9088
|
return targets;
|
|
8766
9089
|
}
|
|
8767
9090
|
async function shouldForceApplyAll(client, workspaceId, application, functionEntries) {
|
|
8768
|
-
const desiredLabels = (await buildMetaRequest(
|
|
9091
|
+
const desiredLabels = (await buildMetaRequest({
|
|
9092
|
+
trn: applicationTrn(workspaceId, application.name),
|
|
9093
|
+
appName: application.name,
|
|
9094
|
+
appId: application.id
|
|
9095
|
+
})).labels;
|
|
8769
9096
|
const candidateTrns = /* @__PURE__ */ new Set();
|
|
8770
9097
|
if (application.subgraphs.length > 0) candidateTrns.add(applicationTrn(workspaceId, application.name));
|
|
8771
9098
|
application.staticWebsiteServices.forEach((website) => {
|
|
@@ -8902,9 +9229,16 @@ async function deploy(options) {
|
|
|
8902
9229
|
return withSpan("deploy", async (rootSpan) => {
|
|
8903
9230
|
rootSpan.setAttribute("deploy.dry_run", options?.dryRun ?? false);
|
|
8904
9231
|
const { config, application, workflowBuildResult, bundledScripts, buildOnly } = await withSpan("build", async () => {
|
|
8905
|
-
const { config, plugins } = await withSpan("build.loadConfig", () => loadConfig(options?.configPath));
|
|
8906
9232
|
const dryRun = options?.dryRun ?? false;
|
|
8907
9233
|
const buildOnly = options?.buildOnly ?? parseBoolean(process.env.TAILOR_PLATFORM_SDK_BUILD_ONLY) === true;
|
|
9234
|
+
const { config, plugins } = await withSpan("build.loadConfig", async () => {
|
|
9235
|
+
const foundPath = loadConfigPath(options?.configPath);
|
|
9236
|
+
if (foundPath && !dryRun && !buildOnly) {
|
|
9237
|
+
const resolvedPath = path.resolve(process.cwd(), foundPath);
|
|
9238
|
+
if (fs$1.existsSync(resolvedPath)) await ensureConfigId(resolvedPath);
|
|
9239
|
+
}
|
|
9240
|
+
return loadConfig(options?.configPath);
|
|
9241
|
+
});
|
|
8908
9242
|
const noCache = options?.noCache ?? false;
|
|
8909
9243
|
const packageJson = await readPackageJson();
|
|
8910
9244
|
const cacheDir = path.resolve(getDistDir(), "cache");
|
|
@@ -8982,7 +9316,7 @@ async function deploy(options) {
|
|
|
8982
9316
|
forceApplyAll,
|
|
8983
9317
|
idpUserTriggerTargets
|
|
8984
9318
|
};
|
|
8985
|
-
const functionRegistry = await withSpan("plan.functionRegistry", () => planFunctionRegistry(client, workspaceId, application.name, functionEntries));
|
|
9319
|
+
const functionRegistry = await withSpan("plan.functionRegistry", () => planFunctionRegistry(client, workspaceId, application.name, application.id, functionEntries));
|
|
8986
9320
|
const unchangedWorkflowJobs = new Set(functionRegistry.changeSet.unchanged.filter((entry) => entry.name.startsWith(WORKFLOW_PREFIX)).map((entry) => entry.name.slice(WORKFLOW_PREFIX.length)));
|
|
8987
9321
|
const [tailorDB, staticWebsite, idp, auth, pipeline, app, executor, workflow, secretManager] = await Promise.all([
|
|
8988
9322
|
withSpan("plan.tailorDB", () => planTailorDB(ctx)),
|
|
@@ -8992,7 +9326,7 @@ async function deploy(options) {
|
|
|
8992
9326
|
withSpan("plan.pipeline", () => planPipeline(ctx)),
|
|
8993
9327
|
withSpan("plan.application", () => planApplication(ctx)),
|
|
8994
9328
|
withSpan("plan.executor", () => planExecutor(ctx)),
|
|
8995
|
-
withSpan("plan.workflow", () => planWorkflow(client, workspaceId, application.name, workflowService?.workflows ?? {}, workflowBuildResult?.mainJobDeps ?? {}, unchangedWorkflowJobs)),
|
|
9329
|
+
withSpan("plan.workflow", () => planWorkflow(client, workspaceId, application.name, application.id, workflowService?.workflows ?? {}, workflowBuildResult?.mainJobDeps ?? {}, unchangedWorkflowJobs)),
|
|
8996
9330
|
withSpan("plan.secretManager", () => planSecretManager(ctx))
|
|
8997
9331
|
]);
|
|
8998
9332
|
return {
|
|
@@ -10688,6 +11022,7 @@ The \`--logs\` option displays logs from the downstream execution when available
|
|
|
10688
11022
|
})
|
|
10689
11023
|
}).strict(),
|
|
10690
11024
|
run: async (args) => {
|
|
11025
|
+
await assertWritable({ profile: args.profile });
|
|
10691
11026
|
const client = await initOperatorClient(await loadAccessToken({
|
|
10692
11027
|
useProfile: true,
|
|
10693
11028
|
profile: args.profile
|
|
@@ -11820,7 +12155,9 @@ function createGenerationManager(params) {
|
|
|
11820
12155
|
*/
|
|
11821
12156
|
async function generate$1(options) {
|
|
11822
12157
|
return withSpan("generate", async (rootSpan) => {
|
|
11823
|
-
const { config, generators, plugins } = await withSpan("generate.loadConfig", async () =>
|
|
12158
|
+
const { config, generators, plugins } = await withSpan("generate.loadConfig", async () => {
|
|
12159
|
+
return loadConfig(options?.configPath);
|
|
12160
|
+
});
|
|
11824
12161
|
const watch = options?.watch ?? false;
|
|
11825
12162
|
rootSpan.setAttribute("generate.watch", watch);
|
|
11826
12163
|
rootSpan.setAttribute("generate.generators.count", generators.length);
|
|
@@ -12192,6 +12529,7 @@ const createCommand$1 = defineAppCommand({
|
|
|
12192
12529
|
})
|
|
12193
12530
|
}).strict(),
|
|
12194
12531
|
run: async (args) => {
|
|
12532
|
+
await assertWritable();
|
|
12195
12533
|
const folder = await createFolder({
|
|
12196
12534
|
organizationId: args["organization-id"],
|
|
12197
12535
|
parentFolderId: args["parent-folder-id"],
|
|
@@ -12230,6 +12568,7 @@ const deleteCommand$1 = defineAppCommand({
|
|
|
12230
12568
|
...confirmationArgs
|
|
12231
12569
|
}).strict(),
|
|
12232
12570
|
run: async (args) => {
|
|
12571
|
+
await assertWritable();
|
|
12233
12572
|
const client = await initOperatorClient(await loadAccessToken());
|
|
12234
12573
|
let folderName;
|
|
12235
12574
|
try {
|
|
@@ -12380,6 +12719,7 @@ const updateCommand$2 = defineAppCommand({
|
|
|
12380
12719
|
})
|
|
12381
12720
|
}).strict(),
|
|
12382
12721
|
run: async (args) => {
|
|
12722
|
+
await assertWritable();
|
|
12383
12723
|
const folder = await updateFolder({
|
|
12384
12724
|
organizationId: args["organization-id"],
|
|
12385
12725
|
folderId: args["folder-id"],
|
|
@@ -12605,6 +12945,7 @@ const updateCommand$1 = defineAppCommand({
|
|
|
12605
12945
|
})
|
|
12606
12946
|
}).strict(),
|
|
12607
12947
|
run: async (args) => {
|
|
12948
|
+
await assertWritable();
|
|
12608
12949
|
const organization = await updateOrganization({
|
|
12609
12950
|
organizationId: args["organization-id"],
|
|
12610
12951
|
name: args.name
|
|
@@ -12648,8 +12989,8 @@ async function execRemove(client, workspaceId, application, config, confirm) {
|
|
|
12648
12989
|
const pipeline = await planPipeline(ctx);
|
|
12649
12990
|
const app = await planApplication(ctx);
|
|
12650
12991
|
const executor = await planExecutor(ctx);
|
|
12651
|
-
const workflow = await planWorkflow(client, workspaceId, application.name, {}, {});
|
|
12652
|
-
const functionRegistry = await planFunctionRegistry(client, workspaceId, application.name, []);
|
|
12992
|
+
const workflow = await planWorkflow(client, workspaceId, application.name, application.id, {}, {});
|
|
12993
|
+
const functionRegistry = await planFunctionRegistry(client, workspaceId, application.name, application.id, []);
|
|
12653
12994
|
const secretManager = await planSecretManager(ctx);
|
|
12654
12995
|
functionRegistry.changeSet.print();
|
|
12655
12996
|
staticWebsite.changeSet.print();
|
|
@@ -12709,6 +13050,7 @@ const removeCommand$1 = defineAppCommand({
|
|
|
12709
13050
|
...confirmationArgs
|
|
12710
13051
|
}).strict(),
|
|
12711
13052
|
run: async (args) => {
|
|
13053
|
+
await assertWritable({ profile: args.profile });
|
|
12712
13054
|
const { client, workspaceId, application, config } = await loadOptions$10({
|
|
12713
13055
|
workspaceId: args["workspace-id"],
|
|
12714
13056
|
profile: args.profile,
|
|
@@ -13372,7 +13714,7 @@ async function generate(options) {
|
|
|
13372
13714
|
if (options.init) await handleInitOption(namespacesWithMigrations, options.yes);
|
|
13373
13715
|
let pluginManager;
|
|
13374
13716
|
if (plugins.length > 0) pluginManager = new PluginManager(plugins);
|
|
13375
|
-
const { defineApplication } = await import("./application-
|
|
13717
|
+
const { defineApplication } = await import("./application-v_E2W-Fz.mjs");
|
|
13376
13718
|
const application = defineApplication({
|
|
13377
13719
|
config,
|
|
13378
13720
|
pluginManager
|
|
@@ -13702,6 +14044,7 @@ const truncateCommand = defineAppCommand({
|
|
|
13702
14044
|
})
|
|
13703
14045
|
}).strict(),
|
|
13704
14046
|
run: async (args) => {
|
|
14047
|
+
await assertWritable({ profile: args.profile });
|
|
13705
14048
|
const types = args.types && args.types.length > 0 ? args.types : void 0;
|
|
13706
14049
|
await $truncate({
|
|
13707
14050
|
workspaceId: args["workspace-id"],
|
|
@@ -14084,9 +14427,11 @@ const createCommand = defineAppCommand({
|
|
|
14084
14427
|
alias: "p",
|
|
14085
14428
|
description: "Profile name to create"
|
|
14086
14429
|
}),
|
|
14087
|
-
"profile-user": arg(z.string().optional(), { description: "User email for the profile (defaults to current user)" })
|
|
14430
|
+
"profile-user": arg(z.string().optional(), { description: "User email for the profile (defaults to current user)" }),
|
|
14431
|
+
permission: arg(z.enum(["write", "read"]).default("write"), { description: "Profile permission (requires --profile-name). 'read' blocks all write commands while the profile is active." })
|
|
14088
14432
|
}).strict(),
|
|
14089
14433
|
run: async (args) => {
|
|
14434
|
+
await assertWritable();
|
|
14090
14435
|
const workspace = await createWorkspace({
|
|
14091
14436
|
name: args.name,
|
|
14092
14437
|
region: args.region,
|
|
@@ -14104,13 +14449,15 @@ const createCommand = defineAppCommand({
|
|
|
14104
14449
|
if (!config.users[profileUser]) throw new Error(`User "${profileUser}" not found.\nPlease verify your user name and login using 'tailor-sdk login' command.`);
|
|
14105
14450
|
config.profiles[profileName] = {
|
|
14106
14451
|
user: profileUser,
|
|
14107
|
-
workspace_id: workspace.id
|
|
14452
|
+
workspace_id: workspace.id,
|
|
14453
|
+
...args.permission === "read" ? { readonly: true } : {}
|
|
14108
14454
|
};
|
|
14109
14455
|
writePlatformConfig(config);
|
|
14110
14456
|
profileInfo = {
|
|
14111
14457
|
name: profileName,
|
|
14112
14458
|
user: profileUser,
|
|
14113
|
-
workspaceId: workspace.id
|
|
14459
|
+
workspaceId: workspace.id,
|
|
14460
|
+
permission: args.permission
|
|
14114
14461
|
};
|
|
14115
14462
|
if (!args.json) logger.success(`Profile "${profileName}" created successfully.`);
|
|
14116
14463
|
}
|
|
@@ -14161,6 +14508,7 @@ const deleteCommand = defineAppCommand({
|
|
|
14161
14508
|
...confirmationArgs
|
|
14162
14509
|
}).strict(),
|
|
14163
14510
|
run: async (args) => {
|
|
14511
|
+
await assertWritable();
|
|
14164
14512
|
const { client, workspaceId } = await loadOptions$7({ workspaceId: args["workspace-id"] });
|
|
14165
14513
|
let workspace;
|
|
14166
14514
|
try {
|
|
@@ -14298,6 +14646,7 @@ const restoreCommand = defineAppCommand({
|
|
|
14298
14646
|
...confirmationArgs
|
|
14299
14647
|
}).strict(),
|
|
14300
14648
|
run: async (args) => {
|
|
14649
|
+
await assertWritable();
|
|
14301
14650
|
const { client, workspaceId } = await loadOptions$5({ workspaceId: args["workspace-id"] });
|
|
14302
14651
|
if (!args.yes) {
|
|
14303
14652
|
if (await prompt.text({ message: `Are you sure you want to restore workspace "${workspaceId}"? (yes/no):` }) !== "yes") {
|
|
@@ -14390,6 +14739,7 @@ const inviteCommand = defineAppCommand({
|
|
|
14390
14739
|
})
|
|
14391
14740
|
}).strict(),
|
|
14392
14741
|
run: async (args) => {
|
|
14742
|
+
await assertWritable({ profile: args.profile });
|
|
14393
14743
|
await inviteUser({
|
|
14394
14744
|
workspaceId: args["workspace-id"],
|
|
14395
14745
|
profile: args.profile,
|
|
@@ -14503,6 +14853,7 @@ const removeCommand = defineAppCommand({
|
|
|
14503
14853
|
...confirmationArgs
|
|
14504
14854
|
}).strict(),
|
|
14505
14855
|
run: async (args) => {
|
|
14856
|
+
await assertWritable({ profile: args.profile });
|
|
14506
14857
|
if (!args.yes) {
|
|
14507
14858
|
if (await prompt.text({ message: `Are you sure you want to remove user "${args.email}" from the workspace? (yes/no):` }) !== "yes") {
|
|
14508
14859
|
logger.info("User removal cancelled.");
|
|
@@ -14567,6 +14918,7 @@ const updateCommand = defineAppCommand({
|
|
|
14567
14918
|
})
|
|
14568
14919
|
}).strict(),
|
|
14569
14920
|
run: async (args) => {
|
|
14921
|
+
await assertWritable({ profile: args.profile });
|
|
14570
14922
|
await updateUser({
|
|
14571
14923
|
workspaceId: args["workspace-id"],
|
|
14572
14924
|
profile: args.profile,
|
|
@@ -15187,7 +15539,7 @@ async function runRepl(options) {
|
|
|
15187
15539
|
const execute = await prepareQueryExecutor(options);
|
|
15188
15540
|
const historyPath = getReplHistoryPath(options.engine, options.profile, options.workspaceId);
|
|
15189
15541
|
const validate = createReplValidator(options.engine);
|
|
15190
|
-
const { highlightSqlLine, highlightGraphqlLine, replTransform } = await import("./repl-editor-
|
|
15542
|
+
const { highlightSqlLine, highlightGraphqlLine, replTransform } = await import("./repl-editor-jZ493eQI.mjs");
|
|
15191
15543
|
const highlight = options.engine === "sql" ? highlightSqlLine : highlightGraphqlLine;
|
|
15192
15544
|
const prompt = createPrompt({
|
|
15193
15545
|
prefix: "",
|
|
@@ -15520,5 +15872,5 @@ function isDeno() {
|
|
|
15520
15872
|
}
|
|
15521
15873
|
|
|
15522
15874
|
//#endregion
|
|
15523
|
-
export { deleteCommand$1 as $, getMigrationDirPath as $t, truncate as A, executionsCommand as At, updateOrganization as B, MIGRATION_LABEL_KEY as Bt, listCommand$2 as C,
|
|
15524
|
-
//# sourceMappingURL=runtime-
|
|
15875
|
+
export { deleteCommand$1 as $, getMigrationDirPath as $t, truncate as A, executionsCommand as At, updateOrganization as B, MIGRATION_LABEL_KEY as Bt, listCommand$2 as C, paginationArgs as Cn, jobsCommand as Ct, resumeWorkflow as D, startWorkflow as Dt, resumeCommand as E, startCommand as Et, showCommand as F, getCommand$6 as Ft, getCommand$1 as G, INITIAL_SCHEMA_NUMBER as Gt, treeCommand as H, bundleMigrationScript as Ht, logBetaWarning as I, getExecutor as It, updateFolder as J, compareLocalTypesWithSnapshot as Jt, getOrganization as K, MIGRATE_FILE_NAME as Kt, remove as L, deploy as Lt, generate as M, listWorkflowExecutions as Mt, generateCommand as N, functionExecutionStatusToString as Nt, listCommand$3 as O, getCommand$5 as Ot, show as P, formatKeyValueTable as Pt, getFolder as Q, getLatestMigrationNumber as Qt, removeCommand$1 as R, executeScript as Rt, listApps as S, pagedLogArgs as Sn, getExecutorJob as St, healthCommand as T, workspaceArgs as Tn, watchExecutorJob as Tt, listCommand$4 as U, DB_TYPES_FILE_NAME as Ut, organizationTree as V, parseMigrationLabelNumber as Vt, listOrganizations as W, DIFF_FILE_NAME as Wt, listFolders as X, createSnapshotFromLocalTypes as Xt, listCommand$5 as Y, compareSnapshots as Yt, getCommand$2 as Z, formatMigrationNumber as Zt, getWorkspace as _, defineAppCommand as _n, webhookCommand as _t, updateUser as a, reconstructSnapshotFromMigrations as an, getCommand$3 as at, createCommand as b, deploymentArgs as bn, listCommand$9 as bt, listCommand as c, hasChanges as cn, tokenCommand as ct, inviteUser as d, trnPrefix as dn, generate$1 as dt, getMigrationFilePath as en, deleteFolder as et, restoreCommand as f, generateUserTypes as fn, listCommand$8 as ft, getCommand as g, assertWritable as gn, listWebhookExecutors as gt, listWorkspaces as h, apiCall as hn, getFunctionRegistry as ht, updateCommand as i, loadDiff as in, listOAuth2Clients as it, truncateCommand as j, getWorkflowExecution as jt, listWorkflows as k, getWorkflow as kt, listUsers as l, getNamespacesWithMigrations as ln, listCommand$7 as lt, listCommand$1 as m, apiCommand as mn, getCommand$4 as mt, query as n, getNextMigrationNumber as nn, createFolder as nt, removeCommand as o, formatDiffSummary as on, getOAuth2Client as ot, restoreWorkspace as p, prompt as pn, listFunctionRegistries as pt, updateCommand$2 as q, SCHEMA_FILE_NAME as qt, queryCommand as r, isValidMigrationNumber as rn, listCommand$6 as rt, removeUser as s, formatMigrationDiff as sn, getMachineUserToken as st, isNativeTypeScriptRuntime as t, getMigrationFiles as tn, createCommand$1 as tt, inviteCommand as u, sdkNameLabelKey as un, listMachineUsers as ut, deleteCommand as v, commonArgs as vn, triggerCommand as vt, getAppHealth as w, toPageDirection as wn, listExecutorJobs as wt, createWorkspace as x, isVerbose as xn, listExecutors as xt, deleteWorkspace as y, confirmationArgs as yn, triggerExecutor as yt, updateCommand$1 as z, waitForExecution$1 as zt };
|
|
15876
|
+
//# sourceMappingURL=runtime-oZgK353r.mjs.map
|