@tailor-platform/sdk 1.73.0 → 1.73.2
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-BXdCl6bp.mjs +3 -0
- package/dist/{application-Dxhdq_nu.mjs → application-Bb9NNp5m.mjs} +34 -10
- package/dist/application-Bb9NNp5m.mjs.map +1 -0
- package/dist/cli/commands/tailordb/migrate/diff-calculator.d.mts +14 -2
- package/dist/cli/index.mjs +17 -17
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/lib.mjs +2 -2
- package/dist/completion/zsh-worker.zsh +1 -1
- package/dist/configure/index.mjs.map +1 -1
- package/dist/configure/services/workflow/job.d.mts +3 -3
- package/dist/configure/services/workflow/wait-point.d.mts +3 -3
- package/dist/job-CtU73PGa.mjs.map +1 -1
- package/dist/{runtime-CEFC05qX.mjs → runtime-B8O6JFS2.mjs} +674 -129
- package/dist/runtime-B8O6JFS2.mjs.map +1 -0
- package/docs/services/tailordb-migration.md +2 -0
- package/package.json +1 -1
- package/dist/application-BhRnH42h.mjs +0 -3
- package/dist/application-Dxhdq_nu.mjs.map +0 -1
- package/dist/runtime-CEFC05qX.mjs.map +0 -1
|
@@ -2,7 +2,7 @@ import { t as db } from "./schema-DAdZSFOd.mjs";
|
|
|
2
2
|
import { $ as CreateUserProfileConfigRequestSchema, A as UpdatePipelineServiceRequestSchema, At as Condition_Operator, B as UpdateExecutorExecutorRequestSchema, Ct as UserProfileProviderConfig_UserProfileProviderType, D as CreatePipelineResolverRequestSchema, Dt as ApplicationSchemaUpdateAttemptStatus, E as UpdateSecretManagerSecretRequestSchema, Et as UpdateApplicationRequestSchema, F as IdPPermissionOperator, G as CreateAuthHookRequestSchema, H as ExecutorTargetType, I as IdPPermissionPermit, J as CreateAuthOAuth2ClientRequestSchema, K as CreateAuthIDPConfigRequestSchema, L as FunctionExecution_Status, M as CreateIdPServiceRequestSchema, Mt as PageDirection, N as UpdateIdPServiceRequestSchema, O as CreatePipelineServiceRequestSchema, Ot as Subgraph_ServiceType, P as IdPLang, Q as CreateTenantConfigRequestSchema, S as UpdateStaticWebsiteRequestSchema, St as TenantProviderConfig_TenantProviderType, T as CreateSecretManagerVaultRequestSchema, Tt as GetApplicationSchemaHealthResponse_ApplicationSchemaHealthStatus, U as ExecutorTriggerType, V as ExecutorJobStatus, W as CreateAuthConnectionRequestSchema, X as CreateAuthSCIMResourceRequestSchema, Y as CreateAuthSCIMConfigRequestSchema, Z as CreateAuthServiceRequestSchema, _ as TailorDBType_Permission_Operator, _t as AuthSCIMAttribute_Mutability, a as WorkspacePlatformUserRole, at as UpdateAuthSCIMConfigRequestSchema, b as AddCustomDomainRequestSchema, bt as AuthSCIMConfig_AuthorizationType, c as UpdateWorkflowRequestSchema, ct as UpdateTenantConfigRequestSchema, d as CreateTailorDBServiceRequestSchema, dt as AuthConnection_Type, et as UpdateAuthConnectionRequestSchema, f as CreateTailorDBTypeRequestSchema, ft as AuthHookPoint, g as TailorDBGQLPermission_Permit, gt as AuthOAuth2Client_GrantType, h as TailorDBGQLPermission_Operator, ht as AuthOAuth2Client_ClientType, it as UpdateAuthOAuth2ClientRequestSchema, j as PipelineResolver_OperationType, jt as FilterSchema, k as UpdatePipelineResolverRequestSchema, kt as ConditionSchema, l as WorkflowExecution_Status, lt as UpdateUserProfileConfigRequestSchema, m as TailorDBGQLPermission_Action, mt as AuthInvokerSchema, nt as UpdateAuthIDPConfigRequestSchema, o as CreateWorkflowJobFunctionRequestSchema, ot as UpdateAuthSCIMResourceRequestSchema, p as UpdateTailorDBTypeRequestSchema, pt as AuthIDPConfig_AuthType, q as CreateAuthMachineUserRequestSchema, rt as UpdateAuthMachineUserRequestSchema, s as CreateWorkflowRequestSchema, st as UpdateAuthServiceRequestSchema, t as OperatorService, tt as UpdateAuthHookRequestSchema, u as WorkflowJobExecution_Status, ut as AuthConnection_Status, v as TailorDBType_Permission_Permit, vt as AuthSCIMAttribute_Type, w as CreateSecretManagerSecretRequestSchema, wt as CreateApplicationRequestSchema, x as CreateStaticWebsiteRequestSchema, y as TailorDBType_PermitAction, yt as AuthSCIMAttribute_Uniqueness, z as CreateExecutorExecutorRequestSchema } from "./service_pb-CIrhGwHk.mjs";
|
|
3
3
|
import { t as assertDefined } from "./assert-DBxo8jPo.mjs";
|
|
4
4
|
import { a as parseBoolean, i as symbols, n as logger, r as styles, t as CIPromptError } from "./logger-BEiZZ3qT.mjs";
|
|
5
|
-
import { $ as initOperatorClient, A as loadAccessToken, G as fetchMachineUserToken, I as loadWorkspaceId, K as fetchPaged, L as platformConfigFromProfile, N as loadMachineUserName, P as loadPlatformClientConfig, R as readPlatformConfig, S as getDistDir, T as loadConfig, V as writePlatformConfig, W as fetchAll, X as getOAuth2ClientId, Y as getConsoleBaseUrl, Z as getPlatformBaseUrl, b as hasGenerationHooks, d as assertUniqueLocalTailorDBTypeNames, et as isDefaultPlatform, f as assertUniqueTailorDBTypeNamesWithExternal, h as platformBundleDefinePlugin, j as loadConfigPath, k as hasUserTokenEntry, l as buildExecutorArgsExpr, m as stringifyFunction, n as generatePluginFilesIfNeeded, nt as byName, p as TailorDBTypeSchema, r as loadApplication, s as HTTP_METHODS, t as defineApplication, tt as resolveStaticWebsiteUrls, u as buildResolverOperationHookExpr, w as hashFile, x as createBundleCache, y as getPluginGenerationDependencies } from "./application-
|
|
5
|
+
import { $ as initOperatorClient, A as loadAccessToken, G as fetchMachineUserToken, I as loadWorkspaceId, K as fetchPaged, L as platformConfigFromProfile, N as loadMachineUserName, P as loadPlatformClientConfig, R as readPlatformConfig, S as getDistDir, T as loadConfig, V as writePlatformConfig, W as fetchAll, X as getOAuth2ClientId, Y as getConsoleBaseUrl, Z as getPlatformBaseUrl, b as hasGenerationHooks, d as assertUniqueLocalTailorDBTypeNames, et as isDefaultPlatform, f as assertUniqueTailorDBTypeNamesWithExternal, h as platformBundleDefinePlugin, j as loadConfigPath, k as hasUserTokenEntry, l as buildExecutorArgsExpr, m as stringifyFunction, n as generatePluginFilesIfNeeded, nt as byName, p as TailorDBTypeSchema, r as loadApplication, s as HTTP_METHODS, t as defineApplication, tt as resolveStaticWebsiteUrls, u as buildResolverOperationHookExpr, w as hashFile, x as createBundleCache, y as getPluginGenerationDependencies } from "./application-Bb9NNp5m.mjs";
|
|
6
6
|
import { o as loadFilesWithIgnores, t as createExecutorService } from "./service-DiEtAsvc.mjs";
|
|
7
7
|
import { t as multiline } from "./multiline-sfHpTZZK.mjs";
|
|
8
8
|
import { t as readPackageJson } from "./package-json-8b0O9TlX.mjs";
|
|
@@ -6365,6 +6365,7 @@ function formatDiffChange(change) {
|
|
|
6365
6365
|
case "type_added": return ` + [Type] ${change.typeName} (new type)`;
|
|
6366
6366
|
case "type_removed": return ` - [Type] ${change.typeName} (removed)`;
|
|
6367
6367
|
case "type_modified": return ` ~ [Type] ${change.typeName}: ${change.reason}`;
|
|
6368
|
+
case "type_settings_modified": return ` ~ [Type Settings] ${change.typeName}: ${change.reason ?? "settings changed"}`;
|
|
6368
6369
|
case "field_added": {
|
|
6369
6370
|
const typeStr = formatFieldType(change.after);
|
|
6370
6371
|
return ` + ${change.fieldName}: ${typeStr}`;
|
|
@@ -6462,6 +6463,7 @@ const DIFF_CHANGE_LABELS = {
|
|
|
6462
6463
|
type_added: "type(s) added",
|
|
6463
6464
|
type_removed: "type(s) removed",
|
|
6464
6465
|
type_modified: "type(s) modified",
|
|
6466
|
+
type_settings_modified: "type setting(s) modified",
|
|
6465
6467
|
field_added: "field(s) added",
|
|
6466
6468
|
field_removed: "field(s) removed",
|
|
6467
6469
|
field_modified: "field(s) modified",
|
|
@@ -6681,6 +6683,21 @@ const typeSettingsPatchSchema = z.looseObject({
|
|
|
6681
6683
|
indexes: snapshotRecordSchema(snapshotIndexConfigSchema).optional(),
|
|
6682
6684
|
files: snapshotRecordSchema(z.string()).optional()
|
|
6683
6685
|
});
|
|
6686
|
+
const snapshotTypeSettingsStateSchema = z.looseObject({
|
|
6687
|
+
description: z.string().optional(),
|
|
6688
|
+
pluralForm: z.string(),
|
|
6689
|
+
settings: z.looseObject({
|
|
6690
|
+
aggregation: z.boolean().optional(),
|
|
6691
|
+
bulkUpsert: z.boolean().optional(),
|
|
6692
|
+
gqlOperations: z.looseObject({
|
|
6693
|
+
create: z.boolean().optional(),
|
|
6694
|
+
update: z.boolean().optional(),
|
|
6695
|
+
delete: z.boolean().optional(),
|
|
6696
|
+
read: z.boolean().optional()
|
|
6697
|
+
}).optional(),
|
|
6698
|
+
publishEvents: z.boolean().optional()
|
|
6699
|
+
}).optional()
|
|
6700
|
+
});
|
|
6684
6701
|
const snapshotPermissionStateSchema = z.looseObject({
|
|
6685
6702
|
recordPermission: snapshotRecordPermissionSchema.optional(),
|
|
6686
6703
|
gqlPermission: snapshotGqlPermissionSchema.optional()
|
|
@@ -6704,6 +6721,13 @@ const typeModifiedChangeSchema = z.looseObject({
|
|
|
6704
6721
|
before: typeSettingsPatchSchema.optional(),
|
|
6705
6722
|
after: typeSettingsPatchSchema.optional()
|
|
6706
6723
|
});
|
|
6724
|
+
const typeSettingsModifiedChangeSchema = z.looseObject({
|
|
6725
|
+
kind: z.literal("type_settings_modified"),
|
|
6726
|
+
typeName: z.string(),
|
|
6727
|
+
reason: z.string().optional(),
|
|
6728
|
+
before: snapshotTypeSettingsStateSchema,
|
|
6729
|
+
after: snapshotTypeSettingsStateSchema
|
|
6730
|
+
});
|
|
6707
6731
|
const fieldAddedChangeSchema = z.looseObject({
|
|
6708
6732
|
kind: z.literal("field_added"),
|
|
6709
6733
|
typeName: z.string(),
|
|
@@ -6806,6 +6830,7 @@ const diffChangeSchema = z.discriminatedUnion("kind", [
|
|
|
6806
6830
|
typeAddedChangeSchema,
|
|
6807
6831
|
typeRemovedChangeSchema,
|
|
6808
6832
|
typeModifiedChangeSchema,
|
|
6833
|
+
typeSettingsModifiedChangeSchema,
|
|
6809
6834
|
fieldAddedChangeSchema,
|
|
6810
6835
|
fieldRemovedChangeSchema,
|
|
6811
6836
|
fieldModifiedChangeSchema,
|
|
@@ -7269,6 +7294,16 @@ function applyDiffToSnapshot(snapshot, diff) {
|
|
|
7269
7294
|
}
|
|
7270
7295
|
break;
|
|
7271
7296
|
}
|
|
7297
|
+
case "type_settings_modified": {
|
|
7298
|
+
const existing = types[change.typeName];
|
|
7299
|
+
if (existing) types[change.typeName] = {
|
|
7300
|
+
...existing,
|
|
7301
|
+
description: change.after.description,
|
|
7302
|
+
pluralForm: change.after.pluralForm,
|
|
7303
|
+
settings: change.after.settings ?? {}
|
|
7304
|
+
};
|
|
7305
|
+
break;
|
|
7306
|
+
}
|
|
7272
7307
|
case "field_added":
|
|
7273
7308
|
case "field_modified": {
|
|
7274
7309
|
const existing = types[change.typeName];
|
|
@@ -7645,10 +7680,10 @@ function compareIndexes(ctx, typeName, oldIndexes, newIndexes) {
|
|
|
7645
7680
|
const oldIndex = assertDefined(assertDefined(oldIndexes, "oldIndexes is undefined when oldKeys has entry")[indexName], `index "${indexName}" missing from oldIndexes`);
|
|
7646
7681
|
const oldFieldsStr = JSON.stringify(oldIndex.fields.toSorted());
|
|
7647
7682
|
const newFieldsStr = JSON.stringify(newIndex.fields.toSorted());
|
|
7648
|
-
if (oldFieldsStr !== newFieldsStr || oldIndex.unique !== newIndex.unique) {
|
|
7683
|
+
if (oldFieldsStr !== newFieldsStr || (oldIndex.unique ?? false) !== (newIndex.unique ?? false)) {
|
|
7649
7684
|
const reasons = [];
|
|
7650
7685
|
if (oldFieldsStr !== newFieldsStr) reasons.push("fields changed");
|
|
7651
|
-
if (oldIndex.unique !== newIndex.unique) reasons.push("unique constraint changed");
|
|
7686
|
+
if ((oldIndex.unique ?? false) !== (newIndex.unique ?? false)) reasons.push("unique constraint changed");
|
|
7652
7687
|
ctx.changes.push({
|
|
7653
7688
|
kind: "index_modified",
|
|
7654
7689
|
typeName,
|
|
@@ -7728,6 +7763,7 @@ function compareRelationships(ctx, typeName, relationshipType, oldRelationships,
|
|
|
7728
7763
|
if (oldRel.targetField !== newRel.targetField) reasons.push("targetField changed");
|
|
7729
7764
|
if (oldRel.sourceField !== newRel.sourceField) reasons.push("sourceField changed");
|
|
7730
7765
|
if (oldRel.isArray !== newRel.isArray) reasons.push("isArray changed");
|
|
7766
|
+
if (oldRel.description !== newRel.description) reasons.push("description changed");
|
|
7731
7767
|
if (reasons.length > 0) ctx.changes.push({
|
|
7732
7768
|
kind: "relationship_modified",
|
|
7733
7769
|
typeName,
|
|
@@ -7750,8 +7786,12 @@ function compareRelationships(ctx, typeName, relationshipType, oldRelationships,
|
|
|
7750
7786
|
* @returns {void}
|
|
7751
7787
|
*/
|
|
7752
7788
|
function comparePermissions(ctx, typeName, oldRecordPerm, newRecordPerm, oldGqlPerm, newGqlPerm) {
|
|
7753
|
-
const
|
|
7754
|
-
const
|
|
7789
|
+
const oldComparableRecordPerm = comparableRecordPermission(oldRecordPerm);
|
|
7790
|
+
const newComparableRecordPerm = comparableRecordPermission(newRecordPerm);
|
|
7791
|
+
const recordPermChanged = JSON.stringify(oldComparableRecordPerm ?? null) !== JSON.stringify(newComparableRecordPerm ?? null);
|
|
7792
|
+
const oldComparableGqlPerm = comparableGqlPermission(oldGqlPerm);
|
|
7793
|
+
const newComparableGqlPerm = comparableGqlPermission(newGqlPerm);
|
|
7794
|
+
const gqlPermChanged = JSON.stringify(oldComparableGqlPerm ?? null) !== JSON.stringify(newComparableGqlPerm ?? null);
|
|
7755
7795
|
if (recordPermChanged || gqlPermChanged) {
|
|
7756
7796
|
const reasons = [];
|
|
7757
7797
|
if (recordPermChanged) reasons.push("record permission");
|
|
@@ -7761,16 +7801,95 @@ function comparePermissions(ctx, typeName, oldRecordPerm, newRecordPerm, oldGqlP
|
|
|
7761
7801
|
typeName,
|
|
7762
7802
|
reason: `${reasons.join(" and ")} changed`,
|
|
7763
7803
|
before: {
|
|
7764
|
-
recordPermission:
|
|
7765
|
-
gqlPermission:
|
|
7804
|
+
recordPermission: oldComparableRecordPerm,
|
|
7805
|
+
gqlPermission: oldComparableGqlPerm
|
|
7766
7806
|
},
|
|
7767
7807
|
after: {
|
|
7768
|
-
recordPermission:
|
|
7769
|
-
gqlPermission:
|
|
7808
|
+
recordPermission: newComparableRecordPerm,
|
|
7809
|
+
gqlPermission: newComparableGqlPerm
|
|
7770
7810
|
}
|
|
7771
7811
|
});
|
|
7772
7812
|
}
|
|
7773
7813
|
}
|
|
7814
|
+
const GQL_ACTION_ORDER = {
|
|
7815
|
+
all: 0,
|
|
7816
|
+
create: 1,
|
|
7817
|
+
read: 2,
|
|
7818
|
+
update: 3,
|
|
7819
|
+
delete: 4,
|
|
7820
|
+
aggregate: 5,
|
|
7821
|
+
bulkUpsert: 6
|
|
7822
|
+
};
|
|
7823
|
+
function sortByJson(items) {
|
|
7824
|
+
return items.map((item) => [JSON.stringify(item), item]).toSorted(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).map(([, item]) => item);
|
|
7825
|
+
}
|
|
7826
|
+
function comparableGqlPermission(permission) {
|
|
7827
|
+
const policies = permission?.map((policy) => ({
|
|
7828
|
+
...policy,
|
|
7829
|
+
conditions: sortByJson(policy.conditions),
|
|
7830
|
+
actions: policy.actions.toSorted((left, right) => GQL_ACTION_ORDER[left] - GQL_ACTION_ORDER[right])
|
|
7831
|
+
}));
|
|
7832
|
+
return policies && policies.length > 0 ? sortByJson(policies) : void 0;
|
|
7833
|
+
}
|
|
7834
|
+
function comparableRecordPermission(permission) {
|
|
7835
|
+
if (!permission) return void 0;
|
|
7836
|
+
if (!Object.values(permission).some((policies) => policies.length > 0)) return void 0;
|
|
7837
|
+
return {
|
|
7838
|
+
create: sortByJson(permission.create.map(canonicalActionPermission)),
|
|
7839
|
+
read: sortByJson(permission.read.map(canonicalActionPermission)),
|
|
7840
|
+
update: sortByJson(permission.update.map(canonicalActionPermission)),
|
|
7841
|
+
delete: sortByJson(permission.delete.map(canonicalActionPermission))
|
|
7842
|
+
};
|
|
7843
|
+
}
|
|
7844
|
+
function canonicalActionPermission(policy) {
|
|
7845
|
+
return {
|
|
7846
|
+
...policy,
|
|
7847
|
+
conditions: sortByJson(policy.conditions)
|
|
7848
|
+
};
|
|
7849
|
+
}
|
|
7850
|
+
function normalizeComparableGqlOperations(operations) {
|
|
7851
|
+
if (!operations) return void 0;
|
|
7852
|
+
return {
|
|
7853
|
+
create: operations.create ?? true,
|
|
7854
|
+
update: operations.update ?? true,
|
|
7855
|
+
delete: operations.delete ?? true,
|
|
7856
|
+
read: operations.read ?? true
|
|
7857
|
+
};
|
|
7858
|
+
}
|
|
7859
|
+
function normalizeComparableSettings(settings) {
|
|
7860
|
+
const normalized = {};
|
|
7861
|
+
if (settings?.aggregation === true) normalized.aggregation = true;
|
|
7862
|
+
if (settings?.bulkUpsert === true) normalized.bulkUpsert = true;
|
|
7863
|
+
if (settings?.publishEvents === true) normalized.publishEvents = true;
|
|
7864
|
+
const gqlOperations = normalizeComparableGqlOperations(settings?.gqlOperations);
|
|
7865
|
+
if (gqlOperations) normalized.gqlOperations = gqlOperations;
|
|
7866
|
+
return Object.keys(normalized).length > 0 ? normalized : void 0;
|
|
7867
|
+
}
|
|
7868
|
+
function typeSettingsState(description, pluralForm, settings) {
|
|
7869
|
+
return {
|
|
7870
|
+
...description ? { description } : {},
|
|
7871
|
+
pluralForm,
|
|
7872
|
+
...settings && { settings }
|
|
7873
|
+
};
|
|
7874
|
+
}
|
|
7875
|
+
function comparableTypeSettings(type) {
|
|
7876
|
+
return typeSettingsState(type.description, inflection.camelize(type.pluralForm, true), normalizeComparableSettings(type.settings));
|
|
7877
|
+
}
|
|
7878
|
+
function snapshotTypeSettingsState(type) {
|
|
7879
|
+
return typeSettingsState(type.description, type.pluralForm, type.settings ?? {});
|
|
7880
|
+
}
|
|
7881
|
+
function compareTypeSettings(ctx, typeName, previous, current) {
|
|
7882
|
+
const previousComparable = comparableTypeSettings(previous);
|
|
7883
|
+
const currentComparable = comparableTypeSettings(current);
|
|
7884
|
+
if (JSON.stringify(previousComparable) === JSON.stringify(currentComparable)) return;
|
|
7885
|
+
ctx.changes.push({
|
|
7886
|
+
kind: "type_settings_modified",
|
|
7887
|
+
typeName,
|
|
7888
|
+
reason: "settings changed",
|
|
7889
|
+
before: snapshotTypeSettingsState(previous),
|
|
7890
|
+
after: snapshotTypeSettingsState(current)
|
|
7891
|
+
});
|
|
7892
|
+
}
|
|
7774
7893
|
/**
|
|
7775
7894
|
* Compare two snapshots and generate a diff
|
|
7776
7895
|
* @param {SchemaSnapshot} previous - Previous schema snapshot
|
|
@@ -7808,6 +7927,7 @@ function compareNormalizedSnapshots(previous, current) {
|
|
|
7808
7927
|
if (!previousTypeNames.has(typeName)) continue;
|
|
7809
7928
|
const prevType = assertDefined(previous.types[typeName], `type "${typeName}" missing from previous snapshot`);
|
|
7810
7929
|
const currType = assertDefined(current.types[typeName], `type "${typeName}" missing from current snapshot`);
|
|
7930
|
+
compareTypeSettings(ctx, typeName, prevType, currType);
|
|
7811
7931
|
compareTypeFields(ctx, typeName, prevType, currType);
|
|
7812
7932
|
compareIndexes(ctx, typeName, prevType.indexes, currType.indexes);
|
|
7813
7933
|
compareFiles(ctx, typeName, prevType.files, currType.files);
|
|
@@ -7909,6 +8029,49 @@ function assertValidMigrationFiles(migrationsDir, namespace) {
|
|
|
7909
8029
|
throw new Error(`Migration file validation failed for namespace "${namespace}":\n${errorMessages}`);
|
|
7910
8030
|
}
|
|
7911
8031
|
}
|
|
8032
|
+
function convertRemoteFieldToSnapshot(remoteField) {
|
|
8033
|
+
const config = {
|
|
8034
|
+
type: remoteField.type,
|
|
8035
|
+
required: remoteField.required
|
|
8036
|
+
};
|
|
8037
|
+
if (remoteField.array) config.array = true;
|
|
8038
|
+
if (remoteField.index) config.index = true;
|
|
8039
|
+
if (remoteField.unique) config.unique = true;
|
|
8040
|
+
if (remoteField.foreignKey) {
|
|
8041
|
+
config.foreignKey = true;
|
|
8042
|
+
if (remoteField.foreignKeyType) config.foreignKeyType = remoteField.foreignKeyType;
|
|
8043
|
+
if (remoteField.foreignKeyField) config.foreignKeyField = remoteField.foreignKeyField;
|
|
8044
|
+
}
|
|
8045
|
+
const allowedValues = remoteField.allowedValues;
|
|
8046
|
+
if (allowedValues.length > 0) config.allowedValues = allowedValues.map((v) => ({
|
|
8047
|
+
value: v.value,
|
|
8048
|
+
...v.description && { description: v.description }
|
|
8049
|
+
}));
|
|
8050
|
+
if (remoteField.description) config.description = remoteField.description;
|
|
8051
|
+
if (remoteField.vector) config.vector = true;
|
|
8052
|
+
if (remoteField.hooks) {
|
|
8053
|
+
config.hooks = {};
|
|
8054
|
+
if (remoteField.hooks.create?.expr) config.hooks.create = { expr: remoteField.hooks.create.expr };
|
|
8055
|
+
if (remoteField.hooks.update?.expr) config.hooks.update = { expr: remoteField.hooks.update.expr };
|
|
8056
|
+
}
|
|
8057
|
+
const validate = remoteField.validate;
|
|
8058
|
+
if (validate.length > 0) config.validate = validate.map((v) => ({
|
|
8059
|
+
script: { expr: convertRemoteValidateExpression(v.script?.expr ?? "", v.action) },
|
|
8060
|
+
errorMessage: v.errorMessage ?? ""
|
|
8061
|
+
}));
|
|
8062
|
+
if (remoteField.serial) config.serial = {
|
|
8063
|
+
start: Number(remoteField.serial.start),
|
|
8064
|
+
...remoteField.serial.maxValue && { maxValue: Number(remoteField.serial.maxValue) },
|
|
8065
|
+
...remoteField.serial.format && { format: remoteField.serial.format }
|
|
8066
|
+
};
|
|
8067
|
+
if (remoteField.scale !== void 0) config.scale = remoteField.scale;
|
|
8068
|
+
const nestedFields = remoteField.fields;
|
|
8069
|
+
if (Object.keys(nestedFields).length > 0) {
|
|
8070
|
+
config.fields = createSnapshotRecord();
|
|
8071
|
+
for (const [fieldName, nestedField] of Object.entries(nestedFields)) config.fields[fieldName] = convertRemoteFieldToSnapshot(nestedField);
|
|
8072
|
+
}
|
|
8073
|
+
return config;
|
|
8074
|
+
}
|
|
7912
8075
|
/**
|
|
7913
8076
|
* Convert remote ParsedTailorDBType to SnapshotFieldConfig for comparison
|
|
7914
8077
|
* @param {ProtoTailorDBType} remoteType - Remote TailorDB type from API
|
|
@@ -7917,62 +8080,204 @@ function assertValidMigrationFiles(migrationsDir, namespace) {
|
|
|
7917
8080
|
function convertRemoteFieldsToSnapshot(remoteType) {
|
|
7918
8081
|
const fields = createSnapshotRecord();
|
|
7919
8082
|
const remoteFields = remoteType.schema?.fields ?? {};
|
|
7920
|
-
for (const [fieldName, remoteField] of Object.entries(remoteFields))
|
|
7921
|
-
|
|
7922
|
-
|
|
7923
|
-
|
|
7924
|
-
|
|
7925
|
-
|
|
7926
|
-
|
|
7927
|
-
|
|
7928
|
-
|
|
7929
|
-
|
|
7930
|
-
|
|
7931
|
-
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
|
|
7936
|
-
|
|
7937
|
-
|
|
7938
|
-
|
|
7939
|
-
if (remoteField.hooks) {
|
|
7940
|
-
config.hooks = {};
|
|
7941
|
-
if (remoteField.hooks.create?.expr) config.hooks.create = { expr: remoteField.hooks.create.expr };
|
|
7942
|
-
if (remoteField.hooks.update?.expr) config.hooks.update = { expr: remoteField.hooks.update.expr };
|
|
7943
|
-
}
|
|
7944
|
-
if (remoteField.validate.length > 0) config.validate = remoteField.validate.map((v) => ({
|
|
7945
|
-
script: { expr: v.script?.expr ?? "" },
|
|
7946
|
-
errorMessage: v.errorMessage ?? ""
|
|
7947
|
-
}));
|
|
7948
|
-
if (remoteField.serial) config.serial = {
|
|
7949
|
-
start: Number(remoteField.serial.start),
|
|
7950
|
-
...remoteField.serial.maxValue && { maxValue: Number(remoteField.serial.maxValue) },
|
|
7951
|
-
...remoteField.serial.format && { format: remoteField.serial.format }
|
|
8083
|
+
for (const [fieldName, remoteField] of Object.entries(remoteFields)) fields[fieldName] = convertRemoteFieldToSnapshot(remoteField);
|
|
8084
|
+
return fields;
|
|
8085
|
+
}
|
|
8086
|
+
function convertRemoteValidateExpression(expr, action) {
|
|
8087
|
+
return action === TailorDBType_PermitAction.DENY && expr.startsWith("!") ? expr.slice(1) : expr;
|
|
8088
|
+
}
|
|
8089
|
+
function convertRemoteSettingsToSnapshot(remoteSettings, expectedSettings) {
|
|
8090
|
+
const settings = {};
|
|
8091
|
+
if (remoteSettings?.aggregation) settings.aggregation = true;
|
|
8092
|
+
if (remoteSettings?.bulkUpsert) settings.bulkUpsert = true;
|
|
8093
|
+
if (remoteSettings?.publishRecordEvents) settings.publishEvents = true;
|
|
8094
|
+
const disabled = remoteSettings?.disableGqlOperations;
|
|
8095
|
+
if (disabled) {
|
|
8096
|
+
const hasDisabledOperation = disabled.create || disabled.update || disabled.delete || disabled.read;
|
|
8097
|
+
if (expectedSettings?.gqlOperations !== void 0 || hasDisabledOperation) settings.gqlOperations = {
|
|
8098
|
+
create: !disabled.create,
|
|
8099
|
+
update: !disabled.update,
|
|
8100
|
+
delete: !disabled.delete,
|
|
8101
|
+
read: !disabled.read
|
|
7952
8102
|
};
|
|
7953
|
-
if (remoteField.scale !== void 0) config.scale = remoteField.scale;
|
|
7954
|
-
fields[fieldName] = config;
|
|
7955
8103
|
}
|
|
7956
|
-
return
|
|
8104
|
+
return Object.keys(settings).length > 0 ? settings : void 0;
|
|
8105
|
+
}
|
|
8106
|
+
function convertRemoteIndexesToSnapshot(remoteIndexes) {
|
|
8107
|
+
const indexes = createSnapshotRecord();
|
|
8108
|
+
for (const [indexName, indexConfig] of Object.entries(remoteIndexes ?? {})) indexes[indexName] = {
|
|
8109
|
+
fields: indexConfig.fieldNames,
|
|
8110
|
+
...indexConfig.unique && { unique: true }
|
|
8111
|
+
};
|
|
8112
|
+
return Object.keys(indexes).length > 0 ? indexes : void 0;
|
|
8113
|
+
}
|
|
8114
|
+
function convertRemoteFilesToSnapshot(remoteFiles) {
|
|
8115
|
+
const files = createSnapshotRecord();
|
|
8116
|
+
for (const [fileName, fileConfig] of Object.entries(remoteFiles ?? {})) files[fileName] = fileConfig.description || "";
|
|
8117
|
+
return Object.keys(files).length > 0 ? files : void 0;
|
|
8118
|
+
}
|
|
8119
|
+
function convertRemoteRelationshipToSnapshot(relationship, direction) {
|
|
8120
|
+
return direction === "forward" ? {
|
|
8121
|
+
targetType: relationship.refType,
|
|
8122
|
+
targetField: relationship.srcField,
|
|
8123
|
+
sourceField: relationship.refField,
|
|
8124
|
+
isArray: relationship.array,
|
|
8125
|
+
description: relationship.description || ""
|
|
8126
|
+
} : {
|
|
8127
|
+
targetType: relationship.refType,
|
|
8128
|
+
targetField: relationship.refField,
|
|
8129
|
+
sourceField: relationship.srcField,
|
|
8130
|
+
isArray: relationship.array,
|
|
8131
|
+
description: relationship.description || ""
|
|
8132
|
+
};
|
|
8133
|
+
}
|
|
8134
|
+
function remoteRelationshipMatchesExpectedDirection(relationship, expected, direction) {
|
|
8135
|
+
const converted = convertRemoteRelationshipToSnapshot(relationship, direction);
|
|
8136
|
+
return converted.targetType === expected.targetType && converted.targetField === expected.targetField && converted.sourceField === expected.sourceField && converted.isArray === expected.isArray;
|
|
8137
|
+
}
|
|
8138
|
+
function inferRemoteRelationshipDirection(relationshipName, relationship, expectedType) {
|
|
8139
|
+
const expectedForward = expectedType?.forwardRelationships?.[relationshipName];
|
|
8140
|
+
const expectedBackward = expectedType?.backwardRelationships?.[relationshipName];
|
|
8141
|
+
if (expectedForward && !expectedBackward) return "forward";
|
|
8142
|
+
if (expectedBackward && !expectedForward) return "backward";
|
|
8143
|
+
if (expectedForward && remoteRelationshipMatchesExpectedDirection(relationship, expectedForward, "forward")) return "forward";
|
|
8144
|
+
if (expectedBackward && remoteRelationshipMatchesExpectedDirection(relationship, expectedBackward, "backward")) return "backward";
|
|
8145
|
+
return relationship.array ? "backward" : "forward";
|
|
8146
|
+
}
|
|
8147
|
+
function convertRemoteRelationshipsToSnapshot(remoteRelationships, expectedType) {
|
|
8148
|
+
const forwardRelationships = createSnapshotRecord();
|
|
8149
|
+
const backwardRelationships = createSnapshotRecord();
|
|
8150
|
+
for (const [relationshipName, relationship] of Object.entries(remoteRelationships ?? {})) {
|
|
8151
|
+
const direction = inferRemoteRelationshipDirection(relationshipName, relationship, expectedType);
|
|
8152
|
+
if (direction === "forward") forwardRelationships[relationshipName] = convertRemoteRelationshipToSnapshot(relationship, direction);
|
|
8153
|
+
else backwardRelationships[relationshipName] = convertRemoteRelationshipToSnapshot(relationship, direction);
|
|
8154
|
+
}
|
|
8155
|
+
return {
|
|
8156
|
+
...Object.keys(forwardRelationships).length > 0 && { forwardRelationships },
|
|
8157
|
+
...Object.keys(backwardRelationships).length > 0 && { backwardRelationships }
|
|
8158
|
+
};
|
|
7957
8159
|
}
|
|
7958
|
-
|
|
8160
|
+
const REMOTE_PERMISSION_PERMITS = /* @__PURE__ */ new Map([[TailorDBType_Permission_Permit.ALLOW, "allow"], [TailorDBType_Permission_Permit.DENY, "deny"]]);
|
|
8161
|
+
const REMOTE_PERMISSION_OPERATORS = /* @__PURE__ */ new Map([
|
|
8162
|
+
[TailorDBType_Permission_Operator.EQ, "eq"],
|
|
8163
|
+
[TailorDBType_Permission_Operator.NE, "ne"],
|
|
8164
|
+
[TailorDBType_Permission_Operator.IN, "in"],
|
|
8165
|
+
[TailorDBType_Permission_Operator.NIN, "nin"],
|
|
8166
|
+
[TailorDBType_Permission_Operator.HAS_ANY, "hasAny"],
|
|
8167
|
+
[TailorDBType_Permission_Operator.NHAS_ANY, "nhasAny"]
|
|
8168
|
+
]);
|
|
8169
|
+
function convertRemotePermit(permit, source) {
|
|
8170
|
+
const converted = REMOTE_PERMISSION_PERMITS.get(permit);
|
|
8171
|
+
if (converted) return converted;
|
|
8172
|
+
throw new Error(`Unsupported ${source} permission permit: ${permit}`);
|
|
8173
|
+
}
|
|
8174
|
+
function convertRemoteOperator(operator, source) {
|
|
8175
|
+
const converted = REMOTE_PERMISSION_OPERATORS.get(operator);
|
|
8176
|
+
if (converted) return converted;
|
|
8177
|
+
throw new Error(`Unsupported ${source} permission operator: ${operator}`);
|
|
8178
|
+
}
|
|
8179
|
+
function convertRemoteValueOperand(operand) {
|
|
8180
|
+
switch (operand?.kind.case) {
|
|
8181
|
+
case "userField": return { user: operand.kind.value };
|
|
8182
|
+
case "recordField": return { record: operand.kind.value };
|
|
8183
|
+
case "oldRecordField": return { oldRecord: operand.kind.value };
|
|
8184
|
+
case "newRecordField": return { newRecord: operand.kind.value };
|
|
8185
|
+
case "value": return toJson(ValueSchema, operand.kind.value);
|
|
8186
|
+
default: throw new Error("Unsupported permission operand");
|
|
8187
|
+
}
|
|
8188
|
+
}
|
|
8189
|
+
function convertRemoteRecordCondition(condition) {
|
|
8190
|
+
return [
|
|
8191
|
+
convertRemoteValueOperand(condition.left),
|
|
8192
|
+
convertRemoteOperator(condition.operator, "record"),
|
|
8193
|
+
convertRemoteValueOperand(condition.right)
|
|
8194
|
+
];
|
|
8195
|
+
}
|
|
8196
|
+
function convertRemoteGqlCondition(condition) {
|
|
8197
|
+
return [
|
|
8198
|
+
convertRemoteValueOperand(condition.left),
|
|
8199
|
+
convertRemoteOperator(condition.operator, "GQL"),
|
|
8200
|
+
convertRemoteValueOperand(condition.right)
|
|
8201
|
+
];
|
|
8202
|
+
}
|
|
8203
|
+
function convertRemoteRecordPolicy(policy) {
|
|
8204
|
+
return {
|
|
8205
|
+
conditions: policy.conditions.map(convertRemoteRecordCondition),
|
|
8206
|
+
permit: convertRemotePermit(policy.permit, "record"),
|
|
8207
|
+
...policy.description && { description: policy.description }
|
|
8208
|
+
};
|
|
8209
|
+
}
|
|
8210
|
+
function convertRemoteRecordPermissionToSnapshot(permission) {
|
|
8211
|
+
const recordPermission = {
|
|
8212
|
+
create: permission?.create.map(convertRemoteRecordPolicy) ?? [],
|
|
8213
|
+
read: permission?.read.map(convertRemoteRecordPolicy) ?? [],
|
|
8214
|
+
update: permission?.update.map(convertRemoteRecordPolicy) ?? [],
|
|
8215
|
+
delete: permission?.delete.map(convertRemoteRecordPolicy) ?? []
|
|
8216
|
+
};
|
|
8217
|
+
return Object.values(recordPermission).some((policies) => policies.length > 0) ? recordPermission : void 0;
|
|
8218
|
+
}
|
|
8219
|
+
function convertRemoteGqlAction(action) {
|
|
8220
|
+
switch (action) {
|
|
8221
|
+
case TailorDBGQLPermission_Action.ALL: return "all";
|
|
8222
|
+
case TailorDBGQLPermission_Action.CREATE: return "create";
|
|
8223
|
+
case TailorDBGQLPermission_Action.READ: return "read";
|
|
8224
|
+
case TailorDBGQLPermission_Action.UPDATE: return "update";
|
|
8225
|
+
case TailorDBGQLPermission_Action.DELETE: return "delete";
|
|
8226
|
+
case TailorDBGQLPermission_Action.AGGREGATE: return "aggregate";
|
|
8227
|
+
case TailorDBGQLPermission_Action.BULK_UPSERT: return "bulkUpsert";
|
|
8228
|
+
default: throw new Error(`Unsupported GQL permission action: ${action}`);
|
|
8229
|
+
}
|
|
8230
|
+
}
|
|
8231
|
+
function convertRemoteGqlPermissionToSnapshot(permission) {
|
|
8232
|
+
const policies = permission?.policies.map((policy) => ({
|
|
8233
|
+
conditions: policy.conditions.map(convertRemoteGqlCondition),
|
|
8234
|
+
actions: policy.actions.map(convertRemoteGqlAction),
|
|
8235
|
+
permit: convertRemotePermit(policy.permit, "GQL"),
|
|
8236
|
+
...policy.description && { description: policy.description }
|
|
8237
|
+
})) ?? [];
|
|
8238
|
+
return policies.length > 0 ? policies : void 0;
|
|
8239
|
+
}
|
|
8240
|
+
function convertRemoteTypeToSnapshot(remoteType, expectedType) {
|
|
8241
|
+
const settings = convertRemoteSettingsToSnapshot(remoteType.schema?.settings, expectedType?.settings);
|
|
8242
|
+
const relationships = convertRemoteRelationshipsToSnapshot(remoteType.schema?.relationships, expectedType);
|
|
8243
|
+
const recordPermission = convertRemoteRecordPermissionToSnapshot(remoteType.schema?.permission);
|
|
7959
8244
|
const snapshotType = {
|
|
7960
8245
|
name: remoteType.name,
|
|
7961
8246
|
pluralForm: remoteType.schema?.settings?.pluralForm || inflection.pluralize(remoteType.name),
|
|
7962
|
-
fields: convertRemoteFieldsToSnapshot(remoteType)
|
|
8247
|
+
fields: convertRemoteFieldsToSnapshot(remoteType),
|
|
8248
|
+
...settings && { settings },
|
|
8249
|
+
...relationships
|
|
7963
8250
|
};
|
|
7964
8251
|
if (remoteType.schema?.description) snapshotType.description = remoteType.schema.description;
|
|
8252
|
+
const indexes = convertRemoteIndexesToSnapshot(remoteType.schema?.indexes);
|
|
8253
|
+
if (indexes) snapshotType.indexes = indexes;
|
|
8254
|
+
const files = convertRemoteFilesToSnapshot(remoteType.schema?.files);
|
|
8255
|
+
if (files) snapshotType.files = files;
|
|
8256
|
+
if (recordPermission) snapshotType.permissions = { record: recordPermission };
|
|
7965
8257
|
return snapshotType;
|
|
7966
8258
|
}
|
|
7967
8259
|
/**
|
|
7968
8260
|
* Convert remote TailorDB types into the normalized snapshot shape used by drift checks.
|
|
7969
8261
|
* @param {ProtoTailorDBType[]} remoteTypes - Remote TailorDB types from the API
|
|
7970
8262
|
* @param {string} namespace - Namespace for the reconstructed snapshot
|
|
8263
|
+
* @param {readonly RemoteGqlPermission[]} remoteGqlPermissions - Remote GQL permissions for the namespace
|
|
8264
|
+
* @param {SchemaSnapshot} expectedSnapshot - Optional snapshot used to disambiguate remote relationship direction
|
|
7971
8265
|
* @returns {NormalizedSchemaSnapshot} Normalized snapshot-shaped remote state
|
|
7972
8266
|
*/
|
|
7973
|
-
function createSnapshotFromRemoteTypes(remoteTypes, namespace) {
|
|
8267
|
+
function createSnapshotFromRemoteTypes(remoteTypes, namespace, remoteGqlPermissions = [], expectedSnapshot) {
|
|
7974
8268
|
const types = createSnapshotRecord();
|
|
7975
|
-
for (const remoteType of remoteTypes) types[remoteType.name] = convertRemoteTypeToSnapshot(remoteType);
|
|
8269
|
+
for (const remoteType of remoteTypes) types[remoteType.name] = convertRemoteTypeToSnapshot(remoteType, expectedSnapshot?.types[remoteType.name]);
|
|
8270
|
+
for (const permission of remoteGqlPermissions) {
|
|
8271
|
+
const { typeName } = permission;
|
|
8272
|
+
const snapshotType = types[typeName];
|
|
8273
|
+
if (!snapshotType) continue;
|
|
8274
|
+
const gqlPermission = convertRemoteGqlPermissionToSnapshot(permission.permission);
|
|
8275
|
+
if (!gqlPermission) continue;
|
|
8276
|
+
snapshotType.permissions = {
|
|
8277
|
+
...snapshotType.permissions,
|
|
8278
|
+
gql: gqlPermission
|
|
8279
|
+
};
|
|
8280
|
+
}
|
|
7976
8281
|
return normalizeSchemaSnapshot({
|
|
7977
8282
|
version: 1,
|
|
7978
8283
|
namespace,
|
|
@@ -7980,6 +8285,107 @@ function createSnapshotFromRemoteTypes(remoteTypes, namespace) {
|
|
|
7980
8285
|
types
|
|
7981
8286
|
});
|
|
7982
8287
|
}
|
|
8288
|
+
function fieldDifferenceValue(value) {
|
|
8289
|
+
if (value === void 0 || value === "") return "none";
|
|
8290
|
+
return String(value);
|
|
8291
|
+
}
|
|
8292
|
+
function fieldDifferenceKey(prefix, key) {
|
|
8293
|
+
return prefix ? `${prefix}.${key}` : key;
|
|
8294
|
+
}
|
|
8295
|
+
function addFieldDifference(differences, prefix, key, remoteValue, snapshotValue) {
|
|
8296
|
+
if (remoteValue === snapshotValue) return;
|
|
8297
|
+
differences.push(`${fieldDifferenceKey(prefix, key)}: remote=${fieldDifferenceValue(remoteValue)}, expected=${fieldDifferenceValue(snapshotValue)}`);
|
|
8298
|
+
}
|
|
8299
|
+
function addBooleanFieldDifference(differences, prefix, key, remoteField, snapshotField) {
|
|
8300
|
+
addFieldDifference(differences, prefix, key, remoteField[key] ?? false, snapshotField[key] ?? false);
|
|
8301
|
+
}
|
|
8302
|
+
function addAllowedValuesDifferences(differences, prefix, remoteField, snapshotField) {
|
|
8303
|
+
const remoteAllowed = remoteField.allowedValues ?? [];
|
|
8304
|
+
const snapshotAllowed = snapshotField.allowedValues ?? [];
|
|
8305
|
+
if (remoteAllowed.length !== snapshotAllowed.length) {
|
|
8306
|
+
differences.push(`${fieldDifferenceKey(prefix, "allowedValues")} count: remote=${remoteAllowed.length}, expected=${snapshotAllowed.length}`);
|
|
8307
|
+
return;
|
|
8308
|
+
}
|
|
8309
|
+
const snapshotAllowedValues = new Map(snapshotAllowed.map((v) => [v.value, v.description]));
|
|
8310
|
+
for (const value of remoteAllowed) {
|
|
8311
|
+
if (!snapshotAllowedValues.has(value.value)) {
|
|
8312
|
+
differences.push(`${fieldDifferenceKey(prefix, "allowedValues")}: remote has '${value.value}' not in snapshot`);
|
|
8313
|
+
return;
|
|
8314
|
+
}
|
|
8315
|
+
const snapshotDescription = snapshotAllowedValues.get(value.value);
|
|
8316
|
+
if ((value.description ?? "") !== (snapshotDescription ?? "")) {
|
|
8317
|
+
addFieldDifference(differences, prefix, `allowedValues.${value.value}.description`, value.description ?? "", snapshotDescription ?? "");
|
|
8318
|
+
return;
|
|
8319
|
+
}
|
|
8320
|
+
}
|
|
8321
|
+
const remoteAllowedValues = new Set(remoteAllowed.map((v) => v.value));
|
|
8322
|
+
for (const value of snapshotAllowed) if (!remoteAllowedValues.has(value.value)) {
|
|
8323
|
+
differences.push(`${fieldDifferenceKey(prefix, "allowedValues")}: snapshot has '${value.value}' not in remote`);
|
|
8324
|
+
return;
|
|
8325
|
+
}
|
|
8326
|
+
}
|
|
8327
|
+
function addHooksDifferences(differences, prefix, remoteField, snapshotField) {
|
|
8328
|
+
addFieldDifference(differences, prefix, "hooks.create", remoteField.hooks?.create?.expr ?? "", snapshotField.hooks?.create?.expr ?? "");
|
|
8329
|
+
addFieldDifference(differences, prefix, "hooks.update", remoteField.hooks?.update?.expr ?? "", snapshotField.hooks?.update?.expr ?? "");
|
|
8330
|
+
}
|
|
8331
|
+
function addValidationDifferences(differences, prefix, remoteField, snapshotField) {
|
|
8332
|
+
const remoteValidate = remoteField.validate ?? [];
|
|
8333
|
+
const snapshotValidate = snapshotField.validate ?? [];
|
|
8334
|
+
if (remoteValidate.length !== snapshotValidate.length) differences.push(`${fieldDifferenceKey(prefix, "validate")} count: remote=${remoteValidate.length}, expected=${snapshotValidate.length}`);
|
|
8335
|
+
const commonLength = Math.min(remoteValidate.length, snapshotValidate.length);
|
|
8336
|
+
for (let index = 0; index < commonLength; index++) {
|
|
8337
|
+
const remoteValidation = assertDefined(remoteValidate[index], `remoteValidate missing index ${index}`);
|
|
8338
|
+
const snapshotValidation = assertDefined(snapshotValidate[index], `snapshotValidate missing index ${index}`);
|
|
8339
|
+
addFieldDifference(differences, prefix, `validate[${index}].script`, remoteValidation.script?.expr ?? "", snapshotValidation.script?.expr ?? "");
|
|
8340
|
+
addFieldDifference(differences, prefix, `validate[${index}].errorMessage`, remoteValidation.errorMessage, snapshotValidation.errorMessage);
|
|
8341
|
+
}
|
|
8342
|
+
}
|
|
8343
|
+
function addSerialDifferences(differences, prefix, remoteField, snapshotField) {
|
|
8344
|
+
addFieldDifference(differences, prefix, "serial.start", remoteField.serial?.start, snapshotField.serial?.start);
|
|
8345
|
+
addFieldDifference(differences, prefix, "serial.maxValue", remoteField.serial?.maxValue, snapshotField.serial?.maxValue);
|
|
8346
|
+
addFieldDifference(differences, prefix, "serial.format", remoteField.serial?.format ?? "", snapshotField.serial?.format ?? "");
|
|
8347
|
+
}
|
|
8348
|
+
function addNestedFieldDifferences(differences, prefix, remoteField, snapshotField) {
|
|
8349
|
+
const remoteFields = remoteField.fields ?? {};
|
|
8350
|
+
const snapshotFields = snapshotField.fields ?? {};
|
|
8351
|
+
const remoteFieldNames = Object.keys(remoteFields);
|
|
8352
|
+
const snapshotFieldNames = Object.keys(snapshotFields);
|
|
8353
|
+
if (remoteFieldNames.length !== snapshotFieldNames.length) differences.push(`${fieldDifferenceKey(prefix, "fields")} count: remote=${remoteFieldNames.length}, expected=${snapshotFieldNames.length}`);
|
|
8354
|
+
for (const fieldName of remoteFieldNames) {
|
|
8355
|
+
const remoteNestedField = remoteFields[fieldName];
|
|
8356
|
+
const snapshotNestedField = snapshotFields[fieldName];
|
|
8357
|
+
const nestedPrefix = fieldDifferenceKey(prefix, `fields.${fieldName}`);
|
|
8358
|
+
if (!snapshotNestedField) {
|
|
8359
|
+
differences.push(`${nestedPrefix}: exists in remote but not snapshot`);
|
|
8360
|
+
continue;
|
|
8361
|
+
}
|
|
8362
|
+
addFieldDifferences(differences, nestedPrefix, assertDefined(remoteNestedField, `remote field "${fieldName}" missing`), snapshotNestedField);
|
|
8363
|
+
}
|
|
8364
|
+
for (const fieldName of snapshotFieldNames) {
|
|
8365
|
+
if (remoteFields[fieldName]) continue;
|
|
8366
|
+
differences.push(`${fieldDifferenceKey(prefix, `fields.${fieldName}`)}: exists in snapshot but not remote`);
|
|
8367
|
+
}
|
|
8368
|
+
}
|
|
8369
|
+
function addFieldDifferences(differences, prefix, remoteField, snapshotField) {
|
|
8370
|
+
addFieldDifference(differences, prefix, "type", remoteField.type, snapshotField.type);
|
|
8371
|
+
addFieldDifference(differences, prefix, "required", remoteField.required, snapshotField.required);
|
|
8372
|
+
for (const key of [
|
|
8373
|
+
"array",
|
|
8374
|
+
"index",
|
|
8375
|
+
"unique",
|
|
8376
|
+
"foreignKey",
|
|
8377
|
+
"vector"
|
|
8378
|
+
]) addBooleanFieldDifference(differences, prefix, key, remoteField, snapshotField);
|
|
8379
|
+
addFieldDifference(differences, prefix, "foreignKeyType", remoteField.foreignKeyType, snapshotField.foreignKeyType);
|
|
8380
|
+
addFieldDifference(differences, prefix, "foreignKeyField", remoteField.foreignKeyField, snapshotField.foreignKeyField);
|
|
8381
|
+
addFieldDifference(differences, prefix, "description", remoteField.description ?? "", snapshotField.description ?? "");
|
|
8382
|
+
addAllowedValuesDifferences(differences, prefix, remoteField, snapshotField);
|
|
8383
|
+
addHooksDifferences(differences, prefix, remoteField, snapshotField);
|
|
8384
|
+
addValidationDifferences(differences, prefix, remoteField, snapshotField);
|
|
8385
|
+
addSerialDifferences(differences, prefix, remoteField, snapshotField);
|
|
8386
|
+
addFieldDifference(differences, prefix, "scale", remoteField.scale, snapshotField.scale);
|
|
8387
|
+
addNestedFieldDifferences(differences, prefix, remoteField, snapshotField);
|
|
8388
|
+
}
|
|
7983
8389
|
/**
|
|
7984
8390
|
* Compare a single field between remote and snapshot
|
|
7985
8391
|
* @param {string} typeName - Name of the type
|
|
@@ -7990,37 +8396,7 @@ function createSnapshotFromRemoteTypes(remoteTypes, namespace) {
|
|
|
7990
8396
|
*/
|
|
7991
8397
|
function compareFields(typeName, fieldName, remoteField, snapshotField) {
|
|
7992
8398
|
const differences = [];
|
|
7993
|
-
|
|
7994
|
-
if (remoteField.required !== snapshotField.required) differences.push(`required: remote=${remoteField.required}, expected=${snapshotField.required}`);
|
|
7995
|
-
const remoteArray = remoteField.array ?? false;
|
|
7996
|
-
const snapshotArray = snapshotField.array ?? false;
|
|
7997
|
-
if (remoteArray !== snapshotArray) differences.push(`array: remote=${remoteArray}, expected=${snapshotArray}`);
|
|
7998
|
-
const remoteUnique = remoteField.unique ?? false;
|
|
7999
|
-
const snapshotUnique = snapshotField.unique ?? false;
|
|
8000
|
-
if (remoteUnique !== snapshotUnique) differences.push(`unique: remote=${remoteUnique}, expected=${snapshotUnique}`);
|
|
8001
|
-
const remoteFk = remoteField.foreignKey ?? false;
|
|
8002
|
-
const snapshotFk = snapshotField.foreignKey ?? false;
|
|
8003
|
-
if (remoteFk !== snapshotFk) differences.push(`foreignKey: remote=${remoteFk}, expected=${snapshotFk}`);
|
|
8004
|
-
if (remoteField.foreignKeyType !== snapshotField.foreignKeyType) differences.push(`foreignKeyType: remote=${remoteField.foreignKeyType ?? "none"}, expected=${snapshotField.foreignKeyType ?? "none"}`);
|
|
8005
|
-
const remoteAllowed = remoteField.allowedValues ?? [];
|
|
8006
|
-
const snapshotAllowed = snapshotField.allowedValues ?? [];
|
|
8007
|
-
const remoteAllowedValues = new Set(remoteAllowed.map((v) => v.value));
|
|
8008
|
-
const snapshotAllowedValues = new Set(snapshotAllowed.map((v) => v.value));
|
|
8009
|
-
if (remoteAllowedValues.size !== snapshotAllowedValues.size) differences.push(`allowedValues count: remote=${remoteAllowedValues.size}, expected=${snapshotAllowedValues.size}`);
|
|
8010
|
-
else {
|
|
8011
|
-
for (const v of remoteAllowedValues) if (!snapshotAllowedValues.has(v)) {
|
|
8012
|
-
differences.push(`allowedValues: remote has '${v}' not in snapshot`);
|
|
8013
|
-
break;
|
|
8014
|
-
}
|
|
8015
|
-
for (const v of snapshotAllowedValues) if (!remoteAllowedValues.has(v)) {
|
|
8016
|
-
differences.push(`allowedValues: snapshot has '${v}' not in remote`);
|
|
8017
|
-
break;
|
|
8018
|
-
}
|
|
8019
|
-
}
|
|
8020
|
-
const remoteVector = remoteField.vector ?? false;
|
|
8021
|
-
const snapshotVector = snapshotField.vector ?? false;
|
|
8022
|
-
if (remoteVector !== snapshotVector) differences.push(`vector: remote=${remoteVector}, expected=${snapshotVector}`);
|
|
8023
|
-
if (remoteField.scale !== snapshotField.scale) differences.push(`scale: remote=${remoteField.scale}, expected=${snapshotField.scale}`);
|
|
8399
|
+
addFieldDifferences(differences, "", remoteField, snapshotField);
|
|
8024
8400
|
if (differences.length > 0) return {
|
|
8025
8401
|
typeName,
|
|
8026
8402
|
kind: "field_mismatch",
|
|
@@ -8037,53 +8413,136 @@ const SYSTEM_FIELDS = /* @__PURE__ */ new Set(["id"]);
|
|
|
8037
8413
|
* Compare remote TailorDB types with a local snapshot
|
|
8038
8414
|
* @param {ProtoTailorDBType[]} remoteTypes - Remote types from listParsedTailorDBTypes API
|
|
8039
8415
|
* @param {SchemaSnapshot} snapshot - Local schema snapshot
|
|
8416
|
+
* @param {readonly RemoteGqlPermission[]} remoteGqlPermissions - Remote GQL permissions for the namespace
|
|
8040
8417
|
* @returns {SchemaDrift[]} List of drifts detected
|
|
8041
8418
|
*/
|
|
8042
|
-
function compareRemoteWithSnapshot(remoteTypes, snapshot) {
|
|
8043
|
-
return compareNormalizedRemoteWithSnapshot(createSnapshotFromRemoteTypes(remoteTypes, snapshot.namespace),
|
|
8419
|
+
function compareRemoteWithSnapshot(remoteTypes, snapshot, remoteGqlPermissions = []) {
|
|
8420
|
+
return compareNormalizedRemoteWithSnapshot(createRemoteComparableSnapshot(createSnapshotFromRemoteTypes(remoteTypes, snapshot.namespace, remoteGqlPermissions, snapshot)), createRemoteComparableSnapshot(snapshot));
|
|
8044
8421
|
}
|
|
8045
|
-
function
|
|
8046
|
-
const
|
|
8047
|
-
const
|
|
8048
|
-
|
|
8049
|
-
|
|
8050
|
-
|
|
8051
|
-
|
|
8052
|
-
|
|
8053
|
-
|
|
8054
|
-
|
|
8055
|
-
|
|
8056
|
-
|
|
8057
|
-
|
|
8058
|
-
|
|
8422
|
+
function createRemoteComparableSnapshot(snapshot) {
|
|
8423
|
+
const types = createSnapshotRecord();
|
|
8424
|
+
for (const [typeName, type] of Object.entries(snapshot.types)) {
|
|
8425
|
+
const fields = createSnapshotRecord();
|
|
8426
|
+
for (const [fieldName, field] of Object.entries(type.fields)) {
|
|
8427
|
+
if (SYSTEM_FIELDS.has(fieldName)) continue;
|
|
8428
|
+
fields[fieldName] = field;
|
|
8429
|
+
}
|
|
8430
|
+
types[typeName] = {
|
|
8431
|
+
...type,
|
|
8432
|
+
fields
|
|
8433
|
+
};
|
|
8434
|
+
}
|
|
8435
|
+
return normalizeSchemaSnapshot({
|
|
8436
|
+
...snapshot,
|
|
8437
|
+
types
|
|
8059
8438
|
});
|
|
8060
|
-
|
|
8061
|
-
|
|
8062
|
-
|
|
8063
|
-
|
|
8064
|
-
|
|
8065
|
-
|
|
8066
|
-
|
|
8067
|
-
|
|
8068
|
-
|
|
8069
|
-
|
|
8439
|
+
}
|
|
8440
|
+
function fieldDriftFromChange(change) {
|
|
8441
|
+
return compareFields(change.typeName, change.fieldName, change.before, change.after) ?? {
|
|
8442
|
+
typeName: change.typeName,
|
|
8443
|
+
kind: "field_mismatch",
|
|
8444
|
+
fieldName: change.fieldName,
|
|
8445
|
+
details: `Field '${change.fieldName}' differs between remote and snapshot`
|
|
8446
|
+
};
|
|
8447
|
+
}
|
|
8448
|
+
function schemaDriftFromDiffChange(change) {
|
|
8449
|
+
switch (change.kind) {
|
|
8450
|
+
case "type_added": return {
|
|
8451
|
+
typeName: change.typeName,
|
|
8452
|
+
kind: "type_missing_remote",
|
|
8453
|
+
details: `Type '${change.typeName}' exists in snapshot but not in remote`
|
|
8454
|
+
};
|
|
8455
|
+
case "type_removed": return {
|
|
8456
|
+
typeName: change.typeName,
|
|
8457
|
+
kind: "type_missing_local",
|
|
8458
|
+
details: `Type '${change.typeName}' exists in remote but not in snapshot`
|
|
8459
|
+
};
|
|
8460
|
+
case "type_settings_modified":
|
|
8461
|
+
case "type_modified": return {
|
|
8462
|
+
typeName: change.typeName,
|
|
8463
|
+
kind: "type_settings_mismatch",
|
|
8464
|
+
details: change.reason ?? "Type settings differ between remote and snapshot"
|
|
8465
|
+
};
|
|
8466
|
+
case "field_added": return {
|
|
8467
|
+
typeName: change.typeName,
|
|
8070
8468
|
kind: "field_missing_remote",
|
|
8071
|
-
fieldName,
|
|
8072
|
-
details: `Field '${fieldName}' exists in snapshot but not in remote`
|
|
8073
|
-
}
|
|
8074
|
-
|
|
8075
|
-
typeName,
|
|
8469
|
+
fieldName: change.fieldName,
|
|
8470
|
+
details: `Field '${change.fieldName}' exists in snapshot but not in remote`
|
|
8471
|
+
};
|
|
8472
|
+
case "field_removed": return {
|
|
8473
|
+
typeName: change.typeName,
|
|
8076
8474
|
kind: "field_missing_local",
|
|
8077
|
-
fieldName,
|
|
8078
|
-
details: `Field '${fieldName}' exists in remote but not in snapshot`
|
|
8079
|
-
}
|
|
8080
|
-
|
|
8081
|
-
|
|
8082
|
-
|
|
8083
|
-
|
|
8084
|
-
|
|
8475
|
+
fieldName: change.fieldName,
|
|
8476
|
+
details: `Field '${change.fieldName}' exists in remote but not in snapshot`
|
|
8477
|
+
};
|
|
8478
|
+
case "field_modified": return fieldDriftFromChange(change);
|
|
8479
|
+
case "index_added": return {
|
|
8480
|
+
typeName: change.typeName,
|
|
8481
|
+
kind: "index_missing_remote",
|
|
8482
|
+
indexName: change.indexName,
|
|
8483
|
+
details: `Index '${change.indexName}' exists in snapshot but not in remote`
|
|
8484
|
+
};
|
|
8485
|
+
case "index_removed": return {
|
|
8486
|
+
typeName: change.typeName,
|
|
8487
|
+
kind: "index_missing_local",
|
|
8488
|
+
indexName: change.indexName,
|
|
8489
|
+
details: `Index '${change.indexName}' exists in remote but not in snapshot`
|
|
8490
|
+
};
|
|
8491
|
+
case "index_modified": return {
|
|
8492
|
+
typeName: change.typeName,
|
|
8493
|
+
kind: "index_mismatch",
|
|
8494
|
+
indexName: change.indexName,
|
|
8495
|
+
details: change.reason ?? `Index '${change.indexName}' differs between remote and snapshot`
|
|
8496
|
+
};
|
|
8497
|
+
case "file_added": return {
|
|
8498
|
+
typeName: change.typeName,
|
|
8499
|
+
kind: "file_missing_remote",
|
|
8500
|
+
fileName: change.fieldName,
|
|
8501
|
+
details: `File '${change.fieldName}' exists in snapshot but not in remote`
|
|
8502
|
+
};
|
|
8503
|
+
case "file_removed": return {
|
|
8504
|
+
typeName: change.typeName,
|
|
8505
|
+
kind: "file_missing_local",
|
|
8506
|
+
fileName: change.fieldName,
|
|
8507
|
+
details: `File '${change.fieldName}' exists in remote but not in snapshot`
|
|
8508
|
+
};
|
|
8509
|
+
case "file_modified": return {
|
|
8510
|
+
typeName: change.typeName,
|
|
8511
|
+
kind: "file_mismatch",
|
|
8512
|
+
fileName: change.fieldName,
|
|
8513
|
+
details: change.reason ?? `File '${change.fieldName}' differs between remote and snapshot`
|
|
8514
|
+
};
|
|
8515
|
+
case "relationship_added": return {
|
|
8516
|
+
typeName: change.typeName,
|
|
8517
|
+
kind: "relationship_missing_remote",
|
|
8518
|
+
relationshipName: change.relationshipName,
|
|
8519
|
+
relationshipType: change.relationshipType,
|
|
8520
|
+
details: `Relationship '${change.relationshipName}' exists in snapshot but not in remote`
|
|
8521
|
+
};
|
|
8522
|
+
case "relationship_removed": return {
|
|
8523
|
+
typeName: change.typeName,
|
|
8524
|
+
kind: "relationship_missing_local",
|
|
8525
|
+
relationshipName: change.relationshipName,
|
|
8526
|
+
relationshipType: change.relationshipType,
|
|
8527
|
+
details: `Relationship '${change.relationshipName}' exists in remote but not in snapshot`
|
|
8528
|
+
};
|
|
8529
|
+
case "relationship_modified": return {
|
|
8530
|
+
typeName: change.typeName,
|
|
8531
|
+
kind: "relationship_mismatch",
|
|
8532
|
+
relationshipName: change.relationshipName,
|
|
8533
|
+
relationshipType: change.relationshipType,
|
|
8534
|
+
details: change.reason ?? `Relationship '${change.relationshipName}' differs between remote and snapshot`
|
|
8535
|
+
};
|
|
8536
|
+
case "permission_modified": return {
|
|
8537
|
+
typeName: change.typeName,
|
|
8538
|
+
kind: "permission_mismatch",
|
|
8539
|
+
details: change.reason ?? "Permissions differ between remote and snapshot"
|
|
8540
|
+
};
|
|
8541
|
+
default: throw new Error("Unsupported diff change");
|
|
8085
8542
|
}
|
|
8086
|
-
|
|
8543
|
+
}
|
|
8544
|
+
function compareNormalizedRemoteWithSnapshot(remoteSnapshot, snapshot) {
|
|
8545
|
+
return compareNormalizedSnapshots(remoteSnapshot, snapshot).changes.map(schemaDriftFromDiffChange);
|
|
8087
8546
|
}
|
|
8088
8547
|
/**
|
|
8089
8548
|
* Format schema drifts for display
|
|
@@ -8102,7 +8561,12 @@ function formatSchemaDrifts(drifts) {
|
|
|
8102
8561
|
for (const [typeName, typeDrifts] of driftsByType) {
|
|
8103
8562
|
lines.push(` Type '${typeName}':`);
|
|
8104
8563
|
for (const drift of typeDrifts) if (drift.fieldName) lines.push(` - Field '${drift.fieldName}': ${drift.details}`);
|
|
8105
|
-
else lines.push(` - ${drift.details}`);
|
|
8564
|
+
else if (drift.indexName) lines.push(` - Index '${drift.indexName}': ${drift.details}`);
|
|
8565
|
+
else if (drift.fileName) lines.push(` - File '${drift.fileName}': ${drift.details}`);
|
|
8566
|
+
else if (drift.relationshipName) {
|
|
8567
|
+
const relationshipType = drift.relationshipType ? ` (${drift.relationshipType})` : "";
|
|
8568
|
+
lines.push(` - Relationship${relationshipType} '${drift.relationshipName}': ${drift.details}`);
|
|
8569
|
+
} else lines.push(` - ${drift.details}`);
|
|
8106
8570
|
}
|
|
8107
8571
|
return lines.join("\n");
|
|
8108
8572
|
}
|
|
@@ -9191,6 +9655,77 @@ async function fetchRemoteTypes(client, workspaceId, namespace) {
|
|
|
9191
9655
|
}
|
|
9192
9656
|
});
|
|
9193
9657
|
}
|
|
9658
|
+
async function fetchRemoteGqlPermissions(client, workspaceId, namespace) {
|
|
9659
|
+
return fetchAll(async (pageToken, maxPageSize) => {
|
|
9660
|
+
try {
|
|
9661
|
+
const { permissions, nextPageToken } = await client.listTailorDBGQLPermissions({
|
|
9662
|
+
workspaceId,
|
|
9663
|
+
namespaceName: namespace,
|
|
9664
|
+
pageToken,
|
|
9665
|
+
pageSize: maxPageSize
|
|
9666
|
+
});
|
|
9667
|
+
return [permissions, nextPageToken];
|
|
9668
|
+
} catch (error) {
|
|
9669
|
+
if (error instanceof ConnectError && error.code === Code.NotFound) return [[], ""];
|
|
9670
|
+
throw error;
|
|
9671
|
+
}
|
|
9672
|
+
});
|
|
9673
|
+
}
|
|
9674
|
+
function definedWhenNotEmpty(value) {
|
|
9675
|
+
return Object.keys(value).length > 0 ? value : void 0;
|
|
9676
|
+
}
|
|
9677
|
+
function namespaceConfig(config, tailorDBInputs, namespace) {
|
|
9678
|
+
return tailorDBInputs.find((input) => input.namespace === namespace)?.config ?? config.db?.[namespace];
|
|
9679
|
+
}
|
|
9680
|
+
function namespaceGqlOperations(config, tailorDBInputs, namespace) {
|
|
9681
|
+
return namespaceConfig(config, tailorDBInputs, namespace)?.gqlOperations;
|
|
9682
|
+
}
|
|
9683
|
+
const GQL_OPERATION_KEYS = [
|
|
9684
|
+
"create",
|
|
9685
|
+
"update",
|
|
9686
|
+
"delete",
|
|
9687
|
+
"read"
|
|
9688
|
+
];
|
|
9689
|
+
function configuredDisabledGqlOperations(operations) {
|
|
9690
|
+
if (!operations) return void 0;
|
|
9691
|
+
if (operations === "query") return {
|
|
9692
|
+
create: false,
|
|
9693
|
+
update: false,
|
|
9694
|
+
delete: false
|
|
9695
|
+
};
|
|
9696
|
+
const disabled = {};
|
|
9697
|
+
for (const key of GQL_OPERATION_KEYS) if (operations[key] === false) disabled[key] = false;
|
|
9698
|
+
return definedWhenNotEmpty(disabled);
|
|
9699
|
+
}
|
|
9700
|
+
function appliedConfiguredDisabledGqlOperations(remoteDisabled, configuredDisabled) {
|
|
9701
|
+
if (!remoteDisabled || !configuredDisabled) return void 0;
|
|
9702
|
+
const disabled = {};
|
|
9703
|
+
for (const key of GQL_OPERATION_KEYS) if (configuredDisabled[key] === false && remoteDisabled[key]) disabled[key] = false;
|
|
9704
|
+
return definedWhenNotEmpty(disabled);
|
|
9705
|
+
}
|
|
9706
|
+
function deployComparableSnapshot(snapshot, remoteTypes, gqlOperations) {
|
|
9707
|
+
const remoteTypesByName = new Map(remoteTypes.map((type) => [type.name, type]));
|
|
9708
|
+
const configuredDisabled = configuredDisabledGqlOperations(gqlOperations);
|
|
9709
|
+
const types = {};
|
|
9710
|
+
for (const [typeName, type] of Object.entries(snapshot.types)) {
|
|
9711
|
+
const settings = { ...type.settings };
|
|
9712
|
+
const remoteSettings = remoteTypesByName.get(typeName)?.schema?.settings;
|
|
9713
|
+
if (type.settings?.publishEvents === void 0 && remoteSettings?.publishRecordEvents) settings.publishEvents = true;
|
|
9714
|
+
if (type.settings?.gqlOperations === void 0) {
|
|
9715
|
+
const disabled = appliedConfiguredDisabledGqlOperations(remoteSettings?.disableGqlOperations, configuredDisabled);
|
|
9716
|
+
if (disabled) settings.gqlOperations = disabled;
|
|
9717
|
+
}
|
|
9718
|
+
const comparableType = { ...type };
|
|
9719
|
+
const comparableSettings = definedWhenNotEmpty(settings);
|
|
9720
|
+
if (comparableSettings) comparableType.settings = comparableSettings;
|
|
9721
|
+
else delete comparableType.settings;
|
|
9722
|
+
types[typeName] = comparableType;
|
|
9723
|
+
}
|
|
9724
|
+
return {
|
|
9725
|
+
...snapshot,
|
|
9726
|
+
types
|
|
9727
|
+
};
|
|
9728
|
+
}
|
|
9194
9729
|
/**
|
|
9195
9730
|
* Get the current migration number from remote metadata
|
|
9196
9731
|
* @param {OperatorClient} client - Operator client instance
|
|
@@ -9215,9 +9750,11 @@ async function getRemoteMigrationNumber(client, workspaceId, namespace) {
|
|
|
9215
9750
|
* @param {OperatorClient} client - Operator client instance
|
|
9216
9751
|
* @param {string} workspaceId - Workspace ID
|
|
9217
9752
|
* @param {NamespaceWithMigrations[]} namespacesWithMigrations - Namespaces with migration config
|
|
9753
|
+
* @param {LoadedConfig} config - Loaded application config
|
|
9754
|
+
* @param {ReadonlyArray<TailorDBDeployInput>} tailorDBInputs - Deploy inputs for namespace defaults
|
|
9218
9755
|
* @returns {Promise<RemoteSchemaVerificationResult[]>} Verification results per namespace
|
|
9219
9756
|
*/
|
|
9220
|
-
async function verifyRemoteSchema(client, workspaceId, namespacesWithMigrations) {
|
|
9757
|
+
async function verifyRemoteSchema(client, workspaceId, namespacesWithMigrations, config, tailorDBInputs) {
|
|
9221
9758
|
const results = [];
|
|
9222
9759
|
for (const { namespace, migrationsDir } of namespacesWithMigrations) {
|
|
9223
9760
|
const remoteMigrationNumber = await getRemoteMigrationNumber(client, workspaceId, namespace);
|
|
@@ -9240,7 +9777,8 @@ async function verifyRemoteSchema(client, workspaceId, namespacesWithMigrations)
|
|
|
9240
9777
|
});
|
|
9241
9778
|
continue;
|
|
9242
9779
|
}
|
|
9243
|
-
const
|
|
9780
|
+
const [remoteTypes, remoteGqlPermissions] = await Promise.all([fetchRemoteTypes(client, workspaceId, namespace), fetchRemoteGqlPermissions(client, workspaceId, namespace)]);
|
|
9781
|
+
const drifts = compareRemoteWithSnapshot(remoteTypes, deployComparableSnapshot(expectedSnapshot, remoteTypes, namespaceGqlOperations(config, tailorDBInputs, namespace)), remoteGqlPermissions);
|
|
9244
9782
|
results.push({
|
|
9245
9783
|
namespace,
|
|
9246
9784
|
remoteMigrationNumber,
|
|
@@ -9274,9 +9812,10 @@ function formatRemoteVerificationResults(results) {
|
|
|
9274
9812
|
* @param {ReadonlyMap<string, Record<string, TailorDBSnapshotType>>} typesByNamespace - Types by namespace
|
|
9275
9813
|
* @param {LoadedConfig} config - Loaded application config (includes path)
|
|
9276
9814
|
* @param {boolean} noSchemaCheck - Whether to skip schema diff check
|
|
9815
|
+
* @param {ReadonlyArray<TailorDBDeployInput>} tailorDBInputs - Deploy inputs for namespace defaults
|
|
9277
9816
|
* @returns {Promise<ValidateAndDetectResult>} Pending migrations and namespaces that have migration directories configured
|
|
9278
9817
|
*/
|
|
9279
|
-
async function validateAndDetectMigrations(client, workspaceId, typesByNamespace, config, noSchemaCheck) {
|
|
9818
|
+
async function validateAndDetectMigrations(client, workspaceId, typesByNamespace, config, noSchemaCheck, tailorDBInputs) {
|
|
9280
9819
|
const namespacesWithMigrations = getNamespacesWithMigrations(config, path.dirname(config.path));
|
|
9281
9820
|
let pendingMigrations = [];
|
|
9282
9821
|
if (namespacesWithMigrations.length > 0) {
|
|
@@ -9291,7 +9830,7 @@ async function validateAndDetectMigrations(client, workspaceId, typesByNamespace
|
|
|
9291
9830
|
logger.info("Or use '--no-schema-check' to skip this check.");
|
|
9292
9831
|
throw new Error("Schema migration check failed");
|
|
9293
9832
|
}
|
|
9294
|
-
const remoteVerificationResults = await verifyRemoteSchema(client, workspaceId, namespacesWithMigrations);
|
|
9833
|
+
const remoteVerificationResults = await verifyRemoteSchema(client, workspaceId, namespacesWithMigrations, config, tailorDBInputs);
|
|
9295
9834
|
if (remoteVerificationResults.some((r) => r.hasDrift)) {
|
|
9296
9835
|
logger.error("Remote schema drift detected:");
|
|
9297
9836
|
logger.log(formatRemoteVerificationResults(remoteVerificationResults));
|
|
@@ -9301,6 +9840,12 @@ async function validateAndDetectMigrations(client, workspaceId, typesByNamespace
|
|
|
9301
9840
|
logger.info(" - Manual schema changes were made directly", { mode: "plain" });
|
|
9302
9841
|
logger.info(" - Migration history is out of sync", { mode: "plain" });
|
|
9303
9842
|
logger.newline();
|
|
9843
|
+
logger.info("To resolve:");
|
|
9844
|
+
logger.info(" - Run 'tailor-sdk tailordb migration status' to compare local vs remote.", { mode: "plain" });
|
|
9845
|
+
logger.info(" - If remote is correct, update local types and run 'migration generate'.", { mode: "plain" });
|
|
9846
|
+
logger.info(" - If local migration history is correct, run 'migration sync <N>' to overwrite remote.", { mode: "plain" });
|
|
9847
|
+
logger.info(" - If only bookkeeping is stale, run 'migration set <N>'.", { mode: "plain" });
|
|
9848
|
+
logger.newline();
|
|
9304
9849
|
logger.info("Use '--no-schema-check' to skip this check (not recommended).");
|
|
9305
9850
|
throw new Error("Remote schema verification failed");
|
|
9306
9851
|
}
|
|
@@ -9375,7 +9920,7 @@ async function applyTailorDB(client, result, phase = "create-update") {
|
|
|
9375
9920
|
if (phase === "create-update") {
|
|
9376
9921
|
const typesByNamespace = /* @__PURE__ */ new Map();
|
|
9377
9922
|
for (const tailordb of migrationContext.tailorDBInputs) typesByNamespace.set(tailordb.namespace, tailordb.types);
|
|
9378
|
-
const { pendingMigrations, namespacesWithMigrations } = await validateAndDetectMigrations(client, migrationContext.workspaceId, typesByNamespace, migrationContext.config, migrationContext.noSchemaCheck);
|
|
9923
|
+
const { pendingMigrations, namespacesWithMigrations } = await validateAndDetectMigrations(client, migrationContext.workspaceId, typesByNamespace, migrationContext.config, migrationContext.noSchemaCheck, migrationContext.tailorDBInputs);
|
|
9379
9924
|
if (pendingMigrations.length > 0) {
|
|
9380
9925
|
processedTypes.reset();
|
|
9381
9926
|
deletedResources.reset();
|
|
@@ -10280,7 +10825,7 @@ function filterJobFunctionVersions(allVersions, usedJobNames) {
|
|
|
10280
10825
|
* @param client - Operator client instance
|
|
10281
10826
|
* @param changeSet - Workflow change set
|
|
10282
10827
|
* @param appName - Application name
|
|
10283
|
-
* @param appId
|
|
10828
|
+
* @param appId - Application ID used for job function metadata when available
|
|
10284
10829
|
* @param unchangedWorkflowJobNames - Job function names used by unchanged workflows
|
|
10285
10830
|
* @returns Map of job function names to versions
|
|
10286
10831
|
*/
|
|
@@ -10366,7 +10911,7 @@ function buildWorkflowValidationShape(workspaceId, workflow) {
|
|
|
10366
10911
|
* @param client - Operator client instance
|
|
10367
10912
|
* @param workspaceId - Workspace ID
|
|
10368
10913
|
* @param appName - Application name
|
|
10369
|
-
* @param appId
|
|
10914
|
+
* @param appId - Application ID used for workflow metadata when available
|
|
10370
10915
|
* @param workflows - Parsed workflows
|
|
10371
10916
|
* @param mainJobDeps - Main job dependencies by workflow
|
|
10372
10917
|
* @param unchangedJobFunctions - Job functions already proven unchanged by function registry plan
|
|
@@ -16160,7 +16705,7 @@ async function generate(options) {
|
|
|
16160
16705
|
if (options.init) await handleInitOption(namespacesWithMigrations, options.yes);
|
|
16161
16706
|
let pluginManager;
|
|
16162
16707
|
if (plugins.length > 0) pluginManager = new PluginManager(plugins);
|
|
16163
|
-
const { defineApplication } = await import("./application-
|
|
16708
|
+
const { defineApplication } = await import("./application-BXdCl6bp.mjs");
|
|
16164
16709
|
const application = defineApplication({
|
|
16165
16710
|
config,
|
|
16166
16711
|
pluginManager
|
|
@@ -18425,4 +18970,4 @@ function isDeno() {
|
|
|
18425
18970
|
|
|
18426
18971
|
//#endregion
|
|
18427
18972
|
export { getOrganization as $, generateAllTypeManifestsFromSnapshot as $t, listCommand$3 as A, apiCommand as An, getExecutorWaitFailureMessage as At, show as B, paginationArgs as Bn, listWorkflowExecutions as Bt, listCommand$2 as C, getNamespacesWithMigrations as Cn, listWebhookExecutors as Ct, waitWorkflowExecution as D, PluginManager as Dn, listCommand$9 as Dt, waitCommand as E, sdkNameLabelKey as En, triggerExecutor as Et, generateCommand as F, configArg as Fn, startWorkflow as Ft, removeCommand$1 as G, deploy as Gt, extractOwnedNamespaces as H, workspaceArgs as Hn, formatKeyValueTable as Ht, generateMigrationScript as I, confirmationArgs as In, getCommand$5 as It, organizationTree as J, bundleMigrationScript as Jt, updateCommand$1 as K, executeScript as Kt, writeDbTypesFile as L, deploymentArgs as Ln, getWorkflow as Lt, truncate as M, assertWritable as Mn, listExecutorJobs as Mt, truncateCommand as N, defineAppCommand as Nn, watchExecutorJob as Nt, resumeCommand as O, generateUserTypes as On, listExecutors as Ot, generate as P, commonArgs as Pn, startCommand as Pt, getCommand$1 as Q, compareSnapshotWithRemote as Qt, getConfiguredEditorCommand as R, isVerbose as Rn, executionsCommand as Rt, listApps as S, hasChanges as Sn, getFunctionRegistry as St, healthCommand as T, resourceTrn as Tn, triggerCommand as Tt, logBetaWarning as U, getCommand$6 as Ut, showCommand as V, toPageDirection as Vn, functionExecutionStatusToString as Vt, remove as W, getExecutor as Wt, listCommand$4 as X, handleOptionalToRequiredError as Xt, treeCommand as Y, MIGRATION_LABEL_KEY as Yt, listOrganizations as Z, parseMigrationLabelNumber as Zt, getWorkspace as _, reconstructSnapshotFromMigrations as _n, listMachineUsers as _t, updateUser as a, SCHEMA_FILE_NAME as an, getFolder as at, createCommand as b, formatDiffSummary as bn, listFunctionRegistries as bt, listCommand as c, compareSnapshots as cn, createCommand$1 as ct, inviteUser as d, getMigrationDirPath as dn, listOAuth2Clients as dt, protoGqlPermission as en, updateCommand$2 as et, restoreCommand as f, getMigrationFilePath as fn, getCommand$3 as ft, getCommand as g, loadDiff as gn, listCommand$7 as gt, listWorkspaces as h, isValidMigrationNumber as hn, tokenCommand as ht, updateCommand as i, MIGRATE_FILE_NAME as in, getCommand$2 as it, listWorkflows as j, apiCall as jn, jobsCommand as jt, resumeWorkflow as k, prompt as kn, getExecutorJob as kt, listUsers as l, createSnapshotFromLocalTypes as ln, createFolder as lt, listCommand$1 as m, getNextMigrationNumber as mn, getMachineUserToken as mt, query as n, DIFF_FILE_NAME as nn, listCommand$5 as nt, removeCommand as o, assertValidMigrationFiles as on, deleteCommand$1 as ot, restoreWorkspace as p, getMigrationFiles as pn, getOAuth2Client as pt, updateOrganization as q, waitForExecution as qt, queryCommand as r, INITIAL_SCHEMA_NUMBER as rn, listFolders as rt, removeUser as s, compareLocalTypesWithSnapshot as sn, deleteFolder as st, isNativeTypeScriptRuntime as t, DB_TYPES_FILE_NAME as tn, updateFolder as tt, inviteCommand as u, getLatestMigrationNumber as un, listCommand$6 as ut, deleteCommand as v, formatMigrationNumber as vn, generate$1 as vt, getAppHealth as w, ensureConfigId as wn, webhookCommand as wt, createWorkspace as x, formatMigrationDiff as xn, getCommand$4 as xt, deleteWorkspace as y, parseMigrationNumberArg as yn, listCommand$8 as yt, openInConfiguredEditor as z, pagedLogArgs as zn, getWorkflowExecution as zt };
|
|
18428
|
-
//# sourceMappingURL=runtime-
|
|
18973
|
+
//# sourceMappingURL=runtime-B8O6JFS2.mjs.map
|