@highstate/backend 0.9.31 → 0.9.32
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/{chunk-QSHSXLO2.js → chunk-ZRKVL3EL.js} +22 -21
- package/dist/chunk-ZRKVL3EL.js.map +1 -0
- package/dist/index.js +88 -13
- package/dist/index.js.map +1 -1
- package/dist/library/worker/main.js +1 -1
- package/dist/library/worker/main.js.map +1 -1
- package/dist/shared/index.js +1 -1
- package/package.json +4 -4
- package/src/business/project.ts +60 -0
- package/src/library/worker/evaluator.ts +1 -1
- package/src/orchestrator/operation-context.ts +37 -10
- package/src/orchestrator/operation-plan.fixtures.ts +1 -0
- package/src/orchestrator/operation-plan.ts +14 -0
- package/src/orchestrator/operation-plan.update.test.ts +70 -0
- package/src/orchestrator/operation-workset.ts +4 -2
- package/src/orchestrator/plan-test-builder.ts +8 -8
- package/src/runner/pulumi.ts +6 -1
- package/src/shared/models/prisma.ts +1 -1
- package/src/shared/models/project/operation.ts +14 -0
- package/src/shared/models/project/state.ts +12 -0
- package/src/shared/resolvers/input-hash.ts +3 -0
- package/src/shared/resolvers/registry.ts +0 -5
- package/dist/chunk-QSHSXLO2.js.map +0 -1
- package/src/shared/resolvers/state.ts +0 -19
|
@@ -53,7 +53,7 @@ function evaluateProject(logger2, components, allInstances, resolvedInputs) {
|
|
|
53
53
|
}
|
|
54
54
|
function _evaluateInstance(instance) {
|
|
55
55
|
const inputs = {};
|
|
56
|
-
logger2.debug(
|
|
56
|
+
logger2.debug(`evaluating instance "%s"`, instance.id);
|
|
57
57
|
for (const [inputName, input] of Object.entries(resolvedInputs[instance.id] ?? {})) {
|
|
58
58
|
inputs[inputName] = input.map((input2) => {
|
|
59
59
|
const evaluated = evaluateInstance(input2.input.instanceId);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/library/worker/evaluator.ts","../../../src/library/worker/loader.lite.ts","../../../src/library/worker/main.ts"],"names":["logger","error","input"],"mappings":";;;;;;AAWO,SAAS,eAAA,CACdA,OAAAA,EACA,UAAA,EACA,YAAA,EACA,cAAA,EACyB;AACzB,EAAA,MAAM,eAAA,GAAkB,IAAI,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,CAAA,QAAA,KAAY,CAAC,QAAA,CAAS,EAAA,EAAI,QAAQ,CAAC,CAAC,CAAA;AAErF,EAAA,MAAM,iBAA0C,EAAC;AACjD,EAAA,MAAM,iBAAyC,EAAC;AAEhD,EAAA,MAAM,eAAA,uBAAsB,GAAA,EAAqC;AAEjE,EAAA,KAAA,MAAW,YAAY,YAAA,EAAc;AACnC,IAAA,IAAI;AACF,MAAA,gBAAA,CAAiB,SAAS,EAAE,CAAA;AAAA,IAC9B,SAAS,KAAA,EAAO;AACd,MAAA,IAAI,iBAAiB,yBAAA,EAA2B;AAE9C,QAAA,OAAO;AAAA,UACL,OAAA,EAAS,KAAA;AAAA,UACT,OAAO,KAAA,CAAM;AAAA,SACf;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,EAAA,OAAO;AAAA,IACL,OAAA,EAAS,IAAA;AAAA,IAET,kBAAkB,mBAAA,EAAoB,CACnC,IAAI,CAAA,QAAA,KAAY,QAAA,CAAS,QAAQ,CAAA,CAEjC,MAAA,CAAO,CAAA,QAAA,KAAY,QAAA,CAAS,SAAS,WAAA,IAAe,CAAC,gBAAgB,GAAA,CAAI,QAAA,CAAS,EAAE,CAAC,CAAA;AAAA,IAExF;AAAA,GACF;AAEA,EAAA,SAAS,iBAAiB,UAAA,EAA0D;AAClF,IAAA,IAAI,OAAA,GAAU,eAAA,CAAgB,GAAA,CAAI,UAAU,CAAA;AAC5C,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,OAAO,OAAA;AAAA,IACT;AAGA,IAAA,MAAM,KAAA,GAAQ,eAAe,UAAU,CAAA;AACvC,IAAA,IAAI,KAAA,EAAO;AAET,MAAA,MAAM,KAAA;AAAA,IACR;AAEA,IAAA,MAAM,QAAA,GAAW,eAAA,CAAgB,GAAA,CAAI,UAAU,CAAA;AAC/C,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,oBAAA,EAAuB,UAAU,CAAA,CAAE,CAAA;AAAA,IACrD;AAEA,IAAA,IAAI;AACF,MAAA,OAAA,GAAU,kBAAkB,QAAQ,CAAA;AAEpC,MAAA,eAAA,CAAgB,GAAA,CAAI,YAAY,OAAO,CAAA;AACvC,MAAA,OAAO,OAAA;AAAA,IACT,SAASC,MAAAA,EAAO;AACd,MAAA,IAAI,QAAA,CAAS,SAAS,WAAA,IAAe,CAAC,gBAAgB,GAAA,CAAI,QAAA,CAAS,EAAE,CAAA,EAAG;AACtE,QAAA,cAAA,CAAe,QAAA,CAAS,EAAE,CAAA,GAAI,aAAA,CAAcA,MAAK,CAAA;AAAA,MACnD;AAEA,MAAA,cAAA,CAAe,UAAU,CAAA,GAAIA,MAAAA;AAC7B,MAAA,MAAMA,MAAAA;AAAA,IACR;AAAA,EACF;AAEA,EAAA,SAAS,kBAAkB,QAAA,EAAkD;AAC3E,IAAA,MAAM,SAAkC,EAAC;AAEzC,IAAAD,
|
|
1
|
+
{"version":3,"sources":["../../../src/library/worker/evaluator.ts","../../../src/library/worker/loader.lite.ts","../../../src/library/worker/main.ts"],"names":["logger","error","input"],"mappings":";;;;;;AAWO,SAAS,eAAA,CACdA,OAAAA,EACA,UAAA,EACA,YAAA,EACA,cAAA,EACyB;AACzB,EAAA,MAAM,eAAA,GAAkB,IAAI,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,CAAA,QAAA,KAAY,CAAC,QAAA,CAAS,EAAA,EAAI,QAAQ,CAAC,CAAC,CAAA;AAErF,EAAA,MAAM,iBAA0C,EAAC;AACjD,EAAA,MAAM,iBAAyC,EAAC;AAEhD,EAAA,MAAM,eAAA,uBAAsB,GAAA,EAAqC;AAEjE,EAAA,KAAA,MAAW,YAAY,YAAA,EAAc;AACnC,IAAA,IAAI;AACF,MAAA,gBAAA,CAAiB,SAAS,EAAE,CAAA;AAAA,IAC9B,SAAS,KAAA,EAAO;AACd,MAAA,IAAI,iBAAiB,yBAAA,EAA2B;AAE9C,QAAA,OAAO;AAAA,UACL,OAAA,EAAS,KAAA;AAAA,UACT,OAAO,KAAA,CAAM;AAAA,SACf;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,EAAA,OAAO;AAAA,IACL,OAAA,EAAS,IAAA;AAAA,IAET,kBAAkB,mBAAA,EAAoB,CACnC,IAAI,CAAA,QAAA,KAAY,QAAA,CAAS,QAAQ,CAAA,CAEjC,MAAA,CAAO,CAAA,QAAA,KAAY,QAAA,CAAS,SAAS,WAAA,IAAe,CAAC,gBAAgB,GAAA,CAAI,QAAA,CAAS,EAAE,CAAC,CAAA;AAAA,IAExF;AAAA,GACF;AAEA,EAAA,SAAS,iBAAiB,UAAA,EAA0D;AAClF,IAAA,IAAI,OAAA,GAAU,eAAA,CAAgB,GAAA,CAAI,UAAU,CAAA;AAC5C,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,OAAO,OAAA;AAAA,IACT;AAGA,IAAA,MAAM,KAAA,GAAQ,eAAe,UAAU,CAAA;AACvC,IAAA,IAAI,KAAA,EAAO;AAET,MAAA,MAAM,KAAA;AAAA,IACR;AAEA,IAAA,MAAM,QAAA,GAAW,eAAA,CAAgB,GAAA,CAAI,UAAU,CAAA;AAC/C,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,oBAAA,EAAuB,UAAU,CAAA,CAAE,CAAA;AAAA,IACrD;AAEA,IAAA,IAAI;AACF,MAAA,OAAA,GAAU,kBAAkB,QAAQ,CAAA;AAEpC,MAAA,eAAA,CAAgB,GAAA,CAAI,YAAY,OAAO,CAAA;AACvC,MAAA,OAAO,OAAA;AAAA,IACT,SAASC,MAAAA,EAAO;AACd,MAAA,IAAI,QAAA,CAAS,SAAS,WAAA,IAAe,CAAC,gBAAgB,GAAA,CAAI,QAAA,CAAS,EAAE,CAAA,EAAG;AACtE,QAAA,cAAA,CAAe,QAAA,CAAS,EAAE,CAAA,GAAI,aAAA,CAAcA,MAAK,CAAA;AAAA,MACnD;AAEA,MAAA,cAAA,CAAe,UAAU,CAAA,GAAIA,MAAAA;AAC7B,MAAA,MAAMA,MAAAA;AAAA,IACR;AAAA,EACF;AAEA,EAAA,SAAS,kBAAkB,QAAA,EAAkD;AAC3E,IAAA,MAAM,SAAkC,EAAC;AAEzC,IAAAD,OAAAA,CAAO,KAAA,CAAM,CAAA,wBAAA,CAAA,EAA4B,QAAA,CAAS,EAAE,CAAA;AAEpD,IAAA,KAAA,MAAW,CAAC,SAAA,EAAW,KAAK,CAAA,IAAK,MAAA,CAAO,OAAA,CAAQ,cAAA,CAAe,QAAA,CAAS,EAAE,CAAA,IAAK,EAAE,CAAA,EAAG;AAClF,MAAA,MAAA,CAAO,SAAS,CAAA,GAAI,KAAA,CAAM,GAAA,CAAI,CAAAE,MAAAA,KAAS;AACrC,QAAA,MAAM,SAAA,GAAY,gBAAA,CAAiBA,MAAAA,CAAM,KAAA,CAAM,UAAU,CAAA;AAEzD,QAAA,OAAO,SAAA,CAAUA,MAAAA,CAAM,KAAA,CAAM,MAAM,CAAA;AAAA,MACrC,CAAC,CAAA;AAAA,IACH;AAEA,IAAA,MAAM,SAAA,GAAY,UAAA,CAAW,QAAA,CAAS,IAAI,CAAA;AAC1C,IAAA,IAAI,CAAC,SAAA,EAAW;AACd,MAAA,MAAM,IAAI,MAAM,CAAA,qBAAA,EAAwB,QAAA,CAAS,IAAI,CAAA,wBAAA,EAA2B,QAAA,CAAS,EAAE,CAAA,CAAE,CAAA;AAAA,IAC/F;AAEA,IAAA,OAAO,SAAA,CAAU;AAAA,MACf,MAAM,QAAA,CAAS,IAAA;AAAA,MACf,MAAM,QAAA,CAAS,IAAA;AAAA,MACf;AAAA,KACD,CAAA;AAAA,EACH;AACF;ACvGA,eAAsB,cAAA,CACpBF,SACA,WAAA,EAC8C;AAC9C,EAAA,MAAM,UAAmC,EAAC;AAC1C,EAAA,KAAA,MAAW,cAAc,WAAA,EAAa;AACpC,IAAA,IAAI;AACF,MAAAA,OAAAA,CAAO,KAAA,CAAM,EAAE,UAAA,IAAc,gBAAgB,CAAA;AAC7C,MAAA,OAAA,CAAQ,UAAU,CAAA,GAAI,MAAM,OAAO,UAAA,CAAA;AAEnC,MAAAA,OAAAA,CAAO,KAAA,CAAM,EAAE,UAAA,IAAc,eAAe,CAAA;AAAA,IAC9C,SAAS,GAAA,EAAK;AACZ,MAAAA,QAAO,KAAA,CAAM,EAAE,UAAA,EAAY,GAAA,IAAO,oBAAoB,CAAA;AAAA,IACxD;AAAA,EACF;AAEA,EAAA,MAAM,aAAwC,EAAC;AAE/C,EAAA,MAAM,YAAA,CAAa,SAAS,UAAU,CAAA;AACtC,EAAAA,QAAO,KAAA,CAAM,mCAAA,EAAqC,OAAO,IAAA,CAAK,UAAU,EAAE,MAAM,CAAA;AAEhF,EAAA,OAAO,UAAA;AACT;AAEA,eAAe,YAAA,CAAa,OAAgB,UAAA,EAAsD;AAChG,EAAA,IAAI,WAAA,CAAY,KAAK,CAAA,EAAG;AACtB,IAAA,UAAA,CAAW,KAAA,CAAM,KAAA,CAAM,IAAI,CAAA,GAAI,KAAA;AAC/B,IAAA;AAAA,EACF;AAEA,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,KAAU,IAAA,EAAM;AAC/C,IAAA;AAAA,EACF;AAEA,EAAA,IAAI,UAAU,KAAA,EAAO;AAEnB,IAAA;AAAA,EACF;AAEA,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACxB,IAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,MAAA,MAAM,YAAA,CAAa,MAAM,UAAU,CAAA;AAAA,IACrC;AAEA,IAAA;AAAA,EACF;AAEA,EAAA,KAAA,MAAW,OAAO,KAAA,EAAO;AACvB,IAAA,MAAM,YAAA,CAAc,KAAA,CAAkC,GAAG,CAAA,EAAG,UAAU,CAAA;AAAA,EACxE;AACF;;;AC9CA,IAAM,IAAA,GAAO,UAAA;AAEb,IAAM,MAAA,GAAS,IAAA,CAAK,EAAE,IAAA,EAAM,kBAAkB,CAAA;AAE9C,IAAI;AACF,EAAA,MAAM,OAAA,GAAU,MAAM,cAAA,CAAe,MAAA,EAAQ,KAAK,kBAAkB,CAAA;AACpE,EAAA,MAAM,SAAS,eAAA,CAAgB,MAAA,EAAQ,SAAS,IAAA,CAAK,YAAA,EAAc,KAAK,cAAc,CAAA;AAEtF,EAAA,UAAA,EAAY,YAAY,MAAM,CAAA;AAChC,CAAA,CAAA,OAAS,KAAA,EAAO;AACd,EAAA,MAAA,CAAO,KAAA,CAAM,EAAE,KAAA,EAAM,EAAG,4BAA4B,CAAA;AAEpD,EAAA,UAAA,EAAY,WAAA,CAAY;AAAA,IACtB,OAAA,EAAS,KAAA;AAAA,IACT,KAAA,EAAO,cAAc,KAAK;AAAA,GAC3B,CAAA;AACH","file":"main.js","sourcesContent":["import type { Logger } from \"pino\"\nimport type { ResolvedInstanceInput } from \"../../shared\"\nimport type { ProjectEvaluationResult } from \"../abstractions\"\nimport {\n type Component,\n getRuntimeInstances,\n type InstanceModel,\n InstanceNameConflictError,\n} from \"@highstate/contract\"\nimport { errorToString } from \"../../common\"\n\nexport function evaluateProject(\n logger: Logger,\n components: Readonly<Record<string, Component>>,\n allInstances: InstanceModel[],\n resolvedInputs: Record<string, Record<string, ResolvedInstanceInput[]>>,\n): ProjectEvaluationResult {\n const allInstancesMap = new Map(allInstances.map(instance => [instance.id, instance]))\n\n const instanceErrors: Record<string, unknown> = {}\n const topLevelErrors: Record<string, string> = {}\n\n const instanceOutputs = new Map<string, Record<string, unknown>>()\n\n for (const instance of allInstances) {\n try {\n evaluateInstance(instance.id)\n } catch (error) {\n if (error instanceof InstanceNameConflictError) {\n // fail the whole evaluation if there's a name conflict\n return {\n success: false,\n error: error.message,\n }\n }\n }\n }\n\n return {\n success: true,\n\n virtualInstances: getRuntimeInstances()\n .map(instance => instance.instance)\n // only include top-level composite instances and their children\n .filter(instance => instance.kind === \"composite\" || !allInstancesMap.has(instance.id)),\n\n topLevelErrors,\n }\n\n function evaluateInstance(instanceId: InstanceModel[\"id\"]): Record<string, unknown> {\n let outputs = instanceOutputs.get(instanceId)\n if (outputs) {\n return outputs\n }\n\n // do not evaluate instance if it has an error, just rethrow it\n const error = instanceErrors[instanceId]\n if (error) {\n // eslint-disable-next-line @typescript-eslint/only-throw-error\n throw error\n }\n\n const instance = allInstancesMap.get(instanceId)\n if (!instance) {\n throw new Error(`Instance not found: ${instanceId}`)\n }\n\n try {\n outputs = _evaluateInstance(instance)\n\n instanceOutputs.set(instanceId, outputs)\n return outputs\n } catch (error) {\n if (instance.kind === \"composite\" || !allInstancesMap.has(instance.id)) {\n topLevelErrors[instance.id] = errorToString(error)\n }\n\n instanceErrors[instanceId] = error\n throw error\n }\n }\n\n function _evaluateInstance(instance: InstanceModel): Record<string, unknown> {\n const inputs: Record<string, unknown> = {}\n\n logger.debug(`evaluating instance \"%s\"`, instance.id)\n\n for (const [inputName, input] of Object.entries(resolvedInputs[instance.id] ?? {})) {\n inputs[inputName] = input.map(input => {\n const evaluated = evaluateInstance(input.input.instanceId)\n\n return evaluated[input.input.output]\n })\n }\n\n const component = components[instance.type]\n if (!component) {\n throw new Error(`Component not found: ${instance.type}, required by instance: ${instance.id}`)\n }\n\n return component({\n name: instance.name,\n args: instance.args as Record<string, never>,\n inputs: inputs as Record<string, never>,\n })\n }\n}\n","import type { Logger } from \"pino\"\nimport { type Component, isComponent } from \"@highstate/contract\"\n\nexport async function loadComponents(\n logger: Logger,\n modulePaths: string[],\n): Promise<Readonly<Record<string, Component>>> {\n const modules: Record<string, unknown> = {}\n for (const modulePath of modulePaths) {\n try {\n logger.debug({ modulePath }, \"loading module\")\n modules[modulePath] = await import(modulePath)\n\n logger.debug({ modulePath }, \"module loaded\")\n } catch (err) {\n logger.error({ modulePath, err }, \"module load failed\")\n }\n }\n\n const components: Record<string, Component> = {}\n\n await _loadLibrary(modules, components)\n logger.debug(\"library loaded with %s components\", Object.keys(components).length)\n\n return components\n}\n\nasync function _loadLibrary(value: unknown, components: Record<string, Component>): Promise<void> {\n if (isComponent(value)) {\n components[value.model.type] = value\n return\n }\n\n if (typeof value !== \"object\" || value === null) {\n return\n }\n\n if (\"_zod\" in value) {\n // this is a zod schema, we can skip it\n return\n }\n\n if (Array.isArray(value)) {\n for (const item of value) {\n await _loadLibrary(item, components)\n }\n\n return\n }\n\n for (const key in value) {\n await _loadLibrary((value as Record<string, unknown>)[key], components)\n }\n}\n","import type { WorkerData } from \"./protocol\"\nimport { parentPort, workerData } from \"node:worker_threads\"\nimport { pino } from \"pino\"\nimport { errorToString } from \"../../common\"\nimport { evaluateProject } from \"./evaluator\"\nimport { loadComponents } from \"./loader.lite\"\n\nconst data = workerData as WorkerData\n\nconst logger = pino({ name: \"library-worker\" })\n\ntry {\n const library = await loadComponents(logger, data.libraryModulePaths)\n const result = evaluateProject(logger, library, data.allInstances, data.resolvedInputs)\n\n parentPort?.postMessage(result)\n} catch (error) {\n logger.error({ error }, \"failed to evaluate project\")\n\n parentPort?.postMessage({\n success: false,\n error: errorToString(error),\n })\n}\n"]}
|
package/dist/shared/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { AccessError, BackendError, BackendUnlockMethodNotFoundError, CannotDeleteLastBackendUnlockMethodError, CannotDeleteLastUnlockMethodError, GraphResolver, InputHashResolver, InputResolver, InstanceLockLostError, InstanceLockedError, InstanceNotFoundError, InstanceStateNotFoundError, InvalidInstanceKindError, MAX_WORKER_START_ATTEMPTS, OperationNotFoundError, ProjectLockedError, ProjectNotFoundError, PromiseTracker, SystemSecretNames, ValidationResolver, WorkerVersionNotFoundError, apiKeyMetaSchema, apiKeyOutputSchema, apiKeyQuerySchema, applyLibraryUpdate, artifactOutputSchema, artifactQuerySchema, backendUnlockMethodInputSchema, backendUnlockMethodMetaSchema, codebaseLibrary, codebaseProjectModelStorage, collectionQueryResult, collectionQuerySchema, createAsyncBatcher, databaseProjectModelStorage, diffLibraries, extractDigestFromImage, finalInstanceOperationStatuses, finalOperationStatuses, forSchema, getAllDependents, getMatchedInjectionInstanceInputs, getResolvedHubInputs, getResolvedInjectionInstanceInputs, getResolvedInstanceInputs, getResolvedInstanceOutputs, getWorkerIdentity, globalProjectSpace, hasObjectMeta, hostPulumiBackend, instanceCustomStatusInputSchema, instanceLockEventSchema, instanceLockOutputSchema, instanceStateEventSchema, int32ToBytes, isFinalOperationStatus, isInstanceDeployed, isTransientInstanceOperationStatus, isTransientOperationStatus, isVirtualGhostInstance, librarySpecSchema, operationEventSchema, operationLaunchInputSchema, operationMetaSchema, operationOptionsSchema, operationOutputSchema, operationPhaseInstanceSchema, operationPhaseSchema, operationPhaseTypeSchema, operationPlanInputSchema, operationStatusSchema, operationTypeSchema, pageDetailsOutputSchema, pageOutputSchema, pageQuerySchema, projectInputSchema, projectModelEventSchema, projectModelStorageSpecSchema, projectOutputSchema, projectUnlockStateSchema, projectUnlockSuiteSchema, pulumiBackendSpecSchema, resolverFactories, secretOutputSchema, secretQuerySchema, serviceAccountOutputSchema, serviceAccountQuerySchema, terminalDetailsOutputSchema, terminalOutputSchema, terminalQuerySchema, terminalSessionOutputSchema, terminalStatusSchema, toApiKeyOutput, toPageOutput, toSecretOutput, toTerminalDetailsOutput, toTerminalOutput, toTerminalSessionOutput, toWorkerOutput, toWorkerVersionOutput, triggerOutputSchema, triggerQuerySchema, unlockMethodInputSchema, unlockMethodMetaSchema, unlockMethodOutputSchema, unlockMethodType, waitAll, workerOutputSchema, workerQuerySchema, workerUnitRegistrationEventSchema, workerVersionOutputSchema, workerVersionStatusSchema } from '../chunk-
|
|
1
|
+
export { AccessError, BackendError, BackendUnlockMethodNotFoundError, CannotDeleteLastBackendUnlockMethodError, CannotDeleteLastUnlockMethodError, GraphResolver, InputHashResolver, InputResolver, InstanceLockLostError, InstanceLockedError, InstanceNotFoundError, InstanceStateNotFoundError, InvalidInstanceKindError, MAX_WORKER_START_ATTEMPTS, OperationNotFoundError, ProjectLockedError, ProjectNotFoundError, PromiseTracker, SystemSecretNames, ValidationResolver, WorkerVersionNotFoundError, apiKeyMetaSchema, apiKeyOutputSchema, apiKeyQuerySchema, applyLibraryUpdate, artifactOutputSchema, artifactQuerySchema, backendUnlockMethodInputSchema, backendUnlockMethodMetaSchema, codebaseLibrary, codebaseProjectModelStorage, collectionQueryResult, collectionQuerySchema, createAsyncBatcher, databaseProjectModelStorage, diffLibraries, extractDigestFromImage, finalInstanceOperationStatuses, finalOperationStatuses, forSchema, getAllDependents, getMatchedInjectionInstanceInputs, getResolvedHubInputs, getResolvedInjectionInstanceInputs, getResolvedInstanceInputs, getResolvedInstanceOutputs, getWorkerIdentity, globalProjectSpace, hasObjectMeta, hostPulumiBackend, instanceCustomStatusInputSchema, instanceLockEventSchema, instanceLockOutputSchema, instanceStateEventSchema, int32ToBytes, isFinalOperationStatus, isInstanceDeployed, isTransientInstanceOperationStatus, isTransientOperationStatus, isVirtualGhostInstance, librarySpecSchema, operationEventSchema, operationLaunchInputSchema, operationMetaSchema, operationOptionsSchema, operationOutputSchema, operationPhaseInstanceSchema, operationPhaseSchema, operationPhaseTypeSchema, operationPlanInputSchema, operationStatusSchema, operationTypeSchema, pageDetailsOutputSchema, pageOutputSchema, pageQuerySchema, projectInputSchema, projectModelEventSchema, projectModelStorageSpecSchema, projectOutputSchema, projectUnlockStateSchema, projectUnlockSuiteSchema, pulumiBackendSpecSchema, resolverFactories, secretOutputSchema, secretQuerySchema, serviceAccountOutputSchema, serviceAccountQuerySchema, stableInstanceInputSchema, terminalDetailsOutputSchema, terminalOutputSchema, terminalQuerySchema, terminalSessionOutputSchema, terminalStatusSchema, toApiKeyOutput, toPageOutput, toSecretOutput, toTerminalDetailsOutput, toTerminalOutput, toTerminalSessionOutput, toWorkerOutput, toWorkerVersionOutput, triggerOutputSchema, triggerQuerySchema, unlockMethodInputSchema, unlockMethodMetaSchema, unlockMethodOutputSchema, unlockMethodType, waitAll, workerOutputSchema, workerQuerySchema, workerUnitRegistrationEventSchema, workerVersionOutputSchema, workerVersionStatusSchema } from '../chunk-ZRKVL3EL.js';
|
|
2
2
|
import '../chunk-I7BWSAN6.js';
|
|
3
3
|
//# sourceMappingURL=index.js.map
|
|
4
4
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@highstate/backend",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.32",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"highstate": {
|
|
6
6
|
"sourceHash": {
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@aws-crypto/crc32": "^5.2.0",
|
|
44
|
-
"@highstate/contract": "^0.9.
|
|
44
|
+
"@highstate/contract": "^0.9.32",
|
|
45
45
|
"@msgpack/msgpack": "^3.1.2",
|
|
46
46
|
"@napi-rs/keyring": "^1.1.8",
|
|
47
47
|
"@noble/ciphers": "^1.3.0",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"zod": "^4.0.5"
|
|
76
76
|
},
|
|
77
77
|
"peerDependencies": {
|
|
78
|
-
"@pulumi/pulumi": "^3.
|
|
78
|
+
"@pulumi/pulumi": "^3.198.0",
|
|
79
79
|
"classic-level": "^2.0.0"
|
|
80
80
|
},
|
|
81
81
|
"peerDependenciesMeta": {
|
|
@@ -98,5 +98,5 @@
|
|
|
98
98
|
"type-fest": "^4.41.0",
|
|
99
99
|
"vitest": "^3.2.4"
|
|
100
100
|
},
|
|
101
|
-
"gitHead": "
|
|
101
|
+
"gitHead": "00a7c1c40e556daeecfa9e5f3f6a1e60d88911ee"
|
|
102
102
|
}
|
package/src/business/project.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
type InstanceModel,
|
|
13
13
|
type InstanceModelPatch,
|
|
14
14
|
isUnitModel,
|
|
15
|
+
parseInstanceId,
|
|
15
16
|
} from "@highstate/contract"
|
|
16
17
|
import { createId } from "@paralleldrive/cuid2"
|
|
17
18
|
import { createProjectLogger } from "../common"
|
|
@@ -166,6 +167,65 @@ export class ProjectService {
|
|
|
166
167
|
where: { id: state.id },
|
|
167
168
|
data: { instanceId: instance.id },
|
|
168
169
|
})
|
|
170
|
+
|
|
171
|
+
// if the instance is composite, we also need to rename all child instance states
|
|
172
|
+
// we will replace all old instance name occurrences with the new names
|
|
173
|
+
// this will not work in general, since child IDs may be arbitrary strings:
|
|
174
|
+
// e.g. we have parent with name "foo" and child with name pattern "{parent}-foo"
|
|
175
|
+
// if we rename parent to "bar", the child should be renamed to "bar-foo",
|
|
176
|
+
// but will be renamed to "bar-bar" with this approach, leading to child recreation
|
|
177
|
+
// it is not ideal, but this is the best we can do in the current evaluation model
|
|
178
|
+
|
|
179
|
+
if (instance.kind !== "composite") {
|
|
180
|
+
return
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const [, oldName] = parseInstanceId(instanceId)
|
|
184
|
+
|
|
185
|
+
// TODO: use proper SQL to do this in the database instead of in memory
|
|
186
|
+
const allStates = await tx.instanceState.findMany({
|
|
187
|
+
where: { source: "virtual" },
|
|
188
|
+
select: { id: true, parentId: true, instanceId: true },
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
const stateMap = new Map<string, { parentId: string | null }>()
|
|
192
|
+
for (const s of allStates) {
|
|
193
|
+
stateMap.set(s.instanceId, s)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const isChild = (s: { parentId: string | null }) => {
|
|
197
|
+
while (s.parentId) {
|
|
198
|
+
if (s.parentId === state.id) {
|
|
199
|
+
return true
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const parent = stateMap.get(s.parentId)
|
|
203
|
+
if (!parent) {
|
|
204
|
+
break
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
s = parent
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return false
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
for (const childState of allStates) {
|
|
214
|
+
if (!isChild(childState)) {
|
|
215
|
+
// reduce the scope of replacement to only child instances to reduce risk of incorrect renames
|
|
216
|
+
continue
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const newChildInstanceId = childState.instanceId.replace(oldName, newName)
|
|
220
|
+
if (newChildInstanceId === childState.instanceId) {
|
|
221
|
+
continue
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
await tx.instanceState.update({
|
|
225
|
+
where: { id: childState.id },
|
|
226
|
+
data: { instanceId: newChildInstanceId },
|
|
227
|
+
})
|
|
228
|
+
}
|
|
169
229
|
})
|
|
170
230
|
|
|
171
231
|
await this.pubsubManager.publish(["project-model", projectId], {
|
|
@@ -83,7 +83,7 @@ export function evaluateProject(
|
|
|
83
83
|
function _evaluateInstance(instance: InstanceModel): Record<string, unknown> {
|
|
84
84
|
const inputs: Record<string, unknown> = {}
|
|
85
85
|
|
|
86
|
-
logger.debug(
|
|
86
|
+
logger.debug(`evaluating instance "%s"`, instance.id)
|
|
87
87
|
|
|
88
88
|
for (const [inputName, input] of Object.entries(resolvedInputs[instance.id] ?? {})) {
|
|
89
89
|
inputs[inputName] = input.map(input => {
|
|
@@ -4,11 +4,12 @@ import type { LibraryBackend } from "../library"
|
|
|
4
4
|
import {
|
|
5
5
|
type ComponentModel,
|
|
6
6
|
type InstanceId,
|
|
7
|
+
type InstanceInput,
|
|
7
8
|
type InstanceModel,
|
|
8
9
|
isUnitModel,
|
|
9
10
|
} from "@highstate/contract"
|
|
10
11
|
import { BetterLock } from "better-lock"
|
|
11
|
-
import { unique } from "remeda"
|
|
12
|
+
import { mapValues, unique } from "remeda"
|
|
12
13
|
import {
|
|
13
14
|
type InputHashNode,
|
|
14
15
|
type InputHashOutput,
|
|
@@ -20,12 +21,14 @@ import {
|
|
|
20
21
|
type LibraryModel,
|
|
21
22
|
type ProjectOutput,
|
|
22
23
|
type ResolvedInstanceInput,
|
|
24
|
+
type StableInstanceInput,
|
|
23
25
|
} from "../shared"
|
|
24
26
|
|
|
25
27
|
export class OperationContext {
|
|
26
28
|
private readonly instanceMap = new Map<InstanceId, InstanceModel>()
|
|
27
29
|
private readonly instanceChildrenMap = new Map<InstanceId, InstanceModel[]>()
|
|
28
30
|
|
|
31
|
+
private readonly stateIdMap = new Map<string, InstanceId>()
|
|
29
32
|
private readonly stateMap = new Map<InstanceId, InstanceState>()
|
|
30
33
|
private readonly dependentStateIdMap = new Map<InstanceId, Set<InstanceId>>()
|
|
31
34
|
private readonly stateChildIdMap = new Map<InstanceId, InstanceId[]>()
|
|
@@ -79,6 +82,7 @@ export class OperationContext {
|
|
|
79
82
|
|
|
80
83
|
public setState(state: InstanceState): void {
|
|
81
84
|
this.stateMap.set(state.instanceId, state)
|
|
85
|
+
this.stateIdMap.set(state.id, state.instanceId)
|
|
82
86
|
|
|
83
87
|
if (state.parentInstanceId) {
|
|
84
88
|
let children = this.stateChildIdMap.get(state.parentInstanceId)
|
|
@@ -94,14 +98,35 @@ export class OperationContext {
|
|
|
94
98
|
if (state.resolvedInputs) {
|
|
95
99
|
for (const inputGroup of Object.values(state.resolvedInputs)) {
|
|
96
100
|
for (const input of inputGroup) {
|
|
97
|
-
|
|
98
|
-
|
|
101
|
+
const instanceId = this.stateIdMap.get(input.stateId)
|
|
102
|
+
if (!instanceId) {
|
|
103
|
+
this.logger.warn(
|
|
104
|
+
`cannot add dependent state for unknown input state ID: ${input.stateId}`,
|
|
105
|
+
)
|
|
106
|
+
continue
|
|
99
107
|
}
|
|
108
|
+
|
|
109
|
+
this.addDependentState(state.instanceId, instanceId)
|
|
100
110
|
}
|
|
101
111
|
}
|
|
102
112
|
}
|
|
103
113
|
}
|
|
104
114
|
|
|
115
|
+
public serializeResolvedInputs(
|
|
116
|
+
resolvedInputs: Record<string, InstanceInput[]>,
|
|
117
|
+
): Record<string, StableInstanceInput[]> {
|
|
118
|
+
return mapValues(resolvedInputs, inputs =>
|
|
119
|
+
inputs.map(input => {
|
|
120
|
+
const state = this.getState(input.instanceId)
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
stateId: state.id,
|
|
124
|
+
output: input.output,
|
|
125
|
+
}
|
|
126
|
+
}),
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
|
|
105
130
|
private addDependentState(instanceId: InstanceId, dependencyId: InstanceId): void {
|
|
106
131
|
let dependentStates = this.dependentStateIdMap.get(dependencyId)
|
|
107
132
|
|
|
@@ -134,15 +159,17 @@ export class OperationContext {
|
|
|
134
159
|
}
|
|
135
160
|
|
|
136
161
|
public getDependencies(instanceId: InstanceId): InstanceModel[] {
|
|
137
|
-
const dependencies: InstanceModel[] = []
|
|
138
162
|
const resolvedInputs = this.resolvedInstanceInputs.get(instanceId)
|
|
163
|
+
if (!resolvedInputs) {
|
|
164
|
+
return []
|
|
165
|
+
}
|
|
139
166
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
167
|
+
const dependencies: InstanceModel[] = []
|
|
168
|
+
|
|
169
|
+
for (const inputGroup of Object.values(resolvedInputs)) {
|
|
170
|
+
for (const resolvedInput of inputGroup) {
|
|
171
|
+
if (resolvedInput.input.instanceId && resolvedInput.input.instanceId !== instanceId) {
|
|
172
|
+
dependencies.push(this.getInstance(resolvedInput.input.instanceId))
|
|
146
173
|
}
|
|
147
174
|
}
|
|
148
175
|
}
|
|
@@ -35,6 +35,12 @@ export function createOperationPlan(
|
|
|
35
35
|
requestedInstanceIds: string[],
|
|
36
36
|
options: OperationOptions,
|
|
37
37
|
): OperationPhase[] {
|
|
38
|
+
if (options.forceUpdateDependencies && options.ignoreDependencies) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
"Operation options are invalid: forceUpdateDependencies and ignoreDependencies cannot both be enabled.",
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
38
44
|
// initialize work state
|
|
39
45
|
const workState: WorkState = {
|
|
40
46
|
included: new Map(),
|
|
@@ -195,6 +201,10 @@ function processUpdateInclusions(
|
|
|
195
201
|
if (workState.included.has(instance.id)) {
|
|
196
202
|
const dependencies = context.getDependencies(instance.id)
|
|
197
203
|
for (const depInstance of dependencies) {
|
|
204
|
+
if (options.ignoreDependencies) {
|
|
205
|
+
continue
|
|
206
|
+
}
|
|
207
|
+
|
|
198
208
|
const shouldInclude = options.forceUpdateDependencies || isOutdated(depInstance, context)
|
|
199
209
|
|
|
200
210
|
if (shouldInclude && !workState.included.has(depInstance.id)) {
|
|
@@ -237,6 +247,10 @@ function processRefreshInclusions(
|
|
|
237
247
|
if (workState.included.has(instance.id)) {
|
|
238
248
|
const dependencies = context.getDependencies(instance.id)
|
|
239
249
|
for (const depInstance of dependencies) {
|
|
250
|
+
if (options.ignoreDependencies) {
|
|
251
|
+
continue
|
|
252
|
+
}
|
|
253
|
+
|
|
240
254
|
const shouldInclude = options.forceUpdateDependencies
|
|
241
255
|
|
|
242
256
|
if (shouldInclude && !workState.included.has(depInstance.id)) {
|
|
@@ -48,6 +48,47 @@ describe("OperationPlan - Update Operations", () => {
|
|
|
48
48
|
},
|
|
49
49
|
)
|
|
50
50
|
|
|
51
|
+
operationPlanTest(
|
|
52
|
+
"1a. should ignore dependencies when option enabled",
|
|
53
|
+
async ({ testBuilder, expect }) => {
|
|
54
|
+
// arrange
|
|
55
|
+
const { context, operation } = await testBuilder()
|
|
56
|
+
.unit("A")
|
|
57
|
+
.unit("B")
|
|
58
|
+
.unit("C")
|
|
59
|
+
.depends("C", "B")
|
|
60
|
+
.depends("B", "A")
|
|
61
|
+
.states({ A: "upToDate", B: "changed", C: "upToDate" })
|
|
62
|
+
.options({ ignoreDependencies: true })
|
|
63
|
+
.request("update", "C")
|
|
64
|
+
.build()
|
|
65
|
+
|
|
66
|
+
// act
|
|
67
|
+
const plan = createOperationPlan(
|
|
68
|
+
context,
|
|
69
|
+
operation.type,
|
|
70
|
+
operation.requestedInstanceIds,
|
|
71
|
+
operation.options,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
// assert
|
|
75
|
+
expect(plan).toMatchInlineSnapshot(`
|
|
76
|
+
[
|
|
77
|
+
{
|
|
78
|
+
"instances": [
|
|
79
|
+
{
|
|
80
|
+
"id": "component.v1:C",
|
|
81
|
+
"message": "explicitly requested",
|
|
82
|
+
"parentId": undefined,
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
"type": "update",
|
|
86
|
+
},
|
|
87
|
+
]
|
|
88
|
+
`)
|
|
89
|
+
},
|
|
90
|
+
)
|
|
91
|
+
|
|
51
92
|
operationPlanTest(
|
|
52
93
|
"2. should not propagate beyond compositional inclusion",
|
|
53
94
|
async ({ testBuilder, expect }) => {
|
|
@@ -158,6 +199,35 @@ describe("OperationPlan - Update Operations", () => {
|
|
|
158
199
|
},
|
|
159
200
|
)
|
|
160
201
|
|
|
202
|
+
operationPlanTest(
|
|
203
|
+
"3a. should reject conflicting dependency options",
|
|
204
|
+
async ({ testBuilder, expect }) => {
|
|
205
|
+
// arrange
|
|
206
|
+
const { context, operation } = await testBuilder()
|
|
207
|
+
.unit("A")
|
|
208
|
+
.unit("B")
|
|
209
|
+
.unit("C")
|
|
210
|
+
.depends("C", "B")
|
|
211
|
+
.depends("B", "A")
|
|
212
|
+
.states({ A: "upToDate", B: "upToDate", C: "upToDate" })
|
|
213
|
+
.options({ forceUpdateDependencies: true, ignoreDependencies: true })
|
|
214
|
+
.request("update", "C")
|
|
215
|
+
.build()
|
|
216
|
+
|
|
217
|
+
// act & assert
|
|
218
|
+
expect(() =>
|
|
219
|
+
createOperationPlan(
|
|
220
|
+
context,
|
|
221
|
+
operation.type,
|
|
222
|
+
operation.requestedInstanceIds,
|
|
223
|
+
operation.options,
|
|
224
|
+
),
|
|
225
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
226
|
+
"[Error: Operation options are invalid: forceUpdateDependencies and ignoreDependencies cannot both be enabled.]",
|
|
227
|
+
)
|
|
228
|
+
},
|
|
229
|
+
)
|
|
230
|
+
|
|
161
231
|
operationPlanTest(
|
|
162
232
|
"4. should include outdated children of substantive composite",
|
|
163
233
|
async ({ testBuilder, expect }) => {
|
|
@@ -120,18 +120,20 @@ export class OperationWorkset {
|
|
|
120
120
|
inputs => inputs.map(input => input.input),
|
|
121
121
|
)
|
|
122
122
|
|
|
123
|
+
const serializedResolvedInputs = this.context.serializeResolvedInputs(resolvedInputs)
|
|
124
|
+
|
|
123
125
|
return [
|
|
124
126
|
{
|
|
125
127
|
stateId: state.id,
|
|
126
128
|
operationId: this.operationId,
|
|
127
129
|
status: "pending",
|
|
128
130
|
model: instance,
|
|
129
|
-
resolvedInputs,
|
|
131
|
+
resolvedInputs: serializedResolvedInputs,
|
|
130
132
|
},
|
|
131
133
|
{
|
|
132
134
|
status: state.status === "undeployed" ? "attempted" : state.status,
|
|
133
135
|
model: instance,
|
|
134
|
-
resolvedInputs,
|
|
136
|
+
resolvedInputs: serializedResolvedInputs,
|
|
135
137
|
},
|
|
136
138
|
]
|
|
137
139
|
}),
|
|
@@ -209,14 +209,6 @@ export class PlanTestBuilder {
|
|
|
209
209
|
const instances = Array.from(this.instances.values())
|
|
210
210
|
const states = Array.from(this.stateMap.values())
|
|
211
211
|
|
|
212
|
-
// Copy resolvedInputs from instances to states for dependency tracking
|
|
213
|
-
states.forEach(state => {
|
|
214
|
-
const instance = instances.find(i => i.id === state.instanceId)
|
|
215
|
-
if (instance?.resolvedInputs) {
|
|
216
|
-
state.resolvedInputs = instance.resolvedInputs
|
|
217
|
-
}
|
|
218
|
-
})
|
|
219
|
-
|
|
220
212
|
// get requested instance IDs
|
|
221
213
|
const requestedInstanceIds = this.requestedInstanceNames.map(name => {
|
|
222
214
|
const instance = this.instances.get(name)
|
|
@@ -234,6 +226,14 @@ export class PlanTestBuilder {
|
|
|
234
226
|
// create context with instances and initial states
|
|
235
227
|
const context = await this.createContext(instances, states)
|
|
236
228
|
|
|
229
|
+
// copy resolvedInputs from instances to states for dependency tracking
|
|
230
|
+
states.forEach(state => {
|
|
231
|
+
const instance = instances.find(i => i.id === state.instanceId)
|
|
232
|
+
if (instance?.resolvedInputs) {
|
|
233
|
+
state.resolvedInputs = context.serializeResolvedInputs(instance.resolvedInputs)
|
|
234
|
+
}
|
|
235
|
+
})
|
|
236
|
+
|
|
237
237
|
// update "upToDate" states with correct input hashes from context
|
|
238
238
|
const updatedStates = states.map(state => {
|
|
239
239
|
const stateEntry = Array.from(this.stateMap.entries()).find(
|
package/src/runner/pulumi.ts
CHANGED
|
@@ -111,7 +111,12 @@ export class LocalPulumiHost {
|
|
|
111
111
|
{
|
|
112
112
|
projectSettings: {
|
|
113
113
|
name: pulumiProjectName,
|
|
114
|
-
runtime:
|
|
114
|
+
runtime: {
|
|
115
|
+
name: "nodejs",
|
|
116
|
+
options: {
|
|
117
|
+
nodeargs: "--no-deprecation",
|
|
118
|
+
},
|
|
119
|
+
},
|
|
115
120
|
main: "index.js",
|
|
116
121
|
},
|
|
117
122
|
stackSettings: stackConfig
|
|
@@ -24,7 +24,7 @@ declare global {
|
|
|
24
24
|
type InstanceModel = contract.InstanceModel
|
|
25
25
|
|
|
26
26
|
type InstanceArgs = Record<string, unknown>
|
|
27
|
-
type InstanceResolvedInputs = Record<string,
|
|
27
|
+
type InstanceResolvedInputs = Record<string, shared.StableInstanceInput[]>
|
|
28
28
|
type UnlockMethodMeta = shared.UnlockMethodMeta
|
|
29
29
|
type ProjectUnlockSuite = shared.ProjectUnlockSuite
|
|
30
30
|
|
|
@@ -61,6 +61,20 @@ export const operationOptionsSchema = z
|
|
|
61
61
|
*/
|
|
62
62
|
forceUpdateDependencies: z.boolean().default(false),
|
|
63
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Ignore dependencies and operate only on explicitly requested instances.
|
|
66
|
+
*
|
|
67
|
+
* **Operation Behavior Impact:**
|
|
68
|
+
* - skips dependency inclusion even when dependencies are failed or undeployed;
|
|
69
|
+
* - caller must explicitly include every prerequisite instance to avoid failures;
|
|
70
|
+
* - complements on-demand or targeted updates where dependency safety is managed externally.
|
|
71
|
+
*
|
|
72
|
+
* **Usage with other options:**
|
|
73
|
+
* - mutually exclusive with `forceUpdateDependencies`;
|
|
74
|
+
* - independent of child/composite inclusion options.
|
|
75
|
+
*/
|
|
76
|
+
ignoreDependencies: z.boolean().default(false),
|
|
77
|
+
|
|
64
78
|
/**
|
|
65
79
|
* Force update all children of composite instances regardless of their state.
|
|
66
80
|
*
|
|
@@ -8,6 +8,18 @@ import type {
|
|
|
8
8
|
} from "../../../database"
|
|
9
9
|
import { z } from "zod"
|
|
10
10
|
|
|
11
|
+
export const stableInstanceInputSchema = z.object({
|
|
12
|
+
stateId: z.string(),
|
|
13
|
+
output: z.string(),
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The instance input that references state IDs instead of instance IDs.
|
|
18
|
+
*
|
|
19
|
+
* This provides a stable reference to an instance output that is not affected by instance ID changes.
|
|
20
|
+
*/
|
|
21
|
+
export type StableInstanceInput = z.infer<typeof stableInstanceInputSchema>
|
|
22
|
+
|
|
11
23
|
/**
|
|
12
24
|
* The instance state aggregate including all related states.
|
|
13
25
|
*/
|
|
@@ -45,6 +45,9 @@ export class InputHashResolver extends GraphResolver<InputHashNode, InputHashOut
|
|
|
45
45
|
}: InputHashNode): InputHashOutput {
|
|
46
46
|
const inputHashSink: Uint8Array[] = []
|
|
47
47
|
|
|
48
|
+
// 0. include the instance id to reflect renames
|
|
49
|
+
inputHashSink.push(Buffer.from(instance.id))
|
|
50
|
+
|
|
48
51
|
// 1. include the component definition hash
|
|
49
52
|
inputHashSink.push(int32ToBytes(component.definitionHash))
|
|
50
53
|
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import type { Logger } from "pino"
|
|
2
|
-
import type { InstanceState } from "../models/project"
|
|
3
2
|
import type { DependentSetHandler, GraphResolver, ResolverOutputHandler } from "./graph-resolver"
|
|
4
3
|
import { InputResolver, type InputResolverNode, type InputResolverOutput } from "./input"
|
|
5
4
|
import { type InputHashNode, type InputHashOutput, InputHashResolver } from "./input-hash"
|
|
6
|
-
import { StateResolver } from "./state"
|
|
7
5
|
import { type ValidationNode, type ValidationOutput, ValidationResolver } from "./validation"
|
|
8
6
|
|
|
9
7
|
export type GraphResolverType = keyof GraphResolverMap
|
|
@@ -12,15 +10,12 @@ export type GraphResolverMap = {
|
|
|
12
10
|
InputResolver: [InputResolverNode, InputResolverOutput]
|
|
13
11
|
InputHashResolver: [InputHashNode, InputHashOutput]
|
|
14
12
|
ValidationResolver: [ValidationNode, ValidationOutput]
|
|
15
|
-
// biome-ignore lint/suspicious/noConfusingVoidType: it is return type
|
|
16
|
-
StateResolver: [InstanceState, void]
|
|
17
13
|
}
|
|
18
14
|
|
|
19
15
|
export const resolverFactories = {
|
|
20
16
|
InputResolver,
|
|
21
17
|
InputHashResolver,
|
|
22
18
|
ValidationResolver,
|
|
23
|
-
StateResolver,
|
|
24
19
|
} as Record<
|
|
25
20
|
GraphResolverType,
|
|
26
21
|
new (
|