@prisma-next/cli 0.12.0-dev.51 → 0.12.0-dev.53
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/dist/cli.mjs +7 -7
- package/dist/{client-DC-UlBLy.mjs → client-DIcitJdy.mjs} +113 -49
- package/dist/client-DIcitJdy.mjs.map +1 -0
- package/dist/commands/contract-infer.mjs +1 -1
- package/dist/commands/db-init.mjs +2 -2
- package/dist/commands/db-schema.mjs +1 -1
- package/dist/commands/db-sign.mjs +1 -1
- package/dist/commands/db-update.mjs +2 -2
- package/dist/commands/db-verify.mjs +1 -1
- package/dist/commands/migrate.d.mts +35 -1
- package/dist/commands/migrate.d.mts.map +1 -1
- package/dist/commands/migrate.mjs +287 -6
- package/dist/commands/migrate.mjs.map +1 -1
- package/dist/commands/migration-check.mjs +2 -2
- package/dist/commands/migration-graph.d.mts.map +1 -1
- package/dist/commands/migration-graph.mjs +4 -2
- package/dist/commands/migration-graph.mjs.map +1 -1
- package/dist/commands/migration-list.d.mts +1 -0
- package/dist/commands/migration-list.d.mts.map +1 -1
- package/dist/commands/migration-list.mjs +1 -1
- package/dist/commands/migration-log.mjs +1 -1
- package/dist/commands/migration-show.mjs +2 -2
- package/dist/commands/migration-status.d.mts.map +1 -1
- package/dist/commands/migration-status.mjs +2 -2
- package/dist/{contract-infer-DpGN9SAj.mjs → contract-infer-BAdhYGQH.mjs} +2 -2
- package/dist/{contract-infer-DpGN9SAj.mjs.map → contract-infer-BAdhYGQH.mjs.map} +1 -1
- package/dist/{db-verify-Cq16Obsw.mjs → db-verify-CiUCDXnv.mjs} +2 -2
- package/dist/{db-verify-Cq16Obsw.mjs.map → db-verify-CiUCDXnv.mjs.map} +1 -1
- package/dist/exports/control-api.mjs +1 -1
- package/dist/{inspect-live-schema-CRDKTNcf.mjs → inspect-live-schema-DegaqKFT.mjs} +2 -2
- package/dist/{inspect-live-schema-CRDKTNcf.mjs.map → inspect-live-schema-DegaqKFT.mjs.map} +1 -1
- package/dist/{migration-check-BxWlQBOs.mjs → migration-check-B2ccCHe7.mjs} +3 -3
- package/dist/{migration-check-BxWlQBOs.mjs.map → migration-check-B2ccCHe7.mjs.map} +1 -1
- package/dist/{migration-command-scaffold-BDd9abqW.mjs → migration-command-scaffold-D6UeN71F.mjs} +2 -2
- package/dist/{migration-command-scaffold-BDd9abqW.mjs.map → migration-command-scaffold-D6UeN71F.mjs.map} +1 -1
- package/dist/{migration-graph-space-render-CeNXh_Wy.mjs → migration-graph-space-render-B0HkTNj3.mjs} +488 -84
- package/dist/migration-graph-space-render-B0HkTNj3.mjs.map +1 -0
- package/dist/{migration-list-vJWFuXca.mjs → migration-list-mYmj2j33.mjs} +6 -4
- package/dist/migration-list-mYmj2j33.mjs.map +1 -0
- package/dist/{migration-log-6rcHQSI4.mjs → migration-log-Dzs18GU7.mjs} +3 -3
- package/dist/{migration-log-6rcHQSI4.mjs.map → migration-log-Dzs18GU7.mjs.map} +1 -1
- package/dist/{migration-path-target-UkxkgXnv.mjs → migration-path-target-DK-B7POa.mjs} +1 -1
- package/dist/{migration-path-target-UkxkgXnv.mjs.map → migration-path-target-DK-B7POa.mjs.map} +1 -1
- package/dist/{migration-status-Bjv91dE7.mjs → migration-status-BT9eCQsf.mjs} +8 -5
- package/dist/migration-status-BT9eCQsf.mjs.map +1 -0
- package/dist/{schemas-DJY2O09F.mjs → schemas-B4xeMrNt.mjs} +1 -1
- package/dist/{schemas-DJY2O09F.mjs.map → schemas-B4xeMrNt.mjs.map} +1 -1
- package/dist/types-qV41eEXH.d.mts.map +1 -1
- package/package.json +18 -18
- package/src/commands/migrate.ts +512 -2
- package/src/commands/migration-graph.ts +2 -0
- package/src/commands/migration-list.ts +3 -0
- package/src/commands/migration-status.ts +4 -0
- package/src/control-api/operations/db-run.ts +14 -3
- package/src/control-api/operations/db-verify.ts +15 -5
- package/src/control-api/operations/migrate.ts +149 -56
- package/src/utils/formatters/migration-graph-layout.ts +187 -42
- package/src/utils/formatters/migration-graph-space-render.ts +11 -1
- package/src/utils/formatters/migration-graph-tree-render.ts +609 -59
- package/src/utils/formatters/migration-list-render.ts +12 -0
- package/src/utils/formatters/migration-list-styler.ts +5 -7
- package/dist/client-DC-UlBLy.mjs.map +0 -1
- package/dist/migration-graph-space-render-CeNXh_Wy.mjs.map +0 -1
- package/dist/migration-list-vJWFuXca.mjs.map +0 -1
- package/dist/migration-status-Bjv91dE7.mjs.map +0 -1
|
@@ -17,9 +17,11 @@ import type {
|
|
|
17
17
|
import { hasOperationPreview } from '@prisma-next/framework-components/control';
|
|
18
18
|
import {
|
|
19
19
|
type ContractSpaceAggregate,
|
|
20
|
+
collectAggregateNamespaces,
|
|
20
21
|
type PlannerError,
|
|
21
22
|
planMigration,
|
|
22
23
|
} from '@prisma-next/migration-tools/aggregate';
|
|
24
|
+
import { blindCast } from '@prisma-next/utils/casts';
|
|
23
25
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
24
26
|
import { notOk, ok } from '@prisma-next/utils/result';
|
|
25
27
|
import { CliStructuredError } from '../../utils/cli-errors';
|
|
@@ -157,7 +159,10 @@ export async function executeRun<TFamilyId extends string, TTargetId extends str
|
|
|
157
159
|
spanId: SPAN_IDS.introspect,
|
|
158
160
|
label: 'Introspecting database schema',
|
|
159
161
|
});
|
|
160
|
-
const schemaIR = await familyInstance.introspect({
|
|
162
|
+
const schemaIR = await familyInstance.introspect({
|
|
163
|
+
driver,
|
|
164
|
+
contract: collectAggregateNamespaces(aggregate),
|
|
165
|
+
});
|
|
161
166
|
onProgress?.({ action, kind: 'spanEnd', spanId: SPAN_IDS.introspect, outcome: 'ok' });
|
|
162
167
|
|
|
163
168
|
// 3. Plan via aggregate planner. App is forced through synth (today's
|
|
@@ -328,7 +333,10 @@ function mapPlannerError(error: PlannerError): DbInitResult | DbUpdateResult {
|
|
|
328
333
|
why: undefined,
|
|
329
334
|
meta: undefined,
|
|
330
335
|
};
|
|
331
|
-
return
|
|
336
|
+
return blindCast<
|
|
337
|
+
DbInitResult | DbUpdateResult,
|
|
338
|
+
'notOk(failure) is shape-compatible with both DbInitResult and DbUpdateResult; the union is the return type of the surrounding function'
|
|
339
|
+
>(notOk(failure));
|
|
332
340
|
}
|
|
333
341
|
if (error.kind === 'extensionPathUnreachable') {
|
|
334
342
|
return buildRunnerFailure({
|
|
@@ -423,5 +431,8 @@ function buildRunnerFailure(args: {
|
|
|
423
431
|
conflicts: undefined,
|
|
424
432
|
...ifDefined('warnings', args.warnings),
|
|
425
433
|
};
|
|
426
|
-
return
|
|
434
|
+
return blindCast<
|
|
435
|
+
DbInitResult | DbUpdateResult,
|
|
436
|
+
'notOk(failure) is shape-compatible with both DbInitResult and DbUpdateResult; the union is the return type of the surrounding function'
|
|
437
|
+
>(notOk(failure));
|
|
427
438
|
}
|
|
@@ -8,11 +8,12 @@ import type {
|
|
|
8
8
|
} from '@prisma-next/framework-components/control';
|
|
9
9
|
import {
|
|
10
10
|
type ContractSpaceMember,
|
|
11
|
+
collectAggregateNamespaces,
|
|
11
12
|
requireHeadRef,
|
|
12
13
|
type VerifierOutput,
|
|
13
14
|
verifyMigration,
|
|
14
15
|
} from '@prisma-next/migration-tools/aggregate';
|
|
15
|
-
import { castAs } from '@prisma-next/utils/casts';
|
|
16
|
+
import { blindCast, castAs } from '@prisma-next/utils/casts';
|
|
16
17
|
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
17
18
|
import { CliStructuredError } from '../../utils/cli-errors';
|
|
18
19
|
import {
|
|
@@ -101,7 +102,12 @@ export async function executeDbVerify<TFamilyId extends string, TTargetId extend
|
|
|
101
102
|
const markersBySpaceId = await familyInstance.readAllMarkers({ driver });
|
|
102
103
|
const schemaIntrospection = skipSchema
|
|
103
104
|
? null
|
|
104
|
-
: await runIntrospection({
|
|
105
|
+
: await runIntrospection({
|
|
106
|
+
driver,
|
|
107
|
+
familyInstance,
|
|
108
|
+
onProgress,
|
|
109
|
+
contract: collectAggregateNamespaces(aggregate),
|
|
110
|
+
});
|
|
105
111
|
|
|
106
112
|
emitVerifySpan(onProgress, 'spanStart');
|
|
107
113
|
const verifyResult = verifyMigration({
|
|
@@ -130,8 +136,9 @@ async function runIntrospection<TFamilyId extends string, TTargetId extends stri
|
|
|
130
136
|
driver: ControlDriverInstance<TFamilyId, TTargetId>;
|
|
131
137
|
familyInstance: ControlFamilyInstance<TFamilyId, unknown>;
|
|
132
138
|
onProgress: OnControlProgress | undefined;
|
|
139
|
+
contract: unknown;
|
|
133
140
|
}): Promise<unknown> {
|
|
134
|
-
const { driver, familyInstance, onProgress } = args;
|
|
141
|
+
const { driver, familyInstance, onProgress, contract } = args;
|
|
135
142
|
onProgress?.({
|
|
136
143
|
action: 'dbVerify',
|
|
137
144
|
kind: 'spanStart',
|
|
@@ -139,7 +146,7 @@ async function runIntrospection<TFamilyId extends string, TTargetId extends stri
|
|
|
139
146
|
label: 'Introspecting database schema',
|
|
140
147
|
});
|
|
141
148
|
try {
|
|
142
|
-
const result = await familyInstance.introspect({ driver });
|
|
149
|
+
const result = await familyInstance.introspect({ driver, contract });
|
|
143
150
|
onProgress?.({
|
|
144
151
|
action: 'dbVerify',
|
|
145
152
|
kind: 'spanEnd',
|
|
@@ -179,7 +186,10 @@ export function createPerMemberVerifier<TFamilyId extends string, TTargetId exte
|
|
|
179
186
|
// The family's `TSchemaIR` is opaque to migration-tools; the
|
|
180
187
|
// aggregate verifier passes through whatever we hand it. The
|
|
181
188
|
// family expects its own IR shape on the way back.
|
|
182
|
-
schema:
|
|
189
|
+
schema: blindCast<
|
|
190
|
+
never,
|
|
191
|
+
'family TSchemaIR is opaque to migration-tools; projectedSchema is passed straight through'
|
|
192
|
+
>(projectedSchema),
|
|
183
193
|
strict: verifyMode === 'strict',
|
|
184
194
|
frameworkComponents,
|
|
185
195
|
});
|
|
@@ -156,58 +156,36 @@ export async function executeMigrate<TFamilyId extends string, TTargetId extends
|
|
|
156
156
|
// The aggregate passed the integrity gate, so every member's head ref
|
|
157
157
|
// is resolved (the app's is synthesised from the live contract).
|
|
158
158
|
const headRef = requireHeadRef(member);
|
|
159
|
-
const
|
|
159
|
+
const memberTargetHash = isAppMember && refHash !== undefined ? refHash : headRef.hash;
|
|
160
|
+
const memberRefInvariants = isAppMember && refHash !== undefined ? refInvariants : undefined;
|
|
160
161
|
const liveMarker = markerRows.get(member.spaceId) ?? null;
|
|
161
162
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
// still surfaces the member in `perSpace[]` as already-at-head;
|
|
170
|
-
// the runner is not invoked for these members because they have
|
|
171
|
-
// no authored ops and (for greenfield extensions) no marker to
|
|
172
|
-
// advance.
|
|
173
|
-
const liveHash = liveMarker?.storageHash;
|
|
174
|
-
if (
|
|
175
|
-
targetHash === liveHash ||
|
|
176
|
-
(liveHash === undefined && targetHash === EMPTY_CONTRACT_HASH)
|
|
177
|
-
) {
|
|
178
|
-
atHeadResolutions.set(
|
|
179
|
-
member.spaceId,
|
|
180
|
-
buildAtHeadResolution({
|
|
181
|
-
aggregateTargetId: aggregate.targetId,
|
|
182
|
-
member,
|
|
183
|
-
targetHash,
|
|
184
|
-
liveMarker,
|
|
185
|
-
}),
|
|
186
|
-
);
|
|
187
|
-
continue;
|
|
188
|
-
}
|
|
189
|
-
return notOk(buildNeverPlannedFailure(member.spaceId, targetHash));
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const targetInvariants =
|
|
193
|
-
isAppMember && refHash !== undefined && refInvariants !== undefined
|
|
194
|
-
? refInvariants
|
|
195
|
-
: headRef.invariants;
|
|
196
|
-
const targetMember: ContractSpaceMember =
|
|
197
|
-
targetHash === headRef.hash && targetInvariants === headRef.invariants
|
|
198
|
-
? member
|
|
199
|
-
: { ...member, headRef: { hash: targetHash, invariants: targetInvariants } };
|
|
200
|
-
|
|
201
|
-
const walked = graphWalkStrategy({
|
|
202
|
-
aggregateTargetId: aggregate.targetId,
|
|
203
|
-
member: targetMember,
|
|
204
|
-
currentMarker: liveMarker,
|
|
205
|
-
...(isAppMember && refName !== undefined ? { refName } : {}),
|
|
163
|
+
const outcome = planMemberPath({
|
|
164
|
+
member,
|
|
165
|
+
aggregate,
|
|
166
|
+
targetHash: memberTargetHash,
|
|
167
|
+
refInvariants: memberRefInvariants,
|
|
168
|
+
liveMarker,
|
|
169
|
+
...(isAppMember ? { refName } : {}),
|
|
206
170
|
});
|
|
207
|
-
|
|
208
|
-
|
|
171
|
+
|
|
172
|
+
if (outcome.kind === 'at-head') {
|
|
173
|
+
// Empty-graph member whose live marker already matches the target.
|
|
174
|
+
// Kept out of the runner schedule so we don't write spurious markers
|
|
175
|
+
// for greenfield extensions, but merged back into the success envelope
|
|
176
|
+
// so every loaded member is represented.
|
|
177
|
+
atHeadResolutions.set(member.spaceId, outcome.plan);
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
if (outcome.kind === 'never-planned') {
|
|
181
|
+
return notOk(buildNeverPlannedFailure(outcome.spaceId, outcome.targetHash));
|
|
182
|
+
}
|
|
183
|
+
if (outcome.kind === 'unreachable') {
|
|
184
|
+
return notOk(
|
|
185
|
+
buildPathNotFoundFailure(outcome.spaceId, outcome.liveMarker, outcome.targetHash),
|
|
186
|
+
);
|
|
209
187
|
}
|
|
210
|
-
if (
|
|
188
|
+
if (outcome.kind === 'unsatisfiable') {
|
|
211
189
|
// Surface the canonical MIGRATION.NO_INVARIANT_PATH envelope
|
|
212
190
|
// (the error rendering pipeline maps it to meta.code +
|
|
213
191
|
// meta.required + meta.missing + meta.structuralPath that the
|
|
@@ -218,10 +196,12 @@ export async function executeMigrate<TFamilyId extends string, TTargetId extends
|
|
|
218
196
|
// string here would leave the structural lookup with a hash that
|
|
219
197
|
// is never a graph node, producing an empty `structuralPath` and
|
|
220
198
|
// a less actionable diagnostic.
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
199
|
+
const structural = findPathWithDecision(
|
|
200
|
+
outcome.targetMember.graph(),
|
|
201
|
+
outcome.liveHash,
|
|
202
|
+
memberTargetHash,
|
|
203
|
+
{ required: new Set<string>() },
|
|
204
|
+
);
|
|
225
205
|
const structuralPath =
|
|
226
206
|
structural.kind === 'ok'
|
|
227
207
|
? structural.decision.selectedPath.map((edge) => ({
|
|
@@ -233,14 +213,14 @@ export async function executeMigrate<TFamilyId extends string, TTargetId extends
|
|
|
233
213
|
}))
|
|
234
214
|
: [];
|
|
235
215
|
throw errorNoInvariantPath({
|
|
236
|
-
...(
|
|
237
|
-
required: targetInvariants,
|
|
238
|
-
missing:
|
|
216
|
+
...(outcome.refName !== undefined ? { refName: outcome.refName } : {}),
|
|
217
|
+
required: outcome.targetInvariants,
|
|
218
|
+
missing: outcome.missing,
|
|
239
219
|
structuralPath,
|
|
240
220
|
});
|
|
241
221
|
}
|
|
242
222
|
|
|
243
|
-
perSpacePlans.set(member.spaceId,
|
|
223
|
+
perSpacePlans.set(member.spaceId, outcome.plan);
|
|
244
224
|
}
|
|
245
225
|
|
|
246
226
|
const canonicalOrder = [...aggregate.extensions.map((m) => m.spaceId), aggregate.app.spaceId];
|
|
@@ -340,6 +320,119 @@ export async function executeMigrate<TFamilyId extends string, TTargetId extends
|
|
|
340
320
|
);
|
|
341
321
|
}
|
|
342
322
|
|
|
323
|
+
/**
|
|
324
|
+
* Outcome variants for one member's path computation.
|
|
325
|
+
*
|
|
326
|
+
* Callers switch on `kind` and map to their own error representation:
|
|
327
|
+
* `executeMigrate` throws / returns `notOk`; `executeMigrateShowCommand`
|
|
328
|
+
* returns a CLI structured error. The shared discriminant guarantees both
|
|
329
|
+
* paths feed `graphWalkStrategy` the same inputs.
|
|
330
|
+
*
|
|
331
|
+
* @internal Exported for `executeMigrateShowCommand` to call.
|
|
332
|
+
*/
|
|
333
|
+
export type MemberPathOutcome =
|
|
334
|
+
| { readonly kind: 'ok'; readonly plan: PerSpacePlan }
|
|
335
|
+
| { readonly kind: 'at-head'; readonly plan: PerSpacePlan }
|
|
336
|
+
| { readonly kind: 'never-planned'; readonly spaceId: string; readonly targetHash: string }
|
|
337
|
+
| {
|
|
338
|
+
readonly kind: 'unreachable';
|
|
339
|
+
readonly spaceId: string;
|
|
340
|
+
readonly liveMarker: ContractMarkerRecordLike | null;
|
|
341
|
+
readonly targetHash: string;
|
|
342
|
+
}
|
|
343
|
+
| {
|
|
344
|
+
readonly kind: 'unsatisfiable';
|
|
345
|
+
readonly spaceId: string;
|
|
346
|
+
readonly isAppMember: boolean;
|
|
347
|
+
readonly missing: readonly string[];
|
|
348
|
+
readonly targetInvariants: readonly string[];
|
|
349
|
+
readonly targetMember: ContractSpaceMember;
|
|
350
|
+
readonly liveHash: string;
|
|
351
|
+
readonly refName: string | undefined;
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Compute the graph-walk path for one contract-space member.
|
|
356
|
+
*
|
|
357
|
+
* Encapsulates the invariant-correct input assembly that both
|
|
358
|
+
* `executeMigrate` and `executeMigrateShowCommand` must use:
|
|
359
|
+
* - `currentMarker` carries the full live marker including `invariants`
|
|
360
|
+
* (not a stripped `{ storageHash, invariants: [] }` shell).
|
|
361
|
+
* - `targetInvariants` uses the caller-supplied `refInvariants` when a
|
|
362
|
+
* `--to` ref was resolved (not always the file head ref's invariants).
|
|
363
|
+
*
|
|
364
|
+
* Both callers map the returned `MemberPathOutcome` to their own error
|
|
365
|
+
* representation; the path-compute logic is shared and identical.
|
|
366
|
+
*
|
|
367
|
+
* @internal Exported for `executeMigrateShowCommand`.
|
|
368
|
+
*/
|
|
369
|
+
export function planMemberPath({
|
|
370
|
+
member,
|
|
371
|
+
aggregate,
|
|
372
|
+
targetHash,
|
|
373
|
+
refInvariants,
|
|
374
|
+
liveMarker,
|
|
375
|
+
refName,
|
|
376
|
+
}: {
|
|
377
|
+
readonly member: ContractSpaceMember;
|
|
378
|
+
readonly aggregate: Pick<ContractSpaceAggregate, 'targetId' | 'app'>;
|
|
379
|
+
readonly targetHash: string;
|
|
380
|
+
readonly refInvariants: readonly string[] | undefined;
|
|
381
|
+
readonly liveMarker: ContractMarkerRecordLike | null;
|
|
382
|
+
readonly refName?: string;
|
|
383
|
+
}): MemberPathOutcome {
|
|
384
|
+
const isAppMember = member.spaceId === aggregate.app.spaceId;
|
|
385
|
+
const headRef = requireHeadRef(member);
|
|
386
|
+
|
|
387
|
+
if (member.graph().nodes.size === 0) {
|
|
388
|
+
const liveHash = liveMarker?.storageHash;
|
|
389
|
+
if (targetHash === liveHash || (liveHash === undefined && targetHash === EMPTY_CONTRACT_HASH)) {
|
|
390
|
+
return {
|
|
391
|
+
kind: 'at-head',
|
|
392
|
+
plan: buildAtHeadResolution({
|
|
393
|
+
aggregateTargetId: aggregate.targetId,
|
|
394
|
+
member,
|
|
395
|
+
targetHash,
|
|
396
|
+
liveMarker,
|
|
397
|
+
}),
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
return { kind: 'never-planned', spaceId: member.spaceId, targetHash };
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const targetInvariants =
|
|
404
|
+
isAppMember && refInvariants !== undefined ? refInvariants : headRef.invariants;
|
|
405
|
+
const targetMember: ContractSpaceMember =
|
|
406
|
+
targetHash === headRef.hash && targetInvariants === headRef.invariants
|
|
407
|
+
? member
|
|
408
|
+
: { ...member, headRef: { hash: targetHash, invariants: targetInvariants } };
|
|
409
|
+
|
|
410
|
+
const walked = graphWalkStrategy({
|
|
411
|
+
aggregateTargetId: aggregate.targetId,
|
|
412
|
+
member: targetMember,
|
|
413
|
+
currentMarker: liveMarker,
|
|
414
|
+
...(isAppMember && refName !== undefined ? { refName } : {}),
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
if (walked.kind === 'unreachable') {
|
|
418
|
+
return { kind: 'unreachable', spaceId: member.spaceId, liveMarker, targetHash };
|
|
419
|
+
}
|
|
420
|
+
if (walked.kind === 'unsatisfiable') {
|
|
421
|
+
const liveHash = liveMarker?.storageHash ?? EMPTY_CONTRACT_HASH;
|
|
422
|
+
return {
|
|
423
|
+
kind: 'unsatisfiable',
|
|
424
|
+
spaceId: member.spaceId,
|
|
425
|
+
isAppMember,
|
|
426
|
+
missing: walked.missing,
|
|
427
|
+
targetInvariants,
|
|
428
|
+
targetMember,
|
|
429
|
+
liveHash,
|
|
430
|
+
refName,
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
return { kind: 'ok', plan: walked.result };
|
|
434
|
+
}
|
|
435
|
+
|
|
343
436
|
/**
|
|
344
437
|
* Build a zero-op {@link PerSpacePlan} for an empty-graph
|
|
345
438
|
* member whose live marker already matches the target. Lets the apply
|