@tailor-platform/sdk 1.60.3 → 1.62.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 +22 -0
- package/dist/{application-pusdxz35.mjs → application-BezXGbrU.mjs} +73 -509
- package/dist/application-BezXGbrU.mjs.map +1 -0
- package/dist/application-DSXntqnV.mjs +4 -0
- package/dist/assert-CKfwrmCV.mjs +10 -0
- package/dist/assert-CKfwrmCV.mjs.map +1 -0
- package/dist/cli/index.mjs +376 -136
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/lib.d.mts +3 -1
- package/dist/cli/lib.mjs +13 -6
- package/dist/cli/lib.mjs.map +1 -1
- package/dist/{client-B-jRdlC_.mjs → client-C68VWo4g.mjs} +1 -1
- package/dist/{client-W5P4NYYX.mjs → client-CobIRHl-.mjs} +207 -2
- package/dist/{client-W5P4NYYX.mjs.map → client-CobIRHl-.mjs.map} +1 -1
- package/dist/configure/index.mjs +2 -2
- package/dist/{crashreport-D3DvAzdg.mjs → crashreport-BhD0y14F.mjs} +2 -2
- package/dist/{crashreport-D3DvAzdg.mjs.map → crashreport-BhD0y14F.mjs.map} +1 -1
- package/dist/{crashreport-lnVTnbB5.mjs → crashreport-D1wKBJ8N.mjs} +1 -1
- package/dist/{mock-Dpu__UeJ.mjs → mock-DMgIygjE.mjs} +3 -2
- package/dist/mock-DMgIygjE.mjs.map +1 -0
- package/dist/plugin/builtin/seed/index.mjs +1 -1
- package/dist/plugin/index.mjs +1 -1
- package/dist/plugin/index.mjs.map +1 -1
- package/dist/registry-D0uB0OrK.mjs.map +1 -1
- package/dist/{repl-editor-Y9QJDL0K.mjs → repl-editor-CJG3sz7A.mjs} +11 -9
- package/dist/repl-editor-CJG3sz7A.mjs.map +1 -0
- package/dist/{runtime-C0_FZWdE.mjs → runtime-C6o4hiYq.mjs} +903 -579
- package/dist/runtime-C6o4hiYq.mjs.map +1 -0
- package/dist/{schema-DYKNTu-n.mjs → schema-1msIhXwA.mjs} +12 -7
- package/dist/schema-1msIhXwA.mjs.map +1 -0
- package/dist/{seed-C0fE2sJB.mjs → seed-BH2FbrPV.mjs} +4 -3
- package/dist/seed-BH2FbrPV.mjs.map +1 -0
- package/dist/service-BHQIerYh.mjs +4 -0
- package/dist/{service-aPT0fx3y.mjs → service-DMohAx8a2.mjs} +3 -3
- package/dist/service-DMohAx8a2.mjs.map +1 -0
- package/dist/service-wI3Hvrgx.mjs +460 -0
- package/dist/service-wI3Hvrgx.mjs.map +1 -0
- package/dist/{types-Ccwchyj5.mjs → types-2Be3wSMc.mjs} +1 -1
- package/dist/{types-BwGth3a1.mjs → types-CmzfQP_m.mjs} +3 -3
- package/dist/types-CmzfQP_m.mjs.map +1 -0
- package/dist/utils/test/index.mjs +1 -1
- package/dist/utils/test/index.mjs.map +1 -1
- package/dist/vitest/index.mjs +7 -4
- package/dist/vitest/index.mjs.map +1 -1
- package/dist/vitest/setup.mjs +1 -1
- package/docs/cli/application.md +11 -10
- package/docs/cli/tailordb.md +54 -0
- package/docs/cli-reference.md +1 -0
- package/docs/services/tailordb-migration.md +17 -1
- package/package.json +2 -1
- package/dist/application-D4tRNn90.mjs +0 -4
- package/dist/application-pusdxz35.mjs.map +0 -1
- package/dist/mock-Dpu__UeJ.mjs.map +0 -1
- package/dist/repl-editor-Y9QJDL0K.mjs.map +0 -1
- package/dist/runtime-C0_FZWdE.mjs.map +0 -1
- package/dist/schema-DYKNTu-n.mjs.map +0 -1
- package/dist/seed-C0fE2sJB.mjs.map +0 -1
- package/dist/service-aPT0fx3y.mjs.map +0 -1
- package/dist/types-BwGth3a1.mjs.map +0 -1
package/dist/cli/index.mjs
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { Et as AuthInvokerSchema, L as CustomDomainStatus, Nt as PATScope, Q as FunctionExecution_Type, _ as userAgent, a as fetchAll, c as fetchPlatformMachineUserToken, d as initOAuth2Client, f as initOperatorClient, l as fetchUserInfo, r as closeConnectionPool, s as fetchPaged } from "../client-CobIRHl-.mjs";
|
|
4
|
+
import { t as assertDefined } from "../assert-CKfwrmCV.mjs";
|
|
4
5
|
import { n as logger, r as styles } from "../logger-DpJyJvNz.mjs";
|
|
5
|
-
import { $ as listCommand$10, An as
|
|
6
|
-
import {
|
|
6
|
+
import { $ as listCommand$10, $t as INITIAL_SCHEMA_NUMBER, An as configArg, At as startCommand, B as logBetaWarning, C as listCommand$13, Cn as generateUserTypes, Dn as assertWritable, Dt as jobsCommand, E as resumeCommand, F as writeDbTypesFile, Fn as paginationArgs, Gt as MIGRATION_LABEL_KEY, H as removeCommand$1, Ht as executeScript, I as getConfiguredEditorCommand, In as toPageDirection, Jt as compareSnapshotWithRemote, K as treeCommand, Kt as handleOptionalToRequiredError, L as openInConfiguredEditor, Ln as workspaceArgs, Lt as functionExecutionStatusToString, Mn as deploymentArgs, Mt as getCommand$6, N as generateCommand$1, Nn as isVerbose, O as listCommand$12, On as defineAppCommand, P as generateMigrationScript, Pn as pagedLogArgs, Pt as executionsCommand, Rt as formatKeyValueTable, Sn as PluginManager, St as triggerCommand, T as healthCommand, Tn as apiCommand, U as updateCommand$3, Vt as deploy, Xt as protoGqlPermission, Y as getCommand$5, Yt as generateAllTypeManifestsFromSnapshot, Z as updateCommand$2, _n as formatMigrationDiff, an as createSnapshotFromLocalTypes, at as createCommand$3, b as createCommand$4, bn as resourceTrn, c as listCommand$14, cn as getMigrationFilePath, dn as isValidMigrationNumber, f as restoreCommand, fn as loadDiff, ft as tokenCommand, g as getCommand$7, gt as listCommand$7, hn as parseMigrationNumberArg, ht as generate, i as updateCommand$4, j as truncateCommand, jn as confirmationArgs, kn as commonArgs, ln as getMigrationFiles, lt as getCommand$3, m as listCommand$15, mn as formatMigrationNumber, nn as assertValidMigrationFiles, o as removeCommand, on as getLatestMigrationNumber, pn as reconstructSnapshotFromMigrations, pt as listCommand$8, q as listCommand$11, qt as parseMigrationLabelNumber, r as queryCommand, rn as compareLocalTypesWithSnapshot, rt as deleteCommand$3, st as listCommand$9, t as isNativeTypeScriptRuntime, tt as getCommand$4, u as inviteCommand, v as deleteCommand$4, vn as hasChanges, vt as getCommand$2, wn as prompt, wt as listCommand$6, xn as sdkNameLabelKey, xt as webhookCommand, yn as getNamespacesWithMigrations, z as showCommand, zt as getCommand$1 } from "../runtime-C6o4hiYq.mjs";
|
|
7
|
+
import { A as resolveTokens, C as loadConfig, E as loadAccessToken, M as writePlatformConfig, O as loadWorkspaceId, T as fetchLatestToken, _ as createLogLevelTreeshakeOptions, a as WorkflowJobSchema, b as getDistDir, c as INVOKER_EXPR, g as composeFunctionTreeshakeOptions, h as platformBundleDefinePlugin, i as resolveInlineSourcemap, j as saveUserTokens, k as readPlatformConfig, o as ResolverSchema, t as defineApplication, v as resolveBundleLogLevel, w as deleteUserTokens, x as hashContent } from "../application-BezXGbrU.mjs";
|
|
8
|
+
import { n as ExecutorSchema } from "../service-wI3Hvrgx.mjs";
|
|
7
9
|
import { t as multiline } from "../multiline-Cf9ODpr1.mjs";
|
|
8
|
-
import { r as isPluginGeneratedType } from "../seed-
|
|
10
|
+
import { r as isPluginGeneratedType } from "../seed-BH2FbrPV.mjs";
|
|
9
11
|
import { t as readPackageJson } from "../package-json-DcQApfPQ.mjs";
|
|
10
12
|
import { n as isCLIError } from "../errors-EsY4XO6O.mjs";
|
|
11
|
-
import { a as JSON_FOOTER_MARKER, i as CRASH_LOG_EXTENSION, o as parseCrashReportConfig, r as sendCrashReport, t as initCrashReporting } from "../crashreport-
|
|
13
|
+
import { a as JSON_FOOTER_MARKER, i as CRASH_LOG_EXTENSION, o as parseCrashReportConfig, r as sendCrashReport, t as initCrashReporting } from "../crashreport-BhD0y14F.mjs";
|
|
12
14
|
import { arg, defineCommand, runCommand, runMain } from "politty";
|
|
13
15
|
import { withCompletionCommand } from "politty/completion";
|
|
14
16
|
import { z } from "zod";
|
|
@@ -74,10 +76,7 @@ const authorizeAuthConnectionCommand = defineAppCommand({
|
|
|
74
76
|
}).strict(),
|
|
75
77
|
run: async (args) => {
|
|
76
78
|
await assertWritable({ profile: args.profile });
|
|
77
|
-
const client = await initOperatorClient(await loadAccessToken({
|
|
78
|
-
useProfile: true,
|
|
79
|
-
profile: args.profile
|
|
80
|
-
}));
|
|
79
|
+
const client = await initOperatorClient(await loadAccessToken({ profile: args.profile }));
|
|
81
80
|
const workspaceId = await loadWorkspaceId({
|
|
82
81
|
workspaceId: args["workspace-id"],
|
|
83
82
|
profile: args.profile
|
|
@@ -178,10 +177,7 @@ const deleteAuthConnectionCommand = defineAppCommand({
|
|
|
178
177
|
}).strict(),
|
|
179
178
|
run: async (args) => {
|
|
180
179
|
await assertWritable({ profile: args.profile });
|
|
181
|
-
const client = await initOperatorClient(await loadAccessToken({
|
|
182
|
-
useProfile: true,
|
|
183
|
-
profile: args.profile
|
|
184
|
-
}));
|
|
180
|
+
const client = await initOperatorClient(await loadAccessToken({ profile: args.profile }));
|
|
185
181
|
const workspaceId = await loadWorkspaceId({
|
|
186
182
|
workspaceId: args["workspace-id"],
|
|
187
183
|
profile: args.profile
|
|
@@ -228,10 +224,7 @@ const listAuthConnectionCommand = defineAppCommand({
|
|
|
228
224
|
...paginationArgs()
|
|
229
225
|
}).strict(),
|
|
230
226
|
run: async (args) => {
|
|
231
|
-
const client = await initOperatorClient(await loadAccessToken({
|
|
232
|
-
useProfile: true,
|
|
233
|
-
profile: args.profile
|
|
234
|
-
}));
|
|
227
|
+
const client = await initOperatorClient(await loadAccessToken({ profile: args.profile }));
|
|
235
228
|
const workspaceId = await loadWorkspaceId({
|
|
236
229
|
workspaceId: args["workspace-id"],
|
|
237
230
|
profile: args.profile
|
|
@@ -308,10 +301,7 @@ const revokeAuthConnectionCommand = defineAppCommand({
|
|
|
308
301
|
}).strict(),
|
|
309
302
|
run: async (args) => {
|
|
310
303
|
await assertWritable({ profile: args.profile });
|
|
311
|
-
const client = await initOperatorClient(await loadAccessToken({
|
|
312
|
-
useProfile: true,
|
|
313
|
-
profile: args.profile
|
|
314
|
-
}));
|
|
304
|
+
const client = await initOperatorClient(await loadAccessToken({ profile: args.profile }));
|
|
315
305
|
const workspaceId = await loadWorkspaceId({
|
|
316
306
|
workspaceId: args["workspace-id"],
|
|
317
307
|
profile: args.profile
|
|
@@ -487,6 +477,7 @@ const deployCommand$1 = defineAppCommand({
|
|
|
487
477
|
description: "Run the command without making any changes"
|
|
488
478
|
}),
|
|
489
479
|
"no-schema-check": arg(z.boolean().optional(), { description: "Skip schema diff check against migration snapshots" }),
|
|
480
|
+
"no-validate": arg(z.boolean().optional(), { description: "Skip client-side validation against platform resource constraints" }),
|
|
490
481
|
"no-cache": arg(z.boolean().optional(), { description: "Disable bundle caching for this run" }),
|
|
491
482
|
"clean-cache": arg(z.boolean().optional(), { description: "Clean the bundle cache before building" })
|
|
492
483
|
}).strict(),
|
|
@@ -501,6 +492,7 @@ const deployCommand$1 = defineAppCommand({
|
|
|
501
492
|
dryRun: args["dry-run"],
|
|
502
493
|
yes: args.yes,
|
|
503
494
|
noSchemaCheck: args["no-schema-check"],
|
|
495
|
+
noValidate: args["no-validate"],
|
|
504
496
|
noCache: args["no-cache"],
|
|
505
497
|
cleanCache: args["clean-cache"]
|
|
506
498
|
});
|
|
@@ -644,9 +636,9 @@ function parseStackTrace(error) {
|
|
|
644
636
|
const match = STACK_FRAME_REGEX.exec(line);
|
|
645
637
|
if (match) frames.push({
|
|
646
638
|
functionName: match[1] || "<anonymous>",
|
|
647
|
-
file: match[2],
|
|
648
|
-
line: Number(match[3]),
|
|
649
|
-
column: Number(match[4])
|
|
639
|
+
file: assertDefined(match[2], "stack frame file missing"),
|
|
640
|
+
line: Number(assertDefined(match[3], "stack frame line missing")),
|
|
641
|
+
column: Number(assertDefined(match[4], "stack frame column missing"))
|
|
650
642
|
});
|
|
651
643
|
}
|
|
652
644
|
return {
|
|
@@ -664,7 +656,7 @@ function extractInlineSourcemap(bundledCode) {
|
|
|
664
656
|
const match = INLINE_SOURCEMAP_REGEX.exec(bundledCode);
|
|
665
657
|
if (!match) return null;
|
|
666
658
|
try {
|
|
667
|
-
const decoded = Buffer.from(match[1], "base64").toString("utf-8");
|
|
659
|
+
const decoded = Buffer.from(assertDefined(match[1], "sourcemap base64 data missing"), "base64").toString("utf-8");
|
|
668
660
|
return new TraceMap(JSON.parse(decoded));
|
|
669
661
|
} catch {
|
|
670
662
|
return null;
|
|
@@ -918,7 +910,7 @@ function composeExecutionErrorString(error) {
|
|
|
918
910
|
*/
|
|
919
911
|
function formatExecutionErrorFallback(error) {
|
|
920
912
|
const [headerLine, ...frameLines] = composeExecutionErrorString(error).split("\n");
|
|
921
|
-
return [` ${styles.error(headerLine
|
|
913
|
+
return [` ${styles.error(headerLine)}`, ...frameLines.map((line) => ` ${styles.dim(line)}`)].join("\n");
|
|
922
914
|
}
|
|
923
915
|
/**
|
|
924
916
|
* Format an execution error for display, applying sourcemap mapping
|
|
@@ -1061,10 +1053,7 @@ Stack traces stay accurate even after later redeploys, because the trace is reso
|
|
|
1061
1053
|
})
|
|
1062
1054
|
}).strict(),
|
|
1063
1055
|
run: async (args) => {
|
|
1064
|
-
const client = await initOperatorClient(await loadAccessToken({
|
|
1065
|
-
useProfile: true,
|
|
1066
|
-
profile: args.profile
|
|
1067
|
-
}));
|
|
1056
|
+
const client = await initOperatorClient(await loadAccessToken({ profile: args.profile }));
|
|
1068
1057
|
const workspaceId = await loadWorkspaceId({
|
|
1069
1058
|
workspaceId: args["workspace-id"],
|
|
1070
1059
|
profile: args.profile
|
|
@@ -1228,7 +1217,7 @@ function generateEntry(detected, sourceFile, env, machineUser, workspaceId) {
|
|
|
1228
1217
|
`;
|
|
1229
1218
|
}
|
|
1230
1219
|
case "workflow-job": {
|
|
1231
|
-
const exportName = detected.exportName;
|
|
1220
|
+
const exportName = assertDefined(detected.exportName, "workflow job export name missing");
|
|
1232
1221
|
return multiline`
|
|
1233
1222
|
import { ${exportName} } from "${absoluteSourcePath}";
|
|
1234
1223
|
|
|
@@ -1294,7 +1283,7 @@ async function detectFunctionType(options) {
|
|
|
1294
1283
|
const rawInput = module.default.input;
|
|
1295
1284
|
let inputSchema;
|
|
1296
1285
|
if (rawInput) {
|
|
1297
|
-
const { t } = await import("../types-
|
|
1286
|
+
const { t } = await import("../types-2Be3wSMc.mjs");
|
|
1298
1287
|
inputSchema = t.object(rawInput);
|
|
1299
1288
|
}
|
|
1300
1289
|
return {
|
|
@@ -1362,11 +1351,15 @@ function detectWorkflowJob(module, jobName) {
|
|
|
1362
1351
|
exportName: match.exportName
|
|
1363
1352
|
};
|
|
1364
1353
|
}
|
|
1365
|
-
if (jobs.length === 1)
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1354
|
+
if (jobs.length === 1) {
|
|
1355
|
+
const [jobEntry] = jobs;
|
|
1356
|
+
const job = assertDefined(jobEntry, "workflow job missing");
|
|
1357
|
+
return {
|
|
1358
|
+
type: "workflow-job",
|
|
1359
|
+
name: job.name,
|
|
1360
|
+
exportName: job.exportName
|
|
1361
|
+
};
|
|
1362
|
+
}
|
|
1370
1363
|
const available = jobs.map((j) => ` - "${j.name}" (export: ${j.exportName})`).join("\n");
|
|
1371
1364
|
throw new Error(`Multiple workflow jobs found. Specify one with --name:\n${available}`);
|
|
1372
1365
|
}
|
|
@@ -1441,10 +1434,7 @@ When a \`.js\` file is provided, detection and bundling are skipped and the file
|
|
|
1441
1434
|
const { config } = await loadConfig(args.config);
|
|
1442
1435
|
const authNamespace = resolveAuthNamespace(config.auth);
|
|
1443
1436
|
const machineUserName = resolveMachineUserName(args["machine-user"], config.auth);
|
|
1444
|
-
const client = await initOperatorClient(await loadAccessToken({
|
|
1445
|
-
useProfile: true,
|
|
1446
|
-
profile: args.profile
|
|
1447
|
-
}));
|
|
1437
|
+
const client = await initOperatorClient(await loadAccessToken({ profile: args.profile }));
|
|
1448
1438
|
const workspaceId = await loadWorkspaceId({
|
|
1449
1439
|
workspaceId: args["workspace-id"],
|
|
1450
1440
|
profile: args.profile
|
|
@@ -1518,7 +1508,7 @@ When a \`.js\` file is provided, detection and bundling are skipped and the file
|
|
|
1518
1508
|
else {
|
|
1519
1509
|
if (result.success) logger.success("Execution succeeded");
|
|
1520
1510
|
else logger.error("Execution failed");
|
|
1521
|
-
if (result.logs
|
|
1511
|
+
if (result.logs.trim()) {
|
|
1522
1512
|
logger.log(styles.bold("\nLogs:"));
|
|
1523
1513
|
for (const line of result.logs.split("\n")) logger.log(` ${line}`);
|
|
1524
1514
|
}
|
|
@@ -1562,7 +1552,7 @@ function resolveMachineUserName(cliMachineUser, authConfig) {
|
|
|
1562
1552
|
const machineUsers = authConfig.machineUsers;
|
|
1563
1553
|
if (machineUsers) {
|
|
1564
1554
|
const keys = Object.keys(machineUsers);
|
|
1565
|
-
if (keys.length > 0) return keys[0];
|
|
1555
|
+
if (keys.length > 0) return assertDefined(keys[0], "machine user key missing");
|
|
1566
1556
|
}
|
|
1567
1557
|
}
|
|
1568
1558
|
throw new Error("Machine user is required. Provide --machine-user or ensure tailor.config.ts has machine users configured.");
|
|
@@ -1617,7 +1607,7 @@ function resolveResolverArg(argStr, inputSchema, machineUser, workspaceId) {
|
|
|
1617
1607
|
type: "machine_user",
|
|
1618
1608
|
workspaceId,
|
|
1619
1609
|
attributes: machineUser.attributes ?? null,
|
|
1620
|
-
attributeList: machineUser.attributeList
|
|
1610
|
+
attributeList: machineUser.attributeList
|
|
1621
1611
|
};
|
|
1622
1612
|
if (!inputSchema.parse({
|
|
1623
1613
|
value: parsed,
|
|
@@ -1751,7 +1741,7 @@ const startAuthServer = async () => {
|
|
|
1751
1741
|
await saveUserTokens(pfConfig, userInfo.email, {
|
|
1752
1742
|
accessToken: tokens.accessToken,
|
|
1753
1743
|
refreshToken: tokens.refreshToken ?? void 0
|
|
1754
|
-
}, new Date(tokens.expiresAt).toISOString());
|
|
1744
|
+
}, new Date(assertDefined(tokens.expiresAt, "token response missing expiresAt")).toISOString());
|
|
1755
1745
|
pfConfig.current_user = userInfo.email;
|
|
1756
1746
|
writePlatformConfig(pfConfig);
|
|
1757
1747
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
@@ -1797,7 +1787,7 @@ async function loginAsMachineUser(args) {
|
|
|
1797
1787
|
const clientSecret = args.clientSecret ?? await prompt.password({ message: "Client secret" });
|
|
1798
1788
|
const tokens = await fetchPlatformMachineUserToken(args.clientId, clientSecret);
|
|
1799
1789
|
const pfConfig = await readPlatformConfig();
|
|
1800
|
-
await saveUserTokens(pfConfig, args.clientId, { accessToken: tokens.accessToken }, new Date(tokens.expiresAt).toISOString());
|
|
1790
|
+
await saveUserTokens(pfConfig, args.clientId, { accessToken: tokens.accessToken }, new Date(assertDefined(tokens.expiresAt, "token response missing expiresAt")).toISOString());
|
|
1801
1791
|
pfConfig.current_user = args.clientId;
|
|
1802
1792
|
writePlatformConfig(pfConfig);
|
|
1803
1793
|
}
|
|
@@ -1821,7 +1811,7 @@ const loginCommand = defineAppCommand({
|
|
|
1821
1811
|
})
|
|
1822
1812
|
}).strict().describe("Machine User Login")]),
|
|
1823
1813
|
run: async (args) => {
|
|
1824
|
-
if ("machine-user" in args
|
|
1814
|
+
if ("machine-user" in args) await loginAsMachineUser({
|
|
1825
1815
|
clientId: args.clientId,
|
|
1826
1816
|
clientSecret: args.clientSecret
|
|
1827
1817
|
});
|
|
@@ -1839,13 +1829,14 @@ const logoutCommand = defineAppCommand({
|
|
|
1839
1829
|
args: z.object({}).strict(),
|
|
1840
1830
|
run: async () => {
|
|
1841
1831
|
const pfConfig = await readPlatformConfig();
|
|
1842
|
-
const
|
|
1843
|
-
|
|
1832
|
+
const currentUser = pfConfig.current_user;
|
|
1833
|
+
const userEntry = currentUser ? pfConfig.users[currentUser] : void 0;
|
|
1834
|
+
if (!userEntry || !currentUser) {
|
|
1844
1835
|
logger.info("You are not logged in.");
|
|
1845
1836
|
return;
|
|
1846
1837
|
}
|
|
1847
1838
|
try {
|
|
1848
|
-
const { accessToken, refreshToken } = await resolveTokens(userEntry,
|
|
1839
|
+
const { accessToken, refreshToken } = await resolveTokens(userEntry, currentUser);
|
|
1849
1840
|
const client = initOAuth2Client();
|
|
1850
1841
|
const tokenTypeHint = refreshToken ? "refresh_token" : "access_token";
|
|
1851
1842
|
await client.revoke({
|
|
@@ -1856,8 +1847,8 @@ const logoutCommand = defineAppCommand({
|
|
|
1856
1847
|
} catch (error) {
|
|
1857
1848
|
logger.warn(`Failed to revoke token: ${error instanceof Error ? error.message : error}`);
|
|
1858
1849
|
}
|
|
1859
|
-
await deleteUserTokens(pfConfig,
|
|
1860
|
-
delete pfConfig.users[
|
|
1850
|
+
await deleteUserTokens(pfConfig, currentUser);
|
|
1851
|
+
delete pfConfig.users[currentUser];
|
|
1861
1852
|
pfConfig.current_user = null;
|
|
1862
1853
|
writePlatformConfig(pfConfig);
|
|
1863
1854
|
logger.success("Successfully logged out from Tailor Platform.");
|
|
@@ -2048,12 +2039,15 @@ const listCommand$4 = defineAppCommand({
|
|
|
2048
2039
|
if (jsonOutput) logger.out([]);
|
|
2049
2040
|
return;
|
|
2050
2041
|
}
|
|
2051
|
-
const profileInfos = profiles.map(([name, profile]) =>
|
|
2052
|
-
name
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2042
|
+
const profileInfos = profiles.map(([name, profile]) => {
|
|
2043
|
+
const p = assertDefined(profile, `profile entry "${name}" is undefined`);
|
|
2044
|
+
return {
|
|
2045
|
+
name,
|
|
2046
|
+
user: p.user,
|
|
2047
|
+
workspaceId: p.workspace_id,
|
|
2048
|
+
permission: p.readonly === true ? "read" : "write"
|
|
2049
|
+
};
|
|
2050
|
+
});
|
|
2057
2051
|
logger.out(profileInfos);
|
|
2058
2052
|
}
|
|
2059
2053
|
});
|
|
@@ -2080,9 +2074,9 @@ const updateCommand$1 = defineAppCommand({
|
|
|
2080
2074
|
}).strict(),
|
|
2081
2075
|
run: async (args) => {
|
|
2082
2076
|
const config = await readPlatformConfig();
|
|
2083
|
-
if (!config.profiles[args.name]) throw new Error(`Profile "${args.name}" not found.`);
|
|
2084
|
-
if (!args.user && !args["workspace-id"] && args.permission === void 0) throw new Error("Please provide at least one property to update.");
|
|
2085
2077
|
const profile = config.profiles[args.name];
|
|
2078
|
+
if (!profile) throw new Error(`Profile "${args.name}" not found.`);
|
|
2079
|
+
if (!args.user && !args["workspace-id"] && args.permission === void 0) throw new Error("Please provide at least one property to update.");
|
|
2086
2080
|
const oldUser = profile.user;
|
|
2087
2081
|
const newUser = args.user || oldUser;
|
|
2088
2082
|
const oldWorkspaceId = profile.workspace_id;
|
|
@@ -2222,10 +2216,7 @@ const createSecretCommand = defineAppCommand({
|
|
|
2222
2216
|
}).strict(),
|
|
2223
2217
|
run: async (args) => {
|
|
2224
2218
|
await assertWritable({ profile: args.profile });
|
|
2225
|
-
const client = await initOperatorClient(await loadAccessToken({
|
|
2226
|
-
useProfile: true,
|
|
2227
|
-
profile: args.profile
|
|
2228
|
-
}));
|
|
2219
|
+
const client = await initOperatorClient(await loadAccessToken({ profile: args.profile }));
|
|
2229
2220
|
const workspaceId = await loadWorkspaceId({
|
|
2230
2221
|
workspaceId: args["workspace-id"],
|
|
2231
2222
|
profile: args.profile
|
|
@@ -2275,10 +2266,7 @@ const deleteSecretCommand = defineAppCommand({
|
|
|
2275
2266
|
}).strict(),
|
|
2276
2267
|
run: async (args) => {
|
|
2277
2268
|
await assertWritable({ profile: args.profile });
|
|
2278
|
-
const client = await initOperatorClient(await loadAccessToken({
|
|
2279
|
-
useProfile: true,
|
|
2280
|
-
profile: args.profile
|
|
2281
|
-
}));
|
|
2269
|
+
const client = await initOperatorClient(await loadAccessToken({ profile: args.profile }));
|
|
2282
2270
|
const workspaceId = await loadWorkspaceId({
|
|
2283
2271
|
workspaceId: args["workspace-id"],
|
|
2284
2272
|
profile: args.profile
|
|
@@ -2327,10 +2315,7 @@ function secretInfo(secret) {
|
|
|
2327
2315
|
* @returns List of secrets
|
|
2328
2316
|
*/
|
|
2329
2317
|
async function secretList(options) {
|
|
2330
|
-
const client = await initOperatorClient(await loadAccessToken({
|
|
2331
|
-
useProfile: true,
|
|
2332
|
-
profile: options.profile
|
|
2333
|
-
}));
|
|
2318
|
+
const client = await initOperatorClient(await loadAccessToken({ profile: options.profile }));
|
|
2334
2319
|
const workspaceId = await loadWorkspaceId({
|
|
2335
2320
|
workspaceId: options.workspaceId,
|
|
2336
2321
|
profile: options.profile
|
|
@@ -2384,10 +2369,7 @@ const updateSecretCommand = defineAppCommand({
|
|
|
2384
2369
|
}).strict(),
|
|
2385
2370
|
run: async (args) => {
|
|
2386
2371
|
await assertWritable({ profile: args.profile });
|
|
2387
|
-
const client = await initOperatorClient(await loadAccessToken({
|
|
2388
|
-
useProfile: true,
|
|
2389
|
-
profile: args.profile
|
|
2390
|
-
}));
|
|
2372
|
+
const client = await initOperatorClient(await loadAccessToken({ profile: args.profile }));
|
|
2391
2373
|
const workspaceId = await loadWorkspaceId({
|
|
2392
2374
|
workspaceId: args["workspace-id"],
|
|
2393
2375
|
profile: args.profile
|
|
@@ -2440,10 +2422,7 @@ const createCommand$1 = defineAppCommand({
|
|
|
2440
2422
|
}).strict(),
|
|
2441
2423
|
run: async (args) => {
|
|
2442
2424
|
await assertWritable({ profile: args.profile });
|
|
2443
|
-
const client = await initOperatorClient(await loadAccessToken({
|
|
2444
|
-
useProfile: true,
|
|
2445
|
-
profile: args.profile
|
|
2446
|
-
}));
|
|
2425
|
+
const client = await initOperatorClient(await loadAccessToken({ profile: args.profile }));
|
|
2447
2426
|
const workspaceId = await loadWorkspaceId({
|
|
2448
2427
|
workspaceId: args["workspace-id"],
|
|
2449
2428
|
profile: args.profile
|
|
@@ -2473,10 +2452,7 @@ const deleteCommand$1 = defineAppCommand({
|
|
|
2473
2452
|
}).strict(),
|
|
2474
2453
|
run: async (args) => {
|
|
2475
2454
|
await assertWritable({ profile: args.profile });
|
|
2476
|
-
const client = await initOperatorClient(await loadAccessToken({
|
|
2477
|
-
useProfile: true,
|
|
2478
|
-
profile: args.profile
|
|
2479
|
-
}));
|
|
2455
|
+
const client = await initOperatorClient(await loadAccessToken({ profile: args.profile }));
|
|
2480
2456
|
const workspaceId = await loadWorkspaceId({
|
|
2481
2457
|
workspaceId: args["workspace-id"],
|
|
2482
2458
|
profile: args.profile
|
|
@@ -2521,10 +2497,7 @@ function vaultInfo(vault) {
|
|
|
2521
2497
|
* @returns List of vaults
|
|
2522
2498
|
*/
|
|
2523
2499
|
async function vaultList(options) {
|
|
2524
|
-
const client = await initOperatorClient(await loadAccessToken({
|
|
2525
|
-
useProfile: true,
|
|
2526
|
-
profile: options?.profile
|
|
2527
|
-
}));
|
|
2500
|
+
const client = await initOperatorClient(await loadAccessToken({ profile: options?.profile }));
|
|
2528
2501
|
const workspaceId = await loadWorkspaceId({
|
|
2529
2502
|
workspaceId: options?.workspaceId,
|
|
2530
2503
|
profile: options?.profile
|
|
@@ -3044,10 +3017,7 @@ const deployCommand = defineAppCommand({
|
|
|
3044
3017
|
run: async (args) => {
|
|
3045
3018
|
await assertWritable({ profile: args.profile });
|
|
3046
3019
|
logger.info(`Deploying static website "${args.name}" from directory: ${args.dir}`);
|
|
3047
|
-
const client = await initOperatorClient(await loadAccessToken({
|
|
3048
|
-
useProfile: true,
|
|
3049
|
-
profile: args.profile
|
|
3050
|
-
}));
|
|
3020
|
+
const client = await initOperatorClient(await loadAccessToken({ profile: args.profile }));
|
|
3051
3021
|
const name = args.name;
|
|
3052
3022
|
const dir = path.resolve(process.cwd(), args.dir);
|
|
3053
3023
|
const workspaceId = await loadWorkspaceId({
|
|
@@ -3093,10 +3063,7 @@ const domainGetCommand = defineAppCommand({
|
|
|
3093
3063
|
})
|
|
3094
3064
|
}).strict(),
|
|
3095
3065
|
run: async (args) => {
|
|
3096
|
-
const client = await initOperatorClient(await loadAccessToken({
|
|
3097
|
-
useProfile: true,
|
|
3098
|
-
profile: args.profile
|
|
3099
|
-
}));
|
|
3066
|
+
const client = await initOperatorClient(await loadAccessToken({ profile: args.profile }));
|
|
3100
3067
|
const workspaceId = await loadWorkspaceId({
|
|
3101
3068
|
workspaceId: args["workspace-id"],
|
|
3102
3069
|
profile: args.profile
|
|
@@ -3136,10 +3103,7 @@ const domainListCommand = defineAppCommand({
|
|
|
3136
3103
|
})
|
|
3137
3104
|
}).strict(),
|
|
3138
3105
|
run: async (args) => {
|
|
3139
|
-
const client = await initOperatorClient(await loadAccessToken({
|
|
3140
|
-
useProfile: true,
|
|
3141
|
-
profile: args.profile
|
|
3142
|
-
}));
|
|
3106
|
+
const client = await initOperatorClient(await loadAccessToken({ profile: args.profile }));
|
|
3143
3107
|
const workspaceId = await loadWorkspaceId({
|
|
3144
3108
|
workspaceId: args["workspace-id"],
|
|
3145
3109
|
profile: args.profile
|
|
@@ -3191,10 +3155,7 @@ const getCommand = defineAppCommand({
|
|
|
3191
3155
|
})
|
|
3192
3156
|
}).strict(),
|
|
3193
3157
|
run: async (args) => {
|
|
3194
|
-
const client = await initOperatorClient(await loadAccessToken({
|
|
3195
|
-
useProfile: true,
|
|
3196
|
-
profile: args.profile
|
|
3197
|
-
}));
|
|
3158
|
+
const client = await initOperatorClient(await loadAccessToken({ profile: args.profile }));
|
|
3198
3159
|
const workspaceId = await loadWorkspaceId({
|
|
3199
3160
|
workspaceId: args["workspace-id"],
|
|
3200
3161
|
profile: args.profile
|
|
@@ -3229,10 +3190,7 @@ const getCommand = defineAppCommand({
|
|
|
3229
3190
|
* @returns List of static websites
|
|
3230
3191
|
*/
|
|
3231
3192
|
async function listStaticWebsites(options) {
|
|
3232
|
-
const client = await initOperatorClient(await loadAccessToken({
|
|
3233
|
-
useProfile: true,
|
|
3234
|
-
profile: options?.profile
|
|
3235
|
-
}));
|
|
3193
|
+
const client = await initOperatorClient(await loadAccessToken({ profile: options?.profile }));
|
|
3236
3194
|
const workspaceId = await loadWorkspaceId({
|
|
3237
3195
|
workspaceId: options?.workspaceId,
|
|
3238
3196
|
profile: options?.profile
|
|
@@ -3250,7 +3208,7 @@ async function listStaticWebsites(options) {
|
|
|
3250
3208
|
workspaceId,
|
|
3251
3209
|
name: site.name,
|
|
3252
3210
|
description: site.description,
|
|
3253
|
-
url: site.url
|
|
3211
|
+
url: site.url,
|
|
3254
3212
|
allowedIpAddresses: site.allowedIpAddresses
|
|
3255
3213
|
}));
|
|
3256
3214
|
}
|
|
@@ -3551,10 +3509,7 @@ function initErdCommand() {
|
|
|
3551
3509
|
async function initErdDeployContext(args) {
|
|
3552
3510
|
initErdCommand();
|
|
3553
3511
|
return {
|
|
3554
|
-
client: await initOperatorClient(await loadAccessToken({
|
|
3555
|
-
useProfile: true,
|
|
3556
|
-
profile: args.profile
|
|
3557
|
-
})),
|
|
3512
|
+
client: await initOperatorClient(await loadAccessToken({ profile: args.profile })),
|
|
3558
3513
|
workspaceId: await loadWorkspaceId({
|
|
3559
3514
|
workspaceId: args.workspaceId,
|
|
3560
3515
|
profile: args.profile
|
|
@@ -4153,18 +4108,13 @@ const erdCommand = defineCommand({
|
|
|
4153
4108
|
*/
|
|
4154
4109
|
async function script(options) {
|
|
4155
4110
|
logBetaWarning("tailordb migration");
|
|
4156
|
-
|
|
4157
|
-
if (isValidMigrationNumber(options.number)) migrationNumber = parseInt(options.number, 10);
|
|
4158
|
-
else if (/^[1-9]\d*$/.test(options.number)) {
|
|
4159
|
-
migrationNumber = parseInt(options.number, 10);
|
|
4160
|
-
if (migrationNumber > 9999) throw new Error(`Migration number ${options.number} is out of range. Expected 1-9999.`);
|
|
4161
|
-
} else throw new Error(`Invalid migration number format: ${options.number}. Expected 4-digit format (e.g., 0001) or integer 1-9999 (e.g., 1).`);
|
|
4111
|
+
const migrationNumber = parseMigrationNumberArg(options.number);
|
|
4162
4112
|
if (migrationNumber === 0) throw new Error(`Migration ${options.number} is the initial schema snapshot and cannot have a migration script.`);
|
|
4163
4113
|
const { config } = await loadConfig(options.configPath);
|
|
4164
4114
|
const namespacesWithMigrations = getNamespacesWithMigrations(config, path.dirname(config.path));
|
|
4165
4115
|
if (namespacesWithMigrations.length === 0) throw new Error("No TailorDB services with migrations configuration found");
|
|
4166
4116
|
const targetNamespace = resolveTargetNamespace(namespacesWithMigrations, options.namespace);
|
|
4167
|
-
const { migrationsDir } = namespacesWithMigrations.find((ns) => ns.namespace === targetNamespace);
|
|
4117
|
+
const { migrationsDir } = assertDefined(namespacesWithMigrations.find((ns) => ns.namespace === targetNamespace), "namespace with migrations not found");
|
|
4168
4118
|
const diffPath = getMigrationFilePath(migrationsDir, migrationNumber, "diff");
|
|
4169
4119
|
if (!fs$1.existsSync(diffPath)) throw new Error(`Migration ${options.number} not found in ${migrationsDir}. Expected ${diffPath}.`);
|
|
4170
4120
|
const migratePath = getMigrationFilePath(migrationsDir, migrationNumber, "migrate");
|
|
@@ -4196,7 +4146,10 @@ function resolveTargetNamespace(namespacesWithMigrations, requested) {
|
|
|
4196
4146
|
if (!namespacesWithMigrations.some((ns) => ns.namespace === requested)) throw new Error(`Namespace "${requested}" not found or does not have migrations configured`);
|
|
4197
4147
|
return requested;
|
|
4198
4148
|
}
|
|
4199
|
-
if (namespacesWithMigrations.length === 1)
|
|
4149
|
+
if (namespacesWithMigrations.length === 1) {
|
|
4150
|
+
const [ns] = namespacesWithMigrations;
|
|
4151
|
+
return assertDefined(ns, "namespace with migrations missing").namespace;
|
|
4152
|
+
}
|
|
4200
4153
|
throw new Error(`Multiple TailorDB services found. Please specify namespace with --namespace flag: ${namespacesWithMigrations.map((ns) => ns.namespace).join(", ")}`);
|
|
4201
4154
|
}
|
|
4202
4155
|
const scriptCommand = defineAppCommand({
|
|
@@ -4244,12 +4197,11 @@ async function set(options) {
|
|
|
4244
4197
|
if (options.namespace) {
|
|
4245
4198
|
if (!namespacesWithMigrations.some((ns) => ns.namespace === options.namespace)) throw new Error(`Namespace "${options.namespace}" not found or does not have migrations configured`);
|
|
4246
4199
|
targetNamespace = options.namespace;
|
|
4247
|
-
} else if (namespacesWithMigrations.length === 1)
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
}));
|
|
4200
|
+
} else if (namespacesWithMigrations.length === 1) {
|
|
4201
|
+
const [ns] = namespacesWithMigrations;
|
|
4202
|
+
targetNamespace = assertDefined(ns, "namespace with migrations missing").namespace;
|
|
4203
|
+
} else throw new Error(`Multiple TailorDB services found. Please specify namespace with --namespace flag: ${namespacesWithMigrations.map((ns) => ns.namespace).join(", ")}`);
|
|
4204
|
+
const client = await initOperatorClient(await loadAccessToken({ profile: options.profile }));
|
|
4253
4205
|
const trn = resourceTrn(await loadWorkspaceId({
|
|
4254
4206
|
workspaceId: options.workspaceId,
|
|
4255
4207
|
profile: options.profile
|
|
@@ -4257,7 +4209,7 @@ async function set(options) {
|
|
|
4257
4209
|
let currentMigration;
|
|
4258
4210
|
try {
|
|
4259
4211
|
const { metadata } = await client.getMetadata({ trn });
|
|
4260
|
-
const label = metadata?.labels
|
|
4212
|
+
const label = metadata?.labels["sdk-migration"];
|
|
4261
4213
|
currentMigration = label ? parseMigrationLabelNumber(label) ?? 0 : 0;
|
|
4262
4214
|
} catch {
|
|
4263
4215
|
currentMigration = 0;
|
|
@@ -4332,10 +4284,7 @@ async function collectMigrationStatuses(options) {
|
|
|
4332
4284
|
if (namespacesWithMigrations.length === 0) throw new Error("No TailorDB services with migrations configuration found");
|
|
4333
4285
|
const targetNamespaces = options.namespace ? namespacesWithMigrations.filter((ns) => ns.namespace === options.namespace) : namespacesWithMigrations;
|
|
4334
4286
|
if (targetNamespaces.length === 0) throw new Error(`Namespace "${options.namespace}" not found or does not have migrations configured`);
|
|
4335
|
-
const client = await initOperatorClient(await loadAccessToken({
|
|
4336
|
-
useProfile: false,
|
|
4337
|
-
profile: options.profile
|
|
4338
|
-
}));
|
|
4287
|
+
const client = await initOperatorClient(await loadAccessToken({ profile: options.profile }));
|
|
4339
4288
|
const workspaceId = await loadWorkspaceId({
|
|
4340
4289
|
workspaceId: options.workspaceId,
|
|
4341
4290
|
profile: options.profile
|
|
@@ -4346,7 +4295,7 @@ async function collectMigrationStatuses(options) {
|
|
|
4346
4295
|
let currentMigration;
|
|
4347
4296
|
try {
|
|
4348
4297
|
const { metadata } = await client.getMetadata({ trn });
|
|
4349
|
-
const label = metadata?.labels
|
|
4298
|
+
const label = metadata?.labels["sdk-migration"];
|
|
4350
4299
|
currentMigration = label ? parseMigrationLabelNumber(label) ?? 0 : 0;
|
|
4351
4300
|
} catch {
|
|
4352
4301
|
currentMigration = 0;
|
|
@@ -4419,6 +4368,295 @@ const statusCommand = defineAppCommand({
|
|
|
4419
4368
|
}
|
|
4420
4369
|
});
|
|
4421
4370
|
|
|
4371
|
+
//#endregion
|
|
4372
|
+
//#region src/cli/commands/tailordb/migrate/sync.ts
|
|
4373
|
+
async function fetchRemoteGqlPermissions(client, workspaceId, namespace) {
|
|
4374
|
+
return fetchAll(async (pageToken, maxPageSize) => {
|
|
4375
|
+
try {
|
|
4376
|
+
const { permissions, nextPageToken } = await client.listTailorDBGQLPermissions({
|
|
4377
|
+
workspaceId,
|
|
4378
|
+
namespaceName: namespace,
|
|
4379
|
+
pageToken,
|
|
4380
|
+
pageSize: maxPageSize
|
|
4381
|
+
});
|
|
4382
|
+
return [permissions, nextPageToken];
|
|
4383
|
+
} catch (error) {
|
|
4384
|
+
if (error instanceof ConnectError && error.code === Code.NotFound) return [[], ""];
|
|
4385
|
+
throw error;
|
|
4386
|
+
}
|
|
4387
|
+
});
|
|
4388
|
+
}
|
|
4389
|
+
async function fetchRemoteTypes(client, workspaceId, namespace) {
|
|
4390
|
+
return fetchAll(async (pageToken, maxPageSize) => {
|
|
4391
|
+
try {
|
|
4392
|
+
const { tailordbTypes, nextPageToken } = await client.listTailorDBTypes({
|
|
4393
|
+
workspaceId,
|
|
4394
|
+
namespaceName: namespace,
|
|
4395
|
+
pageToken,
|
|
4396
|
+
pageSize: maxPageSize
|
|
4397
|
+
});
|
|
4398
|
+
return [tailordbTypes, nextPageToken];
|
|
4399
|
+
} catch (error) {
|
|
4400
|
+
if (error instanceof ConnectError && error.code === Code.NotFound) throw new Error(`Cannot sync: TailorDB namespace "${namespace}" has not been deployed yet.`, { cause: error });
|
|
4401
|
+
throw error;
|
|
4402
|
+
}
|
|
4403
|
+
});
|
|
4404
|
+
}
|
|
4405
|
+
/**
|
|
4406
|
+
* Verify that replaying the full migration history reproduces the current
|
|
4407
|
+
* local type definitions, before anything is sent to the remote.
|
|
4408
|
+
*
|
|
4409
|
+
* Sync force-applies a snapshot reconstructed from the migration history, so
|
|
4410
|
+
* the history itself must be trustworthy. When the reconstruction at the
|
|
4411
|
+
* latest migration does not match the schema defined in the local type files,
|
|
4412
|
+
* either the migration files were edited incorrectly or a schema change has
|
|
4413
|
+
* not been recorded as a migration yet — and overwriting the remote with an
|
|
4414
|
+
* unverified snapshot could destroy data. Fails before any RPC is issued.
|
|
4415
|
+
*
|
|
4416
|
+
* Returns the manifest generation options deploy would use for this
|
|
4417
|
+
* namespace (executor-driven publishRecordEvents and namespace
|
|
4418
|
+
* gqlOperations), so the synced manifests match what deploy produces.
|
|
4419
|
+
* @param loaded - Result of `loadConfig` (config and plugins)
|
|
4420
|
+
* @param target - Namespace whose migration history is being synced
|
|
4421
|
+
* @returns Options for `generateAllTypeManifestsFromSnapshot`
|
|
4422
|
+
*/
|
|
4423
|
+
async function assertMigrationsReproduceLocalTypes(loaded, target) {
|
|
4424
|
+
const { config, plugins } = loaded;
|
|
4425
|
+
const pluginManager = plugins.length > 0 ? new PluginManager(plugins) : void 0;
|
|
4426
|
+
const { defineApplication, generatePluginFilesIfNeeded } = await import("../application-DSXntqnV.mjs");
|
|
4427
|
+
const application = defineApplication({
|
|
4428
|
+
config,
|
|
4429
|
+
pluginManager
|
|
4430
|
+
});
|
|
4431
|
+
const tailordbService = application.tailorDBServices.find((s) => s.namespace === target.namespace);
|
|
4432
|
+
if (!tailordbService) throw new Error(`No TailorDB service found for namespace "${target.namespace}"`);
|
|
4433
|
+
for (const service of application.tailorDBServices) {
|
|
4434
|
+
await service.loadTypes();
|
|
4435
|
+
await service.processNamespacePlugins();
|
|
4436
|
+
}
|
|
4437
|
+
const pluginExecutorFiles = generatePluginFilesIfNeeded(pluginManager, application.tailorDBServices, config.path);
|
|
4438
|
+
const executorService = application.executorService ?? (pluginExecutorFiles.length > 0 ? (await import("../service-BHQIerYh.mjs")).createExecutorService({ config: { files: [] } }) : void 0);
|
|
4439
|
+
await executorService?.loadExecutors();
|
|
4440
|
+
if (pluginExecutorFiles.length > 0) await executorService?.loadPluginExecutorFiles([...pluginExecutorFiles]);
|
|
4441
|
+
const executorUsedTypes = /* @__PURE__ */ new Set();
|
|
4442
|
+
for (const executor of Object.values(executorService?.executors ?? {})) if (executor.trigger.kind === "tailordb") executorUsedTypes.add(executor.trigger.typeName);
|
|
4443
|
+
const manifestOptions = {
|
|
4444
|
+
executorUsedTypes,
|
|
4445
|
+
namespaceGqlOperations: tailordbService.config.gqlOperations
|
|
4446
|
+
};
|
|
4447
|
+
const latestSnapshot = reconstructSnapshotFromMigrations(target.migrationsDir);
|
|
4448
|
+
if (!latestSnapshot) return manifestOptions;
|
|
4449
|
+
const diff = compareLocalTypesWithSnapshot(latestSnapshot, createSnapshotFromLocalTypes(tailordbService.types, target.namespace).types, target.namespace);
|
|
4450
|
+
if (!hasChanges(diff)) return manifestOptions;
|
|
4451
|
+
logger.error(`Migration history does not reproduce the current local schema for namespace ${styles.bold(target.namespace)}:`);
|
|
4452
|
+
logger.log(formatMigrationDiff(diff));
|
|
4453
|
+
logger.newline();
|
|
4454
|
+
logger.info("This usually means one of the following:");
|
|
4455
|
+
logger.info(" - Migration files were edited and replaying them no longer matches the type definitions — fix the migration files.", { mode: "plain" });
|
|
4456
|
+
logger.info(" - Type definitions changed without a new migration — run 'tailor-sdk tailordb migration generate' first.", { mode: "plain" });
|
|
4457
|
+
logger.newline();
|
|
4458
|
+
throw new Error("Refusing to sync: the migration history must reproduce the current local schema before it can be applied to the remote.");
|
|
4459
|
+
}
|
|
4460
|
+
/**
|
|
4461
|
+
* Fetch the namespace's metadata labels and current migration number.
|
|
4462
|
+
*
|
|
4463
|
+
* Only GetMetadata NotFound is treated as "metadata does not exist yet".
|
|
4464
|
+
* Any other failure aborts the sync (which has not mutated anything at this
|
|
4465
|
+
* point): the fetched labels are written back verbatim at the end, so
|
|
4466
|
+
* proceeding with empty labels after a transient error would wipe the
|
|
4467
|
+
* namespace's existing metadata.
|
|
4468
|
+
* @param client - Operator client
|
|
4469
|
+
* @param trn - Namespace TRN
|
|
4470
|
+
* @returns Existing labels and the parsed current migration number
|
|
4471
|
+
*/
|
|
4472
|
+
async function fetchRemoteMigrationState(client, trn) {
|
|
4473
|
+
try {
|
|
4474
|
+
const { metadata } = await client.getMetadata({ trn });
|
|
4475
|
+
const labels = metadata?.labels ?? {};
|
|
4476
|
+
const label = labels[MIGRATION_LABEL_KEY];
|
|
4477
|
+
return {
|
|
4478
|
+
labels,
|
|
4479
|
+
current: label ? parseMigrationLabelNumber(label) : null
|
|
4480
|
+
};
|
|
4481
|
+
} catch (error) {
|
|
4482
|
+
if (error instanceof ConnectError && error.code === Code.NotFound) return {
|
|
4483
|
+
labels: {},
|
|
4484
|
+
current: null
|
|
4485
|
+
};
|
|
4486
|
+
throw error;
|
|
4487
|
+
}
|
|
4488
|
+
}
|
|
4489
|
+
function selectTargetNamespace(namespacesWithMigrations, requested) {
|
|
4490
|
+
if (namespacesWithMigrations.length === 0) throw new Error("No TailorDB services with migrations configuration found");
|
|
4491
|
+
if (requested) {
|
|
4492
|
+
const found = namespacesWithMigrations.find((ns) => ns.namespace === requested);
|
|
4493
|
+
if (!found) throw new Error(`Namespace "${requested}" not found or does not have migrations configured`);
|
|
4494
|
+
return found;
|
|
4495
|
+
}
|
|
4496
|
+
if (namespacesWithMigrations.length > 1) throw new Error(`Multiple TailorDB services found. Please specify namespace with --namespace flag: ${namespacesWithMigrations.map((ns) => ns.namespace).join(", ")}`);
|
|
4497
|
+
return assertDefined(namespacesWithMigrations[0], "namespace with migrations missing");
|
|
4498
|
+
}
|
|
4499
|
+
/**
|
|
4500
|
+
* Sync remote TailorDB schema to a specific migration snapshot.
|
|
4501
|
+
*
|
|
4502
|
+
* Reconstructs the schema state at `<number>` from `0000/schema.json` + diffs,
|
|
4503
|
+
* then issues create/update/delete RPCs so the remote matches that snapshot.
|
|
4504
|
+
* Updates the migration label to `<number>` on success. Before any remote
|
|
4505
|
+
* mutation, verifies that the migration history reproduces the current local
|
|
4506
|
+
* type definitions (see {@link assertMigrationsReproduceLocalTypes}).
|
|
4507
|
+
*
|
|
4508
|
+
* Intended for recovering from drift introduced by `deploy --no-schema-check`
|
|
4509
|
+
* runs against an older revision: instead of having to `git checkout` that
|
|
4510
|
+
* revision and re-deploy, the operator can sync the remote back to a known
|
|
4511
|
+
* snapshot version directly.
|
|
4512
|
+
* @param options - Command options
|
|
4513
|
+
*/
|
|
4514
|
+
async function sync(options) {
|
|
4515
|
+
logBetaWarning("tailordb migration");
|
|
4516
|
+
const targetVersion = parseMigrationNumberArg(options.number);
|
|
4517
|
+
const loaded = await loadConfig(options.configPath);
|
|
4518
|
+
const { config } = loaded;
|
|
4519
|
+
const target = selectTargetNamespace(getNamespacesWithMigrations(config, path.dirname(config.path)), options.namespace);
|
|
4520
|
+
assertValidMigrationFiles(target.migrationsDir, target.namespace);
|
|
4521
|
+
const latest = getLatestMigrationNumber(target.migrationsDir);
|
|
4522
|
+
if (targetVersion > latest) throw new Error(`Migration ${formatMigrationNumber(targetVersion)} does not exist in working tree (latest is ${formatMigrationNumber(latest)}).`);
|
|
4523
|
+
const snapshot = reconstructSnapshotFromMigrations(target.migrationsDir, targetVersion);
|
|
4524
|
+
if (!snapshot) throw new Error(`No initial schema snapshot found in ${target.migrationsDir}. Expected 0000/schema.json.`);
|
|
4525
|
+
const manifestOptions = await assertMigrationsReproduceLocalTypes(loaded, target);
|
|
4526
|
+
const client = await initOperatorClient(await loadAccessToken({ profile: options.profile }));
|
|
4527
|
+
const workspaceId = await loadWorkspaceId({
|
|
4528
|
+
workspaceId: options.workspaceId,
|
|
4529
|
+
profile: options.profile
|
|
4530
|
+
});
|
|
4531
|
+
const trn = resourceTrn(workspaceId, "tailordb", target.namespace);
|
|
4532
|
+
const remoteState = await fetchRemoteMigrationState(client, trn);
|
|
4533
|
+
const remoteTypes = await fetchRemoteTypes(client, workspaceId, target.namespace);
|
|
4534
|
+
const { creates, updates, deletes } = compareSnapshotWithRemote(snapshot, new Set(remoteTypes.map((t) => t.name)));
|
|
4535
|
+
const remoteGqlPermissions = await fetchRemoteGqlPermissions(client, workspaceId, target.namespace);
|
|
4536
|
+
const remoteGqlPermissionTypes = new Set(remoteGqlPermissions.map((p) => p.typeName));
|
|
4537
|
+
const desiredGqlPermissions = Object.entries(snapshot.types).flatMap(([typeName, snapshotType]) => snapshotType.permissions?.gql ? [{
|
|
4538
|
+
typeName,
|
|
4539
|
+
permission: protoGqlPermission(snapshotType.permissions.gql)
|
|
4540
|
+
}] : []);
|
|
4541
|
+
const desiredGqlPermissionTypes = new Set(desiredGqlPermissions.map((p) => p.typeName));
|
|
4542
|
+
const gqlPermissionDeletes = remoteGqlPermissions.filter((p) => !desiredGqlPermissionTypes.has(p.typeName));
|
|
4543
|
+
const current = remoteState.current;
|
|
4544
|
+
logger.newline();
|
|
4545
|
+
logger.info(`Namespace: ${styles.bold(target.namespace)}`);
|
|
4546
|
+
logger.log(` Current migration: ${current === null ? "<unset>" : styles.bold(formatMigrationNumber(current))}`);
|
|
4547
|
+
logger.log(` Target migration: ${styles.bold(formatMigrationNumber(targetVersion))}`);
|
|
4548
|
+
logger.log(` Types to create: ${styles.bold(String(creates.length))}`);
|
|
4549
|
+
logger.log(` Types to update: ${styles.bold(String(updates.length))}`);
|
|
4550
|
+
logger.log(` Types to delete: ${styles.bold(String(deletes.length))}`);
|
|
4551
|
+
logger.log(` GQL permissions to set: ${styles.bold(String(desiredGqlPermissions.length))}`);
|
|
4552
|
+
logger.log(` GQL permissions to delete: ${styles.bold(String(gqlPermissionDeletes.length))}`);
|
|
4553
|
+
logger.newline();
|
|
4554
|
+
if (creates.length + updates.length + deletes.length + desiredGqlPermissions.length + gqlPermissionDeletes.length === 0) logger.info("No types to apply; only the migration label will be updated.");
|
|
4555
|
+
else {
|
|
4556
|
+
logger.warn("This operation will overwrite remote TailorDB types to match the selected snapshot.");
|
|
4557
|
+
if (deletes.length > 0) logger.warn("Existing data in deleted types will be lost.");
|
|
4558
|
+
logger.newline();
|
|
4559
|
+
}
|
|
4560
|
+
logger.warn("Sync never runs migrate.ts scripts; it only applies the schema snapshot and moves the migration label.");
|
|
4561
|
+
logger.newline();
|
|
4562
|
+
if (current !== null && targetVersion < current) {
|
|
4563
|
+
logger.warn(`Migrations ${formatMigrationNumber(targetVersion + 1)}–${formatMigrationNumber(current)} will become pending again and re-execute on the next deploy, including their migrate.ts scripts. Make sure those scripts are idempotent (safe to re-run).`);
|
|
4564
|
+
logger.newline();
|
|
4565
|
+
} else if (current !== null && targetVersion > current) {
|
|
4566
|
+
logger.warn(`Moving the migration label forwards (${formatMigrationNumber(current)} → ${formatMigrationNumber(targetVersion)}): migrate.ts scripts for migrations ${formatMigrationNumber(current + 1)}–${formatMigrationNumber(targetVersion)} will not run on the next deploy.`);
|
|
4567
|
+
logger.newline();
|
|
4568
|
+
}
|
|
4569
|
+
if (!options.yes) {
|
|
4570
|
+
if (!await prompt.confirm({
|
|
4571
|
+
message: `Continue and set migration label to ${formatMigrationNumber(targetVersion)}?`,
|
|
4572
|
+
default: false
|
|
4573
|
+
})) {
|
|
4574
|
+
logger.info("Operation cancelled.");
|
|
4575
|
+
return;
|
|
4576
|
+
}
|
|
4577
|
+
logger.newline();
|
|
4578
|
+
}
|
|
4579
|
+
const manifests = generateAllTypeManifestsFromSnapshot(snapshot, manifestOptions);
|
|
4580
|
+
const manifestFor = (typeName) => {
|
|
4581
|
+
const manifest = manifests.get(typeName);
|
|
4582
|
+
if (!manifest) throw new Error(`Internal error: no manifest generated for type "${typeName}". No changes were applied.`);
|
|
4583
|
+
return manifest;
|
|
4584
|
+
};
|
|
4585
|
+
const createManifests = creates.map((typeName) => manifestFor(typeName));
|
|
4586
|
+
const updateManifests = updates.map((typeName) => manifestFor(typeName));
|
|
4587
|
+
try {
|
|
4588
|
+
await Promise.all([...createManifests.map((tailordbType) => client.createTailorDBType({
|
|
4589
|
+
workspaceId,
|
|
4590
|
+
namespaceName: target.namespace,
|
|
4591
|
+
tailordbType
|
|
4592
|
+
})), ...updateManifests.map((tailordbType) => client.updateTailorDBType({
|
|
4593
|
+
workspaceId,
|
|
4594
|
+
namespaceName: target.namespace,
|
|
4595
|
+
tailordbType
|
|
4596
|
+
}))]);
|
|
4597
|
+
} catch (error) {
|
|
4598
|
+
handleOptionalToRequiredError(error, ["The target snapshot marks a field as required, but existing remote records have no value for it.", "Populate those records first (e.g. with a migration script applied via 'tailor-sdk deploy'), then re-run the sync."]);
|
|
4599
|
+
}
|
|
4600
|
+
await Promise.all(desiredGqlPermissions.map(({ typeName, permission }) => {
|
|
4601
|
+
const request = {
|
|
4602
|
+
workspaceId,
|
|
4603
|
+
namespaceName: target.namespace,
|
|
4604
|
+
typeName,
|
|
4605
|
+
permission
|
|
4606
|
+
};
|
|
4607
|
+
return remoteGqlPermissionTypes.has(typeName) ? client.updateTailorDBGQLPermission(request) : client.createTailorDBGQLPermission(request);
|
|
4608
|
+
}));
|
|
4609
|
+
await Promise.all(gqlPermissionDeletes.map((p) => client.deleteTailorDBGQLPermission({
|
|
4610
|
+
workspaceId,
|
|
4611
|
+
namespaceName: target.namespace,
|
|
4612
|
+
typeName: p.typeName
|
|
4613
|
+
})));
|
|
4614
|
+
await Promise.all(deletes.map((typeName) => client.deleteTailorDBType({
|
|
4615
|
+
workspaceId,
|
|
4616
|
+
namespaceName: target.namespace,
|
|
4617
|
+
tailordbTypeName: typeName
|
|
4618
|
+
})));
|
|
4619
|
+
await client.setMetadata({
|
|
4620
|
+
trn,
|
|
4621
|
+
labels: {
|
|
4622
|
+
...remoteState.labels,
|
|
4623
|
+
[MIGRATION_LABEL_KEY]: `${"m"}${formatMigrationNumber(targetVersion)}`
|
|
4624
|
+
}
|
|
4625
|
+
});
|
|
4626
|
+
logger.success(`Synced namespace ${styles.bold(target.namespace)} to migration ${styles.bold(formatMigrationNumber(targetVersion))}.`);
|
|
4627
|
+
if (targetVersion < latest) {
|
|
4628
|
+
logger.newline();
|
|
4629
|
+
logger.info(`Run 'tailor-sdk deploy' to apply migrations ${formatMigrationNumber(targetVersion + 1)}–${formatMigrationNumber(latest)} from the working tree.`);
|
|
4630
|
+
}
|
|
4631
|
+
}
|
|
4632
|
+
const syncCommand = defineAppCommand({
|
|
4633
|
+
name: "sync",
|
|
4634
|
+
description: "Sync remote TailorDB schema to a specific migration snapshot (recovery from --no-schema-check drift).",
|
|
4635
|
+
args: z.object({
|
|
4636
|
+
...deploymentArgs,
|
|
4637
|
+
...confirmationArgs,
|
|
4638
|
+
number: arg(z.string(), {
|
|
4639
|
+
positional: true,
|
|
4640
|
+
description: "Migration number to sync to (e.g., 0001 or 1; 0 targets the baseline snapshot)"
|
|
4641
|
+
}),
|
|
4642
|
+
namespace: arg(z.string().optional(), {
|
|
4643
|
+
alias: "n",
|
|
4644
|
+
description: "Target TailorDB namespace (required if multiple namespaces exist)"
|
|
4645
|
+
})
|
|
4646
|
+
}).strict(),
|
|
4647
|
+
run: async (args) => {
|
|
4648
|
+
await assertWritable({ profile: args.profile });
|
|
4649
|
+
await sync({
|
|
4650
|
+
configPath: args.config,
|
|
4651
|
+
number: args.number,
|
|
4652
|
+
namespace: args.namespace,
|
|
4653
|
+
yes: args.yes,
|
|
4654
|
+
workspaceId: args["workspace-id"],
|
|
4655
|
+
profile: args.profile
|
|
4656
|
+
});
|
|
4657
|
+
}
|
|
4658
|
+
});
|
|
4659
|
+
|
|
4422
4660
|
//#endregion
|
|
4423
4661
|
//#region src/cli/commands/tailordb/migrate/index.ts
|
|
4424
4662
|
/**
|
|
@@ -4429,6 +4667,7 @@ const statusCommand = defineAppCommand({
|
|
|
4429
4667
|
* - script: Add a migrate.ts template to an existing migration
|
|
4430
4668
|
* - set: Set migration checkpoint to a specific number
|
|
4431
4669
|
* - status: Show migration status for TailorDB namespaces
|
|
4670
|
+
* - sync: Sync remote TailorDB schema to a specific migration snapshot
|
|
4432
4671
|
*/
|
|
4433
4672
|
const migrationCommand = defineCommand({
|
|
4434
4673
|
name: "migration",
|
|
@@ -4437,7 +4676,8 @@ const migrationCommand = defineCommand({
|
|
|
4437
4676
|
generate: generateCommand$1,
|
|
4438
4677
|
script: scriptCommand,
|
|
4439
4678
|
set: setCommand,
|
|
4440
|
-
status: statusCommand
|
|
4679
|
+
status: statusCommand,
|
|
4680
|
+
sync: syncCommand
|
|
4441
4681
|
}
|
|
4442
4682
|
});
|
|
4443
4683
|
|
|
@@ -4472,7 +4712,7 @@ const upgradeCommand = defineAppCommand({
|
|
|
4472
4712
|
run: async (args) => {
|
|
4473
4713
|
const { initTelemetry } = await import("../telemetry-w92bvGdC.mjs");
|
|
4474
4714
|
await initTelemetry();
|
|
4475
|
-
const { upgrade } = await import("../service-
|
|
4715
|
+
const { upgrade } = await import("../service-DMohAx8a2.mjs");
|
|
4476
4716
|
await upgrade({
|
|
4477
4717
|
from: args.from,
|
|
4478
4718
|
dryRun: args["dry-run"],
|
|
@@ -4898,7 +5138,7 @@ runMain(mainCommand, {
|
|
|
4898
5138
|
if (isVerbose() && error.stack) logger.debug(`\nStack trace:\n${error.stack}`);
|
|
4899
5139
|
} else logger.error(`Unknown error: ${error}`);
|
|
4900
5140
|
if (!isCLIError(error) && (!(error instanceof Error) || error instanceof TypeError || error instanceof RangeError)) {
|
|
4901
|
-
const { reportCrash } = await import("../crashreport-
|
|
5141
|
+
const { reportCrash } = await import("../crashreport-D1wKBJ8N.mjs");
|
|
4902
5142
|
await reportCrash(error, "handledError");
|
|
4903
5143
|
}
|
|
4904
5144
|
}
|