@highstate/backend 0.9.14 → 0.9.16
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-RCB4AFGD.js +159 -0
- package/dist/chunk-RCB4AFGD.js.map +1 -0
- package/dist/chunk-WHALQHEZ.js +2017 -0
- package/dist/chunk-WHALQHEZ.js.map +1 -0
- package/dist/highstate.manifest.json +3 -3
- package/dist/index.js +6146 -2174
- package/dist/index.js.map +1 -1
- package/dist/library/worker/main.js +51 -159
- package/dist/library/worker/main.js.map +1 -1
- package/dist/shared/index.js +159 -43
- package/package.json +25 -7
- package/src/artifact/abstractions.ts +46 -0
- package/src/artifact/encryption.ts +69 -0
- package/src/artifact/factory.ts +36 -0
- package/src/artifact/index.ts +3 -0
- package/src/artifact/local.ts +142 -0
- package/src/business/api-key.ts +65 -0
- package/src/business/artifact.ts +288 -0
- package/src/business/backend-unlock.ts +10 -0
- package/src/business/index.ts +9 -0
- package/src/business/instance-lock.ts +124 -0
- package/src/business/instance-state.ts +292 -0
- package/src/business/operation.ts +251 -0
- package/src/business/project-unlock.ts +242 -0
- package/src/business/secret.ts +187 -0
- package/src/business/worker.ts +161 -0
- package/src/common/index.ts +2 -1
- package/src/common/performance.ts +44 -0
- package/src/common/tree.ts +33 -0
- package/src/common/utils.ts +40 -1
- package/src/config.ts +14 -10
- package/src/hotstate/abstractions.ts +48 -0
- package/src/hotstate/factory.ts +17 -0
- package/src/{secret → hotstate}/index.ts +1 -0
- package/src/hotstate/manager.ts +192 -0
- package/src/hotstate/memory.ts +100 -0
- package/src/hotstate/validation.ts +101 -0
- package/src/index.ts +2 -1
- package/src/library/abstractions.ts +10 -23
- package/src/library/factory.ts +2 -2
- package/src/library/local.ts +89 -102
- package/src/library/worker/evaluator.ts +14 -47
- package/src/library/worker/loader.lite.ts +41 -0
- package/src/library/worker/main.ts +14 -65
- package/src/library/worker/protocol.ts +8 -24
- package/src/lock/abstractions.ts +6 -0
- package/src/lock/factory.ts +15 -0
- package/src/{workspace → lock}/index.ts +1 -0
- package/src/lock/manager.ts +82 -0
- package/src/lock/memory.ts +19 -0
- package/src/orchestrator/manager.ts +131 -82
- package/src/orchestrator/operation-workset.ts +188 -77
- package/src/orchestrator/operation.ts +975 -284
- package/src/project/abstractions.ts +20 -7
- package/src/project/factory.ts +1 -1
- package/src/project/index.ts +0 -1
- package/src/project/local.ts +73 -17
- package/src/project/manager.ts +272 -131
- package/src/pubsub/abstractions.ts +13 -0
- package/src/pubsub/factory.ts +19 -0
- package/src/pubsub/index.ts +3 -0
- package/src/pubsub/local.ts +36 -0
- package/src/pubsub/manager.ts +100 -0
- package/src/pubsub/validation.ts +33 -0
- package/src/runner/abstractions.ts +135 -68
- package/src/runner/artifact-env.ts +160 -0
- package/src/runner/factory.ts +20 -5
- package/src/runner/force-abort.ts +117 -0
- package/src/runner/local.ts +281 -372
- package/src/{common → runner}/pulumi.ts +86 -37
- package/src/services.ts +193 -35
- package/src/shared/index.ts +3 -11
- package/src/shared/models/backend/index.ts +3 -0
- package/src/shared/models/backend/project.ts +63 -0
- package/src/shared/models/backend/unlock-method.ts +20 -0
- package/src/shared/models/base.ts +151 -0
- package/src/shared/models/errors.ts +5 -0
- package/src/shared/models/index.ts +4 -0
- package/src/shared/models/project/api-key.ts +62 -0
- package/src/shared/models/project/artifact.ts +113 -0
- package/src/shared/models/project/component.ts +45 -0
- package/src/shared/models/project/index.ts +14 -0
- package/src/shared/{project.ts → models/project/instance.ts} +12 -0
- package/src/shared/models/project/lock.ts +91 -0
- package/src/shared/{operation.ts → models/project/operation.ts} +43 -8
- package/src/shared/models/project/page.ts +57 -0
- package/src/shared/models/project/secret.ts +112 -0
- package/src/shared/models/project/service-account.ts +22 -0
- package/src/shared/models/project/state.ts +432 -0
- package/src/shared/models/project/terminal.ts +99 -0
- package/src/shared/models/project/trigger.ts +56 -0
- package/src/shared/models/project/unlock-method.ts +31 -0
- package/src/shared/models/project/worker.ts +105 -0
- package/src/shared/resolvers/graph-resolver.ts +74 -13
- package/src/shared/resolvers/index.ts +5 -0
- package/src/shared/resolvers/input-hash.ts +53 -15
- package/src/shared/resolvers/input.ts +1 -9
- package/src/shared/resolvers/registry.ts +7 -2
- package/src/shared/resolvers/state.ts +12 -0
- package/src/shared/resolvers/validation.ts +61 -20
- package/src/shared/{async-batcher.ts → utils/async-batcher.ts} +13 -1
- package/src/shared/utils/hash.ts +6 -0
- package/src/shared/utils/index.ts +3 -0
- package/src/shared/utils/promise-tracker.ts +23 -0
- package/src/state/abstractions.ts +330 -101
- package/src/state/encryption.ts +59 -0
- package/src/state/factory.ts +3 -5
- package/src/state/index.ts +3 -0
- package/src/state/keyring.ts +22 -0
- package/src/state/local/backend.ts +299 -0
- package/src/state/local/collection.ts +342 -0
- package/src/state/local/index.ts +2 -0
- package/src/state/manager.ts +804 -18
- package/src/state/repository/index.ts +2 -0
- package/src/state/repository/repository.index.ts +193 -0
- package/src/state/repository/repository.ts +458 -0
- package/src/terminal/{shared.ts → abstractions.ts} +3 -3
- package/src/terminal/docker.ts +18 -14
- package/src/terminal/factory.ts +3 -3
- package/src/terminal/index.ts +1 -1
- package/src/terminal/manager.ts +134 -80
- package/src/terminal/run.sh.ts +22 -10
- package/src/worker/abstractions.ts +42 -0
- package/src/worker/docker.ts +83 -0
- package/src/worker/factory.ts +20 -0
- package/src/worker/index.ts +3 -0
- package/src/worker/manager.ts +139 -0
- package/dist/chunk-C2TJAQAD.js +0 -937
- package/dist/chunk-C2TJAQAD.js.map +0 -1
- package/dist/chunk-WXDYCRTT.js +0 -234
- package/dist/chunk-WXDYCRTT.js.map +0 -1
- package/src/library/worker/loader.ts +0 -114
- package/src/preferences/shared.ts +0 -1
- package/src/project/lock.ts +0 -39
- package/src/secret/abstractions.ts +0 -59
- package/src/secret/factory.ts +0 -22
- package/src/secret/local.ts +0 -152
- package/src/shared/state.ts +0 -270
- package/src/shared/terminal.ts +0 -13
- package/src/state/local.ts +0 -612
- package/src/workspace/abstractions.ts +0 -41
- package/src/workspace/factory.ts +0 -14
- package/src/workspace/local.ts +0 -54
- /package/src/shared/{library.ts → models/backend/library.ts} +0 -0
package/src/library/local.ts
CHANGED
|
@@ -1,15 +1,10 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
InstanceEvaluationResult,
|
|
3
|
-
LibraryBackend,
|
|
4
|
-
ModuleEvaluationResult,
|
|
5
|
-
ResolvedUnitSource,
|
|
6
|
-
} from "./abstractions"
|
|
1
|
+
import type { InstanceEvaluationResult, LibraryBackend, ResolvedUnitSource } from "./abstractions"
|
|
7
2
|
import type { Logger } from "pino"
|
|
8
3
|
import type {
|
|
9
4
|
PackageResolutionResponse,
|
|
10
5
|
PackageResolutionWorkerData,
|
|
11
6
|
} from "./package-resolution-worker"
|
|
12
|
-
import type {
|
|
7
|
+
import type { WorkerData, WorkerResponse } from "./worker/protocol"
|
|
13
8
|
import { fileURLToPath } from "node:url"
|
|
14
9
|
import { EventEmitter, on } from "node:events"
|
|
15
10
|
import { Worker } from "node:worker_threads"
|
|
@@ -23,6 +18,7 @@ import { z } from "zod"
|
|
|
23
18
|
import { readPackageJSON } from "pkg-types"
|
|
24
19
|
import { runScript, installDependencies, addDependency } from "nypm"
|
|
25
20
|
import { flatMap, groupBy, map, pipe, unique } from "remeda"
|
|
21
|
+
import { decode } from "@msgpack/msgpack"
|
|
26
22
|
import { resolveMainLocalProject, stringArrayType } from "../common"
|
|
27
23
|
import {
|
|
28
24
|
diffLibraries,
|
|
@@ -32,8 +28,8 @@ import {
|
|
|
32
28
|
} from "../shared"
|
|
33
29
|
|
|
34
30
|
export const localLibraryBackendConfig = z.object({
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
HIGHSTATE_LIBRARY_BACKEND_LOCAL_PACKAGES: stringArrayType.default("@highstate/library"),
|
|
32
|
+
HIGHSTATE_LIBRARY_BACKEND_LOCAL_WATCH_PATHS: stringArrayType.optional(),
|
|
37
33
|
})
|
|
38
34
|
|
|
39
35
|
interface Events {
|
|
@@ -42,7 +38,7 @@ interface Events {
|
|
|
42
38
|
}
|
|
43
39
|
|
|
44
40
|
type HighstateManifestJson = {
|
|
45
|
-
sourceHashes?: Record<string,
|
|
41
|
+
sourceHashes?: Record<string, number>
|
|
46
42
|
}
|
|
47
43
|
|
|
48
44
|
type LibraryPackage = {
|
|
@@ -59,7 +55,6 @@ export class LocalLibraryBackend implements LibraryBackend {
|
|
|
59
55
|
private readonly eventEmitter = new EventEmitter<Events>()
|
|
60
56
|
|
|
61
57
|
private library: LibraryModel | null = null
|
|
62
|
-
private worker: Worker | null = null
|
|
63
58
|
|
|
64
59
|
private readonly packages = new Map<string, LibraryPackage>()
|
|
65
60
|
private readonly resolvedUnitSources = new Map<string, ResolvedUnitSource>()
|
|
@@ -88,13 +83,12 @@ export class LocalLibraryBackend implements LibraryBackend {
|
|
|
88
83
|
|
|
89
84
|
async loadLibrary(): Promise<LibraryModel> {
|
|
90
85
|
return await this.lock.acquire(async () => {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
return library
|
|
86
|
+
return await this.getLibrary()
|
|
94
87
|
})
|
|
95
88
|
}
|
|
96
89
|
|
|
97
|
-
async *watchLibrary(signal?: AbortSignal): AsyncIterable<LibraryUpdate[]> {
|
|
90
|
+
async *watchLibrary(_libraryId: string, signal?: AbortSignal): AsyncIterable<LibraryUpdate[]> {
|
|
91
|
+
// For local backend, we ignore libraryId since there's only one library
|
|
98
92
|
for await (const [library] of on(this.eventEmitter, "library", { signal })) {
|
|
99
93
|
yield library
|
|
100
94
|
}
|
|
@@ -106,9 +100,12 @@ export class LocalLibraryBackend implements LibraryBackend {
|
|
|
106
100
|
})
|
|
107
101
|
}
|
|
108
102
|
|
|
109
|
-
async getResolvedUnitSources(
|
|
103
|
+
async getResolvedUnitSources(
|
|
104
|
+
_libraryId: string,
|
|
105
|
+
unitTypes: string[],
|
|
106
|
+
): Promise<ResolvedUnitSource[]> {
|
|
110
107
|
return await this.lock.acquire(async () => {
|
|
111
|
-
const
|
|
108
|
+
const library = await this.getLibrary()
|
|
112
109
|
|
|
113
110
|
const units = unitTypes
|
|
114
111
|
.map(type => library.components[type])
|
|
@@ -135,7 +132,11 @@ export class LocalLibraryBackend implements LibraryBackend {
|
|
|
135
132
|
})
|
|
136
133
|
}
|
|
137
134
|
|
|
138
|
-
async *watchResolvedUnitSources(
|
|
135
|
+
async *watchResolvedUnitSources(
|
|
136
|
+
_libraryId: string,
|
|
137
|
+
signal?: AbortSignal,
|
|
138
|
+
): AsyncIterable<ResolvedUnitSource> {
|
|
139
|
+
// For local backend, we ignore libraryId since there's only one library
|
|
139
140
|
for await (const [resolvedUnitSource] of on(this.eventEmitter, "resolvedUnitSource", {
|
|
140
141
|
signal,
|
|
141
142
|
})) {
|
|
@@ -144,49 +145,20 @@ export class LocalLibraryBackend implements LibraryBackend {
|
|
|
144
145
|
}
|
|
145
146
|
|
|
146
147
|
async evaluateCompositeInstances(
|
|
148
|
+
_libraryId: string,
|
|
147
149
|
allInstances: InstanceModel[],
|
|
148
150
|
resolvedInputs: Record<string, Record<string, ResolvedInstanceInput[]>>,
|
|
149
151
|
instanceIds: string[],
|
|
150
152
|
): Promise<InstanceEvaluationResult[]> {
|
|
151
|
-
|
|
152
|
-
this.logger.info("evaluating %d composite instances", instanceIds.length)
|
|
153
|
-
|
|
154
|
-
const [, worker] = await this.getLibrary()
|
|
155
|
-
worker.postMessage({
|
|
156
|
-
type: "evaluate-composite-instances",
|
|
157
|
-
allInstances,
|
|
158
|
-
resolvedInputs,
|
|
159
|
-
instanceIds,
|
|
160
|
-
} satisfies WorkerRequest)
|
|
161
|
-
|
|
162
|
-
this.logger.debug("evaluation request sent")
|
|
163
|
-
|
|
164
|
-
const { results } = await this.getResult(worker, "instance-evaluation-results")
|
|
165
|
-
return results
|
|
166
|
-
})
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
async evaluateModules(modulePaths: string[]): Promise<ModuleEvaluationResult> {
|
|
170
|
-
return await this.lock.acquire(async () => {
|
|
171
|
-
this.logger.info({ msg: "evaluating modules", modulePaths })
|
|
172
|
-
|
|
173
|
-
const [, worker] = await this.getLibrary()
|
|
174
|
-
worker.postMessage({
|
|
175
|
-
type: "evaluate-modules",
|
|
176
|
-
modulePaths,
|
|
177
|
-
} satisfies WorkerRequest)
|
|
153
|
+
this.logger.info("evaluating %d composite instances", instanceIds.length)
|
|
178
154
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
155
|
+
const worker = this.createLibraryWorker({
|
|
156
|
+
libraryModulePaths: this.libraryPackages,
|
|
157
|
+
allInstances,
|
|
158
|
+
resolvedInputs,
|
|
159
|
+
instanceIds,
|
|
183
160
|
})
|
|
184
|
-
}
|
|
185
161
|
|
|
186
|
-
private async getResult<TType extends WorkerResponse["type"]>(
|
|
187
|
-
worker: Worker,
|
|
188
|
-
expectedType: TType,
|
|
189
|
-
): Promise<WorkerResponse & { type: TType }> {
|
|
190
162
|
for await (const [event] of on(worker, "message")) {
|
|
191
163
|
const eventData = event as WorkerResponse
|
|
192
164
|
|
|
@@ -194,69 +166,56 @@ export class LocalLibraryBackend implements LibraryBackend {
|
|
|
194
166
|
throw new Error(`Worker error: ${eventData.error}`)
|
|
195
167
|
}
|
|
196
168
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
`Unexpected response message type "${eventData.type}", expected "${expectedType}"`,
|
|
200
|
-
)
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
return eventData as WorkerResponse & { type: TType }
|
|
169
|
+
this.logger.info("composite instances evaluated successfully")
|
|
170
|
+
return eventData.results
|
|
204
171
|
}
|
|
205
172
|
|
|
206
173
|
throw new Error("Worker ended without sending any response")
|
|
207
174
|
}
|
|
208
175
|
|
|
209
|
-
private async getLibrary(): Promise<
|
|
210
|
-
if (this.library
|
|
211
|
-
return
|
|
176
|
+
private async getLibrary(): Promise<LibraryModel> {
|
|
177
|
+
if (this.library) {
|
|
178
|
+
return this.library
|
|
212
179
|
}
|
|
213
180
|
|
|
214
181
|
return await this.reloadLibrary()
|
|
215
182
|
}
|
|
216
183
|
|
|
217
|
-
private async reloadLibrary(): Promise<
|
|
184
|
+
private async reloadLibrary(): Promise<LibraryModel> {
|
|
218
185
|
this.logger.info("reloading library")
|
|
219
186
|
|
|
220
187
|
this.eventEmitter.emit("library", [{ type: "reload-started" }])
|
|
221
188
|
|
|
222
189
|
await this.ensureLibraryPackagesLoaded(this.libraryPackages, true)
|
|
223
190
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
})
|
|
191
|
+
const loadedPackages = this.packages
|
|
192
|
+
.values()
|
|
193
|
+
.filter(pkg => this.libraryPackages.includes(pkg.name))
|
|
228
194
|
|
|
229
|
-
|
|
230
|
-
const eventData = event as {
|
|
231
|
-
type: string
|
|
232
|
-
library: LibraryModel
|
|
233
|
-
error?: string
|
|
234
|
-
}
|
|
195
|
+
const mergedLibrary: LibraryModel = { components: {}, entities: {} }
|
|
235
196
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
}
|
|
197
|
+
for (const loadedPackage of loadedPackages) {
|
|
198
|
+
const libraryContent = await this.readLibraryContent(loadedPackage)
|
|
239
199
|
|
|
240
|
-
|
|
241
|
-
|
|
200
|
+
for (const [componentType, component] of Object.entries(libraryContent.components)) {
|
|
201
|
+
mergedLibrary.components[componentType] = component
|
|
242
202
|
}
|
|
243
203
|
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
204
|
+
for (const [entityType, entity] of Object.entries(libraryContent.entities)) {
|
|
205
|
+
mergedLibrary.entities[entityType] = entity
|
|
206
|
+
}
|
|
207
|
+
}
|
|
248
208
|
|
|
249
|
-
|
|
250
|
-
this.library = eventData.library
|
|
209
|
+
const updates = diffLibraries(this.library ?? { components: {}, entities: {} }, mergedLibrary)
|
|
251
210
|
|
|
252
|
-
|
|
211
|
+
this.eventEmitter.emit("library", updates)
|
|
212
|
+
this.library = mergedLibrary
|
|
253
213
|
|
|
254
|
-
|
|
214
|
+
this.logger.info("library reloaded")
|
|
255
215
|
|
|
256
|
-
|
|
257
|
-
}
|
|
216
|
+
this.eventEmitter.emit("library", [{ type: "reload-completed" }])
|
|
258
217
|
|
|
259
|
-
|
|
218
|
+
return this.library
|
|
260
219
|
}
|
|
261
220
|
|
|
262
221
|
private async reloadUnitManifest(libraryPackage: LibraryPackage): Promise<void> {
|
|
@@ -347,9 +306,7 @@ export class LocalLibraryBackend implements LibraryBackend {
|
|
|
347
306
|
this.logger.info(`rebuilding library package "${libraryPackage.name}" via build script`)
|
|
348
307
|
await runScript("build", { cwd: libraryPackage.rootPath })
|
|
349
308
|
|
|
350
|
-
if (this.libraryPackages.includes(libraryPackage.name)) {
|
|
351
|
-
await this.reloadLibrary()
|
|
352
|
-
} else {
|
|
309
|
+
if (!this.libraryPackages.includes(libraryPackage.name)) {
|
|
353
310
|
await this.reloadUnitManifest(libraryPackage)
|
|
354
311
|
}
|
|
355
312
|
|
|
@@ -361,7 +318,7 @@ export class LocalLibraryBackend implements LibraryBackend {
|
|
|
361
318
|
const parsedName = LocalLibraryBackend.parseDependencyName(libraryPackage.name)
|
|
362
319
|
|
|
363
320
|
const dependencyPackageNames = pipe(
|
|
364
|
-
[packageJson.dependencies, packageJson.
|
|
321
|
+
[packageJson.dependencies, packageJson.peerDependencies],
|
|
365
322
|
flatMap(deps => Object.keys(deps ?? {})),
|
|
366
323
|
unique(),
|
|
367
324
|
map(LocalLibraryBackend.parseDependencyName),
|
|
@@ -440,8 +397,27 @@ export class LocalLibraryBackend implements LibraryBackend {
|
|
|
440
397
|
}
|
|
441
398
|
}
|
|
442
399
|
|
|
400
|
+
private async readLibraryContent(libraryPackage: LibraryPackage): Promise<LibraryModel> {
|
|
401
|
+
const contentPath = resolve(libraryPackage.rootPath, "dist", "highstate.library.msgpack")
|
|
402
|
+
|
|
403
|
+
try {
|
|
404
|
+
const contentBuffer = await readFile(contentPath)
|
|
405
|
+
const content = decode(contentBuffer) as LibraryModel
|
|
406
|
+
|
|
407
|
+
return content
|
|
408
|
+
} catch (error) {
|
|
409
|
+
this.logger.debug(
|
|
410
|
+
{ error },
|
|
411
|
+
`failed to read highstate library content of package: "%s"`,
|
|
412
|
+
libraryPackage.name,
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
return { components: {}, entities: {} }
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
443
419
|
private async loadLibraryPackages(names: string[], installIfNotFound = false): Promise<void> {
|
|
444
|
-
this.logger.
|
|
420
|
+
this.logger.debug("loading library packages: %s", names.join(", "))
|
|
445
421
|
|
|
446
422
|
const missingPackages: string[] = []
|
|
447
423
|
const packagesToUpdate: LibraryPackage[] = []
|
|
@@ -484,7 +460,10 @@ export class LocalLibraryBackend implements LibraryBackend {
|
|
|
484
460
|
for (const libraryPackage of packagesToUpdate) {
|
|
485
461
|
await this.updateLibraryPackageDependencies(libraryPackage)
|
|
486
462
|
|
|
487
|
-
if (
|
|
463
|
+
if (
|
|
464
|
+
!this.libraryPackages.includes(libraryPackage.name) &&
|
|
465
|
+
libraryPackage.name !== "@highstate/contract"
|
|
466
|
+
) {
|
|
488
467
|
await this.reloadUnitManifest(libraryPackage)
|
|
489
468
|
}
|
|
490
469
|
}
|
|
@@ -500,13 +479,21 @@ export class LocalLibraryBackend implements LibraryBackend {
|
|
|
500
479
|
await this.lock.acquire(async () => {
|
|
501
480
|
const libraryPackage = this.packages.values().find(pkg => path.startsWith(pkg.rootPath))
|
|
502
481
|
|
|
503
|
-
if (libraryPackage) {
|
|
504
|
-
|
|
482
|
+
if (!libraryPackage) {
|
|
483
|
+
return
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
const builtPackages = new Set<string>()
|
|
487
|
+
await this.rebuildLibraryPackage(libraryPackage, false, false, builtPackages)
|
|
488
|
+
|
|
489
|
+
if (this.libraryPackages.some(pkg => builtPackages.has(pkg))) {
|
|
490
|
+
this.logger.info("reloading library due to file change in package: %s", libraryPackage.name)
|
|
491
|
+
await this.reloadLibrary()
|
|
505
492
|
}
|
|
506
493
|
})
|
|
507
494
|
}
|
|
508
495
|
|
|
509
|
-
private createLibraryWorker(workerData:
|
|
496
|
+
private createLibraryWorker(workerData: WorkerData): Worker {
|
|
510
497
|
const workerPathUrl = importMetaResolve(`@highstate/backend/library-worker`, import.meta.url)
|
|
511
498
|
const workerPath = fileURLToPath(workerPathUrl)
|
|
512
499
|
|
|
@@ -524,14 +511,14 @@ export class LocalLibraryBackend implements LibraryBackend {
|
|
|
524
511
|
}
|
|
525
512
|
|
|
526
513
|
static async create(config: z.infer<typeof localLibraryBackendConfig>, logger: Logger) {
|
|
527
|
-
let watchPaths = config.
|
|
514
|
+
let watchPaths = config.HIGHSTATE_LIBRARY_BACKEND_LOCAL_WATCH_PATHS
|
|
528
515
|
if (!watchPaths) {
|
|
529
516
|
const [projectPath] = await resolveMainLocalProject()
|
|
530
517
|
watchPaths = [resolve(projectPath, "packages")]
|
|
531
518
|
}
|
|
532
519
|
|
|
533
520
|
return new LocalLibraryBackend(
|
|
534
|
-
config.
|
|
521
|
+
config.HIGHSTATE_LIBRARY_BACKEND_LOCAL_PACKAGES,
|
|
535
522
|
watchPaths,
|
|
536
523
|
logger.child({ backend: "LibraryBackend", service: "LocalLibraryBackend" }),
|
|
537
524
|
)
|
|
@@ -1,50 +1,17 @@
|
|
|
1
1
|
import type { Logger } from "pino"
|
|
2
|
-
import type {
|
|
3
|
-
import type { Jiti } from "jiti"
|
|
4
|
-
import type { InstanceEvaluationResult, ModuleEvaluationResult } from "../abstractions"
|
|
2
|
+
import type { InstanceEvaluationResult } from "../abstractions"
|
|
5
3
|
import type { ResolvedInstanceInput } from "../../shared"
|
|
6
|
-
import {
|
|
7
|
-
|
|
4
|
+
import {
|
|
5
|
+
getCompositeInstances,
|
|
6
|
+
resetEvaluation,
|
|
7
|
+
type Component,
|
|
8
|
+
type InstanceModel,
|
|
9
|
+
} from "@highstate/contract"
|
|
8
10
|
import { errorToString } from "../../common"
|
|
9
11
|
|
|
10
|
-
const lock = new BetterLock()
|
|
11
|
-
|
|
12
|
-
export function evaluateModules(
|
|
13
|
-
jiti: Jiti,
|
|
14
|
-
logger: Logger,
|
|
15
|
-
modulePaths: string[],
|
|
16
|
-
): Promise<ModuleEvaluationResult> {
|
|
17
|
-
return lock.acquire(async () => {
|
|
18
|
-
resetEvaluation()
|
|
19
|
-
let lastModulePath = ""
|
|
20
|
-
|
|
21
|
-
try {
|
|
22
|
-
for (const modulePath of modulePaths) {
|
|
23
|
-
logger.info("loading module: %s", modulePath)
|
|
24
|
-
|
|
25
|
-
lastModulePath = modulePath
|
|
26
|
-
await jiti.import(modulePath)
|
|
27
|
-
|
|
28
|
-
logger.debug("module loaded: %s", modulePath)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return {
|
|
32
|
-
success: true,
|
|
33
|
-
compositeInstances: getCompositeInstances(),
|
|
34
|
-
}
|
|
35
|
-
} catch (error) {
|
|
36
|
-
return {
|
|
37
|
-
success: false,
|
|
38
|
-
modulePath: lastModulePath,
|
|
39
|
-
error: errorToString(error),
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
})
|
|
43
|
-
}
|
|
44
|
-
|
|
45
12
|
export function evaluateInstances(
|
|
46
13
|
logger: Logger,
|
|
47
|
-
|
|
14
|
+
components: Readonly<Record<string, Component>>,
|
|
48
15
|
allInstances: InstanceModel[],
|
|
49
16
|
resolvedInputs: Record<string, Record<string, ResolvedInstanceInput[]>>,
|
|
50
17
|
instanceIds: string[],
|
|
@@ -53,19 +20,19 @@ export function evaluateInstances(
|
|
|
53
20
|
const allInstancesMap = new Map(allInstances.map(instance => [instance.id, instance]))
|
|
54
21
|
|
|
55
22
|
for (const instanceId of instanceIds ?? []) {
|
|
56
|
-
const externalInstanceIds = new Set(allInstances.map(instance => instance.id))
|
|
57
|
-
externalInstanceIds.delete(instanceId)
|
|
58
|
-
|
|
59
23
|
try {
|
|
60
24
|
logger.debug({ instanceId }, "evaluating top-level instance")
|
|
61
|
-
resetEvaluation(
|
|
25
|
+
resetEvaluation()
|
|
62
26
|
|
|
63
27
|
evaluateInstance(instanceId)
|
|
64
28
|
|
|
65
29
|
results.push({
|
|
66
30
|
success: true,
|
|
67
31
|
instanceId,
|
|
68
|
-
compositeInstances: getCompositeInstances()
|
|
32
|
+
compositeInstances: getCompositeInstances().filter(
|
|
33
|
+
instance =>
|
|
34
|
+
instanceId.includes(instance.instance.id) || !allInstancesMap.has(instance.instance.id),
|
|
35
|
+
),
|
|
69
36
|
})
|
|
70
37
|
} catch (error) {
|
|
71
38
|
results.push({
|
|
@@ -113,7 +80,7 @@ export function evaluateInstances(
|
|
|
113
80
|
})
|
|
114
81
|
}
|
|
115
82
|
|
|
116
|
-
const component =
|
|
83
|
+
const component = components[instance.type]
|
|
117
84
|
if (!component) {
|
|
118
85
|
throw new Error(`Component not found: ${instance.type}, required by instance: ${instanceId}`)
|
|
119
86
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Logger } from "pino"
|
|
2
|
+
import { isComponent, type Component } from "@highstate/contract"
|
|
3
|
+
|
|
4
|
+
export async function loadComponents(
|
|
5
|
+
logger: Logger,
|
|
6
|
+
modulePaths: string[],
|
|
7
|
+
): Promise<Readonly<Record<string, Component>>> {
|
|
8
|
+
const modules: Record<string, unknown> = {}
|
|
9
|
+
for (const modulePath of modulePaths) {
|
|
10
|
+
try {
|
|
11
|
+
logger.debug({ modulePath }, "loading module")
|
|
12
|
+
modules[modulePath] = await import(modulePath)
|
|
13
|
+
|
|
14
|
+
logger.debug({ modulePath }, "module loaded")
|
|
15
|
+
} catch (err) {
|
|
16
|
+
logger.error({ modulePath, err }, "module load failed")
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const components: Record<string, Component> = {}
|
|
21
|
+
|
|
22
|
+
await _loadLibrary(modules, components)
|
|
23
|
+
logger.debug("library loaded with %s components", Object.keys(components).length)
|
|
24
|
+
|
|
25
|
+
return components
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function _loadLibrary(value: unknown, components: Record<string, Component>): Promise<void> {
|
|
29
|
+
if (isComponent(value)) {
|
|
30
|
+
components[value.model.type] = value
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (typeof value !== "object" || value === null) {
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
for (const key in value) {
|
|
39
|
+
await _loadLibrary((value as Record<string, unknown>)[key], components)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -1,82 +1,31 @@
|
|
|
1
|
-
import type { WorkerData,
|
|
1
|
+
import type { WorkerData, WorkerResponse } from "./protocol"
|
|
2
2
|
import { parentPort, workerData } from "node:worker_threads"
|
|
3
|
-
import { createJiti, type Jiti } from "jiti"
|
|
4
3
|
import { pino } from "pino"
|
|
5
|
-
import { mapValues } from "remeda"
|
|
6
4
|
import { errorToString } from "../../common"
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
5
|
+
import { evaluateInstances } from "./evaluator"
|
|
6
|
+
import { loadComponents } from "./loader.lite"
|
|
9
7
|
|
|
10
8
|
const data = workerData as WorkerData
|
|
11
9
|
|
|
12
10
|
const logger = pino({ name: "library-worker", level: data.logLevel })
|
|
13
|
-
let jiti: Jiti
|
|
14
|
-
let library: Library
|
|
15
11
|
|
|
16
12
|
try {
|
|
17
|
-
logger.
|
|
18
|
-
logger.trace({ data }, "worker data")
|
|
13
|
+
const library = await loadComponents(logger, data.libraryModulePaths)
|
|
19
14
|
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
const results = evaluateInstances(
|
|
16
|
+
logger,
|
|
17
|
+
library,
|
|
18
|
+
data.allInstances,
|
|
19
|
+
data.resolvedInputs,
|
|
20
|
+
data.instanceIds,
|
|
21
|
+
)
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
parentPort!.postMessage({
|
|
26
|
-
type: "library",
|
|
27
|
-
library: {
|
|
28
|
-
components: mapValues(library.components, component => component.model),
|
|
29
|
-
entities: library.entities,
|
|
30
|
-
},
|
|
31
|
-
} satisfies WorkerResponse)
|
|
32
|
-
|
|
33
|
-
logger.info("library loaded and sent")
|
|
23
|
+
parentPort!.postMessage({ type: "results", results } satisfies WorkerResponse)
|
|
34
24
|
} catch (error) {
|
|
35
|
-
logger.error({ error }, "failed to
|
|
25
|
+
logger.error({ error }, "failed to evaluate")
|
|
36
26
|
|
|
37
27
|
parentPort!.postMessage({
|
|
38
28
|
type: "error",
|
|
39
29
|
error: errorToString(error),
|
|
40
|
-
})
|
|
30
|
+
} satisfies WorkerResponse)
|
|
41
31
|
}
|
|
42
|
-
|
|
43
|
-
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
44
|
-
parentPort!.on("message", async message => {
|
|
45
|
-
try {
|
|
46
|
-
const request = message as WorkerRequest
|
|
47
|
-
|
|
48
|
-
switch (request.type) {
|
|
49
|
-
case "evaluate-composite-instances": {
|
|
50
|
-
const results = evaluateInstances(
|
|
51
|
-
logger,
|
|
52
|
-
library,
|
|
53
|
-
request.allInstances,
|
|
54
|
-
request.resolvedInputs,
|
|
55
|
-
request.instanceIds,
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
parentPort!.postMessage({
|
|
59
|
-
type: "instance-evaluation-results",
|
|
60
|
-
results,
|
|
61
|
-
} satisfies WorkerResponse)
|
|
62
|
-
break
|
|
63
|
-
}
|
|
64
|
-
case "evaluate-modules": {
|
|
65
|
-
const result = await evaluateModules(jiti, logger, request.modulePaths)
|
|
66
|
-
|
|
67
|
-
parentPort!.postMessage({
|
|
68
|
-
type: "module-evaluation-result",
|
|
69
|
-
result,
|
|
70
|
-
} satisfies WorkerResponse)
|
|
71
|
-
break
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
} catch (error) {
|
|
75
|
-
logger.error({ error }, "failed to evaluate")
|
|
76
|
-
|
|
77
|
-
parentPort!.postMessage({
|
|
78
|
-
type: "error",
|
|
79
|
-
error: errorToString(error),
|
|
80
|
-
} satisfies WorkerResponse)
|
|
81
|
-
}
|
|
82
|
-
})
|
|
@@ -1,37 +1,21 @@
|
|
|
1
1
|
import type { InstanceModel } from "@highstate/contract"
|
|
2
|
-
import type { InstanceEvaluationResult
|
|
3
|
-
import type {
|
|
2
|
+
import type { InstanceEvaluationResult } from "../abstractions"
|
|
3
|
+
import type { ResolvedInstanceInput } from "../../shared"
|
|
4
4
|
|
|
5
5
|
export type WorkerData = {
|
|
6
|
-
|
|
6
|
+
libraryModulePaths: string[]
|
|
7
7
|
logLevel?: string
|
|
8
|
-
}
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
resolvedInputs: Record<string, Record<string, ResolvedInstanceInput[]>>
|
|
15
|
-
instanceIds: string[]
|
|
16
|
-
}
|
|
17
|
-
| {
|
|
18
|
-
type: "evaluate-modules"
|
|
19
|
-
modulePaths: string[]
|
|
20
|
-
}
|
|
9
|
+
allInstances: InstanceModel[]
|
|
10
|
+
resolvedInputs: Record<string, Record<string, ResolvedInstanceInput[]>>
|
|
11
|
+
instanceIds: string[]
|
|
12
|
+
}
|
|
21
13
|
|
|
22
14
|
export type WorkerResponse =
|
|
23
15
|
| {
|
|
24
|
-
type: "
|
|
25
|
-
library: LibraryModel
|
|
26
|
-
}
|
|
27
|
-
| {
|
|
28
|
-
type: "instance-evaluation-results"
|
|
16
|
+
type: "results"
|
|
29
17
|
results: InstanceEvaluationResult[]
|
|
30
18
|
}
|
|
31
|
-
| {
|
|
32
|
-
type: "module-evaluation-result"
|
|
33
|
-
result: ModuleEvaluationResult
|
|
34
|
-
}
|
|
35
19
|
| {
|
|
36
20
|
type: "error"
|
|
37
21
|
error: string
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { LockBackend } from "./abstractions"
|
|
2
|
+
import { z } from "zod"
|
|
3
|
+
import { MemoryLockBackend } from "./memory"
|
|
4
|
+
|
|
5
|
+
export const lockBackendConfig = z.object({
|
|
6
|
+
HIGHSTATE_LOCK_BACKEND_TYPE: z.enum(["memory"]).default("memory"),
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
export function createLockBackend(config: z.infer<typeof lockBackendConfig>): LockBackend {
|
|
10
|
+
switch (config.HIGHSTATE_LOCK_BACKEND_TYPE) {
|
|
11
|
+
case "memory": {
|
|
12
|
+
return MemoryLockBackend.create()
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|