@prisma-next/cli 0.3.0-pr.99.5 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +381 -128
- package/dist/agent-skill-mongo.md +106 -0
- package/dist/agent-skill-postgres.md +106 -0
- package/dist/cli-errors-BDCYR5ap.mjs +4 -0
- package/dist/cli-errors-DStABy9d.d.mts +3 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.js +1 -2910
- package/dist/cli.mjs +261 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/client-DiUkJAeN.mjs +987 -0
- package/dist/client-DiUkJAeN.mjs.map +1 -0
- package/dist/commands/contract-emit.d.mts +7 -0
- package/dist/commands/contract-emit.d.mts.map +1 -0
- package/dist/commands/contract-emit.mjs +9 -0
- package/dist/commands/contract-infer.d.mts +7 -0
- package/dist/commands/contract-infer.d.mts.map +1 -0
- package/dist/commands/contract-infer.mjs +10 -0
- package/dist/commands/db-init.d.mts +7 -0
- package/dist/commands/db-init.d.mts.map +1 -0
- package/dist/commands/db-init.mjs +126 -0
- package/dist/commands/db-init.mjs.map +1 -0
- package/dist/commands/db-schema.d.mts +7 -0
- package/dist/commands/db-schema.d.mts.map +1 -0
- package/dist/commands/db-schema.mjs +56 -0
- package/dist/commands/db-schema.mjs.map +1 -0
- package/dist/commands/db-sign.d.mts +7 -0
- package/dist/commands/db-sign.d.mts.map +1 -0
- package/dist/commands/db-sign.mjs +137 -0
- package/dist/commands/db-sign.mjs.map +1 -0
- package/dist/commands/db-update.d.mts +7 -0
- package/dist/commands/db-update.d.mts.map +1 -0
- package/dist/commands/db-update.mjs +123 -0
- package/dist/commands/db-update.mjs.map +1 -0
- package/dist/commands/db-verify.d.mts +7 -0
- package/dist/commands/db-verify.d.mts.map +1 -0
- package/dist/commands/db-verify.mjs +323 -0
- package/dist/commands/db-verify.mjs.map +1 -0
- package/dist/commands/migration-apply.d.mts +36 -0
- package/dist/commands/migration-apply.d.mts.map +1 -0
- package/dist/commands/migration-apply.mjs +245 -0
- package/dist/commands/migration-apply.mjs.map +1 -0
- package/dist/commands/migration-new.d.mts +8 -0
- package/dist/commands/migration-new.d.mts.map +1 -0
- package/dist/commands/migration-new.mjs +152 -0
- package/dist/commands/migration-new.mjs.map +1 -0
- package/dist/commands/migration-plan.d.mts +47 -0
- package/dist/commands/migration-plan.d.mts.map +1 -0
- package/dist/commands/migration-plan.mjs +313 -0
- package/dist/commands/migration-plan.mjs.map +1 -0
- package/dist/commands/migration-ref.d.mts +43 -0
- package/dist/commands/migration-ref.d.mts.map +1 -0
- package/dist/commands/migration-ref.mjs +195 -0
- package/dist/commands/migration-ref.mjs.map +1 -0
- package/dist/commands/migration-show.d.mts +28 -0
- package/dist/commands/migration-show.d.mts.map +1 -0
- package/dist/commands/migration-show.mjs +140 -0
- package/dist/commands/migration-show.mjs.map +1 -0
- package/dist/commands/migration-status.d.mts +86 -0
- package/dist/commands/migration-status.d.mts.map +1 -0
- package/dist/commands/migration-status.mjs +9 -0
- package/dist/commands/migration-verify.d.mts +16 -0
- package/dist/commands/migration-verify.d.mts.map +1 -0
- package/dist/commands/migration-verify.mjs +110 -0
- package/dist/commands/migration-verify.mjs.map +1 -0
- package/dist/config-loader-C4VXKl8f.mjs +43 -0
- package/dist/config-loader-C4VXKl8f.mjs.map +1 -0
- package/dist/{config-loader.d.ts → config-loader.d.mts} +8 -3
- package/dist/config-loader.d.mts.map +1 -0
- package/dist/config-loader.mjs +3 -0
- package/dist/contract-emit-D2wDXfyo.mjs +191 -0
- package/dist/contract-emit-D2wDXfyo.mjs.map +1 -0
- package/dist/contract-emit-Zm_sd1wQ.mjs +112 -0
- package/dist/contract-emit-Zm_sd1wQ.mjs.map +1 -0
- package/dist/contract-emit-kN-IkKTE.mjs +6 -0
- package/dist/contract-enrichment-CGW6mm-E.mjs +79 -0
- package/dist/contract-enrichment-CGW6mm-E.mjs.map +1 -0
- package/dist/contract-infer-DozZT511.mjs +90 -0
- package/dist/contract-infer-DozZT511.mjs.map +1 -0
- package/dist/exports/config-types.d.mts +2 -0
- package/dist/exports/config-types.mjs +3 -0
- package/dist/exports/control-api.d.mts +624 -0
- package/dist/exports/control-api.d.mts.map +1 -0
- package/dist/exports/control-api.mjs +8 -0
- package/dist/{load-ts-contract.d.ts → exports/index.d.mts} +12 -7
- package/dist/exports/index.d.mts.map +1 -0
- package/dist/exports/index.mjs +142 -0
- package/dist/exports/index.mjs.map +1 -0
- package/dist/extract-operation-statements-DZUJNmL3.mjs +13 -0
- package/dist/extract-operation-statements-DZUJNmL3.mjs.map +1 -0
- package/dist/extract-sql-ddl-DDMX-9mz.mjs +26 -0
- package/dist/extract-sql-ddl-DDMX-9mz.mjs.map +1 -0
- package/dist/framework-components-BAsliT4V.mjs +59 -0
- package/dist/framework-components-BAsliT4V.mjs.map +1 -0
- package/dist/init-6Pvm_esG.mjs +430 -0
- package/dist/init-6Pvm_esG.mjs.map +1 -0
- package/dist/inspect-live-schema-BYnhztxZ.mjs +91 -0
- package/dist/inspect-live-schema-BYnhztxZ.mjs.map +1 -0
- package/dist/migration-command-scaffold-CntCcntR.mjs +105 -0
- package/dist/migration-command-scaffold-CntCcntR.mjs.map +1 -0
- package/dist/migration-status-CJANY4yr.mjs +1583 -0
- package/dist/migration-status-CJANY4yr.mjs.map +1 -0
- package/dist/migrations-DTZBYXm1.mjs +173 -0
- package/dist/migrations-DTZBYXm1.mjs.map +1 -0
- package/dist/progress-adapter-B-YvmcDu.mjs +43 -0
- package/dist/progress-adapter-B-YvmcDu.mjs.map +1 -0
- package/dist/quick-reference-mongo.md +93 -0
- package/dist/quick-reference-postgres.md +91 -0
- package/dist/result-handler-oK_vA-Fn.mjs +697 -0
- package/dist/result-handler-oK_vA-Fn.mjs.map +1 -0
- package/dist/terminal-ui-C5k88MmW.mjs +274 -0
- package/dist/terminal-ui-C5k88MmW.mjs.map +1 -0
- package/dist/validate-contract-deps-esa-VQ0h.mjs +37 -0
- package/dist/validate-contract-deps-esa-VQ0h.mjs.map +1 -0
- package/dist/verify-DlFQ2FOw.mjs +385 -0
- package/dist/verify-DlFQ2FOw.mjs.map +1 -0
- package/package.json +87 -40
- package/src/cli.ts +118 -58
- package/src/commands/contract-emit.ts +101 -78
- package/src/commands/contract-infer-paths.ts +32 -0
- package/src/commands/contract-infer.ts +143 -0
- package/src/commands/db-init.ts +97 -219
- package/src/commands/db-schema.ts +77 -0
- package/src/commands/db-sign.ts +46 -73
- package/src/commands/db-update.ts +236 -0
- package/src/commands/db-verify.ts +409 -119
- package/src/commands/init/detect-package-manager.ts +47 -0
- package/src/commands/init/index.ts +21 -0
- package/src/commands/init/init.ts +203 -0
- package/src/commands/init/templates/agent-skill-mongo.md +106 -0
- package/src/commands/init/templates/agent-skill-postgres.md +106 -0
- package/src/commands/init/templates/agent-skill.ts +19 -0
- package/src/commands/init/templates/code-templates.ts +168 -0
- package/src/commands/init/templates/quick-reference-mongo.md +93 -0
- package/src/commands/init/templates/quick-reference-postgres.md +91 -0
- package/src/commands/init/templates/quick-reference.ts +19 -0
- package/src/commands/init/templates/render.ts +20 -0
- package/src/commands/init/templates/tsconfig.ts +35 -0
- package/src/commands/inspect-live-schema.ts +170 -0
- package/src/commands/migration-apply.ts +427 -0
- package/src/commands/migration-new.ts +260 -0
- package/src/commands/migration-plan.ts +519 -0
- package/src/commands/migration-ref.ts +305 -0
- package/src/commands/migration-show.ts +246 -0
- package/src/commands/migration-status.ts +864 -0
- package/src/commands/migration-verify.ts +180 -0
- package/src/config-loader.ts +13 -3
- package/src/control-api/client.ts +205 -183
- package/src/control-api/contract-enrichment.ts +119 -0
- package/src/control-api/errors.ts +9 -0
- package/src/control-api/operations/contract-emit.ts +181 -0
- package/src/control-api/operations/db-init.ts +53 -49
- package/src/control-api/operations/db-update.ts +220 -0
- package/src/control-api/operations/extract-operation-statements.ts +14 -0
- package/src/control-api/operations/extract-sql-ddl.ts +47 -0
- package/src/control-api/operations/migration-apply.ts +191 -0
- package/src/control-api/operations/migration-helpers.ts +49 -0
- package/src/control-api/types.ts +274 -52
- package/src/exports/config-types.ts +4 -3
- package/src/exports/control-api.ts +15 -5
- package/src/load-ts-contract.ts +30 -19
- package/src/utils/cli-errors.ts +14 -8
- package/src/utils/command-helpers.ts +302 -3
- package/src/utils/formatters/emit.ts +67 -0
- package/src/utils/formatters/errors.ts +82 -0
- package/src/utils/formatters/graph-migration-mapper.ts +240 -0
- package/src/utils/formatters/graph-render.ts +1323 -0
- package/src/utils/formatters/graph-types.ts +120 -0
- package/src/utils/formatters/help.ts +380 -0
- package/src/utils/formatters/helpers.ts +28 -0
- package/src/utils/formatters/migrations.ts +346 -0
- package/src/utils/formatters/styled.ts +212 -0
- package/src/utils/formatters/verify.ts +621 -0
- package/src/utils/framework-components.ts +13 -10
- package/src/utils/global-flags.ts +41 -23
- package/src/utils/migration-command-scaffold.ts +184 -0
- package/src/utils/migration-types.ts +12 -0
- package/src/utils/progress-adapter.ts +18 -29
- package/src/utils/result-handler.ts +12 -13
- package/src/utils/shutdown.ts +92 -0
- package/src/utils/suggest-command.ts +31 -0
- package/src/utils/terminal-ui.ts +276 -0
- package/src/utils/validate-contract-deps.ts +49 -0
- package/dist/chunk-AGOTG4L3.js +0 -965
- package/dist/chunk-AGOTG4L3.js.map +0 -1
- package/dist/chunk-HLLI4YL7.js +0 -180
- package/dist/chunk-HLLI4YL7.js.map +0 -1
- package/dist/chunk-HWYQOCAJ.js +0 -47
- package/dist/chunk-HWYQOCAJ.js.map +0 -1
- package/dist/chunk-VG2R7DGF.js +0 -735
- package/dist/chunk-VG2R7DGF.js.map +0 -1
- package/dist/cli.d.ts +0 -2
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/commands/contract-emit.d.ts +0 -3
- package/dist/commands/contract-emit.d.ts.map +0 -1
- package/dist/commands/contract-emit.js +0 -10
- package/dist/commands/contract-emit.js.map +0 -1
- package/dist/commands/db-init.d.ts +0 -3
- package/dist/commands/db-init.d.ts.map +0 -1
- package/dist/commands/db-init.js +0 -257
- package/dist/commands/db-init.js.map +0 -1
- package/dist/commands/db-introspect.d.ts +0 -3
- package/dist/commands/db-introspect.d.ts.map +0 -1
- package/dist/commands/db-introspect.js +0 -155
- package/dist/commands/db-introspect.js.map +0 -1
- package/dist/commands/db-schema-verify.d.ts +0 -3
- package/dist/commands/db-schema-verify.d.ts.map +0 -1
- package/dist/commands/db-schema-verify.js +0 -171
- package/dist/commands/db-schema-verify.js.map +0 -1
- package/dist/commands/db-sign.d.ts +0 -3
- package/dist/commands/db-sign.d.ts.map +0 -1
- package/dist/commands/db-sign.js +0 -195
- package/dist/commands/db-sign.js.map +0 -1
- package/dist/commands/db-verify.d.ts +0 -3
- package/dist/commands/db-verify.d.ts.map +0 -1
- package/dist/commands/db-verify.js +0 -193
- package/dist/commands/db-verify.js.map +0 -1
- package/dist/config-loader.d.ts.map +0 -1
- package/dist/config-loader.js +0 -7
- package/dist/config-loader.js.map +0 -1
- package/dist/control-api/client.d.ts +0 -13
- package/dist/control-api/client.d.ts.map +0 -1
- package/dist/control-api/operations/db-init.d.ts +0 -29
- package/dist/control-api/operations/db-init.d.ts.map +0 -1
- package/dist/control-api/types.d.ts +0 -387
- package/dist/control-api/types.d.ts.map +0 -1
- package/dist/exports/config-types.d.ts +0 -3
- package/dist/exports/config-types.d.ts.map +0 -1
- package/dist/exports/config-types.js +0 -6
- package/dist/exports/config-types.js.map +0 -1
- package/dist/exports/control-api.d.ts +0 -13
- package/dist/exports/control-api.d.ts.map +0 -1
- package/dist/exports/control-api.js +0 -7
- package/dist/exports/control-api.js.map +0 -1
- package/dist/exports/index.d.ts +0 -4
- package/dist/exports/index.d.ts.map +0 -1
- package/dist/exports/index.js +0 -176
- package/dist/exports/index.js.map +0 -1
- package/dist/load-ts-contract.d.ts.map +0 -1
- package/dist/utils/cli-errors.d.ts +0 -7
- package/dist/utils/cli-errors.d.ts.map +0 -1
- package/dist/utils/command-helpers.d.ts +0 -12
- package/dist/utils/command-helpers.d.ts.map +0 -1
- package/dist/utils/framework-components.d.ts +0 -70
- package/dist/utils/framework-components.d.ts.map +0 -1
- package/dist/utils/global-flags.d.ts +0 -25
- package/dist/utils/global-flags.d.ts.map +0 -1
- package/dist/utils/output.d.ts +0 -142
- package/dist/utils/output.d.ts.map +0 -1
- package/dist/utils/progress-adapter.d.ts +0 -26
- package/dist/utils/progress-adapter.d.ts.map +0 -1
- package/dist/utils/result-handler.d.ts +0 -15
- package/dist/utils/result-handler.d.ts.map +0 -1
- package/src/commands/db-introspect.ts +0 -227
- package/src/commands/db-schema-verify.ts +0 -238
- package/src/utils/output.ts +0 -1471
|
@@ -1,25 +1,42 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
1
|
+
import type { Contract, ContractMarkerRecord } from '@prisma-next/contract/types';
|
|
2
|
+
import { emit as emitContractArtifacts } from '@prisma-next/emitter';
|
|
3
|
+
import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
|
|
4
4
|
import type {
|
|
5
5
|
ControlDriverInstance,
|
|
6
6
|
ControlFamilyInstance,
|
|
7
|
-
|
|
7
|
+
ControlStack,
|
|
8
|
+
CoreSchemaView,
|
|
8
9
|
SignDatabaseResult,
|
|
9
10
|
VerifyDatabaseResult,
|
|
10
11
|
VerifyDatabaseSchemaResult,
|
|
11
|
-
} from '@prisma-next/
|
|
12
|
+
} from '@prisma-next/framework-components/control';
|
|
13
|
+
import {
|
|
14
|
+
createControlStack,
|
|
15
|
+
hasMigrations,
|
|
16
|
+
hasSchemaView,
|
|
17
|
+
} from '@prisma-next/framework-components/control';
|
|
18
|
+
import { ifDefined } from '@prisma-next/utils/defined';
|
|
12
19
|
import { notOk, ok } from '@prisma-next/utils/result';
|
|
13
20
|
import { assertFrameworkComponentsCompatible } from '../utils/framework-components';
|
|
21
|
+
import { enrichContract } from './contract-enrichment';
|
|
22
|
+
import { ContractValidationError } from './errors';
|
|
14
23
|
import { executeDbInit } from './operations/db-init';
|
|
24
|
+
import { executeDbUpdate } from './operations/db-update';
|
|
25
|
+
import { executeMigrationApply } from './operations/migration-apply';
|
|
15
26
|
import type {
|
|
27
|
+
ControlActionName,
|
|
16
28
|
ControlClient,
|
|
17
29
|
ControlClientOptions,
|
|
18
30
|
DbInitOptions,
|
|
19
31
|
DbInitResult,
|
|
32
|
+
DbUpdateOptions,
|
|
33
|
+
DbUpdateResult,
|
|
20
34
|
EmitOptions,
|
|
21
35
|
EmitResult,
|
|
22
36
|
IntrospectOptions,
|
|
37
|
+
MigrationApplyOptions,
|
|
38
|
+
MigrationApplyResult,
|
|
39
|
+
OnControlProgress,
|
|
23
40
|
SchemaVerifyOptions,
|
|
24
41
|
SignOptions,
|
|
25
42
|
VerifyOptions,
|
|
@@ -45,9 +62,9 @@ export function createControlClient(options: ControlClientOptions): ControlClien
|
|
|
45
62
|
*/
|
|
46
63
|
class ControlClientImpl implements ControlClient {
|
|
47
64
|
private readonly options: ControlClientOptions;
|
|
48
|
-
private stack:
|
|
65
|
+
private stack: ControlStack | null = null;
|
|
49
66
|
private driver: ControlDriverInstance<string, string> | null = null;
|
|
50
|
-
private familyInstance: ControlFamilyInstance<string> | null = null;
|
|
67
|
+
private familyInstance: ControlFamilyInstance<string, unknown> | null = null;
|
|
51
68
|
private frameworkComponents: ReadonlyArray<
|
|
52
69
|
TargetBoundComponentDescriptor<string, string>
|
|
53
70
|
> | null = null;
|
|
@@ -64,15 +81,14 @@ class ControlClientImpl implements ControlClient {
|
|
|
64
81
|
return; // Idempotent
|
|
65
82
|
}
|
|
66
83
|
|
|
67
|
-
|
|
68
|
-
|
|
84
|
+
this.stack = createControlStack({
|
|
85
|
+
family: this.options.family,
|
|
69
86
|
target: this.options.target,
|
|
70
87
|
adapter: this.options.adapter,
|
|
71
88
|
driver: this.options.driver,
|
|
72
89
|
extensionPacks: this.options.extensionPacks,
|
|
73
90
|
});
|
|
74
91
|
|
|
75
|
-
// Create family instance using the stack
|
|
76
92
|
this.familyInstance = this.options.family.create(this.stack);
|
|
77
93
|
|
|
78
94
|
// Validate and type-narrow framework components
|
|
@@ -113,12 +129,8 @@ class ControlClientImpl implements ControlClient {
|
|
|
113
129
|
);
|
|
114
130
|
}
|
|
115
131
|
|
|
116
|
-
// Create driver instance
|
|
117
|
-
// Cast through any since connection type is driver-specific at runtime.
|
|
118
|
-
// The driver descriptor is typed with any for TConnection in ControlClientOptions,
|
|
119
|
-
// but createControlPlaneStack defaults it to string. We bridge this at runtime.
|
|
120
132
|
// biome-ignore lint/suspicious/noExplicitAny: required for runtime connection type flexibility
|
|
121
|
-
this.driver = await this.stack
|
|
133
|
+
this.driver = await this.stack.driver.create(resolvedConnection as any);
|
|
122
134
|
}
|
|
123
135
|
|
|
124
136
|
async close(): Promise<void> {
|
|
@@ -130,7 +142,7 @@ class ControlClientImpl implements ControlClient {
|
|
|
130
142
|
|
|
131
143
|
private async ensureConnected(): Promise<{
|
|
132
144
|
driver: ControlDriverInstance<string, string>;
|
|
133
|
-
familyInstance: ControlFamilyInstance<string>;
|
|
145
|
+
familyInstance: ControlFamilyInstance<string, unknown>;
|
|
134
146
|
frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<string, string>>;
|
|
135
147
|
}> {
|
|
136
148
|
// Auto-init if needed
|
|
@@ -151,47 +163,47 @@ class ControlClientImpl implements ControlClient {
|
|
|
151
163
|
};
|
|
152
164
|
}
|
|
153
165
|
|
|
154
|
-
async
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
});
|
|
173
|
-
} catch (error) {
|
|
174
|
-
onProgress?.({
|
|
175
|
-
action: 'verify',
|
|
176
|
-
kind: 'spanEnd',
|
|
177
|
-
spanId: 'connect',
|
|
178
|
-
outcome: 'error',
|
|
179
|
-
});
|
|
180
|
-
throw error;
|
|
181
|
-
}
|
|
166
|
+
private async connectWithProgress(
|
|
167
|
+
connection: unknown | undefined,
|
|
168
|
+
action: ControlActionName,
|
|
169
|
+
onProgress?: OnControlProgress,
|
|
170
|
+
): Promise<void> {
|
|
171
|
+
if (connection === undefined) return;
|
|
172
|
+
onProgress?.({
|
|
173
|
+
action,
|
|
174
|
+
kind: 'spanStart',
|
|
175
|
+
spanId: 'connect',
|
|
176
|
+
label: 'Connecting to database...',
|
|
177
|
+
});
|
|
178
|
+
try {
|
|
179
|
+
await this.connect(connection);
|
|
180
|
+
onProgress?.({ action, kind: 'spanEnd', spanId: 'connect', outcome: 'ok' });
|
|
181
|
+
} catch (error) {
|
|
182
|
+
onProgress?.({ action, kind: 'spanEnd', spanId: 'connect', outcome: 'error' });
|
|
183
|
+
throw error;
|
|
182
184
|
}
|
|
185
|
+
}
|
|
183
186
|
|
|
187
|
+
async verify(options: VerifyOptions): Promise<VerifyDatabaseResult> {
|
|
188
|
+
const { onProgress } = options;
|
|
189
|
+
await this.connectWithProgress(options.connection, 'verify', onProgress);
|
|
184
190
|
const { driver, familyInstance } = await this.ensureConnected();
|
|
185
191
|
|
|
186
192
|
// Validate contract using family instance
|
|
187
|
-
|
|
193
|
+
let contract: Contract;
|
|
194
|
+
try {
|
|
195
|
+
contract = familyInstance.validateContract(options.contract);
|
|
196
|
+
} catch (error) {
|
|
197
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
198
|
+
throw new ContractValidationError(message, error);
|
|
199
|
+
}
|
|
188
200
|
|
|
189
201
|
// Emit verify span
|
|
190
202
|
onProgress?.({
|
|
191
203
|
action: 'verify',
|
|
192
204
|
kind: 'spanStart',
|
|
193
205
|
spanId: 'verify',
|
|
194
|
-
label: 'Verifying
|
|
206
|
+
label: 'Verifying database marker...',
|
|
195
207
|
});
|
|
196
208
|
|
|
197
209
|
try {
|
|
@@ -201,7 +213,7 @@ class ControlClientImpl implements ControlClient {
|
|
|
201
213
|
// metadata for error reporting.
|
|
202
214
|
const result = await familyInstance.verify({
|
|
203
215
|
driver,
|
|
204
|
-
|
|
216
|
+
contract,
|
|
205
217
|
expectedTargetId: this.options.target.targetId,
|
|
206
218
|
contractPath: '',
|
|
207
219
|
});
|
|
@@ -227,38 +239,17 @@ class ControlClientImpl implements ControlClient {
|
|
|
227
239
|
|
|
228
240
|
async schemaVerify(options: SchemaVerifyOptions): Promise<VerifyDatabaseSchemaResult> {
|
|
229
241
|
const { onProgress } = options;
|
|
230
|
-
|
|
231
|
-
// Connect with progress span if connection provided
|
|
232
|
-
if (options.connection !== undefined) {
|
|
233
|
-
onProgress?.({
|
|
234
|
-
action: 'schemaVerify',
|
|
235
|
-
kind: 'spanStart',
|
|
236
|
-
spanId: 'connect',
|
|
237
|
-
label: 'Connecting to database...',
|
|
238
|
-
});
|
|
239
|
-
try {
|
|
240
|
-
await this.connect(options.connection);
|
|
241
|
-
onProgress?.({
|
|
242
|
-
action: 'schemaVerify',
|
|
243
|
-
kind: 'spanEnd',
|
|
244
|
-
spanId: 'connect',
|
|
245
|
-
outcome: 'ok',
|
|
246
|
-
});
|
|
247
|
-
} catch (error) {
|
|
248
|
-
onProgress?.({
|
|
249
|
-
action: 'schemaVerify',
|
|
250
|
-
kind: 'spanEnd',
|
|
251
|
-
spanId: 'connect',
|
|
252
|
-
outcome: 'error',
|
|
253
|
-
});
|
|
254
|
-
throw error;
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
242
|
+
await this.connectWithProgress(options.connection, 'schemaVerify', onProgress);
|
|
258
243
|
const { driver, familyInstance, frameworkComponents } = await this.ensureConnected();
|
|
259
244
|
|
|
260
245
|
// Validate contract using family instance
|
|
261
|
-
|
|
246
|
+
let contract: Contract;
|
|
247
|
+
try {
|
|
248
|
+
contract = familyInstance.validateContract(options.contract);
|
|
249
|
+
} catch (error) {
|
|
250
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
251
|
+
throw new ContractValidationError(message, error);
|
|
252
|
+
}
|
|
262
253
|
|
|
263
254
|
// Emit schemaVerify span
|
|
264
255
|
onProgress?.({
|
|
@@ -272,7 +263,7 @@ class ControlClientImpl implements ControlClient {
|
|
|
272
263
|
// Delegate to family instance schemaVerify method
|
|
273
264
|
const result = await familyInstance.schemaVerify({
|
|
274
265
|
driver,
|
|
275
|
-
|
|
266
|
+
contract,
|
|
276
267
|
strict: options.strict ?? false,
|
|
277
268
|
contractPath: '',
|
|
278
269
|
frameworkComponents,
|
|
@@ -299,38 +290,17 @@ class ControlClientImpl implements ControlClient {
|
|
|
299
290
|
|
|
300
291
|
async sign(options: SignOptions): Promise<SignDatabaseResult> {
|
|
301
292
|
const { onProgress } = options;
|
|
302
|
-
|
|
303
|
-
// Connect with progress span if connection provided
|
|
304
|
-
if (options.connection !== undefined) {
|
|
305
|
-
onProgress?.({
|
|
306
|
-
action: 'sign',
|
|
307
|
-
kind: 'spanStart',
|
|
308
|
-
spanId: 'connect',
|
|
309
|
-
label: 'Connecting to database...',
|
|
310
|
-
});
|
|
311
|
-
try {
|
|
312
|
-
await this.connect(options.connection);
|
|
313
|
-
onProgress?.({
|
|
314
|
-
action: 'sign',
|
|
315
|
-
kind: 'spanEnd',
|
|
316
|
-
spanId: 'connect',
|
|
317
|
-
outcome: 'ok',
|
|
318
|
-
});
|
|
319
|
-
} catch (error) {
|
|
320
|
-
onProgress?.({
|
|
321
|
-
action: 'sign',
|
|
322
|
-
kind: 'spanEnd',
|
|
323
|
-
spanId: 'connect',
|
|
324
|
-
outcome: 'error',
|
|
325
|
-
});
|
|
326
|
-
throw error;
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
|
|
293
|
+
await this.connectWithProgress(options.connection, 'sign', onProgress);
|
|
330
294
|
const { driver, familyInstance } = await this.ensureConnected();
|
|
331
295
|
|
|
332
296
|
// Validate contract using family instance
|
|
333
|
-
|
|
297
|
+
let contract: Contract;
|
|
298
|
+
try {
|
|
299
|
+
contract = familyInstance.validateContract(options.contract);
|
|
300
|
+
} catch (error) {
|
|
301
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
302
|
+
throw new ContractValidationError(message, error);
|
|
303
|
+
}
|
|
334
304
|
|
|
335
305
|
// Emit sign span
|
|
336
306
|
onProgress?.({
|
|
@@ -344,9 +314,9 @@ class ControlClientImpl implements ControlClient {
|
|
|
344
314
|
// Delegate to family instance sign method
|
|
345
315
|
const result = await familyInstance.sign({
|
|
346
316
|
driver,
|
|
347
|
-
|
|
317
|
+
contract,
|
|
348
318
|
contractPath: options.contractPath ?? '',
|
|
349
|
-
...(
|
|
319
|
+
...ifDefined('configPath', options.configPath),
|
|
350
320
|
});
|
|
351
321
|
|
|
352
322
|
onProgress?.({
|
|
@@ -370,86 +340,91 @@ class ControlClientImpl implements ControlClient {
|
|
|
370
340
|
|
|
371
341
|
async dbInit(options: DbInitOptions): Promise<DbInitResult> {
|
|
372
342
|
const { onProgress } = options;
|
|
343
|
+
await this.connectWithProgress(options.connection, 'dbInit', onProgress);
|
|
344
|
+
const { driver, familyInstance, frameworkComponents } = await this.ensureConnected();
|
|
373
345
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
onProgress?.({
|
|
377
|
-
action: 'dbInit',
|
|
378
|
-
kind: 'spanStart',
|
|
379
|
-
spanId: 'connect',
|
|
380
|
-
label: 'Connecting to database...',
|
|
381
|
-
});
|
|
382
|
-
try {
|
|
383
|
-
await this.connect(options.connection);
|
|
384
|
-
onProgress?.({
|
|
385
|
-
action: 'dbInit',
|
|
386
|
-
kind: 'spanEnd',
|
|
387
|
-
spanId: 'connect',
|
|
388
|
-
outcome: 'ok',
|
|
389
|
-
});
|
|
390
|
-
} catch (error) {
|
|
391
|
-
onProgress?.({
|
|
392
|
-
action: 'dbInit',
|
|
393
|
-
kind: 'spanEnd',
|
|
394
|
-
spanId: 'connect',
|
|
395
|
-
outcome: 'error',
|
|
396
|
-
});
|
|
397
|
-
throw error;
|
|
398
|
-
}
|
|
346
|
+
if (!hasMigrations(this.options.target)) {
|
|
347
|
+
throw new Error(`Target "${this.options.target.targetId}" does not support migrations`);
|
|
399
348
|
}
|
|
400
349
|
|
|
350
|
+
let contract: Contract;
|
|
351
|
+
try {
|
|
352
|
+
contract = familyInstance.validateContract(options.contract);
|
|
353
|
+
} catch (error) {
|
|
354
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
355
|
+
throw new ContractValidationError(message, error);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return executeDbInit({
|
|
359
|
+
driver,
|
|
360
|
+
familyInstance,
|
|
361
|
+
contract,
|
|
362
|
+
mode: options.mode,
|
|
363
|
+
migrations: this.options.target.migrations,
|
|
364
|
+
frameworkComponents,
|
|
365
|
+
...ifDefined('onProgress', onProgress),
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
async dbUpdate(options: DbUpdateOptions): Promise<DbUpdateResult> {
|
|
370
|
+
const { onProgress } = options;
|
|
371
|
+
await this.connectWithProgress(options.connection, 'dbUpdate', onProgress);
|
|
401
372
|
const { driver, familyInstance, frameworkComponents } = await this.ensureConnected();
|
|
402
373
|
|
|
403
|
-
|
|
404
|
-
if (!this.options.target.migrations) {
|
|
374
|
+
if (!hasMigrations(this.options.target)) {
|
|
405
375
|
throw new Error(`Target "${this.options.target.targetId}" does not support migrations`);
|
|
406
376
|
}
|
|
407
377
|
|
|
408
|
-
|
|
409
|
-
|
|
378
|
+
let contract: Contract;
|
|
379
|
+
try {
|
|
380
|
+
contract = familyInstance.validateContract(options.contract);
|
|
381
|
+
} catch (error) {
|
|
382
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
383
|
+
throw new ContractValidationError(message, error);
|
|
384
|
+
}
|
|
410
385
|
|
|
411
|
-
|
|
412
|
-
return executeDbInit({
|
|
386
|
+
return executeDbUpdate({
|
|
413
387
|
driver,
|
|
414
388
|
familyInstance,
|
|
415
|
-
|
|
389
|
+
contract,
|
|
416
390
|
mode: options.mode,
|
|
417
391
|
migrations: this.options.target.migrations,
|
|
418
392
|
frameworkComponents,
|
|
419
|
-
...(
|
|
393
|
+
...ifDefined('acceptDataLoss', options.acceptDataLoss),
|
|
394
|
+
...ifDefined('onProgress', onProgress),
|
|
420
395
|
});
|
|
421
396
|
}
|
|
422
397
|
|
|
423
|
-
async
|
|
424
|
-
const
|
|
398
|
+
async readMarker(): Promise<ContractMarkerRecord | null> {
|
|
399
|
+
const { driver, familyInstance } = await this.ensureConnected();
|
|
400
|
+
return familyInstance.readMarker({ driver });
|
|
401
|
+
}
|
|
425
402
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
});
|
|
434
|
-
try {
|
|
435
|
-
await this.connect(options.connection);
|
|
436
|
-
onProgress?.({
|
|
437
|
-
action: 'introspect',
|
|
438
|
-
kind: 'spanEnd',
|
|
439
|
-
spanId: 'connect',
|
|
440
|
-
outcome: 'ok',
|
|
441
|
-
});
|
|
442
|
-
} catch (error) {
|
|
443
|
-
onProgress?.({
|
|
444
|
-
action: 'introspect',
|
|
445
|
-
kind: 'spanEnd',
|
|
446
|
-
spanId: 'connect',
|
|
447
|
-
outcome: 'error',
|
|
448
|
-
});
|
|
449
|
-
throw error;
|
|
450
|
-
}
|
|
403
|
+
async migrationApply(options: MigrationApplyOptions): Promise<MigrationApplyResult> {
|
|
404
|
+
const { onProgress } = options;
|
|
405
|
+
await this.connectWithProgress(options.connection, 'migrationApply', onProgress);
|
|
406
|
+
const { driver, familyInstance, frameworkComponents } = await this.ensureConnected();
|
|
407
|
+
|
|
408
|
+
if (!hasMigrations(this.options.target)) {
|
|
409
|
+
throw new Error(`Target "${this.options.target.targetId}" does not support migrations`);
|
|
451
410
|
}
|
|
452
411
|
|
|
412
|
+
return executeMigrationApply({
|
|
413
|
+
driver,
|
|
414
|
+
familyInstance,
|
|
415
|
+
originHash: options.originHash,
|
|
416
|
+
destinationHash: options.destinationHash,
|
|
417
|
+
pendingMigrations: options.pendingMigrations,
|
|
418
|
+
migrations: this.options.target.migrations,
|
|
419
|
+
frameworkComponents,
|
|
420
|
+
targetId: this.options.target.targetId,
|
|
421
|
+
...(onProgress ? { onProgress } : {}),
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
async introspect(options?: IntrospectOptions): Promise<unknown> {
|
|
426
|
+
const onProgress = options?.onProgress;
|
|
427
|
+
await this.connectWithProgress(options?.connection, 'introspect', onProgress);
|
|
453
428
|
const { driver, familyInstance } = await this.ensureConnected();
|
|
454
429
|
|
|
455
430
|
// TODO: Pass schema option to familyInstance.introspect when schema filtering is implemented
|
|
@@ -488,7 +463,7 @@ class ControlClientImpl implements ControlClient {
|
|
|
488
463
|
|
|
489
464
|
toSchemaView(schemaIR: unknown): CoreSchemaView | undefined {
|
|
490
465
|
this.init();
|
|
491
|
-
if (this.familyInstance
|
|
466
|
+
if (this.familyInstance && hasSchemaView(this.familyInstance)) {
|
|
492
467
|
return this.familyInstance.toSchemaView(schemaIR);
|
|
493
468
|
}
|
|
494
469
|
return undefined;
|
|
@@ -505,7 +480,6 @@ class ControlClientImpl implements ControlClient {
|
|
|
505
480
|
throw new Error('Family instance was not initialized. This is a bug.');
|
|
506
481
|
}
|
|
507
482
|
|
|
508
|
-
// Resolve contract source
|
|
509
483
|
let contractRaw: unknown;
|
|
510
484
|
onProgress?.({
|
|
511
485
|
action: 'emit',
|
|
@@ -515,14 +489,27 @@ class ControlClientImpl implements ControlClient {
|
|
|
515
489
|
});
|
|
516
490
|
|
|
517
491
|
try {
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
492
|
+
const sourceContext = {
|
|
493
|
+
composedExtensionPacks: (this.options.extensionPacks ?? []).map((p) => p.id),
|
|
494
|
+
};
|
|
495
|
+
const providerResult = await contractConfig.sourceProvider(sourceContext);
|
|
496
|
+
if (!providerResult.ok) {
|
|
497
|
+
onProgress?.({
|
|
498
|
+
action: 'emit',
|
|
499
|
+
kind: 'spanEnd',
|
|
500
|
+
spanId: 'resolveSource',
|
|
501
|
+
outcome: 'error',
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
return notOk({
|
|
505
|
+
code: 'CONTRACT_SOURCE_INVALID',
|
|
506
|
+
summary: providerResult.failure.summary,
|
|
507
|
+
why: providerResult.failure.summary,
|
|
508
|
+
meta: providerResult.failure.meta,
|
|
509
|
+
diagnostics: providerResult.failure,
|
|
510
|
+
});
|
|
525
511
|
}
|
|
512
|
+
contractRaw = providerResult.value;
|
|
526
513
|
|
|
527
514
|
onProgress?.({
|
|
528
515
|
action: 'emit',
|
|
@@ -538,10 +525,20 @@ class ControlClientImpl implements ControlClient {
|
|
|
538
525
|
outcome: 'error',
|
|
539
526
|
});
|
|
540
527
|
|
|
528
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
541
529
|
return notOk({
|
|
542
530
|
code: 'CONTRACT_SOURCE_INVALID',
|
|
543
531
|
summary: 'Failed to resolve contract source',
|
|
544
|
-
why:
|
|
532
|
+
why: message,
|
|
533
|
+
diagnostics: {
|
|
534
|
+
summary: 'Contract source provider threw an exception',
|
|
535
|
+
diagnostics: [
|
|
536
|
+
{
|
|
537
|
+
code: 'PROVIDER_THROW',
|
|
538
|
+
message,
|
|
539
|
+
},
|
|
540
|
+
],
|
|
541
|
+
},
|
|
545
542
|
meta: undefined,
|
|
546
543
|
});
|
|
547
544
|
}
|
|
@@ -555,7 +552,31 @@ class ControlClientImpl implements ControlClient {
|
|
|
555
552
|
});
|
|
556
553
|
|
|
557
554
|
try {
|
|
558
|
-
const
|
|
555
|
+
const enrichedIR = enrichContract(contractRaw as Contract, this.frameworkComponents ?? []);
|
|
556
|
+
|
|
557
|
+
try {
|
|
558
|
+
this.familyInstance.validateContract(enrichedIR);
|
|
559
|
+
} catch (error) {
|
|
560
|
+
onProgress?.({
|
|
561
|
+
action: 'emit',
|
|
562
|
+
kind: 'spanEnd',
|
|
563
|
+
spanId: 'emit',
|
|
564
|
+
outcome: 'error',
|
|
565
|
+
});
|
|
566
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
567
|
+
return notOk({
|
|
568
|
+
code: 'CONTRACT_VALIDATION_FAILED',
|
|
569
|
+
summary: 'Contract validation failed',
|
|
570
|
+
why: message,
|
|
571
|
+
meta: undefined,
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
const result = await emitContractArtifacts(
|
|
576
|
+
enrichedIR,
|
|
577
|
+
this.stack!,
|
|
578
|
+
this.options.family.emission,
|
|
579
|
+
);
|
|
559
580
|
|
|
560
581
|
onProgress?.({
|
|
561
582
|
action: 'emit',
|
|
@@ -565,10 +586,11 @@ class ControlClientImpl implements ControlClient {
|
|
|
565
586
|
});
|
|
566
587
|
|
|
567
588
|
return ok({
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
589
|
+
storageHash: result.storageHash,
|
|
590
|
+
...ifDefined('executionHash', result.executionHash),
|
|
591
|
+
profileHash: result.profileHash,
|
|
592
|
+
contractJson: result.contractJson,
|
|
593
|
+
contractDts: result.contractDts,
|
|
572
594
|
});
|
|
573
595
|
} catch (error) {
|
|
574
596
|
onProgress?.({
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import type { Contract } from '@prisma-next/contract/types';
|
|
2
|
+
import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
|
|
3
|
+
|
|
4
|
+
type CapabilityMatrix = Record<string, Record<string, boolean>>;
|
|
5
|
+
|
|
6
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
7
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function sortDeep(value: unknown): unknown {
|
|
11
|
+
if (Array.isArray(value)) {
|
|
12
|
+
return value.map(sortDeep);
|
|
13
|
+
}
|
|
14
|
+
if (!isPlainObject(value)) {
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
const entries = Object.entries(value).sort(([a], [b]) => a.localeCompare(b));
|
|
18
|
+
const next: Record<string, unknown> = {};
|
|
19
|
+
for (const [key, child] of entries) {
|
|
20
|
+
next[key] = sortDeep(child);
|
|
21
|
+
}
|
|
22
|
+
return next;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function sortDeepTyped<T>(value: T): T {
|
|
26
|
+
return sortDeep(value) as T;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function extractCapabilityMatrix(value: unknown): CapabilityMatrix {
|
|
30
|
+
if (!isPlainObject(value)) return {};
|
|
31
|
+
|
|
32
|
+
const out: CapabilityMatrix = {};
|
|
33
|
+
for (const [namespace, maybeCaps] of Object.entries(value)) {
|
|
34
|
+
if (!isPlainObject(maybeCaps)) continue;
|
|
35
|
+
const caps: Record<string, boolean> = {};
|
|
36
|
+
for (const [key, flag] of Object.entries(maybeCaps)) {
|
|
37
|
+
if (typeof flag === 'boolean') {
|
|
38
|
+
caps[key] = flag;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (Object.keys(caps).length > 0) {
|
|
42
|
+
out[namespace] = caps;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return out;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function mergeCapabilities(left: CapabilityMatrix, right: CapabilityMatrix): CapabilityMatrix {
|
|
50
|
+
const next: CapabilityMatrix = { ...left };
|
|
51
|
+
for (const [namespace, capabilities] of Object.entries(right)) {
|
|
52
|
+
next[namespace] = {
|
|
53
|
+
...(left[namespace] ?? {}),
|
|
54
|
+
...capabilities,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return next;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function extractExtensionPackMeta(
|
|
61
|
+
component: TargetBoundComponentDescriptor<string, string>,
|
|
62
|
+
): Record<string, unknown> {
|
|
63
|
+
const { kind, id, version, capabilities, types } = component;
|
|
64
|
+
const base: Record<string, unknown> = {
|
|
65
|
+
kind,
|
|
66
|
+
id,
|
|
67
|
+
familyId: component.familyId,
|
|
68
|
+
targetId: component.targetId,
|
|
69
|
+
version,
|
|
70
|
+
};
|
|
71
|
+
if (capabilities) {
|
|
72
|
+
base['capabilities'] = capabilities;
|
|
73
|
+
}
|
|
74
|
+
if (types) {
|
|
75
|
+
if (types.codecTypes) {
|
|
76
|
+
const { controlPlaneHooks: _, codecInstances: _ci, ...cleanedCodecTypes } = types.codecTypes;
|
|
77
|
+
base['types'] = { ...types, codecTypes: cleanedCodecTypes };
|
|
78
|
+
} else {
|
|
79
|
+
base['types'] = types;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return base;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Enriches a raw contract with framework-derived metadata:
|
|
87
|
+
* capabilities from all component descriptors and extension pack metadata
|
|
88
|
+
* from extension descriptors. Produces deterministically sorted output.
|
|
89
|
+
*/
|
|
90
|
+
export function enrichContract(
|
|
91
|
+
ir: Contract,
|
|
92
|
+
components: ReadonlyArray<TargetBoundComponentDescriptor<string, string>>,
|
|
93
|
+
): Contract {
|
|
94
|
+
let mergedCapabilities = ir.capabilities;
|
|
95
|
+
const extensionPacksMeta: Record<string, unknown> = {};
|
|
96
|
+
|
|
97
|
+
for (const component of components) {
|
|
98
|
+
if (component.capabilities) {
|
|
99
|
+
mergedCapabilities = mergeCapabilities(
|
|
100
|
+
mergedCapabilities,
|
|
101
|
+
extractCapabilityMatrix(component.capabilities),
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
if (component.kind === 'extension') {
|
|
105
|
+
extensionPacksMeta[component.id] = extractExtensionPackMeta(component);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const extensionPacks =
|
|
110
|
+
Object.keys(extensionPacksMeta).length > 0
|
|
111
|
+
? { ...ir.extensionPacks, ...extensionPacksMeta }
|
|
112
|
+
: ir.extensionPacks;
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
...ir,
|
|
116
|
+
capabilities: sortDeepTyped(mergedCapabilities),
|
|
117
|
+
extensionPacks: sortDeepTyped(extensionPacks),
|
|
118
|
+
};
|
|
119
|
+
}
|