@tailor-platform/sdk 1.84.0 → 1.85.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 +26 -0
- package/dist/application-C-zsrNjn.mjs +3 -0
- package/dist/{application-CnCljDwk.mjs → application-m5tYtz0-.mjs} +235 -62
- package/dist/application-m5tYtz0-.mjs.map +1 -0
- package/dist/cli/commands/deploy/tailordb/index.d.mts +1 -0
- package/dist/cli/commands/generate/seed/bundler.d.mts +2 -1
- package/dist/cli/commands/tailordb/migrate/schema-checks.d.mts +5 -0
- package/dist/cli/index.mjs +229 -22
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/lib.mjs +92 -36
- package/dist/cli/lib.mjs.map +1 -1
- package/dist/completion/zsh-worker.zsh +52 -2
- package/dist/node-builtins-CmaL2Cbq.mjs +36 -0
- package/dist/node-builtins-CmaL2Cbq.mjs.map +1 -0
- package/dist/plugin/builtin/seed/index.mjs +1 -1
- package/dist/{register-typescript-runtime-BCvWR3HC.mjs → register-typescript-runtime-nG-n9lPw.mjs} +336 -268
- package/dist/register-typescript-runtime-nG-n9lPw.mjs.map +1 -0
- package/dist/{seed-DFFigL8V.mjs → seed-6eTFj-3Q.mjs} +158 -41
- package/dist/seed-6eTFj-3Q.mjs.map +1 -0
- package/dist/vitest/index.mjs +3 -35
- package/dist/vitest/index.mjs.map +1 -1
- package/docs/cli/tailordb.md +29 -7
- package/docs/cli-reference.md +16 -15
- package/docs/generator/builtin.md +7 -0
- package/docs/services/tailordb-migration.md +11 -2
- package/package.json +7 -8
- package/dist/application-5FOWIxFQ.mjs +0 -3
- package/dist/application-CnCljDwk.mjs.map +0 -1
- package/dist/register-typescript-runtime-BCvWR3HC.mjs.map +0 -1
- package/dist/seed-DFFigL8V.mjs.map +0 -1
|
@@ -19,8 +19,9 @@ type SeedBundleResult = {
|
|
|
19
19
|
* 4. Exports as main() for TestExecScript
|
|
20
20
|
* @param namespace - TailorDB namespace
|
|
21
21
|
* @param typeNames - List of type names to include in the seed
|
|
22
|
+
* @param baseDir - Directory whose dependencies and tsconfig the generated entry uses
|
|
22
23
|
* @returns Bundled seed script result
|
|
23
24
|
*/
|
|
24
|
-
declare function bundleSeedScript(namespace: string, typeNames: string[]): Promise<SeedBundleResult>;
|
|
25
|
+
declare function bundleSeedScript(namespace: string, typeNames: string[], baseDir?: string): Promise<SeedBundleResult>;
|
|
25
26
|
//#endregion
|
|
26
27
|
export { SeedBundleResult, bundleSeedScript };
|
package/dist/cli/index.mjs
CHANGED
|
@@ -3,8 +3,8 @@ import { r as parseInputFields } from "../schema-CYrle8JZ.mjs";
|
|
|
3
3
|
import { At as PATScope, Ct as AuthInvokerSchema, E as CustomDomainStatus, W as FunctionExecution_Type } from "../workspace_resource_pb-DXnO2AT-.mjs";
|
|
4
4
|
import { t as assertDefined } from "../assert-DBxo8jPo.mjs";
|
|
5
5
|
import { n as logger, r as styles } from "../logger-CXQq9YIp.mjs";
|
|
6
|
-
import { $n as
|
|
7
|
-
import { A as
|
|
6
|
+
import { $n as writePlatformConfig, $t as logRemoteDriftGuidance, An as hasChanges, At as startCommand, Bn as deleteUserTokens, Cn as isValidMigrationNumber, Ct as listCommand$7, Dn as parseMigrationNumberArg, Dt as jobsCommand, E as listCommand$13, En as formatMigrationNumber, F as openInConfiguredEditor, Fn as generateUserTypes, G as treeCommand, Gn as loadConsoleBaseUrl, H as updateCommand$3, Hn as hasAnyUserTokenEntry, Ht as listCommand$16, In as prompt, J as getCommand$5, Jn as loadWorkspaceId, Jt as ensureConfigId, K as listCommand$12, Kn as loadMachineUserName, Kt as formatKeyValueTable, L as showCommand, Ln as apiCall, Lt as functionExecutionStatusToString, M as generateMigrationScript, Mn as resourceTrn, Mt as getCommand$6, N as writeDbTypesFile, Nn as sdkNameLabelKey, On as formatDiffSummary, P as getConfiguredEditorCommand, Pn as PluginManager, Pt as executionsCommand, Q as listCommand$11, Qn as saveUserTokens, Qt as checkMigrationDiffs, R as extractOwnedNamespaces, Rn as assertWritable, Rt as getCommand$1, S as waitCommand, Tn as reconstructSnapshotFromMigrations, Un as hasUserTokenEntry, V as removeCommand$1, Vn as fetchLatestToken, Vt as deployFromCLI, Wn as loadAccessToken, Wt as createCommand$4, X as updateCommand$2, Xn as readPlatformConfig, Yn as platformConfigFromProfile, Yt as executeScript, Zn as resolveUserTokenKey, _n as formatSchemaDrifts, _t as getCommand$2, an as compareSnapshotWithRemote, ar as isVerbose, bn as getMigrationFilePath, bt as webhookCommand, c as listCommand$15, cr as paginationArgs, ct as getCommand$3, dr as workspaceArgs, dt as tokenCommand, en as toTailorDBDeployInput, er as defineAppCommand, et as getCommand$4, f as restoreCommand, ft as listCommand$9, g as deleteCommand$5, gn as createSnapshotFromLocalTypes, ht as listCommand$8, i as updateCommand$4, in as parseMigrationLabelNumber, ir as deploymentArgs, it as createCommand$3, j as generateCommand$1, jn as getNamespacesWithMigrations, k as truncateCommand, kn as formatMigrationDiff, lr as resolveMachineUserInputSource, m as getCommand$7, mn as compareLocalTypesWithSnapshot, mt as generate, nn as MIGRATION_LABEL_KEY, nr as configArg, nt as deleteCommand$4, o as removeCommand, on as generateAllTypeManifestsFromSnapshot, or as multiConfigArg, ot as listCommand$10, pn as assertValidMigrationFiles, qn as loadStoredUserTokens, qt as workspaceNameSchema, r as queryCommand, rn as handleOptionalToRequiredError, rr as confirmationArgs, sn as protoGqlPermission, sr as pagedLogArgs, t as registerTypeScriptRuntime, tn as verifyRemoteSchema, tr as commonArgs, u as inviteCommand, un as INITIAL_SCHEMA_NUMBER, ur as toPageDirection, vn as getLatestMigrationNumber, w as resumeCommand, wn as loadDiff, x as healthCommand, xn as getMigrationFiles, xt as triggerCommand, y as listCommand$14, z as logBetaWarning, zn as loadConfig } from "../register-typescript-runtime-nG-n9lPw.mjs";
|
|
7
|
+
import { A as hashContent$1, C as createLogLevelTreeshakeOptions, F as fetchAllTolerant, G as initOperatorClient, H as getOrNull, K as isDefaultPlatform, L as fetchPaged, M as closeConnectionPool, N as defaultPlatformBaseUrl, P as fetchAll, R as fetchPlatformMachineUserToken, S as composeFunctionTreeshakeOptions, T as createBundleLog, W as initOAuth2Client, a as resolveInlineSourcemap, b as resolveTSConfigWithFallback, c as ResolverSchema, k as getDistDir, m as assertUniqueLocalTailorDBTypeNames, p as buildResolverPermissionAndInputCheckExpr, s as WorkflowJobSchema, t as defineApplication, u as INVOKER_EXPR, v as createGeneratedEntryResolverPlugin, w as resolveBundleLogLevel, x as platformBundleDefinePlugin, y as createTsconfigPathsPlugin, z as fetchUserInfo } from "../application-m5tYtz0-.mjs";
|
|
8
8
|
import { n as ExecutorSchema } from "../service-CPJWhTLn.mjs";
|
|
9
9
|
import { t as multiline } from "../multiline-sfHpTZZK.mjs";
|
|
10
10
|
import { t as isPluginGeneratedType } from "../type-source-DH_LH20p.mjs";
|
|
@@ -1706,22 +1706,29 @@ async function bundleForTestRun(options) {
|
|
|
1706
1706
|
const entryContent = generateEntry(detected, sourceFile, env, machineUser, workspaceId);
|
|
1707
1707
|
fs$1.writeFileSync(entryPath, entryContent);
|
|
1708
1708
|
const tsconfig = await resolveTSConfigWithFallback(baseDir);
|
|
1709
|
+
const bundleLog = createBundleLog({ tsconfig });
|
|
1710
|
+
const buildResult = await rolldown.build({
|
|
1711
|
+
plugins: [
|
|
1712
|
+
createGeneratedEntryResolverPlugin(entryPath, baseDir),
|
|
1713
|
+
createTsconfigPathsPlugin(),
|
|
1714
|
+
platformBundleDefinePlugin
|
|
1715
|
+
],
|
|
1716
|
+
input: entryPath,
|
|
1717
|
+
write: false,
|
|
1718
|
+
output: {
|
|
1719
|
+
format: "esm",
|
|
1720
|
+
sourcemap: inlineSourcemap ? "inline" : true,
|
|
1721
|
+
minify: inlineSourcemap ? { mangle: { keepNames: true } } : true,
|
|
1722
|
+
codeSplitting: false,
|
|
1723
|
+
dir: process.cwd()
|
|
1724
|
+
},
|
|
1725
|
+
tsconfig,
|
|
1726
|
+
treeshake: composeFunctionTreeshakeOptions([createLogLevelTreeshakeOptions(bundleLogLevel)]),
|
|
1727
|
+
...bundleLog.options
|
|
1728
|
+
});
|
|
1729
|
+
bundleLog.assertAllResolved();
|
|
1709
1730
|
return {
|
|
1710
|
-
bundledCode:
|
|
1711
|
-
plugins: [platformBundleDefinePlugin],
|
|
1712
|
-
input: entryPath,
|
|
1713
|
-
write: false,
|
|
1714
|
-
output: {
|
|
1715
|
-
format: "esm",
|
|
1716
|
-
sourcemap: inlineSourcemap ? "inline" : true,
|
|
1717
|
-
minify: inlineSourcemap ? { mangle: { keepNames: true } } : true,
|
|
1718
|
-
codeSplitting: false,
|
|
1719
|
-
dir: process.cwd()
|
|
1720
|
-
},
|
|
1721
|
-
tsconfig,
|
|
1722
|
-
treeshake: composeFunctionTreeshakeOptions([createLogLevelTreeshakeOptions(bundleLogLevel)]),
|
|
1723
|
-
logLevel: "silent"
|
|
1724
|
-
})).output[0].code,
|
|
1731
|
+
bundledCode: buildResult.output[0].code,
|
|
1725
1732
|
scriptName
|
|
1726
1733
|
};
|
|
1727
1734
|
}
|
|
@@ -6630,7 +6637,7 @@ async function set(options) {
|
|
|
6630
6637
|
let currentMigration;
|
|
6631
6638
|
try {
|
|
6632
6639
|
const { metadata } = await client.getMetadata({ trn });
|
|
6633
|
-
const label = metadata?.labels[
|
|
6640
|
+
const label = metadata?.labels[MIGRATION_LABEL_KEY];
|
|
6634
6641
|
currentMigration = label ? parseMigrationLabelNumber(label) ?? 0 : 0;
|
|
6635
6642
|
} catch {
|
|
6636
6643
|
currentMigration = 0;
|
|
@@ -6664,7 +6671,7 @@ async function set(options) {
|
|
|
6664
6671
|
trn,
|
|
6665
6672
|
labels: {
|
|
6666
6673
|
...existingLabels,
|
|
6667
|
-
|
|
6674
|
+
[MIGRATION_LABEL_KEY]: `m${formatMigrationNumber(migrationNumber)}`
|
|
6668
6675
|
}
|
|
6669
6676
|
});
|
|
6670
6677
|
logger.success(`Migration checkpoint set to ${styles.bold(formatMigrationNumber(migrationNumber))} for namespace ${styles.bold(targetNamespace)}`);
|
|
@@ -6716,7 +6723,7 @@ async function collectMigrationStatuses(options) {
|
|
|
6716
6723
|
let currentMigration;
|
|
6717
6724
|
try {
|
|
6718
6725
|
const { metadata } = await client.getMetadata({ trn });
|
|
6719
|
-
const label = metadata?.labels[
|
|
6726
|
+
const label = metadata?.labels[MIGRATION_LABEL_KEY];
|
|
6720
6727
|
currentMigration = label ? parseMigrationLabelNumber(label) ?? 0 : 0;
|
|
6721
6728
|
} catch {
|
|
6722
6729
|
currentMigration = 0;
|
|
@@ -6839,7 +6846,7 @@ async function fetchRemoteTypes(client, workspaceId, namespace) {
|
|
|
6839
6846
|
async function assertMigrationsReproduceLocalTypes(loaded, target) {
|
|
6840
6847
|
const { config, plugins } = loaded;
|
|
6841
6848
|
const pluginManager = plugins.length > 0 ? new PluginManager(plugins) : void 0;
|
|
6842
|
-
const { defineApplication, generatePluginFilesIfNeeded } = await import("../application-
|
|
6849
|
+
const { defineApplication, generatePluginFilesIfNeeded } = await import("../application-C-zsrNjn.mjs");
|
|
6843
6850
|
const application = defineApplication({
|
|
6844
6851
|
config,
|
|
6845
6852
|
pluginManager
|
|
@@ -7070,6 +7077,204 @@ const syncCommand = defineAppCommand({
|
|
|
7070
7077
|
}
|
|
7071
7078
|
});
|
|
7072
7079
|
|
|
7080
|
+
//#endregion
|
|
7081
|
+
//#region src/cli/commands/tailordb/migrate/validate.ts
|
|
7082
|
+
/**
|
|
7083
|
+
* Assert that every migration in the local history whose diff requires a data
|
|
7084
|
+
* migration script has a migrate.ts on disk or an explicit skip acknowledgment
|
|
7085
|
+
* @param {string} migrationsDir - Migrations directory path
|
|
7086
|
+
* @param {string} namespace - TailorDB namespace (for error messages)
|
|
7087
|
+
*/
|
|
7088
|
+
function assertRequiredMigrationScripts(migrationsDir, namespace) {
|
|
7089
|
+
const missing = [];
|
|
7090
|
+
for (const file of getMigrationFiles(migrationsDir)) {
|
|
7091
|
+
if (file.type !== "diff") continue;
|
|
7092
|
+
const diff = loadDiff(file.path);
|
|
7093
|
+
if (!diff.requiresMigrationScript || diff.scriptSkipped) continue;
|
|
7094
|
+
if (!fs$1.existsSync(getMigrationFilePath(migrationsDir, file.number, "migrate"))) missing.push(file.number);
|
|
7095
|
+
}
|
|
7096
|
+
if (missing.length > 0) throw new Error(`Migration(s) ${missing.map(formatMigrationNumber).join(", ")} in namespace "${namespace}" require a migration script but have no migrate.ts. Add one with 'tailor-sdk tailordb migration script <number>', or record that no script is needed with 'tailor-sdk tailordb migration script <number> --no-script --reason "..."'.`);
|
|
7097
|
+
}
|
|
7098
|
+
/**
|
|
7099
|
+
* Build per-namespace reports from the checks that completed
|
|
7100
|
+
* @param {BuildValidationReportsOptions} options - Completed validation results
|
|
7101
|
+
* @returns {NamespaceValidationReport[]} Validation reports per namespace
|
|
7102
|
+
*/
|
|
7103
|
+
function buildValidationReports(options) {
|
|
7104
|
+
const { targetNamespaces, migrationFileErrors, localResults, remoteResults } = options;
|
|
7105
|
+
return targetNamespaces.map((target) => {
|
|
7106
|
+
const fileError = migrationFileErrors.get(target.namespace);
|
|
7107
|
+
if (fileError !== void 0) return {
|
|
7108
|
+
namespace: target.namespace,
|
|
7109
|
+
valid: false,
|
|
7110
|
+
migrationFiles: {
|
|
7111
|
+
valid: false,
|
|
7112
|
+
error: fileError
|
|
7113
|
+
}
|
|
7114
|
+
};
|
|
7115
|
+
const local = assertDefined(localResults.find((result) => result.namespace === target.namespace), `local schema check result missing for namespace "${target.namespace}"`);
|
|
7116
|
+
const localSchema = {
|
|
7117
|
+
hasDiff: local.hasDiff,
|
|
7118
|
+
...local.diff ? { diff: local.diff } : {}
|
|
7119
|
+
};
|
|
7120
|
+
if (remoteResults === void 0) return {
|
|
7121
|
+
namespace: target.namespace,
|
|
7122
|
+
valid: false,
|
|
7123
|
+
migrationFiles: { valid: true },
|
|
7124
|
+
localSchema,
|
|
7125
|
+
remoteSchema: { skipped: "check_failed" }
|
|
7126
|
+
};
|
|
7127
|
+
const remote = assertDefined(remoteResults.find((result) => result.namespace === target.namespace), `remote schema check result missing for namespace "${target.namespace}"`);
|
|
7128
|
+
const checkpointMissingLocal = remote.checkpointMissingLocal === true;
|
|
7129
|
+
const remoteSchema = {
|
|
7130
|
+
remoteMigrationNumber: remote.remoteMigrationNumber,
|
|
7131
|
+
hasDrift: remote.hasDrift,
|
|
7132
|
+
drifts: remote.drifts,
|
|
7133
|
+
...remote.skipped ? { skipped: remote.skipped } : {},
|
|
7134
|
+
...checkpointMissingLocal ? { checkpointMissingLocal: true } : {}
|
|
7135
|
+
};
|
|
7136
|
+
return {
|
|
7137
|
+
namespace: target.namespace,
|
|
7138
|
+
valid: !localSchema.hasDiff && !remoteSchema.hasDrift && !checkpointMissingLocal,
|
|
7139
|
+
migrationFiles: { valid: true },
|
|
7140
|
+
localSchema,
|
|
7141
|
+
remoteSchema
|
|
7142
|
+
};
|
|
7143
|
+
});
|
|
7144
|
+
}
|
|
7145
|
+
/**
|
|
7146
|
+
* Run all migration validation checks and collect per-namespace reports
|
|
7147
|
+
* @param {ValidateOptions} options - Command options
|
|
7148
|
+
* @returns {Promise<CollectedValidationReports>} Validation reports and any remote error
|
|
7149
|
+
*/
|
|
7150
|
+
async function collectValidationReports(options) {
|
|
7151
|
+
const { config, plugins } = await loadConfig(options.configPath);
|
|
7152
|
+
const namespacesWithMigrations = getNamespacesWithMigrations(config, path.dirname(config.path));
|
|
7153
|
+
if (namespacesWithMigrations.length === 0) throw new Error("No TailorDB services with migrations configuration found");
|
|
7154
|
+
const targetNamespaces = options.namespace ? namespacesWithMigrations.filter((ns) => ns.namespace === options.namespace) : namespacesWithMigrations;
|
|
7155
|
+
if (targetNamespaces.length === 0) throw new Error(`Namespace "${options.namespace}" not found or does not have migrations configured`);
|
|
7156
|
+
const pluginManager = plugins.length > 0 ? new PluginManager(plugins) : void 0;
|
|
7157
|
+
const { defineApplication } = await import("../application-C-zsrNjn.mjs");
|
|
7158
|
+
const application = defineApplication({
|
|
7159
|
+
config,
|
|
7160
|
+
pluginManager
|
|
7161
|
+
});
|
|
7162
|
+
for (const service of application.tailorDBServices) {
|
|
7163
|
+
await service.loadTypes();
|
|
7164
|
+
await service.processNamespacePlugins();
|
|
7165
|
+
}
|
|
7166
|
+
assertUniqueLocalTailorDBTypeNames({ tailorDBServices: application.tailorDBServices });
|
|
7167
|
+
for (const { namespace } of targetNamespaces) if (!application.tailorDBServices.some((s) => s.namespace === namespace)) throw new Error(`No TailorDB service found for namespace "${namespace}"`);
|
|
7168
|
+
const tailorDBInputs = application.tailorDBServices.map(toTailorDBDeployInput);
|
|
7169
|
+
const typesByNamespace = new Map(tailorDBInputs.map((input) => [input.namespace, input.types]));
|
|
7170
|
+
const migrationFileErrors = /* @__PURE__ */ new Map();
|
|
7171
|
+
const checkableNamespaces = [];
|
|
7172
|
+
for (const target of targetNamespaces) try {
|
|
7173
|
+
assertValidMigrationFiles(target.migrationsDir, target.namespace);
|
|
7174
|
+
reconstructSnapshotFromMigrations(target.migrationsDir);
|
|
7175
|
+
assertRequiredMigrationScripts(target.migrationsDir, target.namespace);
|
|
7176
|
+
checkableNamespaces.push(target);
|
|
7177
|
+
} catch (error) {
|
|
7178
|
+
migrationFileErrors.set(target.namespace, error instanceof Error ? error.message : String(error));
|
|
7179
|
+
}
|
|
7180
|
+
const localReportOptions = {
|
|
7181
|
+
targetNamespaces,
|
|
7182
|
+
migrationFileErrors,
|
|
7183
|
+
localResults: await checkMigrationDiffs(typesByNamespace, checkableNamespaces)
|
|
7184
|
+
};
|
|
7185
|
+
if (checkableNamespaces.length === 0) return { reports: buildValidationReports(localReportOptions) };
|
|
7186
|
+
let remoteResults;
|
|
7187
|
+
try {
|
|
7188
|
+
remoteResults = await verifyRemoteSchema(await initOperatorClient(await loadAccessToken({ profile: options.profile })), await loadWorkspaceId({
|
|
7189
|
+
workspaceId: options.workspaceId,
|
|
7190
|
+
profile: options.profile
|
|
7191
|
+
}), checkableNamespaces, config, tailorDBInputs);
|
|
7192
|
+
} catch (remoteError) {
|
|
7193
|
+
return {
|
|
7194
|
+
reports: buildValidationReports(localReportOptions),
|
|
7195
|
+
remoteError
|
|
7196
|
+
};
|
|
7197
|
+
}
|
|
7198
|
+
return { reports: buildValidationReports({
|
|
7199
|
+
...localReportOptions,
|
|
7200
|
+
remoteResults
|
|
7201
|
+
}) };
|
|
7202
|
+
}
|
|
7203
|
+
function printValidationReports(reports) {
|
|
7204
|
+
for (const report of reports) {
|
|
7205
|
+
logger.newline();
|
|
7206
|
+
logger.info(`Namespace: ${styles.bold(report.namespace)}`);
|
|
7207
|
+
if (!report.migrationFiles.valid) {
|
|
7208
|
+
logger.log(` Migration files: ${styles.error("invalid")}`);
|
|
7209
|
+
logger.log(` ${report.migrationFiles.error}`);
|
|
7210
|
+
continue;
|
|
7211
|
+
}
|
|
7212
|
+
logger.log(` Migration files: ${styles.success("OK")}`);
|
|
7213
|
+
const local = report.localSchema;
|
|
7214
|
+
if (local?.hasDiff) if (local.diff) {
|
|
7215
|
+
logger.log(` Local schema: ${styles.error("changes not in migration files")}`);
|
|
7216
|
+
logger.log(` ${formatDiffSummary(local.diff)}`);
|
|
7217
|
+
logger.log(formatMigrationDiff(local.diff));
|
|
7218
|
+
} else logger.log(` Local schema: ${styles.error("no migration snapshot found")}`);
|
|
7219
|
+
else logger.log(` Local schema: ${styles.success("OK")}`);
|
|
7220
|
+
const remote = report.remoteSchema;
|
|
7221
|
+
if (remote?.skipped === "check_failed") logger.log(` Remote schema: ${styles.error("not checked")}`);
|
|
7222
|
+
else if (remote?.checkpointMissingLocal) logger.log(` Remote schema: ${styles.error(`remote migration ${formatMigrationNumber(remote.remoteMigrationNumber)} is not in the local migration history`)}`);
|
|
7223
|
+
else if (remote?.hasDrift) {
|
|
7224
|
+
logger.log(` Remote schema: ${styles.error("drift detected")} (remote migration: ${formatMigrationNumber(remote.remoteMigrationNumber)})`);
|
|
7225
|
+
logger.log(formatSchemaDrifts(remote.drifts));
|
|
7226
|
+
} else if (remote?.skipped === "not_deployed") logger.log(` Remote schema: ${styles.dim("skipped (namespace not deployed)")}`);
|
|
7227
|
+
else if (remote?.skipped === "no_migration_label") logger.log(` Remote schema: ${styles.dim("skipped (deployed namespace has no migration state)")}`);
|
|
7228
|
+
else if (remote?.skipped === "no_snapshot") logger.log(` Remote schema: ${styles.dim(`skipped (no local snapshot for remote migration ${formatMigrationNumber(remote.remoteMigrationNumber)})`)}`);
|
|
7229
|
+
else if (remote) logger.log(` Remote schema: ${styles.success("OK")} (remote migration: ${formatMigrationNumber(remote.remoteMigrationNumber)})`);
|
|
7230
|
+
}
|
|
7231
|
+
logger.newline();
|
|
7232
|
+
}
|
|
7233
|
+
function printResolutionHints(reports) {
|
|
7234
|
+
if (reports.some((r) => r.localSchema?.hasDiff && !r.localSchema.diff)) logger.info("Run 'tailor-sdk tailordb migration generate' to create the initial snapshot.");
|
|
7235
|
+
if (reports.some((r) => r.localSchema?.diff)) logger.info("Run 'tailor-sdk tailordb migration generate' to create migration files.");
|
|
7236
|
+
if (reports.some((r) => r.remoteSchema?.checkpointMissingLocal)) logger.info("The remote migration checkpoint is ahead of the local history. Pull the latest migration files, or run 'tailor-sdk tailordb migration status' to compare.");
|
|
7237
|
+
if (reports.some((r) => r.remoteSchema?.hasDrift)) logRemoteDriftGuidance();
|
|
7238
|
+
}
|
|
7239
|
+
/**
|
|
7240
|
+
* Validate migration files and schema state without deploying
|
|
7241
|
+
* @param {ValidateOptions} options - Command options
|
|
7242
|
+
*/
|
|
7243
|
+
async function validate(options) {
|
|
7244
|
+
logBetaWarning("tailordb migration");
|
|
7245
|
+
const collected = await collectValidationReports(options);
|
|
7246
|
+
const { reports } = collected;
|
|
7247
|
+
const invalidCount = reports.filter((r) => !r.valid).length;
|
|
7248
|
+
if (options.json) logger.out(reports);
|
|
7249
|
+
else {
|
|
7250
|
+
printValidationReports(reports);
|
|
7251
|
+
if (invalidCount === 0 && !("remoteError" in collected)) logger.success("All migration validation checks passed.");
|
|
7252
|
+
else printResolutionHints(reports);
|
|
7253
|
+
}
|
|
7254
|
+
if ("remoteError" in collected) throw collected.remoteError;
|
|
7255
|
+
if (invalidCount > 0) throw new Error(`Migration validation failed for ${invalidCount} namespace(s)`);
|
|
7256
|
+
}
|
|
7257
|
+
const validateCommand = defineAppCommand({
|
|
7258
|
+
name: "validate",
|
|
7259
|
+
description: "Validate the full migration history and detect schema drift (local types vs. migration snapshot, remote schema vs. migration checkpoint) without deploying. This includes the migration and schema-drift checks used by 'deploy' and exits with a non-zero code when issues are found.",
|
|
7260
|
+
args: z.object({
|
|
7261
|
+
...deploymentArgs,
|
|
7262
|
+
namespace: arg(z.string().optional(), {
|
|
7263
|
+
alias: "n",
|
|
7264
|
+
description: "Target TailorDB namespace (validates all namespaces if not specified)"
|
|
7265
|
+
})
|
|
7266
|
+
}).strict(),
|
|
7267
|
+
run: async (args) => {
|
|
7268
|
+
await validate({
|
|
7269
|
+
configPath: args.config,
|
|
7270
|
+
namespace: args.namespace,
|
|
7271
|
+
workspaceId: args["workspace-id"],
|
|
7272
|
+
profile: args.profile,
|
|
7273
|
+
json: logger.jsonMode
|
|
7274
|
+
});
|
|
7275
|
+
}
|
|
7276
|
+
});
|
|
7277
|
+
|
|
7073
7278
|
//#endregion
|
|
7074
7279
|
//#region src/cli/commands/tailordb/migrate/index.ts
|
|
7075
7280
|
/**
|
|
@@ -7081,6 +7286,7 @@ const syncCommand = defineAppCommand({
|
|
|
7081
7286
|
* - set: Set migration checkpoint to a specific number
|
|
7082
7287
|
* - status: Show migration status for TailorDB namespaces
|
|
7083
7288
|
* - sync: Sync remote TailorDB schema to a specific migration snapshot
|
|
7289
|
+
* - validate: Validate migration files and detect schema drift without deploying
|
|
7084
7290
|
*/
|
|
7085
7291
|
const migrationCommand = defineCommand({
|
|
7086
7292
|
name: "migration",
|
|
@@ -7090,7 +7296,8 @@ const migrationCommand = defineCommand({
|
|
|
7090
7296
|
script: scriptCommand,
|
|
7091
7297
|
set: setCommand,
|
|
7092
7298
|
status: statusCommand,
|
|
7093
|
-
sync: syncCommand
|
|
7299
|
+
sync: syncCommand,
|
|
7300
|
+
validate: validateCommand
|
|
7094
7301
|
}
|
|
7095
7302
|
});
|
|
7096
7303
|
|