@highstate/backend 0.7.8 → 0.7.9

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.
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "sourceHashes": {
3
- "./dist/index.js": "e5a4106585f6ef91330ece00fcd73d51b7bad213ae36b46e9d98c13eb398d65f",
4
- "./dist/shared/index.js": "7356c923eb12cbe487a811f5dae2d7ce79452ee8d4cbe856c5bc0beb9752d738",
5
- "./dist/library/worker/main.js": "5ba7f6868be5fc81df24c54ffb1acafbe50762216c4261d631d041ece1d5a572",
6
- "./dist/library/source-resolution-worker.js": "4589af58d254b32b8a9708528839e1da9450b5ebe8d74cd8d5cf713e52c1304b"
3
+ "./dist/index.js": "23e14a2a7ffd7bea598c3bebe69326a3c387d7a77f290481731250488f75b3ca",
4
+ "./dist/shared/index.js": "ce531a480a772514e20f94ec9116c98580dba419c8f5507bb85b6cef40a5c1ae",
5
+ "./dist/library/worker/main.js": "11bbed95f67a1756408bcbea93b87af1ed913ebbf7dbd35978027b0b3e219c05",
6
+ "./dist/library/package-resolution-worker.js": "4bfa368ad35a64c109df9e5a468c057791164760fe166c4eb5d9a889dee4d7bc"
7
7
  }
8
8
  }
package/dist/index.js CHANGED
@@ -32,7 +32,6 @@ import {
32
32
  updateResourceCount,
33
33
  valueToString
34
34
  } from "./chunk-EQ4LMS7B.js";
35
- import "./chunk-DGUM43GV.js";
36
35
 
37
36
  // src/secret/abstractions.ts
