@highstate/backend 0.19.1 → 0.20.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/dist/{chunk-V2NILDHS.js → chunk-52MY2TCE.js} +347 -19
- package/dist/chunk-52MY2TCE.js.map +1 -0
- package/dist/{chunk-I7BWSAN6.js → chunk-UAWBPTDW.js} +3 -3
- package/dist/{chunk-I7BWSAN6.js.map → chunk-UAWBPTDW.js.map} +1 -1
- package/dist/highstate.manifest.json +4 -4
- package/dist/index.js +4159 -785
- package/dist/index.js.map +1 -1
- package/dist/library/worker/main.js +5 -2
- package/dist/library/worker/main.js.map +1 -1
- package/dist/shared/index.js +2 -2
- package/package.json +7 -7
- package/prisma/backend/_schema/object.prisma +12 -0
- package/prisma/backend/sqlite/migrations/20260222113554_add_object_tracking/migration.sql +7 -0
- package/prisma/project/artifact.prisma +3 -0
- package/prisma/project/entity.prisma +125 -0
- package/prisma/project/instance.prisma +6 -0
- package/prisma/project/migrations/20260301210131_add_entity_tracking/migration.sql +70 -0
- package/prisma/project/migrations/20260302212734_add_resource_hooks_flag/migration.sql +1 -0
- package/prisma/project/operation.prisma +3 -0
- package/src/business/artifact.test.ts +22 -2
- package/src/business/artifact.ts +7 -1
- package/src/business/entity-snapshot.test.ts +684 -0
- package/src/business/entity-snapshot.ts +904 -0
- package/src/business/evaluation.test.ts +56 -0
- package/src/business/evaluation.ts +102 -22
- package/src/business/global-search.test.ts +344 -0
- package/src/business/global-search.ts +902 -0
- package/src/business/index.ts +4 -0
- package/src/business/instance-lock.ts +58 -74
- package/src/business/instance-state.test.ts +15 -1
- package/src/business/instance-state.ts +37 -14
- package/src/business/object-ref-index.test.ts +140 -0
- package/src/business/object-ref-index.ts +193 -0
- package/src/business/operation.test.ts +15 -1
- package/src/business/operation.ts +4 -0
- package/src/business/project-model.ts +154 -13
- package/src/business/project-unlock.ts +25 -2
- package/src/business/project.ts +9 -0
- package/src/business/secret.test.ts +35 -2
- package/src/business/secret.ts +32 -9
- package/src/business/settings.ts +761 -0
- package/src/business/unit-output.test.ts +477 -0
- package/src/business/unit-output.ts +461 -0
- package/src/business/worker.ts +55 -4
- package/src/database/_generated/backend/postgresql/browser.ts +6 -0
- package/src/database/_generated/backend/postgresql/client.ts +6 -0
- package/src/database/_generated/backend/postgresql/internal/class.ts +23 -5
- package/src/database/_generated/backend/postgresql/internal/prismaNamespace.ts +89 -5
- package/src/database/_generated/backend/postgresql/internal/prismaNamespaceBrowser.ts +9 -0
- package/src/database/_generated/backend/postgresql/models/Object.ts +1076 -0
- package/src/database/_generated/backend/postgresql/models.ts +1 -0
- package/src/database/_generated/backend/sqlite/browser.ts +6 -0
- package/src/database/_generated/backend/sqlite/client.ts +6 -0
- package/src/database/_generated/backend/sqlite/internal/class.ts +23 -5
- package/src/database/_generated/backend/sqlite/internal/prismaNamespace.ts +89 -5
- package/src/database/_generated/backend/sqlite/internal/prismaNamespaceBrowser.ts +9 -0
- package/src/database/_generated/backend/sqlite/models/Object.ts +1074 -0
- package/src/database/_generated/backend/sqlite/models.ts +1 -0
- package/src/database/_generated/project/browser.ts +23 -0
- package/src/database/_generated/project/client.ts +23 -0
- package/src/database/_generated/project/commonInputTypes.ts +87 -53
- package/src/database/_generated/project/enums.ts +8 -0
- package/src/database/_generated/project/internal/class.ts +53 -5
- package/src/database/_generated/project/internal/prismaNamespace.ts +367 -13
- package/src/database/_generated/project/internal/prismaNamespaceBrowser.ts +48 -1
- package/src/database/_generated/project/models/Artifact.ts +199 -11
- package/src/database/_generated/project/models/Entity.ts +1274 -0
- package/src/database/_generated/project/models/EntitySnapshot.ts +2389 -0
- package/src/database/_generated/project/models/EntitySnapshotContent.ts +1260 -0
- package/src/database/_generated/project/models/EntitySnapshotReference.ts +1449 -0
- package/src/database/_generated/project/models/InstanceState.ts +361 -1
- package/src/database/_generated/project/models/Operation.ts +148 -3
- package/src/database/_generated/project/models/OperationLog.ts +0 -4
- package/src/database/_generated/project/models.ts +4 -0
- package/src/database/migration.ts +3 -0
- package/src/library/worker/evaluator.ts +7 -1
- package/src/orchestrator/manager.ts +7 -0
- package/src/orchestrator/operation-context.captured-outputs.test.ts +118 -0
- package/src/orchestrator/operation-context.ts +154 -16
- package/src/orchestrator/operation-plan.destroy.test.md +33 -12
- package/src/orchestrator/operation-plan.destroy.test.ts +140 -2
- package/src/orchestrator/operation-plan.fixtures.ts +2 -0
- package/src/orchestrator/operation-plan.md +4 -1
- package/src/orchestrator/operation-plan.ts +286 -92
- package/src/orchestrator/operation-plan.update.test.md +286 -11
- package/src/orchestrator/operation-plan.update.test.ts +656 -5
- package/src/orchestrator/operation-workset.ts +72 -22
- package/src/orchestrator/operation.cancel.test.ts +4 -0
- package/src/orchestrator/operation.composite.test.ts +341 -0
- package/src/orchestrator/operation.destroy.test.ts +4 -0
- package/src/orchestrator/operation.output-validation.failure.test.ts +124 -0
- package/src/orchestrator/operation.preview.test.ts +4 -0
- package/src/orchestrator/operation.refresh.test.ts +4 -0
- package/src/orchestrator/operation.test-utils.ts +52 -13
- package/src/orchestrator/operation.ts +228 -68
- package/src/orchestrator/operation.update.failure.test.ts +4 -0
- package/src/orchestrator/operation.update.skip.test.ts +110 -0
- package/src/orchestrator/operation.update.test.ts +4 -0
- package/src/orchestrator/plan-test-builder.ts +1 -0
- package/src/orchestrator/unit-input-values.test.ts +450 -0
- package/src/orchestrator/unit-input-values.ts +281 -0
- package/src/pubsub/manager.ts +3 -0
- package/src/runner/abstractions.ts +23 -54
- package/src/runner/local.ts +109 -85
- package/src/services.ts +52 -1
- package/src/shared/models/prisma.ts +1 -0
- package/src/shared/models/project/entity.ts +121 -0
- package/src/shared/models/project/index.ts +1 -0
- package/src/shared/models/project/operation.ts +61 -3
- package/src/shared/models/project/state.ts +10 -0
- package/src/shared/models/project/worker.ts +7 -0
- package/src/shared/resolvers/effective-output-type.test.ts +494 -0
- package/src/shared/resolvers/effective-output-type.ts +162 -0
- package/src/shared/resolvers/index.ts +1 -0
- package/src/shared/resolvers/input.ts +59 -9
- package/src/shared/utils/index.ts +1 -0
- package/src/shared/utils/stable-json.ts +41 -0
- package/src/terminal/manager.ts +6 -0
- package/src/worker/manager.ts +97 -1
- package/dist/chunk-V2NILDHS.js.map +0 -1
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { errorToString } from '../../chunk-X2WG3WGL.js';
|
|
2
|
-
import '../../chunk-
|
|
2
|
+
import '../../chunk-UAWBPTDW.js';
|
|
3
3
|
import { workerData, parentPort } from 'node:worker_threads';
|
|
4
4
|
import { pino } from 'pino';
|
|
5
5
|
import { InstanceNameConflictError, getRuntimeInstances, parseArgumentValue, isComponent } from '@highstate/contract';
|
|
6
6
|
import { mapValues } from 'remeda';
|
|
7
7
|
|
|
8
|
+
function toCloneSafeInstanceModel(instance) {
|
|
9
|
+
return JSON.parse(JSON.stringify(instance));
|
|
10
|
+
}
|
|
8
11
|
function evaluateProject(logger2, components, allInstances, resolvedInputs) {
|
|
9
12
|
const allInstancesMap = new Map(allInstances.map((instance) => [instance.id, instance]));
|
|
10
13
|
const instanceErrors = {};
|
|
@@ -24,7 +27,7 @@ function evaluateProject(logger2, components, allInstances, resolvedInputs) {
|
|
|
24
27
|
}
|
|
25
28
|
return {
|
|
26
29
|
success: true,
|
|
27
|
-
virtualInstances: getRuntimeInstances().map((instance) => instance.instance).filter((instance) => instance.kind === "composite" || !allInstancesMap.has(instance.id)),
|
|
30
|
+
virtualInstances: getRuntimeInstances().map((instance) => toCloneSafeInstanceModel(instance.instance)).filter((instance) => instance.kind === "composite" || !allInstancesMap.has(instance.id)),
|
|
28
31
|
topLevelErrors
|
|
29
32
|
};
|
|
30
33
|
function evaluateInstance(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":"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","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 parseArgumentValue,\n} from \"@highstate/contract\"\nimport { mapValues } from \"remeda\"\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 const parsedArgs = mapValues(instance.args ?? {}, rawValue => parseArgumentValue(rawValue))\n\n return component({\n name: instance.name,\n args: parsedArgs 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"]}
|
|
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":"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","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 parseArgumentValue,\n} from \"@highstate/contract\"\nimport { mapValues } from \"remeda\"\nimport { errorToString } from \"../../common\"\n\nfunction toCloneSafeInstanceModel(instance: InstanceModel): InstanceModel {\n // Runtime instances may contain Proxy-based accessors in outputs/resolvedOutputs.\n // Serialize to plain data so worker thread postMessage can structured-clone it.\n return JSON.parse(JSON.stringify(instance)) as InstanceModel\n}\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 => toCloneSafeInstanceModel(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 const parsedArgs = mapValues(instance.args ?? {}, rawValue => parseArgumentValue(rawValue))\n\n return component({\n name: instance.name,\n args: parsedArgs 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, 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-
|
|
2
|
-
import '../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, entityDetailsOutputSchema, entityOutputSchema, entityQuerySchema, entityReferenceOutputSchema, entitySnapshotDetailsOutputSchema, entitySnapshotListItemOutputSchema, entitySnapshotOutputSchema, 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, resolveEffectiveOutputType, resolverFactories, secretOutputSchema, secretQuerySchema, serviceAccountOutputSchema, serviceAccountQuerySchema, stableInstanceInputSchema, stableJsonStringify, terminalDetailsOutputSchema, terminalOutputSchema, terminalQuerySchema, terminalSessionOutputSchema, terminalStatusSchema, toApiKeyOutput, toCommonEntityMeta, toPageOutput, toSecretOutput, toTerminalDetailsOutput, toTerminalOutput, toTerminalSessionOutput, toWorkerOutput, toWorkerVersionOutput, triggerOutputSchema, triggerQuerySchema, unlockMethodInputSchema, unlockMethodMetaSchema, unlockMethodOutputSchema, unlockMethodType, waitAll, workerOutputSchema, workerQuerySchema, workerUnitRegistrationEventSchema, workerVersionOutputSchema, workerVersionStatusEventSchema, workerVersionStatusSchema } from '../chunk-52MY2TCE.js';
|
|
2
|
+
import '../chunk-UAWBPTDW.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.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -34,10 +34,10 @@
|
|
|
34
34
|
"@noble/ciphers": "^1.3.0",
|
|
35
35
|
"@noble/hashes": "^1.8.0",
|
|
36
36
|
"@paralleldrive/cuid2": "^2.2.2",
|
|
37
|
-
"@prisma/adapter-libsql": "7.
|
|
38
|
-
"@prisma/client": "7.
|
|
39
|
-
"@prisma/driver-adapter-utils": "7.
|
|
40
|
-
"@pulumi/pulumi": "3.
|
|
37
|
+
"@prisma/adapter-libsql": "7.4.1",
|
|
38
|
+
"@prisma/client": "7.4.1",
|
|
39
|
+
"@prisma/driver-adapter-utils": "7.4.1",
|
|
40
|
+
"@pulumi/pulumi": "3.220.0",
|
|
41
41
|
"age-encryption": "^0.2.3",
|
|
42
42
|
"ajv": "^8.17.1",
|
|
43
43
|
"ansi-colors": "^4.1.3",
|
|
@@ -57,14 +57,14 @@
|
|
|
57
57
|
"p-queue": "^8.0.0",
|
|
58
58
|
"pino": "^9.6.0",
|
|
59
59
|
"pkg-types": "^1.2.1",
|
|
60
|
-
"prisma": "7.
|
|
60
|
+
"prisma": "7.4.1",
|
|
61
61
|
"remeda": "^2.21.0",
|
|
62
62
|
"ulid": "^3.0.1",
|
|
63
63
|
"uuid": "^11.1.0",
|
|
64
64
|
"watcher": "^2.3.1",
|
|
65
65
|
"yaml": "^2.7.1",
|
|
66
66
|
"zod": "^4.0.5",
|
|
67
|
-
"@highstate/contract": "0.
|
|
67
|
+
"@highstate/contract": "0.20.0"
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
70
70
|
"@biomejs/biome": "2.2.0",
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/// The object allows to track arbitrary object across multiple projects and search them globally by their IDs.
|
|
2
|
+
/// This also allow to correlate different entities across different projects.
|
|
3
|
+
model Object {
|
|
4
|
+
/// The CUIDv2(d) of the object.
|
|
5
|
+
id String
|
|
6
|
+
|
|
7
|
+
/// The ID of the project that knows this object.
|
|
8
|
+
/// Multiple projects can reference the same object, but each project can only reference an object once.
|
|
9
|
+
projectId String
|
|
10
|
+
|
|
11
|
+
@@id([id, projectId])
|
|
12
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/// This model represents and instance of Highstate entity produced by one or many component instances one or many times.
|
|
2
|
+
/// Entity tracks EntitySnapshots by their unique IDs allowing to correlate them across different operations and instances.
|
|
3
|
+
/// Entities also can be tracked globally across different projects by referencing them in the Object model at the backend level.
|
|
4
|
+
model Entity {
|
|
5
|
+
/// The CUIDv2 or CUIDv2d of the entity.
|
|
6
|
+
///
|
|
7
|
+
/// The ID is calculated by the backend as CUIDv2d(entityType, identity), where identity is a user-provided string value that is expected to be globally unique for each entity of the same type.
|
|
8
|
+
id String @id
|
|
9
|
+
|
|
10
|
+
/// The type of the entity.
|
|
11
|
+
type String
|
|
12
|
+
|
|
13
|
+
/// The identity of the entity.
|
|
14
|
+
identity String
|
|
15
|
+
|
|
16
|
+
/// The snapshots of the entity.
|
|
17
|
+
snapshots EntitySnapshot[]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/// This model represents an immutable snapshot of an entity at a certain point of time
|
|
21
|
+
/// provide by some component instance during an operation.
|
|
22
|
+
model EntitySnapshot {
|
|
23
|
+
/// The CUIDv2 of the entity snapshot.
|
|
24
|
+
id String @id @default(cuid(2))
|
|
25
|
+
|
|
26
|
+
/// The SHA-256 hash of the entity snapshot content.
|
|
27
|
+
/// The content is stored separately in the EntitySnapshotContent model and can be shared between different snapshots with the same content hash.
|
|
28
|
+
contentHash String
|
|
29
|
+
|
|
30
|
+
/// The ID of the entity this snapshot belongs to.
|
|
31
|
+
entityId String
|
|
32
|
+
|
|
33
|
+
/// The ID of the operation that created this snapshot.
|
|
34
|
+
operationId String
|
|
35
|
+
|
|
36
|
+
/// The ID of the instance state produced this entity snapshot.
|
|
37
|
+
stateId String
|
|
38
|
+
|
|
39
|
+
/// The name of the instance outputs where this entity were referenced (including nested entities).
|
|
40
|
+
///
|
|
41
|
+
/// ![string[]]
|
|
42
|
+
referencedInOutputs Json
|
|
43
|
+
|
|
44
|
+
/// The name of the outputs that exported this entity directly.
|
|
45
|
+
///
|
|
46
|
+
/// ![string[]]
|
|
47
|
+
exportedInOutputs Json
|
|
48
|
+
|
|
49
|
+
/// The time when the entity snapshot was created.
|
|
50
|
+
createdAt DateTime @default(now())
|
|
51
|
+
|
|
52
|
+
/// The content of the entity snapshot.
|
|
53
|
+
content EntitySnapshotContent @relation(fields: [contentHash], references: [hash])
|
|
54
|
+
|
|
55
|
+
/// The entity this snapshot belongs to.
|
|
56
|
+
entity Entity @relation(fields: [entityId], references: [id])
|
|
57
|
+
|
|
58
|
+
/// The operation that created this snapshot.
|
|
59
|
+
operation Operation @relation(fields: [operationId], references: [id])
|
|
60
|
+
|
|
61
|
+
/// The instance state that produced this entity snapshot.
|
|
62
|
+
state InstanceState @relation(fields: [stateId], references: [id])
|
|
63
|
+
|
|
64
|
+
/// The snapshots of entities referenced by this entity snapshot.
|
|
65
|
+
/// For explicit references (specified manually by their IDs) the last snapshot of the referenced entity will be used.
|
|
66
|
+
/// For implicit references (collected via includes) the same snapshot of the same operation will be used.
|
|
67
|
+
references EntitySnapshotReference[] @relation("EntitySnapshotReferences")
|
|
68
|
+
|
|
69
|
+
/// The snapshots of entities that reference this entity snapshot.
|
|
70
|
+
referencedBy EntitySnapshotReference[] @relation("EntitySnapshotReferencedBy")
|
|
71
|
+
|
|
72
|
+
/// The artifacts referenced by this entity snapshot.
|
|
73
|
+
artifacts Artifact[]
|
|
74
|
+
|
|
75
|
+
@@index([entityId, createdAt(sort: Desc)])
|
|
76
|
+
@@index([operationId])
|
|
77
|
+
@@index([stateId, createdAt(sort: Desc)])
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
enum EntityReferenceKind {
|
|
81
|
+
explicit
|
|
82
|
+
inclusion
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
model EntitySnapshotReference {
|
|
86
|
+
/// The CUIDv2 of the entity snapshot relation.
|
|
87
|
+
fromId String
|
|
88
|
+
|
|
89
|
+
/// The CUIDv2 of the referenced entity snapshot.
|
|
90
|
+
toId String
|
|
91
|
+
|
|
92
|
+
/// The kind of the reference, which can be either explicit or inclusion (implicit).
|
|
93
|
+
kind EntityReferenceKind
|
|
94
|
+
|
|
95
|
+
/// The group of the references.
|
|
96
|
+
/// It can be either the exlicit group name provided by the entity explicit reference,
|
|
97
|
+
/// or name of the inclusion field of the parent entity for implicit references.
|
|
98
|
+
group String
|
|
99
|
+
|
|
100
|
+
/// The entity snapshot that holds the reference.
|
|
101
|
+
from EntitySnapshot @relation("EntitySnapshotReferences", fields: [fromId], references: [id])
|
|
102
|
+
|
|
103
|
+
/// The entity snapshot that is referenced.
|
|
104
|
+
to EntitySnapshot @relation("EntitySnapshotReferencedBy", fields: [toId], references: [id])
|
|
105
|
+
|
|
106
|
+
@@id([fromId, toId, kind, group])
|
|
107
|
+
@@index([toId])
|
|
108
|
+
@@index([fromId])
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
model EntitySnapshotContent {
|
|
112
|
+
/// The SHA-256 hash of the entity snapshot content.
|
|
113
|
+
hash String @id
|
|
114
|
+
|
|
115
|
+
/// The metadata of the entity at the time of the snapshot.
|
|
116
|
+
///
|
|
117
|
+
/// [EntityMeta]
|
|
118
|
+
meta Json?
|
|
119
|
+
|
|
120
|
+
/// The content of the entity snapshot, which is opaque to the backend.
|
|
121
|
+
content Json
|
|
122
|
+
|
|
123
|
+
/// The entity snapshots that have this content.
|
|
124
|
+
snapshots EntitySnapshot[]
|
|
125
|
+
}
|
|
@@ -125,6 +125,9 @@ model InstanceState {
|
|
|
125
125
|
/// [InstanceStatusFields]
|
|
126
126
|
statusFields Json?
|
|
127
127
|
|
|
128
|
+
/// Whether the instance has resource hooks and requires running program on destroy to properly clean up resources.
|
|
129
|
+
hasResourceHooks Boolean @default(false)
|
|
130
|
+
|
|
128
131
|
/// The parent instance.
|
|
129
132
|
parent InstanceState? @relation("InstanceHierarchy", fields: [parentId], references: [id])
|
|
130
133
|
|
|
@@ -166,4 +169,7 @@ model InstanceState {
|
|
|
166
169
|
|
|
167
170
|
/// The user viewports associated with this instance.
|
|
168
171
|
userViewports UserCompositeViewport[]
|
|
172
|
+
|
|
173
|
+
/// The entity snapshots associated with this instance.
|
|
174
|
+
entitySnapshots EntitySnapshot[]
|
|
169
175
|
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
-- CreateTable
|
|
2
|
+
CREATE TABLE "Entity" (
|
|
3
|
+
"id" TEXT NOT NULL PRIMARY KEY,
|
|
4
|
+
"type" TEXT NOT NULL,
|
|
5
|
+
"identity" TEXT NOT NULL
|
|
6
|
+
);
|
|
7
|
+
|
|
8
|
+
-- CreateTable
|
|
9
|
+
CREATE TABLE "EntitySnapshot" (
|
|
10
|
+
"id" TEXT NOT NULL PRIMARY KEY,
|
|
11
|
+
"contentHash" TEXT NOT NULL,
|
|
12
|
+
"entityId" TEXT NOT NULL,
|
|
13
|
+
"operationId" TEXT NOT NULL,
|
|
14
|
+
"stateId" TEXT NOT NULL,
|
|
15
|
+
"referencedInOutputs" JSONB NOT NULL,
|
|
16
|
+
"exportedInOutputs" JSONB NOT NULL,
|
|
17
|
+
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
18
|
+
CONSTRAINT "EntitySnapshot_contentHash_fkey" FOREIGN KEY ("contentHash") REFERENCES "EntitySnapshotContent" ("hash") ON DELETE RESTRICT ON UPDATE CASCADE,
|
|
19
|
+
CONSTRAINT "EntitySnapshot_entityId_fkey" FOREIGN KEY ("entityId") REFERENCES "Entity" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
|
|
20
|
+
CONSTRAINT "EntitySnapshot_operationId_fkey" FOREIGN KEY ("operationId") REFERENCES "Operation" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
|
|
21
|
+
CONSTRAINT "EntitySnapshot_stateId_fkey" FOREIGN KEY ("stateId") REFERENCES "InstanceState" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
-- CreateTable
|
|
25
|
+
CREATE TABLE "EntitySnapshotReference" (
|
|
26
|
+
"fromId" TEXT NOT NULL,
|
|
27
|
+
"toId" TEXT NOT NULL,
|
|
28
|
+
"kind" TEXT NOT NULL,
|
|
29
|
+
"group" TEXT NOT NULL,
|
|
30
|
+
|
|
31
|
+
PRIMARY KEY ("fromId", "toId", "kind", "group"),
|
|
32
|
+
CONSTRAINT "EntitySnapshotReference_fromId_fkey" FOREIGN KEY ("fromId") REFERENCES "EntitySnapshot" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
|
|
33
|
+
CONSTRAINT "EntitySnapshotReference_toId_fkey" FOREIGN KEY ("toId") REFERENCES "EntitySnapshot" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
-- CreateTable
|
|
37
|
+
CREATE TABLE "EntitySnapshotContent" (
|
|
38
|
+
"hash" TEXT NOT NULL PRIMARY KEY,
|
|
39
|
+
"meta" JSONB,
|
|
40
|
+
"content" JSONB NOT NULL
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
-- CreateTable
|
|
44
|
+
CREATE TABLE "_ArtifactToEntitySnapshot" (
|
|
45
|
+
"A" TEXT NOT NULL,
|
|
46
|
+
"B" TEXT NOT NULL,
|
|
47
|
+
CONSTRAINT "_ArtifactToEntitySnapshot_A_fkey" FOREIGN KEY ("A") REFERENCES "Artifact" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
|
48
|
+
CONSTRAINT "_ArtifactToEntitySnapshot_B_fkey" FOREIGN KEY ("B") REFERENCES "EntitySnapshot" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
-- CreateIndex
|
|
52
|
+
CREATE INDEX "EntitySnapshot_entityId_createdAt_idx" ON "EntitySnapshot"("entityId", "createdAt" DESC);
|
|
53
|
+
|
|
54
|
+
-- CreateIndex
|
|
55
|
+
CREATE INDEX "EntitySnapshot_operationId_idx" ON "EntitySnapshot"("operationId");
|
|
56
|
+
|
|
57
|
+
-- CreateIndex
|
|
58
|
+
CREATE INDEX "EntitySnapshot_stateId_createdAt_idx" ON "EntitySnapshot"("stateId", "createdAt" DESC);
|
|
59
|
+
|
|
60
|
+
-- CreateIndex
|
|
61
|
+
CREATE INDEX "EntitySnapshotReference_toId_idx" ON "EntitySnapshotReference"("toId");
|
|
62
|
+
|
|
63
|
+
-- CreateIndex
|
|
64
|
+
CREATE INDEX "EntitySnapshotReference_fromId_idx" ON "EntitySnapshotReference"("fromId");
|
|
65
|
+
|
|
66
|
+
-- CreateIndex
|
|
67
|
+
CREATE UNIQUE INDEX "_ArtifactToEntitySnapshot_AB_unique" ON "_ArtifactToEntitySnapshot"("A", "B");
|
|
68
|
+
|
|
69
|
+
-- CreateIndex
|
|
70
|
+
CREATE INDEX "_ArtifactToEntitySnapshot_B_index" ON "_ArtifactToEntitySnapshot"("B");
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ALTER TABLE "InstanceState" ADD COLUMN "hasResourceHooks" BOOLEAN NOT NULL DEFAULT false;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ArtifactBackend } from "../artifact"
|
|
2
|
+
import type { ObjectRefIndexService } from "./object-ref-index"
|
|
2
3
|
import { createId } from "@paralleldrive/cuid2"
|
|
3
4
|
import { describe, type MockedObject, vi } from "vitest"
|
|
4
5
|
import { test } from "../test-utils"
|
|
@@ -10,6 +11,7 @@ const createContent = async function* (text: string): AsyncIterable<Uint8Array>
|
|
|
10
11
|
|
|
11
12
|
const artifactTest = test.extend<{
|
|
12
13
|
artifactBackend: MockedObject<ArtifactBackend>
|
|
14
|
+
objectRefIndexService: MockedObject<ObjectRefIndexService>
|
|
13
15
|
artifactService: ArtifactService
|
|
14
16
|
}>({
|
|
15
17
|
artifactBackend: async ({}, use) => {
|
|
@@ -22,10 +24,19 @@ const artifactTest = test.extend<{
|
|
|
22
24
|
await use(artifactBackend)
|
|
23
25
|
},
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
objectRefIndexService: async ({}, use) => {
|
|
28
|
+
const objectRefIndexService = vi.mockObject({
|
|
29
|
+
track: vi.fn().mockResolvedValue(undefined),
|
|
30
|
+
} as unknown as ObjectRefIndexService)
|
|
31
|
+
|
|
32
|
+
await use(objectRefIndexService)
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
artifactService: async ({ database, artifactBackend, objectRefIndexService, logger }, use) => {
|
|
26
36
|
const service = new ArtifactService(
|
|
27
37
|
database,
|
|
28
38
|
artifactBackend,
|
|
39
|
+
objectRefIndexService,
|
|
29
40
|
logger.child({ service: "ArtifactService" }),
|
|
30
41
|
)
|
|
31
42
|
|
|
@@ -36,7 +47,14 @@ const artifactTest = test.extend<{
|
|
|
36
47
|
describe("store", () => {
|
|
37
48
|
artifactTest(
|
|
38
49
|
"stores new artifact successfully",
|
|
39
|
-
async ({
|
|
50
|
+
async ({
|
|
51
|
+
artifactService,
|
|
52
|
+
projectDatabase,
|
|
53
|
+
project,
|
|
54
|
+
artifactBackend,
|
|
55
|
+
objectRefIndexService,
|
|
56
|
+
expect,
|
|
57
|
+
}) => {
|
|
40
58
|
// arrange
|
|
41
59
|
const hash = createId()
|
|
42
60
|
const size = 1024
|
|
@@ -72,6 +90,8 @@ describe("store", () => {
|
|
|
72
90
|
expect.any(Object),
|
|
73
91
|
)
|
|
74
92
|
|
|
93
|
+
expect(objectRefIndexService.track).toHaveBeenCalledWith(project.id, [artifact.id])
|
|
94
|
+
|
|
75
95
|
// verify artifact was created with reference
|
|
76
96
|
const dbArtifact = await projectDatabase.artifact.findUnique({
|
|
77
97
|
where: { id: artifact.id },
|
package/src/business/artifact.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { CommonObjectMeta } from "@highstate/contract"
|
|
|
2
2
|
import type { Logger } from "pino"
|
|
3
3
|
import type { ArtifactBackend } from "../artifact"
|
|
4
4
|
import type { Artifact, DatabaseManager, ProjectTransaction } from "../database"
|
|
5
|
+
import type { ObjectRefIndexService } from "./object-ref-index"
|
|
5
6
|
import { createId } from "@paralleldrive/cuid2"
|
|
6
7
|
|
|
7
8
|
export const artifactChunkSize = 1024 * 1024 // 1 MB
|
|
@@ -13,6 +14,7 @@ export class ArtifactService {
|
|
|
13
14
|
constructor(
|
|
14
15
|
private readonly database: DatabaseManager,
|
|
15
16
|
private readonly artifactBackend: ArtifactBackend,
|
|
17
|
+
private readonly objectRefIndexService: ObjectRefIndexService,
|
|
16
18
|
private readonly logger: Logger,
|
|
17
19
|
) {}
|
|
18
20
|
|
|
@@ -53,7 +55,7 @@ export class ArtifactService {
|
|
|
53
55
|
await this.artifactBackend.store(projectId, artifactId, artifactChunkSize, content)
|
|
54
56
|
}
|
|
55
57
|
|
|
56
|
-
|
|
58
|
+
const artifact = await database.$transaction(async tx => {
|
|
57
59
|
// create or update the main artifact record
|
|
58
60
|
const artifact = await tx.artifact.upsert({
|
|
59
61
|
where: { id: artifactId },
|
|
@@ -73,6 +75,10 @@ export class ArtifactService {
|
|
|
73
75
|
|
|
74
76
|
return artifact
|
|
75
77
|
})
|
|
78
|
+
|
|
79
|
+
await this.objectRefIndexService.track(projectId, [artifact.id])
|
|
80
|
+
|
|
81
|
+
return artifact
|
|
76
82
|
}
|
|
77
83
|
|
|
78
84
|
/**
|