@ductape/cli 0.2.23 → 0.3.1
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 +7 -0
- package/README.md +7 -0
- package/dist/commands/db-migrate.js +47 -8
- package/dist/commands/migrate-codebase.d.ts +190 -0
- package/dist/commands/migrate-codebase.js +776 -0
- package/dist/commands/migration-artifact.d.ts +28 -0
- package/dist/commands/migration-artifact.js +101 -0
- package/dist/commands/migration-assurance.d.ts +10 -0
- package/dist/commands/migration-assurance.js +120 -0
- package/dist/commands/migration-capabilities.d.ts +53 -0
- package/dist/commands/migration-capabilities.js +66 -0
- package/dist/commands/migration-database.d.ts +26 -0
- package/dist/commands/migration-database.js +162 -0
- package/dist/commands/migration-e2e.d.ts +68 -0
- package/dist/commands/migration-e2e.js +189 -0
- package/dist/commands/migration-environments.d.ts +20 -0
- package/dist/commands/migration-environments.js +58 -0
- package/dist/commands/migration-generated.d.ts +68 -0
- package/dist/commands/migration-generated.js +217 -0
- package/dist/commands/migration-portfolio.d.ts +88 -0
- package/dist/commands/migration-portfolio.js +223 -0
- package/dist/commands/migration-products.d.ts +23 -0
- package/dist/commands/migration-products.js +88 -0
- package/dist/commands/migration-review.d.ts +248 -0
- package/dist/commands/migration-review.js +624 -0
- package/dist/commands/migration-secrets.d.ts +24 -0
- package/dist/commands/migration-secrets.js +93 -0
- package/dist/commands/migration-slice.d.ts +132 -0
- package/dist/commands/migration-slice.js +266 -0
- package/dist/commands/migration-verification.d.ts +12 -0
- package/dist/commands/migration-verification.js +194 -0
- package/dist/commands/products.d.ts +1 -0
- package/dist/commands/products.js +33 -1
- package/dist/commands/secrets.d.ts +8 -0
- package/dist/commands/secrets.js +44 -0
- package/dist/index.js +376 -4
- package/dist/lib/migration-artifact.d.ts +19 -0
- package/dist/lib/migration-artifact.js +114 -0
- package/package.json +8 -1
- package/API_PARITY.md +0 -58
- package/DB_MIGRATE_PLAN.md +0 -223
- package/ductape.example.ts +0 -18
- package/src/commands/apply.ts +0 -153
- package/src/commands/apps-import.ts +0 -43
- package/src/commands/apps.ts +0 -92
- package/src/commands/cloud.ts +0 -68
- package/src/commands/completion.ts +0 -119
- package/src/commands/db-migrate.ts +0 -209
- package/src/commands/db-schema.ts +0 -429
- package/src/commands/db.ts +0 -63
- package/src/commands/generate.ts +0 -74
- package/src/commands/graph.ts +0 -51
- package/src/commands/init.ts +0 -39
- package/src/commands/install.ts +0 -76
- package/src/commands/link.ts +0 -84
- package/src/commands/login.ts +0 -145
- package/src/commands/logout.ts +0 -7
- package/src/commands/platform.ts +0 -123
- package/src/commands/products.ts +0 -228
- package/src/commands/profiles.ts +0 -23
- package/src/commands/resources.ts +0 -213
- package/src/commands/secrets.ts +0 -46
- package/src/commands/unlink.ts +0 -15
- package/src/commands/whoami.ts +0 -27
- package/src/commands/workspaces.ts +0 -98
- package/src/index.ts +0 -523
- package/src/lib/api-client.ts +0 -88
- package/src/lib/app-import.ts +0 -82
- package/src/lib/apply-loaders.ts +0 -30
- package/src/lib/config.ts +0 -230
- package/src/lib/context-store.ts +0 -77
- package/src/lib/db-methods.ts +0 -56
- package/src/lib/db-types.ts +0 -104
- package/src/lib/encryption.ts +0 -12
- package/src/lib/forms/enums.ts +0 -52
- package/src/lib/forms/index.ts +0 -8
- package/src/lib/forms/prompt.ts +0 -209
- package/src/lib/forms/schemas.ts +0 -288
- package/src/lib/forms/types.ts +0 -27
- package/src/lib/http.ts +0 -76
- package/src/lib/integrations.ts +0 -110
- package/src/lib/interactive-opts.ts +0 -13
- package/src/lib/migration-files.ts +0 -58
- package/src/lib/output.ts +0 -39
- package/src/lib/platform-api.ts +0 -209
- package/src/lib/proxy/context.ts +0 -94
- package/src/lib/proxy/db-proxy.ts +0 -40
- package/src/lib/proxy/graph-proxy.ts +0 -64
- package/src/lib/proxy/sdk-proxy.ts +0 -58
- package/src/lib/read-body.ts +0 -57
- package/src/lib/resources.ts +0 -160
- package/src/lib/schema-loader.ts +0 -152
- package/src/lib/snippets.ts +0 -165
- package/src/lib/templates.ts +0 -157
- package/src/lib/workspace-context.ts +0 -43
- package/src/lib/workspaces.ts +0 -283
- package/test/apply-loaders.test.ts +0 -40
- package/test/encryption.test.ts +0 -20
- package/test/forms.test.ts +0 -25
- package/test/http.test.ts +0 -15
- package/test/platform-api.test.ts +0 -42
- package/test/resources.test.ts +0 -23
- package/test/workspace-resolve.test.ts +0 -29
- package/tsconfig.json +0 -15
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
type JsonSchema = {
|
|
2
|
+
$schema: string;
|
|
3
|
+
title: string;
|
|
4
|
+
type: 'object';
|
|
5
|
+
required: string[];
|
|
6
|
+
properties: Record<string, unknown>;
|
|
7
|
+
additionalProperties: boolean;
|
|
8
|
+
};
|
|
9
|
+
export declare const MIGRATION_ARTIFACT_SCHEMAS: {
|
|
10
|
+
version: number;
|
|
11
|
+
artifacts: JsonSchema[];
|
|
12
|
+
definitions: JsonSchema[];
|
|
13
|
+
};
|
|
14
|
+
export declare function runMigrationArtifactValidate(opts: {
|
|
15
|
+
file: string;
|
|
16
|
+
json?: boolean;
|
|
17
|
+
}): void;
|
|
18
|
+
export declare function runMigrationArtifactRecover(opts: {
|
|
19
|
+
file: string;
|
|
20
|
+
json?: boolean;
|
|
21
|
+
}): void;
|
|
22
|
+
export declare function runMigrationArtifactMigrate(opts: {
|
|
23
|
+
file: string;
|
|
24
|
+
output: string;
|
|
25
|
+
json?: boolean;
|
|
26
|
+
}): void;
|
|
27
|
+
export declare function runMigrationArtifactSchemas(json?: boolean): void;
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { printJson } from '../lib/output.js';
|
|
3
|
+
import { migrateMigrationJson, readMigrationJson, recoverMigrationJson, } from '../lib/migration-artifact.js';
|
|
4
|
+
const objectSchema = (title, required, properties = {}) => ({
|
|
5
|
+
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
6
|
+
title,
|
|
7
|
+
type: 'object',
|
|
8
|
+
required,
|
|
9
|
+
properties,
|
|
10
|
+
additionalProperties: true,
|
|
11
|
+
});
|
|
12
|
+
const artifact = (kind, required) => objectSchema(kind, ['version', 'artifact_kind', ...required], { version: { const: 1 }, artifact_kind: { const: kind } });
|
|
13
|
+
export const MIGRATION_ARTIFACT_SCHEMAS = {
|
|
14
|
+
version: 2,
|
|
15
|
+
artifacts: [
|
|
16
|
+
artifact('ai_review_bootstrap', ['authority', 'source', 'mode', 'destination', 'review_queue']),
|
|
17
|
+
artifact('ai_migration_review_ledger', ['source', 'entries']),
|
|
18
|
+
artifact('ductape_migration_slice', ['tag', 'name', 'ledger', 'files', 'surfaces', 'product_boundary']),
|
|
19
|
+
artifact('ductape_migration_portfolio', ['ledger', 'partitions', 'slices', 'cross_slice_contracts']),
|
|
20
|
+
artifact('ductape_migration_partition_proposal', ['authority', 'ledger', 'limits', 'partitions']),
|
|
21
|
+
artifact('ductape_migration_e2e_baseline', ['source', 'created_at', 'command', 'suite', 'baseline', 'final']),
|
|
22
|
+
artifact('ductape_migration_generated_vendor_evidence', ['analysis', 'generated', 'vendor']),
|
|
23
|
+
artifact('ductape_migration_assurance', [
|
|
24
|
+
'session_flows', 'feature_resilience', 'third_party_calls', 'components',
|
|
25
|
+
'provider_migrations', 'runtime', 'sdk_matrix',
|
|
26
|
+
]),
|
|
27
|
+
artifact('ductape_migration_verification_matrix', ['categories']),
|
|
28
|
+
artifact('ductape_database_baseline', [
|
|
29
|
+
'migration_id', 'database_tag', 'owners', 'providers', 'schema_snapshots',
|
|
30
|
+
'structural_comparison',
|
|
31
|
+
]),
|
|
32
|
+
artifact('ductape_database_data', [
|
|
33
|
+
'migration_id', 'transformations', 'batching', 'checkpoints', 'idempotency',
|
|
34
|
+
'resume_strategy', 'validation', 'reconciliation',
|
|
35
|
+
]),
|
|
36
|
+
artifact('ductape_database_cutover', [
|
|
37
|
+
'migration_id', 'phases', 'expand', 'backfill', 'dual_compatibility', 'contract',
|
|
38
|
+
'deployment_order', 'migration_locking', 'backup', 'restore_test', 'rollback',
|
|
39
|
+
]),
|
|
40
|
+
artifact('ductape_review_ledger_signature', [
|
|
41
|
+
'ledger', 'ledger_sha256', 'algorithm', 'key_id', 'signature', 'signed_at',
|
|
42
|
+
]),
|
|
43
|
+
artifact('ductape_service_product_map', ['analysis', 'mappings', 'assets', 'promotions']),
|
|
44
|
+
artifact('ductape_secret_migration_map', ['analysis', 'classifications']),
|
|
45
|
+
artifact('ductape_sdk_capability_matrix', ['authority', 'unsupported_policy', 'sdks']),
|
|
46
|
+
],
|
|
47
|
+
definitions: [
|
|
48
|
+
objectSchema('migration_e2e_definition', ['command', 'suite_files', 'baseline']),
|
|
49
|
+
objectSchema('migration_e2e_final_evidence', ['command', 'status', 'evidence', 'environment', 'suite_root']),
|
|
50
|
+
objectSchema('migration_slice_definition', [
|
|
51
|
+
'surfaces', 'product_boundary', 'sdk_capabilities', 'interface_contracts',
|
|
52
|
+
'functional_requirements', 'operational_requirements', 'parity_evidence',
|
|
53
|
+
'frontend_parity', 'required_assets', 'tests', 'failure_tests', 'runtime_evidence',
|
|
54
|
+
'smoke_checks', 'cutover_conditions', 'rollback_conditions', 'deployment_cutover',
|
|
55
|
+
]),
|
|
56
|
+
objectSchema('migration_portfolio_definition', ['partitions', 'slices', 'cross_slice_contracts']),
|
|
57
|
+
objectSchema('migration_database_definition', ['baseline', 'data', 'cutover']),
|
|
58
|
+
objectSchema('migration_product_map_definition', ['mappings', 'assets', 'promotions']),
|
|
59
|
+
objectSchema('migration_secret_map_definition', ['classifications']),
|
|
60
|
+
objectSchema('migration_generated_vendor_definition', ['generated', 'vendor']),
|
|
61
|
+
objectSchema('migration_review_record_definition', [
|
|
62
|
+
'purpose', 'dependencies', 'interfaces', 'functional_parity_requirements',
|
|
63
|
+
'operational_parity_requirements', 'findings',
|
|
64
|
+
]),
|
|
65
|
+
objectSchema('migration_assurance_definition', [
|
|
66
|
+
'version', 'artifact_kind', 'session_flows', 'feature_resilience', 'third_party_calls',
|
|
67
|
+
'components', 'provider_migrations', 'runtime', 'sdk_matrix',
|
|
68
|
+
]),
|
|
69
|
+
objectSchema('migration_verification_definition', ['version', 'artifact_kind', 'categories']),
|
|
70
|
+
objectSchema('migration_environment_inventory', ['environments']),
|
|
71
|
+
],
|
|
72
|
+
};
|
|
73
|
+
export function runMigrationArtifactValidate(opts) {
|
|
74
|
+
const value = readMigrationJson(opts.file);
|
|
75
|
+
const schema = MIGRATION_ARTIFACT_SCHEMAS.artifacts.find((candidate) => candidate.properties.artifact_kind?.const === value.artifact_kind);
|
|
76
|
+
if (!schema || value.version !== 1) {
|
|
77
|
+
throw new Error('[ARTIFACT_VERSION_UNSUPPORTED] Unknown artifact kind or version.');
|
|
78
|
+
}
|
|
79
|
+
const missing = schema.required.filter((field) => !(field in value) || value[field] == null);
|
|
80
|
+
if (missing.length) {
|
|
81
|
+
throw new Error(`[ARTIFACT_SCHEMA_INVALID] Missing required fields: ${missing.join(', ')}`);
|
|
82
|
+
}
|
|
83
|
+
printJson({
|
|
84
|
+
file: path.resolve(opts.file),
|
|
85
|
+
valid: true,
|
|
86
|
+
artifact_kind: value.artifact_kind,
|
|
87
|
+
version: value.version,
|
|
88
|
+
schema_catalog_version: MIGRATION_ARTIFACT_SCHEMAS.version,
|
|
89
|
+
}, Boolean(opts.json));
|
|
90
|
+
}
|
|
91
|
+
export function runMigrationArtifactRecover(opts) {
|
|
92
|
+
recoverMigrationJson(opts.file);
|
|
93
|
+
printJson({ file: path.resolve(opts.file), recovered: true }, Boolean(opts.json));
|
|
94
|
+
}
|
|
95
|
+
export function runMigrationArtifactMigrate(opts) {
|
|
96
|
+
migrateMigrationJson(opts.file, opts.output);
|
|
97
|
+
printJson({ source: path.resolve(opts.file), output: path.resolve(opts.output), version: 1 }, Boolean(opts.json));
|
|
98
|
+
}
|
|
99
|
+
export function runMigrationArtifactSchemas(json) {
|
|
100
|
+
printJson(MIGRATION_ARTIFACT_SCHEMAS, Boolean(json));
|
|
101
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { printJson } from '../lib/output.js';
|
|
3
|
+
import { readMigrationJson } from '../lib/migration-artifact.js';
|
|
4
|
+
function read(file) {
|
|
5
|
+
return readMigrationJson(file);
|
|
6
|
+
}
|
|
7
|
+
function evidenceComplete(value) {
|
|
8
|
+
const item = value;
|
|
9
|
+
return Boolean(item?.requirement?.trim() && item.evidence?.trim() &&
|
|
10
|
+
Array.isArray(item.tests) && item.tests.length &&
|
|
11
|
+
item.tests.every((test) => typeof test === 'string' && test.trim()));
|
|
12
|
+
}
|
|
13
|
+
function requireEvidenceArray(blockers, label, values) {
|
|
14
|
+
if (!Array.isArray(values) || values.length === 0) {
|
|
15
|
+
blockers.push(`${label}: at least one evidence record is required`);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
values.forEach((value, index) => {
|
|
19
|
+
if (!evidenceComplete(value))
|
|
20
|
+
blockers.push(`${label}[${index}]: requirement, evidence, and tests are required`);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
const requiredSession = [
|
|
24
|
+
'frontend_to_backend', 'backend_to_event', 'event_to_feature', 'scheduled_work',
|
|
25
|
+
'refresh', 'revocation', 'logout', 'account_switching', 'expiry', 'actor_audit_not_authorization',
|
|
26
|
+
];
|
|
27
|
+
const requiredFeature = [
|
|
28
|
+
'duplicate_delivery', 'signal_handling', 'compensation', 'restart_recovery',
|
|
29
|
+
'layered_retry_budget', 'fallback', 'quota', 'health_check',
|
|
30
|
+
];
|
|
31
|
+
const requiredComponents = {
|
|
32
|
+
graph: ['schema', 'edges', 'indexes', 'traversal', 'reconciliation'],
|
|
33
|
+
vector: ['dimensions', 'metric', 'embedding_version', 'metadata_schema', 'batching', 'quality'],
|
|
34
|
+
storage: ['object_keys', 'metadata', 'acl', 'encryption', 'retention', 'multipart', 'checksums'],
|
|
35
|
+
cache: ['keys', 'ttl', 'invalidation', 'stampede', 'consistency', 'fallback'],
|
|
36
|
+
notifications: ['templates', 'channels', 'providers', 'suppression', 'retry', 'delivery', 'privacy'],
|
|
37
|
+
analytics: ['event_schema', 'identity', 'consent', 'masking', 'delivery', 'dashboard_parity'],
|
|
38
|
+
};
|
|
39
|
+
function requireNamedRequirements(blockers, label, values, required) {
|
|
40
|
+
requireEvidenceArray(blockers, label, values);
|
|
41
|
+
const names = new Set(Array.isArray(values) ? values.map((value) => value?.requirement) : []);
|
|
42
|
+
for (const requirement of required) {
|
|
43
|
+
if (!names.has(requirement))
|
|
44
|
+
blockers.push(`${label}: missing requirement ${requirement}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export function validateMigrationAssurance(file) {
|
|
48
|
+
const resolved = path.resolve(file);
|
|
49
|
+
const value = read(resolved);
|
|
50
|
+
if (value.artifact_kind !== 'ductape_migration_assurance' || value.version !== 1) {
|
|
51
|
+
throw new Error('Invalid migration assurance artifact version.');
|
|
52
|
+
}
|
|
53
|
+
const blockers = [];
|
|
54
|
+
requireNamedRequirements(blockers, 'session_flows', value.session_flows, requiredSession);
|
|
55
|
+
requireNamedRequirements(blockers, 'feature_resilience', value.feature_resilience, requiredFeature);
|
|
56
|
+
requireEvidenceArray(blockers, 'third_party_calls', value.third_party_calls);
|
|
57
|
+
for (const [index, call] of (value.third_party_calls ?? []).entries()) {
|
|
58
|
+
for (const field of [
|
|
59
|
+
'call_path', 'client', 'base_url', 'auth', 'headers', 'timeout', 'retries',
|
|
60
|
+
'pagination', 'rate_limits', 'error_normalization', 'webhook_replay',
|
|
61
|
+
]) {
|
|
62
|
+
if (!String(call?.[field] ?? '').trim())
|
|
63
|
+
blockers.push(`third_party_calls[${index}].${field}: required`);
|
|
64
|
+
}
|
|
65
|
+
if (!Array.isArray(call.wrapper_chain) || !call.wrapper_chain.length) {
|
|
66
|
+
blockers.push(`third_party_calls[${index}].wrapper_chain: at least one inspected call site is required`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
for (const [component, requirements] of Object.entries(requiredComponents)) {
|
|
70
|
+
requireNamedRequirements(blockers, `components.${component}`, value.components?.[component], requirements);
|
|
71
|
+
}
|
|
72
|
+
requireEvidenceArray(blockers, 'provider_migrations', value.provider_migrations);
|
|
73
|
+
for (const language of ['typescript', 'go', 'java', 'dotnet']) {
|
|
74
|
+
requireEvidenceArray(blockers, `runtime.procedures.${language}`, value.runtime?.procedures?.[language]);
|
|
75
|
+
}
|
|
76
|
+
for (const kind of ['request', 'query', 'event', 'feature', 'external_effect']) {
|
|
77
|
+
requireEvidenceArray(blockers, `runtime.correlations.${kind}`, value.runtime?.correlations?.[kind]);
|
|
78
|
+
}
|
|
79
|
+
for (const concern of [
|
|
80
|
+
'trace_log_compatibility', 'load_concurrency', 'lifecycle_recovery',
|
|
81
|
+
'redaction', 'monitoring_alerts',
|
|
82
|
+
]) {
|
|
83
|
+
requireEvidenceArray(blockers, `runtime.${concern}`, value.runtime?.[concern]);
|
|
84
|
+
}
|
|
85
|
+
const matrices = new Map((value.sdk_matrix ?? []).map((entry) => [entry.language, entry]));
|
|
86
|
+
for (const language of ['typescript', 'go', 'java', 'dotnet']) {
|
|
87
|
+
const matrix = matrices.get(language);
|
|
88
|
+
if (!matrix) {
|
|
89
|
+
blockers.push(`sdk_matrix: missing ${language}`);
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (!matrix.package?.trim() || !matrix.version?.trim() || !matrix.unsupported_policy?.trim()) {
|
|
93
|
+
blockers.push(`sdk_matrix.${language}: package, version, and unsupported_policy are required`);
|
|
94
|
+
}
|
|
95
|
+
for (const field of ['lifecycle_requirements', 'error_mapping', 'examples', 'anti_patterns']) {
|
|
96
|
+
if (!Array.isArray(matrix[field]) || !matrix[field].length) {
|
|
97
|
+
blockers.push(`sdk_matrix.${language}.${field}: at least one entry is required`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (!Array.isArray(matrix.capabilities) || !matrix.capabilities.length) {
|
|
101
|
+
blockers.push(`sdk_matrix.${language}.capabilities: at least one capability is required`);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
matrix.capabilities.forEach((capability, index) => {
|
|
105
|
+
if (!capability.name?.trim() || !capability.installed_evidence?.trim() ||
|
|
106
|
+
!Array.isArray(capability.parity_tests) || !capability.parity_tests.length) {
|
|
107
|
+
blockers.push(`sdk_matrix.${language}.capabilities[${index}]: installed evidence and parity tests are required`);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return { file: resolved, ready: blockers.length === 0, blockers };
|
|
113
|
+
}
|
|
114
|
+
export function runMigrationAssuranceValidate(opts) {
|
|
115
|
+
const result = validateMigrationAssurance(opts.file);
|
|
116
|
+
printJson(result, Boolean(opts.json));
|
|
117
|
+
if (opts.strict && !result.ready) {
|
|
118
|
+
throw new Error('Migration assurance is not ready: resolve every reported blocker.');
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export type SupportedLanguage = 'typescript' | 'go' | 'java' | 'dotnet';
|
|
2
|
+
export declare const SDK_CAPABILITY_MATRIX: {
|
|
3
|
+
readonly version: 1;
|
|
4
|
+
readonly artifact_kind: "ductape_sdk_capability_matrix";
|
|
5
|
+
readonly authority: "source_verified_catalog";
|
|
6
|
+
readonly generated_from: "repository source; update and test this catalog with every supported SDK release";
|
|
7
|
+
readonly unsupported_policy: {
|
|
8
|
+
readonly rule: "Never infer cross-language parity or synthesize a missing API.";
|
|
9
|
+
readonly action: "Inspect the installed package exports, record the capability as unsupported, and stop for an approved fallback.";
|
|
10
|
+
};
|
|
11
|
+
readonly sdks: readonly [{
|
|
12
|
+
readonly language: "typescript";
|
|
13
|
+
readonly package: "@ductape/sdk";
|
|
14
|
+
readonly version: "0.1.101";
|
|
15
|
+
readonly status: "supported";
|
|
16
|
+
readonly source_version: "sdk/ts/package.json";
|
|
17
|
+
readonly public_surface_evidence: "sdk/ts/src/index.ts";
|
|
18
|
+
readonly capabilities: readonly ["actions", "agents", "cache", "cloud", "databases", "events", "features", "graphs", "imports", "jobs", "logs", "models", "notifications", "products", "resilience", "secrets", "sessions", "storage", "vectors", "warehouse"];
|
|
19
|
+
readonly lifecycle: readonly ["await asynchronous operations", "close/disconnect owned clients during application shutdown"];
|
|
20
|
+
}, {
|
|
21
|
+
readonly language: "go";
|
|
22
|
+
readonly package: "github.com/ductape/ductape/sdk/go";
|
|
23
|
+
readonly version: "v0.0.1";
|
|
24
|
+
readonly status: "supported";
|
|
25
|
+
readonly source_version: "sdk/go git tag v0.0.1";
|
|
26
|
+
readonly public_surface_evidence: "sdk/go/ductape";
|
|
27
|
+
readonly capabilities: readonly ["actions", "agents", "cache", "cloud", "databases", "events", "features", "graphs", "imports", "jobs", "logs", "models", "notifications", "products", "resilience", "secrets", "sessions", "storage", "vectors", "warehouse"];
|
|
28
|
+
readonly lifecycle: readonly ["propagate context.Context cancellation", "close owned clients during application shutdown"];
|
|
29
|
+
}, {
|
|
30
|
+
readonly language: "java";
|
|
31
|
+
readonly package: "app.ductape:ductape-sdk";
|
|
32
|
+
readonly version: "0.1.9-SNAPSHOT";
|
|
33
|
+
readonly status: "supported-source-snapshot";
|
|
34
|
+
readonly source_version: "sdk/java/build.gradle";
|
|
35
|
+
readonly public_surface_evidence: "sdk/java/src/main/java/app/ductape/sdk/Ductape.java";
|
|
36
|
+
readonly capabilities: readonly ["actions", "agents", "cache", "cloud", "databases", "events", "features", "graphs", "imports", "jobs", "logs", "models", "notifications", "products", "resilience", "secrets", "sessions", "storage", "vectors", "warehouse"];
|
|
37
|
+
readonly lifecycle: readonly ["propagate interruption/cancellation", "close owned clients during application shutdown"];
|
|
38
|
+
}, {
|
|
39
|
+
readonly language: "dotnet";
|
|
40
|
+
readonly package: "Ductape.Sdk";
|
|
41
|
+
readonly version: "0.1.12";
|
|
42
|
+
readonly status: "supported";
|
|
43
|
+
readonly source_version: "sdk/dotnet/src/Ductape.Sdk/Ductape.Sdk.csproj";
|
|
44
|
+
readonly public_surface_evidence: "sdk/dotnet/src/Ductape.Sdk/Ductape.cs";
|
|
45
|
+
readonly capabilities: readonly ["actions", "agents", "cache", "cloud", "databases", "events", "features", "graphs", "imports", "jobs", "logs", "models", "notifications", "products", "resilience", "secrets", "sessions", "storage", "vectors", "warehouse"];
|
|
46
|
+
readonly lifecycle: readonly ["propagate CancellationToken", "dispose owned clients during application shutdown"];
|
|
47
|
+
}];
|
|
48
|
+
};
|
|
49
|
+
export declare function runMigrationCapabilities(opts: {
|
|
50
|
+
language?: SupportedLanguage;
|
|
51
|
+
version?: string;
|
|
52
|
+
json?: boolean;
|
|
53
|
+
}): void;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { printJson } from '../lib/output.js';
|
|
2
|
+
const COMMON_RUNTIME_CAPABILITIES = [
|
|
3
|
+
'actions', 'agents', 'cache', 'cloud', 'databases', 'events', 'features',
|
|
4
|
+
'graphs', 'imports', 'jobs', 'logs', 'models', 'notifications', 'products',
|
|
5
|
+
'resilience', 'secrets', 'sessions', 'storage', 'vectors', 'warehouse',
|
|
6
|
+
];
|
|
7
|
+
export const SDK_CAPABILITY_MATRIX = {
|
|
8
|
+
version: 1,
|
|
9
|
+
artifact_kind: 'ductape_sdk_capability_matrix',
|
|
10
|
+
authority: 'source_verified_catalog',
|
|
11
|
+
generated_from: 'repository source; update and test this catalog with every supported SDK release',
|
|
12
|
+
unsupported_policy: {
|
|
13
|
+
rule: 'Never infer cross-language parity or synthesize a missing API.',
|
|
14
|
+
action: 'Inspect the installed package exports, record the capability as unsupported, and stop for an approved fallback.',
|
|
15
|
+
},
|
|
16
|
+
sdks: [
|
|
17
|
+
{
|
|
18
|
+
language: 'typescript',
|
|
19
|
+
package: '@ductape/sdk',
|
|
20
|
+
version: '0.1.101',
|
|
21
|
+
status: 'supported',
|
|
22
|
+
source_version: 'sdk/ts/package.json',
|
|
23
|
+
public_surface_evidence: 'sdk/ts/src/index.ts',
|
|
24
|
+
capabilities: [...COMMON_RUNTIME_CAPABILITIES],
|
|
25
|
+
lifecycle: ['await asynchronous operations', 'close/disconnect owned clients during application shutdown'],
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
language: 'go',
|
|
29
|
+
package: 'github.com/ductape/ductape/sdk/go',
|
|
30
|
+
version: 'v0.0.1',
|
|
31
|
+
status: 'supported',
|
|
32
|
+
source_version: 'sdk/go git tag v0.0.1',
|
|
33
|
+
public_surface_evidence: 'sdk/go/ductape',
|
|
34
|
+
capabilities: [...COMMON_RUNTIME_CAPABILITIES],
|
|
35
|
+
lifecycle: ['propagate context.Context cancellation', 'close owned clients during application shutdown'],
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
language: 'java',
|
|
39
|
+
package: 'app.ductape:ductape-sdk',
|
|
40
|
+
version: '0.1.9-SNAPSHOT',
|
|
41
|
+
status: 'supported-source-snapshot',
|
|
42
|
+
source_version: 'sdk/java/build.gradle',
|
|
43
|
+
public_surface_evidence: 'sdk/java/src/main/java/app/ductape/sdk/Ductape.java',
|
|
44
|
+
capabilities: [...COMMON_RUNTIME_CAPABILITIES],
|
|
45
|
+
lifecycle: ['propagate interruption/cancellation', 'close owned clients during application shutdown'],
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
language: 'dotnet',
|
|
49
|
+
package: 'Ductape.Sdk',
|
|
50
|
+
version: '0.1.12',
|
|
51
|
+
status: 'supported',
|
|
52
|
+
source_version: 'sdk/dotnet/src/Ductape.Sdk/Ductape.Sdk.csproj',
|
|
53
|
+
public_surface_evidence: 'sdk/dotnet/src/Ductape.Sdk/Ductape.cs',
|
|
54
|
+
capabilities: [...COMMON_RUNTIME_CAPABILITIES],
|
|
55
|
+
lifecycle: ['propagate CancellationToken', 'dispose owned clients during application shutdown'],
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
};
|
|
59
|
+
export function runMigrationCapabilities(opts) {
|
|
60
|
+
const sdks = SDK_CAPABILITY_MATRIX.sdks.filter((sdk) => (!opts.language || sdk.language === opts.language) &&
|
|
61
|
+
(!opts.version || sdk.version === opts.version));
|
|
62
|
+
if (!sdks.length) {
|
|
63
|
+
throw new Error(`No supported SDK capability matrix matches ${opts.language ?? '*'}@${opts.version ?? '*'}.`);
|
|
64
|
+
}
|
|
65
|
+
printJson({ ...SDK_CAPABILITY_MATRIX, sdks }, Boolean(opts.json));
|
|
66
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export declare const REQUIRED_DATABASE_FIELDS: {
|
|
2
|
+
readonly baseline: readonly ["database_tag", "owners", "providers", "code_schema_evidence", "migration_history_evidence", "applied_history_evidence", "live_snd_schema_evidence", "proposed_schema_evidence", "drift", "objects", "security", "topology", "compatibility", "performance_baseline"];
|
|
3
|
+
readonly data: readonly ["transformations", "batching", "checkpoints", "idempotency", "resume_strategy", "validation", "reconciliation", "failure_recovery", "pii_controls", "retention", "seed_data"];
|
|
4
|
+
readonly cutover: readonly ["phases", "expand", "backfill", "dual_compatibility", "contract", "deployment_order", "migration_locking", "backup", "restore_test", "rollback", "irreversible_changes", "monitoring", "reconciliation", "approval"];
|
|
5
|
+
};
|
|
6
|
+
export declare function initDatabaseMigration(opts: {
|
|
7
|
+
definition: string;
|
|
8
|
+
output: string;
|
|
9
|
+
}): {
|
|
10
|
+
output: string;
|
|
11
|
+
};
|
|
12
|
+
export declare function validateDatabaseMigration(directory: string): {
|
|
13
|
+
directory: string;
|
|
14
|
+
ready: boolean;
|
|
15
|
+
blockers: string[];
|
|
16
|
+
};
|
|
17
|
+
export declare function runMigrationDatabaseInit(opts: {
|
|
18
|
+
definition: string;
|
|
19
|
+
output: string;
|
|
20
|
+
json?: boolean;
|
|
21
|
+
}): void;
|
|
22
|
+
export declare function runMigrationDatabaseValidate(opts: {
|
|
23
|
+
directory: string;
|
|
24
|
+
strict?: boolean;
|
|
25
|
+
json?: boolean;
|
|
26
|
+
}): void;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import crypto from 'node:crypto';
|
|
4
|
+
import { printJson } from '../lib/output.js';
|
|
5
|
+
import { readMigrationJson, writeMigrationJson } from '../lib/migration-artifact.js';
|
|
6
|
+
export const REQUIRED_DATABASE_FIELDS = {
|
|
7
|
+
baseline: ['database_tag', 'owners', 'providers', 'code_schema_evidence', 'migration_history_evidence', 'applied_history_evidence', 'live_snd_schema_evidence', 'proposed_schema_evidence', 'drift', 'objects', 'security', 'topology', 'compatibility', 'performance_baseline'],
|
|
8
|
+
data: ['transformations', 'batching', 'checkpoints', 'idempotency', 'resume_strategy', 'validation', 'reconciliation', 'failure_recovery', 'pii_controls', 'retention', 'seed_data'],
|
|
9
|
+
cutover: ['phases', 'expand', 'backfill', 'dual_compatibility', 'contract', 'deployment_order', 'migration_locking', 'backup', 'restore_test', 'rollback', 'irreversible_changes', 'monitoring', 'reconciliation', 'approval'],
|
|
10
|
+
};
|
|
11
|
+
function read(file) { return readMigrationJson(file); }
|
|
12
|
+
function missing(value, keys) {
|
|
13
|
+
return keys.filter(k => value[k] == null || value[k] === '' || (Array.isArray(value[k]) && !value[k].length));
|
|
14
|
+
}
|
|
15
|
+
function digest(file) {
|
|
16
|
+
return crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
|
|
17
|
+
}
|
|
18
|
+
function object(value) {
|
|
19
|
+
return Boolean(value && typeof value === 'object' && !Array.isArray(value));
|
|
20
|
+
}
|
|
21
|
+
function text(value) {
|
|
22
|
+
return typeof value === 'string' && value.trim().length > 0;
|
|
23
|
+
}
|
|
24
|
+
export function initDatabaseMigration(opts) {
|
|
25
|
+
const d = read(path.resolve(opts.definition));
|
|
26
|
+
const out = path.resolve(opts.output);
|
|
27
|
+
fs.mkdirSync(out, { recursive: true });
|
|
28
|
+
for (const name of ['baseline', 'data', 'cutover']) {
|
|
29
|
+
if (!d[name] || typeof d[name] !== 'object')
|
|
30
|
+
throw new Error(`Definition requires ${name}.`);
|
|
31
|
+
writeMigrationJson(path.join(out, `database-${name}.json`), { version: 1, artifact_kind: `ductape_database_${name}`, ...d[name] }, { createOnly: true });
|
|
32
|
+
}
|
|
33
|
+
return { output: out };
|
|
34
|
+
}
|
|
35
|
+
export function validateDatabaseMigration(directory) {
|
|
36
|
+
const dir = path.resolve(directory);
|
|
37
|
+
const blockers = [];
|
|
38
|
+
const artifacts = {};
|
|
39
|
+
for (const name of ['baseline', 'data', 'cutover']) {
|
|
40
|
+
const file = path.join(dir, `database-${name}.json`);
|
|
41
|
+
if (!fs.existsSync(file)) {
|
|
42
|
+
blockers.push(`${name}: artifact missing`);
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const artifact = read(file);
|
|
46
|
+
artifacts[name] = artifact;
|
|
47
|
+
if (artifact.version !== 1 || artifact.artifact_kind !== `ductape_database_${name}`) {
|
|
48
|
+
blockers.push(`${name}: unsupported artifact version or kind`);
|
|
49
|
+
}
|
|
50
|
+
for (const field of missing(artifact, REQUIRED_DATABASE_FIELDS[name]))
|
|
51
|
+
blockers.push(`${name}.${field}: evidence required`);
|
|
52
|
+
if (!text(artifact.migration_id))
|
|
53
|
+
blockers.push(`${name}.migration_id: required for cross-artifact consistency`);
|
|
54
|
+
}
|
|
55
|
+
const baseline = artifacts.baseline;
|
|
56
|
+
if (baseline) {
|
|
57
|
+
if (!text(baseline.database_tag) || !Array.isArray(baseline.owners) || !baseline.owners.every(text) ||
|
|
58
|
+
!Array.isArray(baseline.providers) || !baseline.providers.every((provider) => object(provider) && text(provider.provider) && text(provider.environment) && text(provider.evidence))) {
|
|
59
|
+
blockers.push('baseline: database_tag, owners, and typed provider environment evidence are required');
|
|
60
|
+
}
|
|
61
|
+
const snapshots = baseline.schema_snapshots;
|
|
62
|
+
const snapshotKinds = new Set();
|
|
63
|
+
if (!Array.isArray(snapshots) || !snapshots.length) {
|
|
64
|
+
blockers.push('baseline.schema_snapshots: typed schema provenance is required');
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
for (const [index, snapshot] of snapshots.entries()) {
|
|
68
|
+
if (!object(snapshot) || !text(snapshot.kind) || !text(snapshot.database_tag) ||
|
|
69
|
+
!text(snapshot.structure_hash) || !text(snapshot.source_file) || !text(snapshot.checksum) ||
|
|
70
|
+
!text(snapshot.captured_at) || !text(snapshot.evidence)) {
|
|
71
|
+
blockers.push(`baseline.schema_snapshots[${index}]: kind, database_tag, structure hash, source checksum, timestamp, and evidence are required`);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
snapshotKinds.add(snapshot.kind);
|
|
75
|
+
const sourceFile = path.resolve(snapshot.source_file);
|
|
76
|
+
if (!fs.existsSync(sourceFile) || digest(sourceFile) !== snapshot.checksum) {
|
|
77
|
+
blockers.push(`baseline.schema_snapshots[${index}]: source evidence is stale or missing`);
|
|
78
|
+
}
|
|
79
|
+
if (snapshot.database_tag !== baseline.database_tag) {
|
|
80
|
+
blockers.push(`baseline.schema_snapshots[${index}]: database_tag mismatch`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
for (const kind of ['code', 'migration', 'applied', 'live', 'proposed']) {
|
|
84
|
+
if (!snapshotKinds.has(kind))
|
|
85
|
+
blockers.push(`baseline.schema_snapshots: missing ${kind} structure`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const comparison = baseline.structural_comparison;
|
|
89
|
+
if (!object(comparison) || !['matched', 'approved_differences'].includes(String(comparison.status)) ||
|
|
90
|
+
!Array.isArray(comparison.differences) || !text(comparison.evidence) ||
|
|
91
|
+
(comparison.status === 'approved_differences' && !comparison.differences.length)) {
|
|
92
|
+
blockers.push('baseline.structural_comparison: matched or evidenced approved differences are required');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const data = artifacts.data;
|
|
96
|
+
if (data) {
|
|
97
|
+
if (!Array.isArray(data.transformations) || !data.transformations.length) {
|
|
98
|
+
blockers.push('data.transformations: at least one versioned source/target mapping is required');
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
for (const [index, transformation] of data.transformations.entries()) {
|
|
102
|
+
if (!object(transformation) || !text(transformation.id) || !text(transformation.version) ||
|
|
103
|
+
!text(transformation.source) || !text(transformation.target) ||
|
|
104
|
+
!Array.isArray(transformation.field_mappings) || !transformation.field_mappings.length ||
|
|
105
|
+
!transformation.field_mappings.every((mapping) => object(mapping) && text(mapping.source) && text(mapping.target) && text(mapping.transformation))) {
|
|
106
|
+
blockers.push(`data.transformations[${index}]: versioned source/target field mappings are required`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const batching = data.batching;
|
|
111
|
+
if (!object(batching) || !Number.isInteger(batching.batch_size) || Number(batching.batch_size) <= 0 ||
|
|
112
|
+
!Number.isInteger(batching.max_concurrency) || Number(batching.max_concurrency) <= 0 ||
|
|
113
|
+
!Number.isFinite(batching.memory_limit_mb) || Number(batching.memory_limit_mb) <= 0 ||
|
|
114
|
+
!Number.isFinite(batching.timeout_ms) || Number(batching.timeout_ms) <= 0) {
|
|
115
|
+
blockers.push('data.batching: positive batch_size, max_concurrency, memory_limit_mb, and timeout_ms are required');
|
|
116
|
+
}
|
|
117
|
+
const checkpoints = data.checkpoints;
|
|
118
|
+
if (!object(checkpoints) || !text(checkpoints.store) || !text(checkpoints.key) ||
|
|
119
|
+
checkpoints.atomic !== true || !text(checkpoints.replay_test)) {
|
|
120
|
+
blockers.push('data.checkpoints: persistent store, key, atomic writes, and replay test evidence are required');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const cutover = artifacts.cutover;
|
|
124
|
+
if (cutover) {
|
|
125
|
+
const phases = cutover.phases;
|
|
126
|
+
if (!Array.isArray(phases) || !phases.length) {
|
|
127
|
+
blockers.push('cutover.phases: ordered phases are required');
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
const ids = new Set(phases.filter(object).map((phase) => phase.id));
|
|
131
|
+
const orders = phases.filter(object).map((phase) => Number(phase.order));
|
|
132
|
+
if (new Set(orders).size !== orders.length || orders.some((order) => !Number.isInteger(order) || order < 1)) {
|
|
133
|
+
blockers.push('cutover.phases: unique positive order values are required');
|
|
134
|
+
}
|
|
135
|
+
for (const [index, phase] of phases.entries()) {
|
|
136
|
+
if (!object(phase) || !text(phase.id) || !Array.isArray(phase.prerequisites) ||
|
|
137
|
+
!phase.prerequisites.every((required) => ids.has(required))) {
|
|
138
|
+
blockers.push(`cutover.phases[${index}]: id and existing prerequisites are required`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
const ids = ['baseline', 'data', 'cutover'].map((name) => artifacts[name]?.migration_id);
|
|
144
|
+
if (ids.every(text) && new Set(ids).size !== 1)
|
|
145
|
+
blockers.push('database artifacts use different migration_id values');
|
|
146
|
+
if (baseline && data && cutover) {
|
|
147
|
+
for (const [name, artifact] of [['data', data], ['cutover', cutover]]) {
|
|
148
|
+
if (artifact.database_tag !== baseline.database_tag)
|
|
149
|
+
blockers.push(`${name}.database_tag does not match baseline`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return { directory: dir, ready: !blockers.length, blockers };
|
|
153
|
+
}
|
|
154
|
+
export function runMigrationDatabaseInit(opts) {
|
|
155
|
+
printJson(initDatabaseMigration(opts), Boolean(opts.json));
|
|
156
|
+
}
|
|
157
|
+
export function runMigrationDatabaseValidate(opts) {
|
|
158
|
+
const r = validateDatabaseMigration(opts.directory);
|
|
159
|
+
printJson(r, Boolean(opts.json));
|
|
160
|
+
if (opts.strict && !r.ready)
|
|
161
|
+
throw new Error('Database migration is not ready: resolve every reported blocker.');
|
|
162
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
interface E2EDefinition {
|
|
2
|
+
command: string;
|
|
3
|
+
suite_files: string[];
|
|
4
|
+
baseline: {
|
|
5
|
+
status: 'passed' | 'failed';
|
|
6
|
+
evidence: string[];
|
|
7
|
+
environment: string;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
interface FinalEvidence {
|
|
11
|
+
command: string;
|
|
12
|
+
status: 'passed' | 'failed';
|
|
13
|
+
evidence: string[];
|
|
14
|
+
environment: string;
|
|
15
|
+
suite_root: string;
|
|
16
|
+
}
|
|
17
|
+
interface E2EManifest {
|
|
18
|
+
version: 1;
|
|
19
|
+
artifact_kind: 'ductape_migration_e2e_baseline';
|
|
20
|
+
source: string;
|
|
21
|
+
created_at: string;
|
|
22
|
+
command: string;
|
|
23
|
+
suite: Array<{
|
|
24
|
+
file: string;
|
|
25
|
+
checksum: string;
|
|
26
|
+
}>;
|
|
27
|
+
baseline: E2EDefinition['baseline'];
|
|
28
|
+
final: (FinalEvidence & {
|
|
29
|
+
recorded_at: string;
|
|
30
|
+
}) | null;
|
|
31
|
+
}
|
|
32
|
+
export declare function initializeMigrationE2E(opts: {
|
|
33
|
+
source: string;
|
|
34
|
+
definition: string;
|
|
35
|
+
output?: string;
|
|
36
|
+
}): {
|
|
37
|
+
path: string;
|
|
38
|
+
manifest: E2EManifest;
|
|
39
|
+
};
|
|
40
|
+
export declare function recordMigrationE2EFinal(manifestFile: string, evidenceFile: string): E2EManifest;
|
|
41
|
+
export declare function validateMigrationE2E(manifestFile: string): {
|
|
42
|
+
manifest: string;
|
|
43
|
+
ready: boolean;
|
|
44
|
+
suite_files: number;
|
|
45
|
+
blockers: string[];
|
|
46
|
+
};
|
|
47
|
+
export declare function validateMigrationE2EBaseline(manifestFile: string): {
|
|
48
|
+
manifest: string;
|
|
49
|
+
ready: boolean;
|
|
50
|
+
blockers: string[];
|
|
51
|
+
};
|
|
52
|
+
export declare function runMigrationE2EInit(opts: {
|
|
53
|
+
source: string;
|
|
54
|
+
definition: string;
|
|
55
|
+
output?: string;
|
|
56
|
+
json?: boolean;
|
|
57
|
+
}): void;
|
|
58
|
+
export declare function runMigrationE2EFinal(opts: {
|
|
59
|
+
manifest: string;
|
|
60
|
+
evidence: string;
|
|
61
|
+
json?: boolean;
|
|
62
|
+
}): void;
|
|
63
|
+
export declare function runMigrationE2EValidate(opts: {
|
|
64
|
+
manifest: string;
|
|
65
|
+
strict?: boolean;
|
|
66
|
+
json?: boolean;
|
|
67
|
+
}): void;
|
|
68
|
+
export {};
|