@highstate/backend 0.7.2 → 0.7.4
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/{index.mjs → index.js} +1254 -915
- package/dist/library/source-resolution-worker.js +55 -0
- package/dist/library/worker/main.js +216 -0
- package/dist/{terminal-CqIsctlZ.mjs → library-BW5oPM7V.js} +210 -87
- package/dist/shared/index.js +6 -0
- package/dist/utils-ByadNcv4.js +102 -0
- package/package.json +15 -19
- package/src/common/index.ts +3 -0
- package/src/common/local.ts +22 -0
- package/src/common/pulumi.ts +230 -0
- package/src/common/utils.ts +137 -0
- package/src/config.ts +40 -0
- package/src/index.ts +6 -0
- package/src/library/abstractions.ts +83 -0
- package/src/library/factory.ts +20 -0
- package/src/library/index.ts +2 -0
- package/src/library/local.ts +404 -0
- package/src/library/source-resolution-worker.ts +96 -0
- package/src/library/worker/evaluator.ts +119 -0
- package/src/library/worker/loader.ts +110 -0
- package/src/library/worker/main.ts +82 -0
- package/src/library/worker/protocol.ts +38 -0
- package/src/orchestrator/index.ts +1 -0
- package/src/orchestrator/manager.ts +165 -0
- package/src/orchestrator/operation-workset.ts +483 -0
- package/src/orchestrator/operation.ts +647 -0
- package/src/preferences/shared.ts +1 -0
- package/src/project/abstractions.ts +89 -0
- package/src/project/factory.ts +11 -0
- package/src/project/index.ts +4 -0
- package/src/project/local.ts +412 -0
- package/src/project/lock.ts +39 -0
- package/src/project/manager.ts +374 -0
- package/src/runner/abstractions.ts +146 -0
- package/src/runner/factory.ts +22 -0
- package/src/runner/index.ts +2 -0
- package/src/runner/local.ts +698 -0
- package/src/secret/abstractions.ts +59 -0
- package/src/secret/factory.ts +22 -0
- package/src/secret/index.ts +2 -0
- package/src/secret/local.ts +152 -0
- package/src/services.ts +133 -0
- package/src/shared/index.ts +10 -0
- package/src/shared/library.ts +77 -0
- package/src/shared/operation.ts +85 -0
- package/src/shared/project.ts +62 -0
- package/src/shared/resolvers/graph-resolver.ts +111 -0
- package/src/shared/resolvers/input-hash.ts +77 -0
- package/src/shared/resolvers/input.ts +314 -0
- package/src/shared/resolvers/registry.ts +10 -0
- package/src/shared/resolvers/validation.ts +94 -0
- package/src/shared/state.ts +262 -0
- package/src/shared/terminal.ts +13 -0
- package/src/state/abstractions.ts +222 -0
- package/src/state/factory.ts +22 -0
- package/src/state/index.ts +3 -0
- package/src/state/local.ts +605 -0
- package/src/state/manager.ts +33 -0
- package/src/terminal/docker.ts +90 -0
- package/src/terminal/factory.ts +20 -0
- package/src/terminal/index.ts +3 -0
- package/src/terminal/manager.ts +330 -0
- package/src/terminal/run.sh.ts +37 -0
- package/src/terminal/shared.ts +50 -0
- package/src/workspace/abstractions.ts +41 -0
- package/src/workspace/factory.ts +14 -0
- package/src/workspace/index.ts +2 -0
- package/src/workspace/local.ts +54 -0
- package/dist/index.d.ts +0 -760
- package/dist/library/worker/main.mjs +0 -164
- package/dist/runner/source-resolution-worker.mjs +0 -22
- package/dist/shared/index.d.ts +0 -85
- package/dist/shared/index.mjs +0 -54
- package/dist/terminal-Cm2WqcyB.d.ts +0 -1589
@@ -0,0 +1,59 @@
|
|
1
|
+
export class SecretAccessDeniedError extends Error {
|
2
|
+
constructor(projectId: string, key: string) {
|
3
|
+
super(`Access to the secrets of component "${projectId}.${key}" is denied.`)
|
4
|
+
}
|
5
|
+
}
|
6
|
+
|
7
|
+
export interface SecretBackend {
|
8
|
+
/**
|
9
|
+
* Check if the state and secrets of the project are locked.
|
10
|
+
* The backend may not implement this method.
|
11
|
+
*
|
12
|
+
* @param projectId The ID of the project.
|
13
|
+
* @param signal The signal to abort the operation.
|
14
|
+
*
|
15
|
+
* @returns `true` if the project is locked, `false` if the project is unlocked.
|
16
|
+
*/
|
17
|
+
isLocked?(projectId: string, signal?: AbortSignal): Promise<boolean>
|
18
|
+
|
19
|
+
/**
|
20
|
+
* Unlock the state and secrets of the project.
|
21
|
+
* The backend may not implement this method.
|
22
|
+
*
|
23
|
+
* @param projectId The ID of the project.
|
24
|
+
* @param password The password to unlock the secrets.
|
25
|
+
* @param signal The signal to abort the operation.
|
26
|
+
*
|
27
|
+
* @returns `true` if the project is unlocked, `false` if the password is incorrect.
|
28
|
+
*/
|
29
|
+
unlock?(projectId: string, password: string, signal?: AbortSignal): Promise<boolean>
|
30
|
+
|
31
|
+
/**
|
32
|
+
* Get the secrets of the component.
|
33
|
+
*
|
34
|
+
* @param projectId The ID of the project.
|
35
|
+
* @param instanceId The ID of the instance.
|
36
|
+
* @param signal The signal to abort the operation.
|
37
|
+
*
|
38
|
+
* @returns A record of secret key-value pairs or `null` if the secrets are not found.
|
39
|
+
* @throws {SecretAccessDeniedError} If access to the secrets is denied.
|
40
|
+
*/
|
41
|
+
get(projectId: string, instanceId: string, signal?: AbortSignal): Promise<Record<string, unknown>>
|
42
|
+
|
43
|
+
/**
|
44
|
+
* Set the secrets of the component.
|
45
|
+
*
|
46
|
+
* @param projectId The ID of the project.
|
47
|
+
* @param instanceId The ID of the instance.
|
48
|
+
* @param values The record of secret key-value pairs.
|
49
|
+
* @param signal The signal to abort the operation.
|
50
|
+
*
|
51
|
+
* @throws {SecretAccessDeniedError} If access to the secrets is denied.
|
52
|
+
*/
|
53
|
+
set(
|
54
|
+
projectId: string,
|
55
|
+
instanceId: string,
|
56
|
+
values: Record<string, unknown>,
|
57
|
+
signal?: AbortSignal,
|
58
|
+
): Promise<void>
|
59
|
+
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
import type { SecretBackend } from "./abstractions"
|
2
|
+
import type { LocalPulumiHost } from "../common"
|
3
|
+
import type { Logger } from "pino"
|
4
|
+
import { z } from "zod"
|
5
|
+
import { LocalSecretBackend, localSecretBackendConfig } from "./local"
|
6
|
+
|
7
|
+
export const secretBackendConfig = z.object({
|
8
|
+
HIGHSTATE_BACKEND_SECRET_TYPE: z.enum(["local"]).default("local"),
|
9
|
+
...localSecretBackendConfig.shape,
|
10
|
+
})
|
11
|
+
|
12
|
+
export function createSecretBackend(
|
13
|
+
config: z.infer<typeof secretBackendConfig>,
|
14
|
+
localPulumiHost: LocalPulumiHost,
|
15
|
+
logger: Logger,
|
16
|
+
): Promise<SecretBackend> {
|
17
|
+
switch (config.HIGHSTATE_BACKEND_SECRET_TYPE) {
|
18
|
+
case "local": {
|
19
|
+
return LocalSecretBackend.create(config, localPulumiHost, logger)
|
20
|
+
}
|
21
|
+
}
|
22
|
+
}
|
@@ -0,0 +1,152 @@
|
|
1
|
+
import type { SecretBackend } from "./abstractions"
|
2
|
+
import type { Logger } from "pino"
|
3
|
+
import { mapKeys, mapValues, pickBy } from "remeda"
|
4
|
+
import { z } from "zod"
|
5
|
+
import { LocalPulumiHost, resolveMainLocalProject, stringToValue, valueToString } from "../common"
|
6
|
+
|
7
|
+
export const localSecretBackendConfig = z.object({
|
8
|
+
HIGHSTATE_BACKEND_SECRET_PROJECT_PATH: z.string().optional(),
|
9
|
+
HIGHSTATE_BACKEND_SECRET_PROJECT_NAME: z.string().optional(),
|
10
|
+
})
|
11
|
+
|
12
|
+
/**
|
13
|
+
* The backend for storing secrets in the local Pulumi project.
|
14
|
+
* It is the simplest backend and should be used for development and single-user projects.
|
15
|
+
*/
|
16
|
+
export class LocalSecretBackend implements SecretBackend {
|
17
|
+
private constructor(
|
18
|
+
private readonly projectPath: string,
|
19
|
+
private readonly projectName: string,
|
20
|
+
private readonly pulumiProjectHost: LocalPulumiHost,
|
21
|
+
private readonly logger: Logger,
|
22
|
+
) {
|
23
|
+
this.logger.debug({ msg: "initialized", projectPath, projectName })
|
24
|
+
}
|
25
|
+
|
26
|
+
isLocked(projectId: string): Promise<boolean> {
|
27
|
+
return Promise.resolve(!this.pulumiProjectHost.hasPassword(projectId))
|
28
|
+
}
|
29
|
+
|
30
|
+
async unlock(projectId: string, password: string, signal?: AbortSignal): Promise<boolean> {
|
31
|
+
this.pulumiProjectHost.setPassword(projectId, password)
|
32
|
+
|
33
|
+
try {
|
34
|
+
// try to run a command to check if the password is correct
|
35
|
+
await this.pulumiProjectHost.runLocal(
|
36
|
+
{
|
37
|
+
projectId,
|
38
|
+
pulumiProjectName: this.projectName,
|
39
|
+
pulumiStackName: projectId,
|
40
|
+
projectPath: this.projectPath,
|
41
|
+
},
|
42
|
+
async stack => {
|
43
|
+
this.logger.debug({ projectId }, "checking password")
|
44
|
+
|
45
|
+
await stack.info(true)
|
46
|
+
},
|
47
|
+
signal,
|
48
|
+
)
|
49
|
+
|
50
|
+
return true
|
51
|
+
} catch (error) {
|
52
|
+
if (error instanceof Error) {
|
53
|
+
if (error.message.includes("incorrect passphrase")) {
|
54
|
+
this.logger.debug({ projectId, error }, "incorrect passphrase")
|
55
|
+
|
56
|
+
this.pulumiProjectHost.removePassword(projectId)
|
57
|
+
return false
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
61
|
+
throw error
|
62
|
+
}
|
63
|
+
}
|
64
|
+
|
65
|
+
get(
|
66
|
+
projectId: string,
|
67
|
+
instanceId: string,
|
68
|
+
signal?: AbortSignal,
|
69
|
+
): Promise<Record<string, unknown>> {
|
70
|
+
return this.pulumiProjectHost.runLocal(
|
71
|
+
{
|
72
|
+
projectId,
|
73
|
+
pulumiProjectName: this.projectName,
|
74
|
+
pulumiStackName: projectId,
|
75
|
+
projectPath: this.projectPath,
|
76
|
+
},
|
77
|
+
async stack => {
|
78
|
+
this.logger.debug({ projectId, instanceId }, "getting secrets")
|
79
|
+
|
80
|
+
const config = await stack.getAllConfig()
|
81
|
+
signal?.throwIfAborted()
|
82
|
+
|
83
|
+
const prefix = this.getPrefix(projectId, instanceId)
|
84
|
+
const secrets = pickBy(config, (_, key) => key.startsWith(prefix))
|
85
|
+
const trimmedSecrets = mapKeys(secrets, key => key.slice(prefix.length))
|
86
|
+
|
87
|
+
return mapValues(trimmedSecrets, value => stringToValue(value.value))
|
88
|
+
},
|
89
|
+
signal,
|
90
|
+
)
|
91
|
+
}
|
92
|
+
|
93
|
+
set(
|
94
|
+
projectId: string,
|
95
|
+
instanceId: string,
|
96
|
+
values: Record<string, unknown>,
|
97
|
+
signal?: AbortSignal,
|
98
|
+
): Promise<void> {
|
99
|
+
return this.pulumiProjectHost.runLocal(
|
100
|
+
{
|
101
|
+
projectId,
|
102
|
+
pulumiProjectName: this.projectName,
|
103
|
+
pulumiStackName: projectId,
|
104
|
+
projectPath: this.projectPath,
|
105
|
+
},
|
106
|
+
async stack => {
|
107
|
+
this.logger.debug({ projectId, instanceId }, "setting secrets")
|
108
|
+
|
109
|
+
const componentSecrets = mapValues(
|
110
|
+
mapKeys(values, key => `${this.getPrefix(projectId, instanceId)}${key}`),
|
111
|
+
value => ({
|
112
|
+
value: valueToString(value),
|
113
|
+
secret: true,
|
114
|
+
}),
|
115
|
+
)
|
116
|
+
|
117
|
+
const config = await stack.getAllConfig()
|
118
|
+
signal?.throwIfAborted()
|
119
|
+
|
120
|
+
Object.assign(config, componentSecrets)
|
121
|
+
|
122
|
+
await stack.setAllConfig(config)
|
123
|
+
},
|
124
|
+
signal,
|
125
|
+
)
|
126
|
+
}
|
127
|
+
|
128
|
+
private getPrefix(projectId: string, instanceId: string) {
|
129
|
+
// replace all semicolons with dashes since extra semicolons are not allowed in Pulumi config keys
|
130
|
+
instanceId = instanceId.replace(/:/g, "_")
|
131
|
+
|
132
|
+
return `${this.projectName}:${projectId}/${instanceId}/`
|
133
|
+
}
|
134
|
+
|
135
|
+
static async create(
|
136
|
+
config: z.infer<typeof localSecretBackendConfig>,
|
137
|
+
pulumiProjectHost: LocalPulumiHost,
|
138
|
+
logger: Logger,
|
139
|
+
) {
|
140
|
+
const [projectPath, projectName] = await resolveMainLocalProject(
|
141
|
+
config.HIGHSTATE_BACKEND_SECRET_PROJECT_PATH,
|
142
|
+
config.HIGHSTATE_BACKEND_SECRET_PROJECT_NAME,
|
143
|
+
)
|
144
|
+
|
145
|
+
return new LocalSecretBackend(
|
146
|
+
projectPath,
|
147
|
+
projectName,
|
148
|
+
pulumiProjectHost,
|
149
|
+
logger.child({ backend: "SecretBackend", service: "LocalSecretBackend" }),
|
150
|
+
)
|
151
|
+
}
|
152
|
+
}
|
package/src/services.ts
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
import type { RunnerBackend } from "./runner"
|
2
|
+
import { pino, type Logger } from "pino"
|
3
|
+
import { createWorkspaceBackend, type WorkspaceBackend } from "./workspace"
|
4
|
+
import { createStateBackend, StateManager, type StateBackend } from "./state"
|
5
|
+
import { type Config, loadConfig } from "./config"
|
6
|
+
import { type LibraryBackend, createLibraryBackend } from "./library"
|
7
|
+
import { OperationManager } from "./orchestrator"
|
8
|
+
import {
|
9
|
+
type ProjectBackend,
|
10
|
+
ProjectLockManager,
|
11
|
+
ProjectManager,
|
12
|
+
createProjectBackend,
|
13
|
+
} from "./project"
|
14
|
+
import { createRunnerBackend } from "./runner"
|
15
|
+
import { type SecretBackend, createSecretBackend } from "./secret"
|
16
|
+
import { type TerminalBackend, TerminalManager, createTerminalBackend } from "./terminal"
|
17
|
+
import { LocalPulumiHost } from "./common"
|
18
|
+
|
19
|
+
export type Services = {
|
20
|
+
readonly logger: Logger
|
21
|
+
readonly libraryBackend: LibraryBackend
|
22
|
+
readonly secretBackend: SecretBackend
|
23
|
+
readonly runnerBackend: RunnerBackend
|
24
|
+
readonly projectBackend: ProjectBackend
|
25
|
+
readonly projectManager: ProjectManager
|
26
|
+
readonly stateBackend: StateBackend
|
27
|
+
readonly stateManager: StateManager
|
28
|
+
readonly operationManager: OperationManager
|
29
|
+
readonly terminalBackend: TerminalBackend
|
30
|
+
readonly terminalManager: TerminalManager
|
31
|
+
readonly workspaceBackend: WorkspaceBackend
|
32
|
+
}
|
33
|
+
|
34
|
+
export interface CreateServicesOptions {
|
35
|
+
/**
|
36
|
+
* The config to use. If not provided, it will be loaded from the environment.
|
37
|
+
*/
|
38
|
+
readonly config?: Config
|
39
|
+
|
40
|
+
/**
|
41
|
+
* The already created services to use. If the particular service is not provided, it will be created.
|
42
|
+
*/
|
43
|
+
readonly services?: Partial<Services>
|
44
|
+
}
|
45
|
+
|
46
|
+
export async function createServices({
|
47
|
+
config,
|
48
|
+
services: {
|
49
|
+
logger,
|
50
|
+
libraryBackend,
|
51
|
+
secretBackend,
|
52
|
+
runnerBackend,
|
53
|
+
projectBackend,
|
54
|
+
projectManager,
|
55
|
+
stateBackend,
|
56
|
+
stateManager,
|
57
|
+
operationManager,
|
58
|
+
terminalBackend,
|
59
|
+
terminalManager,
|
60
|
+
workspaceBackend,
|
61
|
+
} = {},
|
62
|
+
}: CreateServicesOptions = {}): Promise<Services> {
|
63
|
+
config ??= await loadConfig()
|
64
|
+
|
65
|
+
logger ??= pino({
|
66
|
+
name: config.HIGHSTATE_BACKEND_LOGGER_NAME,
|
67
|
+
level: config.HIGHSTATE_BACKEND_LOGGER_LEVEL,
|
68
|
+
errorKey: "error",
|
69
|
+
})
|
70
|
+
|
71
|
+
const localPulumiHost = LocalPulumiHost.create(logger)
|
72
|
+
const projectLockManager = new ProjectLockManager()
|
73
|
+
|
74
|
+
stateManager ??= new StateManager()
|
75
|
+
|
76
|
+
libraryBackend ??= await createLibraryBackend(config, logger)
|
77
|
+
secretBackend ??= await createSecretBackend(config, localPulumiHost, logger)
|
78
|
+
stateBackend ??= await createStateBackend(config, localPulumiHost, logger)
|
79
|
+
runnerBackend ??= createRunnerBackend(config, localPulumiHost, libraryBackend)
|
80
|
+
projectBackend ??= await createProjectBackend(config)
|
81
|
+
|
82
|
+
terminalBackend ??= createTerminalBackend(config, logger)
|
83
|
+
terminalManager ??= TerminalManager.create(terminalBackend, stateBackend, runnerBackend, logger)
|
84
|
+
|
85
|
+
operationManager ??= await OperationManager.create(
|
86
|
+
runnerBackend,
|
87
|
+
stateBackend,
|
88
|
+
libraryBackend,
|
89
|
+
projectBackend,
|
90
|
+
secretBackend,
|
91
|
+
projectLockManager,
|
92
|
+
stateManager,
|
93
|
+
logger,
|
94
|
+
)
|
95
|
+
|
96
|
+
projectManager ??= ProjectManager.create(
|
97
|
+
projectBackend,
|
98
|
+
stateBackend,
|
99
|
+
libraryBackend,
|
100
|
+
projectLockManager,
|
101
|
+
stateManager,
|
102
|
+
logger,
|
103
|
+
)
|
104
|
+
|
105
|
+
workspaceBackend ??= await createWorkspaceBackend(config)
|
106
|
+
|
107
|
+
logger.info("services created")
|
108
|
+
|
109
|
+
return {
|
110
|
+
logger,
|
111
|
+
libraryBackend,
|
112
|
+
secretBackend,
|
113
|
+
runnerBackend,
|
114
|
+
projectBackend,
|
115
|
+
projectManager,
|
116
|
+
stateBackend,
|
117
|
+
stateManager,
|
118
|
+
operationManager,
|
119
|
+
terminalBackend,
|
120
|
+
terminalManager,
|
121
|
+
workspaceBackend,
|
122
|
+
}
|
123
|
+
}
|
124
|
+
|
125
|
+
let sharedServicesPromise: Promise<Services> | undefined
|
126
|
+
|
127
|
+
export function getSharedServices(options: CreateServicesOptions = {}): Promise<Services> {
|
128
|
+
if (!sharedServicesPromise) {
|
129
|
+
sharedServicesPromise = createServices(options)
|
130
|
+
}
|
131
|
+
|
132
|
+
return sharedServicesPromise
|
133
|
+
}
|
@@ -0,0 +1,10 @@
|
|
1
|
+
export * from "./project"
|
2
|
+
export * from "./state"
|
3
|
+
export * from "./operation"
|
4
|
+
export * from "./resolvers/graph-resolver"
|
5
|
+
export * from "./resolvers/registry"
|
6
|
+
export * from "./resolvers/input"
|
7
|
+
export * from "./resolvers/input-hash"
|
8
|
+
export * from "./resolvers/validation"
|
9
|
+
export * from "./terminal"
|
10
|
+
export * from "./library"
|
@@ -0,0 +1,77 @@
|
|
1
|
+
import type { ComponentModel, Entity } from "@highstate/contract"
|
2
|
+
|
3
|
+
export type LibraryModel = {
|
4
|
+
components: Record<string, ComponentModel>
|
5
|
+
entities: Record<string, Entity>
|
6
|
+
}
|
7
|
+
|
8
|
+
export type LibraryUpdate =
|
9
|
+
| {
|
10
|
+
type: "component-updated"
|
11
|
+
component: ComponentModel
|
12
|
+
}
|
13
|
+
| {
|
14
|
+
type: "entity-updated"
|
15
|
+
entity: Entity
|
16
|
+
}
|
17
|
+
| {
|
18
|
+
type: "component-removed"
|
19
|
+
componentType: string
|
20
|
+
}
|
21
|
+
| {
|
22
|
+
type: "entity-removed"
|
23
|
+
entityType: string
|
24
|
+
}
|
25
|
+
|
26
|
+
export function diffLibraries(oldLibrary: LibraryModel, newLibrary: LibraryModel): LibraryUpdate[] {
|
27
|
+
const updates: LibraryUpdate[] = []
|
28
|
+
|
29
|
+
for (const [componentType, newComponent] of Object.entries(newLibrary.components)) {
|
30
|
+
const existingComponent = oldLibrary.components[componentType]
|
31
|
+
if (existingComponent?.definitionHash !== newComponent.definitionHash) {
|
32
|
+
updates.push({ type: "component-updated", component: newComponent })
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
36
|
+
for (const componentType of Object.keys(oldLibrary.components)) {
|
37
|
+
if (!newLibrary.components[componentType]) {
|
38
|
+
updates.push({ type: "component-removed", componentType })
|
39
|
+
}
|
40
|
+
}
|
41
|
+
|
42
|
+
for (const [entityType, newEntity] of Object.entries(newLibrary.entities)) {
|
43
|
+
const existingEntity = oldLibrary.entities[entityType]
|
44
|
+
if (existingEntity?.definitionHash !== newEntity.definitionHash) {
|
45
|
+
updates.push({ type: "entity-updated", entity: newEntity })
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
for (const entityType of Object.keys(oldLibrary.entities)) {
|
50
|
+
if (!newLibrary.entities[entityType]) {
|
51
|
+
updates.push({ type: "entity-removed", entityType })
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
return updates
|
56
|
+
}
|
57
|
+
|
58
|
+
export function applyLibraryUpdate(
|
59
|
+
components: Record<string, ComponentModel>,
|
60
|
+
entities: Record<string, Entity>,
|
61
|
+
update: LibraryUpdate,
|
62
|
+
): void {
|
63
|
+
switch (update.type) {
|
64
|
+
case "component-updated":
|
65
|
+
components[update.component.type] = update.component
|
66
|
+
break
|
67
|
+
case "entity-updated":
|
68
|
+
entities[update.entity.type] = update.entity
|
69
|
+
break
|
70
|
+
case "component-removed":
|
71
|
+
delete components[update.componentType]
|
72
|
+
break
|
73
|
+
case "entity-removed":
|
74
|
+
delete entities[update.entityType]
|
75
|
+
break
|
76
|
+
}
|
77
|
+
}
|
@@ -0,0 +1,85 @@
|
|
1
|
+
import { z } from "zod"
|
2
|
+
|
3
|
+
export const operationTypeSchema = z.enum(["update", "preview", "destroy", "recreate", "refresh"])
|
4
|
+
|
5
|
+
export const operationStatusSchema = z.enum([
|
6
|
+
"pending",
|
7
|
+
"running",
|
8
|
+
"completed",
|
9
|
+
"failed",
|
10
|
+
"cancelled",
|
11
|
+
])
|
12
|
+
|
13
|
+
export const operationOptionsSchema = z.object({
|
14
|
+
/**
|
15
|
+
* Whether to force update all dependencies of the instances even if they are not changed.
|
16
|
+
*
|
17
|
+
* Only applicable for `update`, `preview`, `recreate`, and `refresh` operations.
|
18
|
+
* By default, `false`.
|
19
|
+
*/
|
20
|
+
forceUpdateDependencies: z.boolean().default(false),
|
21
|
+
|
22
|
+
/**
|
23
|
+
* Whether to destroy all dependents of the instances when destroying them.
|
24
|
+
*
|
25
|
+
* Only applicable for `destroy`.
|
26
|
+
* By default, `true`.
|
27
|
+
*/
|
28
|
+
destroyDependentInstances: z.boolean().default(true),
|
29
|
+
|
30
|
+
/**
|
31
|
+
* Whether to invoke destroy triggers when destroying the instances.
|
32
|
+
*
|
33
|
+
* Only applicable for `destroy`.
|
34
|
+
* By default, `true`.
|
35
|
+
*/
|
36
|
+
invokeDestroyTriggers: z.boolean().default(true),
|
37
|
+
|
38
|
+
/**
|
39
|
+
* Whether to delete unreachable resources when updating or destroying the instances.
|
40
|
+
* This is potentially dangerous and should be used with caution.
|
41
|
+
*
|
42
|
+
* By default, `false`.
|
43
|
+
*/
|
44
|
+
deleteUnreachableResources: z.boolean().default(false),
|
45
|
+
|
46
|
+
/**
|
47
|
+
* Whether to refresh the state before running the operation.
|
48
|
+
*
|
49
|
+
* By default, `false`.
|
50
|
+
*/
|
51
|
+
refresh: z.boolean().default(false),
|
52
|
+
})
|
53
|
+
|
54
|
+
export const projectOperationRequestSchema = z.object({
|
55
|
+
projectId: z.string(),
|
56
|
+
type: operationTypeSchema,
|
57
|
+
instanceIds: z.array(z.string()),
|
58
|
+
options: operationOptionsSchema.partial().optional(),
|
59
|
+
})
|
60
|
+
|
61
|
+
export const projectOperationSchema = z.object({
|
62
|
+
id: z.string().uuid(),
|
63
|
+
status: operationStatusSchema,
|
64
|
+
|
65
|
+
projectId: z.string(),
|
66
|
+
type: operationTypeSchema,
|
67
|
+
instanceIds: z.array(z.string()),
|
68
|
+
affectedInstanceIds: z.array(z.string()).default(() => []),
|
69
|
+
|
70
|
+
options: operationOptionsSchema.default(() => ({})),
|
71
|
+
|
72
|
+
error: z.string().nullable(),
|
73
|
+
startedAt: z.number(),
|
74
|
+
completedAt: z.number().nullable(),
|
75
|
+
})
|
76
|
+
|
77
|
+
export type OperationType = z.infer<typeof operationTypeSchema>
|
78
|
+
export type OperationStatus = z.infer<typeof operationStatusSchema>
|
79
|
+
export type ProjectOperation = z.infer<typeof projectOperationSchema>
|
80
|
+
export type ProjectOperationRequest = z.infer<typeof projectOperationRequestSchema>
|
81
|
+
export type OperationOptions = z.infer<typeof operationOptionsSchema>
|
82
|
+
|
83
|
+
export function isFinalOperationStatus(status: OperationStatus): boolean {
|
84
|
+
return status === "completed" || status === "failed" || status === "cancelled"
|
85
|
+
}
|
@@ -0,0 +1,62 @@
|
|
1
|
+
import { z } from "zod"
|
2
|
+
|
3
|
+
export const positionSchema = z.object({
|
4
|
+
x: z.number(),
|
5
|
+
y: z.number(),
|
6
|
+
})
|
7
|
+
|
8
|
+
export const instanceInputSchema = z.object({
|
9
|
+
instanceId: z.string(),
|
10
|
+
output: z.string(),
|
11
|
+
})
|
12
|
+
|
13
|
+
export const hubInstanceInputSchema = z.object({
|
14
|
+
hubId: z.string(),
|
15
|
+
})
|
16
|
+
|
17
|
+
export const instanceModelPatchSchema = z.object({
|
18
|
+
args: z.record(z.unknown()).optional(),
|
19
|
+
inputs: z.record(z.array(instanceInputSchema)).optional(),
|
20
|
+
hubInputs: z.record(z.array(hubInstanceInputSchema)).optional(),
|
21
|
+
injectionInputs: z.array(hubInstanceInputSchema).optional(),
|
22
|
+
position: positionSchema.optional(),
|
23
|
+
})
|
24
|
+
|
25
|
+
export const instanceModelSchema = z.object({
|
26
|
+
id: z.string(),
|
27
|
+
type: z.string(),
|
28
|
+
name: z.string(),
|
29
|
+
|
30
|
+
...instanceModelPatchSchema.shape,
|
31
|
+
resolvedInputs: z.record(z.array(instanceInputSchema)).optional(),
|
32
|
+
|
33
|
+
parentId: z.string().optional(),
|
34
|
+
outputs: z.record(z.array(instanceInputSchema)).optional(),
|
35
|
+
resolvedOutputs: z.record(z.array(instanceInputSchema)).optional(),
|
36
|
+
})
|
37
|
+
|
38
|
+
export const compositeInstanceSchema = z.object({
|
39
|
+
instance: instanceModelSchema,
|
40
|
+
children: z.array(instanceModelSchema),
|
41
|
+
inputHash: z.string().optional(),
|
42
|
+
})
|
43
|
+
|
44
|
+
export type CompositeInstance = z.infer<typeof compositeInstanceSchema>
|
45
|
+
|
46
|
+
export const hubModelPatchSchema = z.object({
|
47
|
+
position: positionSchema.optional(),
|
48
|
+
inputs: z.array(instanceInputSchema).optional(),
|
49
|
+
injectionInputs: z.array(hubInstanceInputSchema).optional(),
|
50
|
+
})
|
51
|
+
|
52
|
+
export const hubModelSchema = z.object({
|
53
|
+
id: z.string().nanoid(),
|
54
|
+
position: positionSchema,
|
55
|
+
|
56
|
+
inputs: z.array(instanceInputSchema).optional(),
|
57
|
+
injectionInputs: z.array(hubInstanceInputSchema).optional(),
|
58
|
+
})
|
59
|
+
|
60
|
+
export type InstanceModelPatch = z.infer<typeof instanceModelPatchSchema>
|
61
|
+
export type HubModel = z.infer<typeof hubModelSchema>
|
62
|
+
export type HubModelPatch = z.infer<typeof hubModelPatchSchema>
|