38
37
  var SecretAccessDeniedError = class extends Error {
@@ -173,49 +172,43 @@ import { z as z4 } from "zod";
173
172
  import { fileURLToPath } from "node:url";
174
173
  import { EventEmitter, on } from "node:events";
175
174
  import { Worker } from "node:worker_threads";
176
- import { basename, dirname, relative, resolve } from "node:path";
175
+ import { resolve } from "node:path";
177
176
  import { readFile } from "node:fs/promises";
178
177
  import { isUnitModel } from "@highstate/contract";
179
178
  import Watcher from "watcher";
180
179
  import { BetterLock } from "better-lock";
181
180
  import { resolve as importMetaResolve } from "import-meta-resolve";
182
181
  import { z as z3 } from "zod";
183
- import { readPackageJSON, resolvePackageJSON } from "pkg-types";
182
+ import { readPackageJSON } from "pkg-types";
183
+ import { runScript, installDependencies, addDependency } from "nypm";
184
+ import { flatMap, groupBy, map, pipe, unique } from "remeda";
184
185
  var localLibraryBackendConfig = z3.object({
185
- HIGHSTATE_BACKEND_LIBRARY_LOCAL_MODULES: stringArrayType.default("@highstate/library"),
186
- HIGHSTATE_BACKEND_LIBRARY_LOCAL_SOURCE_BASE_PATH: z3.string().optional(),
187
- HIGHSTATE_BACKEND_LIBRARY_LOCAL_EXTRA_SOURCE_WATCH_PATHS: stringArrayType.default("")
186
+ HIGHSTATE_BACKEND_LIBRARY_LOCAL_PACKAGES: stringArrayType.default("@highstate/library"),
187
+ HIGHSTATE_BACKEND_LIBRARY_LOCAL_WATCH_PATHS: stringArrayType.optional()
188
188
  });
189
189
  var LocalLibraryBackend = class _LocalLibraryBackend {
190
- constructor(modulePaths, sourceBasePath, extraSourceWatchPaths, logger) {
191
- this.modulePaths = modulePaths;
192
- this.sourceBasePath = sourceBasePath;
190
+ constructor(libraryPackages, watchPaths, logger) {
191
+ this.libraryPackages = libraryPackages;
193
192
  this.logger = logger;
194
- this.libraryWatcher = new Watcher(modulePaths, { recursive: true, ignoreInitial: true });
195
- this.libraryWatcher.on("all", (event, path) => {
196
- const prefixPath = modulePaths.find((modulePath) => path.startsWith(modulePath));
197
- this.logger.info({ msg: "library event", event, path: relative(prefixPath, path) });
198
- void this.lock.acquire(() => this.updateLibrary());
199
- });
200
- this.sourceWatcher = new Watcher([sourceBasePath, ...extraSourceWatchPaths], {
193
+ this.watcher = new Watcher(watchPaths, {
201
194
  recursive: true,
202
195
  ignoreInitial: true,
203
- ignore: /\.git|node_modules/
196
+ ignore: /\.git|node_modules|dist/
204
197
  });
205
- this.sourceWatcher.on("all", (_, path) => {
206
- if (!path.endsWith("highstate.manifest.json")) {
198
+ this.watcher.on("all", (event, path) => {
199
+ this.logger.debug({ event, path }, "library event");
200
+ if (!path.endsWith(".json") && !path.endsWith(".ts")) {
207
201
  return;
208
202
  }
209
- void this.updateUnitSourceHashes(path);
203
+ void this.handleFileEvent(path);
210
204
  });
211
- this.logger.debug({ msg: "initialized", modulePaths });
212
205
  }
213
- libraryWatcher;
214
- sourceWatcher;
206
+ watcher;
215
207
  lock = new BetterLock();
216
208
  eventEmitter = new EventEmitter();
217
209
  library = null;
218
210
  worker = null;
211
+ packages = /* @__PURE__ */ new Map();
219
212
  resolvedUnitSources = /* @__PURE__ */ new Map();
220
213
  async loadLibrary() {
221
214
  return await this.lock.acquire(async () => {
@@ -228,22 +221,27 @@ var LocalLibraryBackend = class _LocalLibraryBackend {
228
221
  yield library;
229
222
  }
230
223
  }
231
- async getResolvedUnitSources() {
232
- return await this.lock.acquire(async () => {
233
- const [library] = await this.getLibrary();
234
- if (!this.resolvedUnitSources.size) {
235
- await this.syncUnitSources(library);
236
- }
224
+ getLoadedResolvedUnitSources() {
225
+ return this.lock.acquire(() => {
237
226
  return Array.from(this.resolvedUnitSources.values());
238
227
  });
239
228
  }
240
- async getResolvedUnitSource(unitType) {
229
+ async getResolvedUnitSources(unitTypes) {
241
230
  return await this.lock.acquire(async () => {
242
231
  const [library] = await this.getLibrary();
243
- if (!this.resolvedUnitSources.size) {
244
- await this.syncUnitSources(library);
232
+ const units = unitTypes.map((type) => library.components[type]).filter(isUnitModel);
233
+ const packageNames = Object.keys(groupBy(units, (unit) => unit.source.package));
234
+ await this.ensureLibraryPackagesLoaded(packageNames, true);
235
+ const result = [];
236
+ for (const unitType of unitTypes) {
237
+ const resolvedUnitSource = this.resolvedUnitSources.get(unitType);
238
+ if (resolvedUnitSource) {
239
+ result.push(resolvedUnitSource);
240
+ } else {
241
+ this.logger.warn(`resolved unit source not found for unit: "%s"`, unitType);
242
+ }
245
243
  }
246
- return this.resolvedUnitSources.get(unitType) ?? null;
244
+ return result;
247
245
  });
248
246
  }
249
247
  async *watchResolvedUnitSources(signal) {
@@ -253,63 +251,6 @@ var LocalLibraryBackend = class _LocalLibraryBackend {
253
251
  yield resolvedUnitSource;
254
252
  }
255
253
  }
256
- async syncUnitSources(library) {
257
- const unitsToResolve = /* @__PURE__ */ new Map();
258
- for (const component of Object.values(library.components)) {
259
- if (!isUnitModel(component)) {
260
- continue;
261
- }
262
- const existingResolvedSource = this.resolvedUnitSources.get(component.type);
263
- const expectedSource = JSON.stringify(component.source);
264
- if (existingResolvedSource?.serializedSource !== expectedSource) {
265
- unitsToResolve.set(component.type, component);
266
- }
267
- }
268
- await this.runSourceResolution(unitsToResolve);
269
- }
270
- async runSourceResolution(units) {
271
- const workerPathUrl = importMetaResolve(
272
- `@highstate/backend/source-resolution-worker`,
273
- import.meta.url
274
- );
275
- const workerPath = fileURLToPath(workerPathUrl);
276
- const worker = new Worker(workerPath, {
277
- workerData: {
278
- requests: Array.from(units.values()).map((unit) => ({
279
- unitType: unit.type,
280
- source: unit.source
281
- })),
282
- sourceBasePath: this.sourceBasePath,
283
- logLevel: "error"
284
- }
285
- });
286
- for await (const [event] of on(worker, "message")) {
287
- const eventData = event;
288
- if (eventData.type !== "result") {
289
- throw new Error(`Unexpected message type '${eventData.type}', expected 'result'`);
290
- }
291
- for (const result of eventData.results) {
292
- const unit = units.get(result.unitType);
293
- if (!unit) {
294
- this.logger.warn("unit not found for resolved source", { unitType: result.unitType });
295
- continue;
296
- }
297
- const resolvedSource = {
298
- unitType: result.unitType,
299
- serializedSource: JSON.stringify(unit.source),
300
- projectPath: result.projectPath,
301
- packageJsonPath: result.packageJsonPath,
302
- allowedDependencies: result.allowedDependencies,
303
- sourceHash: result.sourceHash
304
- };
305
- this.resolvedUnitSources.set(result.unitType, resolvedSource);
306
- this.eventEmitter.emit("resolvedUnitSource", resolvedSource);
307
- }
308
- this.logger.info("unit sources synced");
309
- return;
310
- }
311
- throw new Error("Worker ended without sending the result");
312
- }
313
254
  async evaluateCompositeInstances(allInstances, resolvedInputs, instanceIds) {
314
255
  return await this.lock.acquire(async () => {
315
256
  this.logger.info("evaluating %d composite instances", instanceIds.length);
@@ -357,11 +298,14 @@ var LocalLibraryBackend = class _LocalLibraryBackend {
357
298
  if (this.library && this.worker) {
358
299
  return [this.library, this.worker];
359
300
  }
360
- return await this.updateLibrary();
301
+ return await this.reloadLibrary();
361
302
  }
362
- async updateLibrary() {
363
- this.logger.info("creating library worker");
364
- this.worker = this.createWorker({ modulePaths: this.modulePaths, logLevel: "silent" });
303
+ async reloadLibrary() {
304
+ this.logger.info("reloading library");
305
+ this.worker = this.createLibraryWorker({
306
+ modulePaths: this.libraryPackages,
307
+ logLevel: "silent"
308
+ });
365
309
  for await (const [event] of on(this.worker, "message")) {
366
310
  const eventData = event;
367
311
  if (eventData.type === "error") {
@@ -377,36 +321,26 @@ var LocalLibraryBackend = class _LocalLibraryBackend {
377
321
  this.eventEmitter.emit("library", updates);
378
322
  this.library = eventData.library;
379
323
  this.logger.info("library reloaded");
380
- await this.syncUnitSources(eventData.library);
381
324
  return [this.library, this.worker];
382
325
  }
383
326
  throw new Error("Worker ended without sending library model");
384
327
  }
385
- createWorker(workerData) {
386
- const workerPathUrl = importMetaResolve(`@highstate/backend/library-worker`, import.meta.url);
387
- const workerPath = fileURLToPath(workerPathUrl);
388
- return new Worker(workerPath, { workerData });
389
- }
390
- async updateUnitSourceHashes(path) {
391
- const packageJsonPath = await resolvePackageJSON(path);
392
- const packageJson = await readPackageJSON(path);
393
- const library = await this.loadLibrary();
394
- const manifestPath = resolve(dirname(packageJsonPath), "dist", "highstate.manifest.json");
395
- let manifest;
396
- try {
397
- manifest = JSON.parse(await readFile(manifestPath, "utf8"));
398
- } catch (error) {
399
- this.logger.debug(
400
- { error },
401
- `failed to read highstate manifest for package "%s"`,
402
- packageJson.name
328
+ async reloadUnitManifest(libraryPackage) {
329
+ const library = this.library;
330
+ if (!library) {
331
+ this.logger.warn(
332
+ `library not loaded, cannot reload unit manifest for package: "%s"`,
333
+ libraryPackage.name
403
334
  );
335
+ return;
404
336
  }
337
+ const manifest = await this.readLibraryPackageManifest(libraryPackage);
338
+ const packageJson = await readPackageJSON(libraryPackage.rootPath);
405
339
  for (const unit of Object.values(library.components)) {
406
340
  if (!isUnitModel(unit)) {
407
341
  continue;
408
342
  }
409
- if (unit.source.package !== packageJson.name) {
343
+ if (unit.source.package !== libraryPackage.name) {
410
344
  continue;
411
345
  }
412
346
  const relativePath = unit.source.path ? `./dist/${unit.source.path}/index.js` : `./dist/index.js`;
@@ -416,40 +350,184 @@ var LocalLibraryBackend = class _LocalLibraryBackend {
416
350
  continue;
417
351
  }
418
352
  const resolvedSource = this.resolvedUnitSources.get(unit.type);
419
- if (!resolvedSource) {
420
- this.logger.warn(`resolved source not found for unit: "%s"`, unit.type);
421
- continue;
422
- }
423
353
  const newResolvedSource = {
424
- ...resolvedSource,
425
- sourceHash
354
+ unitType: unit.type,
355
+ sourceHash,
356
+ projectPath: resolve(libraryPackage.rootPath, relativePath),
357
+ allowedDependencies: Object.keys(packageJson.peerDependencies ?? {})
426
358
  };
359
+ if (resolvedSource?.sourceHash === newResolvedSource.sourceHash && resolvedSource?.projectPath === newResolvedSource.projectPath) {
360
+ continue;
361
+ }
427
362
  this.resolvedUnitSources.set(unit.type, newResolvedSource);
428
363
  this.eventEmitter.emit("resolvedUnitSource", newResolvedSource);
429
- this.logger.info(`updated source hash for unit: "%s"`, unit.type);
364
+ this.logger.debug(`updated source for unit: "%s"`, unit.type);
430
365
  }
431
366
  }
367
+ async ensureLibraryPackagesLoaded(names, installIfNotFound = false) {
368
+ const packagesToLoad = names.filter((name) => !this.packages.has(name));
369
+ if (packagesToLoad.length > 0) {
370
+ await this.loadLibraryPackages(packagesToLoad, installIfNotFound);
371
+ }
372
+ }
373
+ async rebuildLibraryPackage(libraryPackage, installDeps = false, updateDeps = false, rebuiltPackages = /* @__PURE__ */ new Set()) {
374
+ if (rebuiltPackages.has(libraryPackage.name)) {
375
+ return;
376
+ }
377
+ rebuiltPackages.add(libraryPackage.name);
378
+ if (installDeps) {
379
+ this.logger.info(`installing dependencies for package "${libraryPackage.name}"`);
380
+ await installDependencies({ cwd: libraryPackage.rootPath });
381
+ }
382
+ if (updateDeps) {
383
+ await this.updateLibraryPackageDependencies(libraryPackage);
384
+ }
385
+ this.logger.info(`rebuilding library package "${libraryPackage.name}" via build script`);
386
+ await runScript("build", { cwd: libraryPackage.rootPath });
387
+ if (this.libraryPackages.includes(libraryPackage.name)) {
388
+ await this.reloadLibrary();
389
+ } else {
390
+ await this.reloadUnitManifest(libraryPackage);
391
+ }
392
+ await this.rebuildLibraryPackageDependents(libraryPackage, rebuiltPackages);
393
+ }
394
+ async updateLibraryPackageDependencies(libraryPackage) {
395
+ const packageJson = await readPackageJSON(libraryPackage.rootPath);
396
+ const parsedName = _LocalLibraryBackend.parseDependencyName(libraryPackage.name);
397
+ const dependencyPackageNames = pipe(
398
+ [packageJson.dependencies, packageJson.devDependencies, packageJson.peerDependencies],
399
+ flatMap((deps) => Object.keys(deps ?? {})),
400
+ unique(),
401
+ map(_LocalLibraryBackend.parseDependencyName)
402
+ );
403
+ const sameScopeDependencies = dependencyPackageNames.filter(
404
+ (dep) => dep.scope === parsedName.scope && dep.name !== parsedName.name
405
+ );
406
+ await this.ensureLibraryPackagesLoaded(sameScopeDependencies.map((dep) => dep.name));
407
+ for (const dependency of sameScopeDependencies) {
408
+ const dependencyPackage = this.packages.get(dependency.name);
409
+ if (!dependencyPackage) {
410
+ this.logger.warn(`dependency package not found for graph update: "%s"`, dependency.name);
411
+ continue;
412
+ }
413
+ libraryPackage.dependencies.add(dependency.name);
414
+ dependencyPackage.dependents.add(libraryPackage.name);
415
+ }
416
+ }
417
+ async rebuildLibraryPackageDependents(libraryPackage, rebuiltPackages = /* @__PURE__ */ new Set()) {
418
+ const promises = [];
419
+ for (const dependent of libraryPackage.dependents) {
420
+ const dependentPackage = this.packages.get(dependent);
421
+ if (!dependentPackage) {
422
+ this.logger.warn(`dependent package not found for rebuild: "%s"`, dependent);
423
+ continue;
424
+ }
425
+ promises.push(this.rebuildLibraryPackage(dependentPackage, false, false, rebuiltPackages));
426
+ }
427
+ await Promise.all(promises);
428
+ }
429
+ static parseDependencyName(dependency) {
430
+ if (dependency.startsWith("@")) {
431
+ const parts = dependency.split("/");
432
+ return {
433
+ name: dependency,
434
+ scope: parts[0]
435
+ };
436
+ }
437
+ return {
438
+ name: dependency,
439
+ scope: null
440
+ };
441
+ }
442
+ async readLibraryPackageManifest(libraryPackage) {
443
+ const manifestPath = resolve(libraryPackage.rootPath, "dist", "highstate.manifest.json");
444
+ try {
445
+ const manifest = JSON.parse(await readFile(manifestPath, "utf8"));
446
+ return manifest;
447
+ } catch (error) {
448
+ this.logger.debug(
449
+ { error },
450
+ `failed to read highstate manifest of package: "%s"`,
451
+ libraryPackage.name
452
+ );
453
+ return void 0;
454
+ }
455
+ }
456
+ async loadLibraryPackages(names, installIfNotFound = false) {
457
+ this.logger.info("loading library packages: %s", names.join(", "));
458
+ const missingPackages = [];
459
+ const packagesToUpdate = [];
460
+ const worker = this.createPackageResolutionWorker({ packageNames: names });
461
+ for await (const [event] of on(worker, "message")) {
462
+ const eventData = event;
463
+ if (eventData.type !== "result") {
464
+ continue;
465
+ }
466
+ for (const result of eventData.results) {
467
+ if (result.type === "success") {
468
+ const libraryPackage = {
469
+ name: result.packageName,
470
+ rootPath: result.packageRootPath,
471
+ dependencies: /* @__PURE__ */ new Set(),
472
+ dependents: /* @__PURE__ */ new Set()
473
+ };
474
+ this.packages.set(result.packageName, libraryPackage);
475
+ packagesToUpdate.push(libraryPackage);
476
+ this.logger.info(`loaded library package: "%s"`, result.packageName);
477
+ } else if (result.type === "not-found") {
478
+ missingPackages.push(result.packageName);
479
+ } else {
480
+ this.logger.error(
481
+ `failed to load library package "%s": %s`,
482
+ result.packageName,
483
+ result.error
484
+ );
485
+ }
486
+ }
487
+ break;
488
+ }
489
+ for (const libraryPackage of packagesToUpdate) {
490
+ await this.updateLibraryPackageDependencies(libraryPackage);
491
+ if (!this.libraryPackages.includes(libraryPackage.name)) {
492
+ await this.reloadUnitManifest(libraryPackage);
493
+ }
494
+ }
495
+ if (installIfNotFound && missingPackages.length > 0) {
496
+ this.logger.info("installing missing library packages: %s", missingPackages.join(", "));
497
+ await addDependency(missingPackages);
498
+ await this.loadLibraryPackages(missingPackages);
499
+ }
500
+ }
501
+ async handleFileEvent(path) {
502
+ await this.lock.acquire(async () => {
503
+ const libraryPackage = this.packages.values().find((pkg) => path.startsWith(pkg.rootPath));
504
+ if (libraryPackage) {
505
+ await this.rebuildLibraryPackage(libraryPackage);
506
+ }
507
+ });
508
+ }
509
+ createLibraryWorker(workerData) {
510
+ const workerPathUrl = importMetaResolve(`@highstate/backend/library-worker`, import.meta.url);
511
+ const workerPath = fileURLToPath(workerPathUrl);
512
+ return new Worker(workerPath, { workerData });
513
+ }
514
+ createPackageResolutionWorker(workerData) {
515
+ const workerPathUrl = importMetaResolve(
516
+ `@highstate/backend/package-resolution-worker`,
517
+ import.meta.url
518
+ );
519
+ const workerPath = fileURLToPath(workerPathUrl);
520
+ return new Worker(workerPath, { workerData });
521
+ }
432
522
  static async create(config, logger) {
433
- const modulePaths = [];
434
- for (const module of config.HIGHSTATE_BACKEND_LIBRARY_LOCAL_MODULES) {
435
- const url = importMetaResolve(module, import.meta.url);
436
- let path = fileURLToPath(url);
437
- if (basename(path).includes(".")) {
438
- path = dirname(path);
439
- }
440
- modulePaths.push(path);
441
- }
442
- let sourceBasePath = config.HIGHSTATE_BACKEND_LIBRARY_LOCAL_SOURCE_BASE_PATH;
443
- const extraSourceWatchPaths = config.HIGHSTATE_BACKEND_LIBRARY_LOCAL_EXTRA_SOURCE_WATCH_PATHS;
444
- if (!sourceBasePath) {
523
+ let watchPaths = config.HIGHSTATE_BACKEND_LIBRARY_LOCAL_WATCH_PATHS;
524
+ if (!watchPaths) {
445
525
  const [projectPath] = await resolveMainLocalProject();
446
- sourceBasePath = resolve(projectPath, "units");
447
- extraSourceWatchPaths.push(projectPath);
526
+ watchPaths = [resolve(projectPath, "packages")];
448
527
  }
449
528
  return new _LocalLibraryBackend(
450
- modulePaths,
451
- sourceBasePath,
452
- extraSourceWatchPaths,
529
+ config.HIGHSTATE_BACKEND_LIBRARY_LOCAL_PACKAGES,
530
+ watchPaths,
453
531
  logger.child({ backend: "LibraryBackend", service: "LocalLibraryBackend" })
454
532
  );
455
533
  }
@@ -842,6 +920,30 @@ var ProjectManager = class _ProjectManager {
842
920
  yield children;
843
921
  }
844
922
  }
923
+ /**
924
+ * Loads the full info of a project, including instances, hubs, and composite instances.
925
+ *
926
+ * Also filters out instances that are not in the library.
927
+ *
928
+ * @param projectId The ID of the project to load.
929
+ */
930
+ async getProject(projectId) {
931
+ const [{ instances, hubs }, compositeInstances, library] = await Promise.all([
932
+ this.projectBackend.getProject(projectId),
933
+ this.stateBackend.getCompositeInstances(projectId),
934
+ this.libraryBackend.loadLibrary()
935
+ ]);
936
+ const filteredInstances = instances.filter((instance) => instance.type in library.components);
937
+ const filteredCompositeInstances = compositeInstances.filter((instance) => instance.instance.type in library.components).map((instance) => ({
938
+ ...instance,
939
+ children: instance.children.filter((child) => child.type in library.components)
940
+ }));
941
+ return {
942
+ instances: filteredInstances,
943
+ hubs,
944
+ compositeInstances: filteredCompositeInstances
945
+ };
946
+ }
845
947
  async createInstance(projectId, instance) {
846
948
  const createdInstance = await this.projectBackend.createInstance(projectId, instance);
847
949
  await this.updateCompositeInstance(projectId, createdInstance);
@@ -986,9 +1088,10 @@ var ProjectManager = class _ProjectManager {
986
1088
  }
987
1089
  let sourceHash;
988
1090
  if (isUnitModel2(library.components[instance.type])) {
989
- const resolvedUnit = await this.libraryBackend.getResolvedUnitSource(instance.type);
1091
+ const resolvedUnits = await this.libraryBackend.getResolvedUnitSources([instance.type]);
1092
+ const resolvedUnit = resolvedUnits.find((unit) => unit.unitType === instance.type);
990
1093
  if (!resolvedUnit) {
991
- throw new Error(`Resolved unit not found: ${instance.type}`);
1094
+ throw new Error(`Resolved unit not found for type "${instance.type}"`);
992
1095
  }
993
1096
  sourceHash = resolvedUnit.sourceHash;
994
1097
  }
@@ -1088,7 +1191,7 @@ import { z as z7 } from "zod";
1088
1191
  import spawn from "nano-spawn";
1089
1192
 
1090
1193
  // src/terminal/run.sh.ts
1091
- var runScript = `set -e -o pipefail
1194
+ var runScript2 = `set -e -o pipefail
1092
1195
  read -r data
1093
1196
 
1094
1197
  # Extract env and files as key-value pairs, and command as an array
@@ -1143,7 +1246,7 @@ var DockerTerminalBackend = class _DockerTerminalBackend {
1143
1246
  const hsTempDir = resolve3(tmpdir(), "highstate");
1144
1247
  await mkdir2(hsTempDir, { recursive: true });
1145
1248
  const runScriptPath = resolve3(hsTempDir, "run.sh");
1146
- await writeFile2(runScriptPath, runScript, { mode: 493 });
1249
+ await writeFile2(runScriptPath, runScript2, { mode: 493 });
1147
1250
  const args = [
1148
1251
  "run",
1149
1252
  "-i",
@@ -1630,10 +1733,7 @@ var LocalRunnerBackend = class _LocalRunnerBackend {
1630
1733
  async updateWorker(options, configMap, preview) {
1631
1734
  const instanceId = _LocalRunnerBackend.getInstanceId(options);
1632
1735
  try {
1633
- const resolvedSource = await this.libraryBackend.getResolvedUnitSource(options.instanceType);
1634
- if (!resolvedSource) {
1635
- throw new Error(`Resolved unit source not found for ${options.instanceType}`);
1636
- }
1736
+ const resolvedSource = await this.getResolvedUnitSource(options.instanceType);
1637
1737
  await this.pulumiProjectHost.runLocal(
1638
1738
  {
1639
1739
  projectId: options.projectId,
@@ -1737,7 +1837,7 @@ var LocalRunnerBackend = class _LocalRunnerBackend {
1737
1837
  async destroyWorker(options) {
1738
1838
  const instanceId = _LocalRunnerBackend.getInstanceId(options);
1739
1839
  try {
1740
- const resolvedSource = await this.libraryBackend.getResolvedUnitSource(options.instanceType);
1840
+ const resolvedSource = await this.getResolvedUnitSource(options.instanceType);
1741
1841
  if (!resolvedSource) {
1742
1842
  throw new Error(`Resolved unit source not found for ${options.instanceType}`);
1743
1843
  }
@@ -1980,6 +2080,14 @@ var LocalRunnerBackend = class _LocalRunnerBackend {
1980
2080
  await ensureDependencyInstalled(packageName);
1981
2081
  return true;
1982
2082
  }
2083
+ async getResolvedUnitSource(instanceType) {
2084
+ const sources = await this.libraryBackend.getResolvedUnitSources([instanceType]);
2085
+ const source = sources.find((source2) => source2.unitType === instanceType);
2086
+ if (!source) {
2087
+ throw new Error(`Resolved unit source not found for ${instanceType}`);
2088
+ }
2089
+ return source;
2090
+ }
1983
2091
  static getStackName(options) {
1984
2092
  return `${options.projectId}_${options.instanceName}`;
1985
2093
  }
@@ -2538,6 +2646,7 @@ import { mapValues as mapValues3 } from "remeda";
2538
2646
 
2539
2647
  // src/orchestrator/operation-workset.ts
2540
2648
  import { isUnitModel as isUnitModel3 } from "@highstate/contract";
2649
+ import { unique as unique2 } from "remeda";
2541
2650
  var OperationWorkset = class _OperationWorkset {
2542
2651
  constructor(operation, library, stateManager, logger) {
2543
2652
  this.operation = operation;
@@ -2788,13 +2897,15 @@ var OperationWorkset = class _OperationWorkset {
2788
2897
  return Array.from(instanceIds);
2789
2898
  }
2790
2899
  static async load(operation, projectBackend, libraryBackend, stateBackend, stateManager, logger, signal) {
2791
- const [library, unitSources, project, compositeInstances, states] = await Promise.all([
2900
+ const [library, project, compositeInstances, states] = await Promise.all([
2792
2901
  libraryBackend.loadLibrary(signal),
2793
- libraryBackend.getResolvedUnitSources(),
2794
2902
  projectBackend.getProject(operation.projectId, signal),
2795
2903
  stateBackend.getCompositeInstances(operation.projectId, signal),
2796
2904
  stateBackend.getAllInstanceStates(operation.projectId, signal)
2797
2905
  ]);
2906
+ const unitSources = await libraryBackend.getResolvedUnitSources(
2907
+ unique2(project.instances.map((i) => i.type))
2908
+ );
2798
2909
  const workset = new _OperationWorkset(
2799
2910
  operation,
2800
2911
  library,