@highstate/backend 0.7.6 → 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.
@@ -5,19 +5,24 @@ import type {
5
5
  ResolvedUnitSource,
6
6
  } from "./abstractions"
7
7
  import type { Logger } from "pino"
8
- import type { HighstateManifestJson, SourceResolutionResult } from "./source-resolution-worker"
8
+ import type {
9
+ PackageResolutionResponse,
10
+ PackageResolutionWorkerData,
11
+ } from "./package-resolution-worker"
9
12
  import type { WorkerRequest, WorkerResponse } from "./worker/protocol"
10
13
  import { fileURLToPath } from "node:url"
11
14
  import { EventEmitter, on } from "node:events"
12
15
  import { Worker } from "node:worker_threads"
13
- import { basename, dirname, relative, resolve } from "node:path"
16
+ import { resolve } from "node:path"
14
17
  import { readFile } from "node:fs/promises"
15
- import { type InstanceModel, isUnitModel, type UnitModel } from "@highstate/contract"
18
+ import { type InstanceModel, isUnitModel } from "@highstate/contract"
16
19
  import Watcher from "watcher"
17
20
  import { BetterLock } from "better-lock"
18
21
  import { resolve as importMetaResolve } from "import-meta-resolve"
19
22
  import { z } from "zod"
20
- import { readPackageJSON, resolvePackageJSON } from "pkg-types"
23
+ import { readPackageJSON } from "pkg-types"
24
+ import { runScript, installDependencies, addDependency } from "nypm"
25
+ import { flatMap, groupBy, map, pipe, unique } from "remeda"
21
26
  import { resolveMainLocalProject, stringArrayType } from "../common"
22
27
  import {
23
28
  diffLibraries,
@@ -27,9 +32,8 @@ import {
27
32
  } from "../shared"
28
33
 
29
34
  export const localLibraryBackendConfig = z.object({
30
- HIGHSTATE_BACKEND_LIBRARY_LOCAL_MODULES: stringArrayType.default("@highstate/library"),
31
- HIGHSTATE_BACKEND_LIBRARY_LOCAL_SOURCE_BASE_PATH: z.string().optional(),
32
- HIGHSTATE_BACKEND_LIBRARY_LOCAL_EXTRA_SOURCE_WATCH_PATHS: stringArrayType.default(""),
35
+ HIGHSTATE_BACKEND_LIBRARY_LOCAL_PACKAGES: stringArrayType.default("@highstate/library"),
36
+ HIGHSTATE_BACKEND_LIBRARY_LOCAL_WATCH_PATHS: stringArrayType.optional(),
33
37
  })
34
38
 
35
39
  interface Events {
@@ -37,9 +41,19 @@ interface Events {
37
41
  resolvedUnitSource: [ResolvedUnitSource]
38
42
  }
39
43
 
44
+ type HighstateManifestJson = {
45
+ sourceHashes?: Record<string, string>
46
+ }
47
+
48
+ type LibraryPackage = {
49
+ name: string
50
+ rootPath: string
51
+ dependencies: Set<string>
52
+ dependents: Set<string>
53
+ }
54
+
40
55
  export class LocalLibraryBackend implements LibraryBackend {
41
- private readonly libraryWatcher: Watcher
42
- private readonly sourceWatcher: Watcher
56
+ private readonly watcher: Watcher
43
57
 
44
58
  private readonly lock = new BetterLock()
45
59
  private readonly eventEmitter = new EventEmitter<Events>()
@@ -47,37 +61,29 @@ export class LocalLibraryBackend implements LibraryBackend {
47
61
  private library: LibraryModel | null = null
48
62
  private worker: Worker | null = null
49
63
 
64
+ private readonly packages = new Map<string, LibraryPackage>()
50
65
  private readonly resolvedUnitSources = new Map<string, ResolvedUnitSource>()
51
66
 
52
67
  private constructor(
53
- private readonly modulePaths: string[],
54
- private readonly sourceBasePath: string,
55
- extraSourceWatchPaths: string[],
68
+ private readonly libraryPackages: string[],
69
+ watchPaths: string[],
56
70
  private readonly logger: Logger,
57
71
  ) {
58
- this.libraryWatcher = new Watcher(modulePaths, { recursive: true, ignoreInitial: true })
59
- this.libraryWatcher.on("all", (event: string, path: string) => {
60
- const prefixPath = modulePaths.find(modulePath => path.startsWith(modulePath))
61
-
62
- this.logger.info({ msg: "library event", event, path: relative(prefixPath!, path) })
63
- void this.lock.acquire(() => this.updateLibrary())
64
- })
65
-
66
- this.sourceWatcher = new Watcher([sourceBasePath, ...extraSourceWatchPaths], {
72
+ this.watcher = new Watcher(watchPaths, {
67
73
  recursive: true,
68
74
  ignoreInitial: true,
69
- ignore: /\.git|node_modules/,
75
+ ignore: /\.git|node_modules|dist/,
70
76
  })
71
77
 
72
- this.sourceWatcher.on("all", (_, path: string) => {
73
- if (!path.endsWith("highstate.manifest.json")) {
78
+ this.watcher.on("all", (event: string, path: string) => {
79
+ this.logger.debug({ event, path }, "library event")
80
+
81
+ if (!path.endsWith(".json") && !path.endsWith(".ts")) {
74
82
  return
75
83
  }
76
84
 
77
- void this.updateUnitSourceHashes(path)
85
+ void this.handleFileEvent(path)
78
86
  })
79
-
80
- this.logger.debug({ msg: "initialized", modulePaths })
81
87
  }
82
88
 
83
89
  async loadLibrary(): Promise<LibraryModel> {
@@ -94,29 +100,34 @@ export class LocalLibraryBackend implements LibraryBackend {
94
100
  }
95
101
  }
96
102
 
97
- async getResolvedUnitSources(): Promise<readonly ResolvedUnitSource[]> {
98
- return await this.lock.acquire(async () => {
99
- // ensure library is loaded
100
- const [library] = await this.getLibrary()
101
-
102
- if (!this.resolvedUnitSources.size) {
103
- await this.syncUnitSources(library)
104
- }
105
-
103
+ getLoadedResolvedUnitSources(): Promise<ResolvedUnitSource[]> {
104
+ return this.lock.acquire(() => {
106
105
  return Array.from(this.resolvedUnitSources.values())
107
106
  })
108
107
  }
109
108
 
110
- async getResolvedUnitSource(unitType: string): Promise<ResolvedUnitSource | null> {
109
+ async getResolvedUnitSources(unitTypes: string[]): Promise<ResolvedUnitSource[]> {
111
110
  return await this.lock.acquire(async () => {
112
- // ensure library is loaded
113
111
  const [library] = await this.getLibrary()
114
112
 
115
- if (!this.resolvedUnitSources.size) {
116
- await this.syncUnitSources(library)
113
+ const units = unitTypes.map(type => library.components[type]).filter(isUnitModel)
114
+ const packageNames = Object.keys(groupBy(units, unit => unit.source.package))
115
+
116
+ await this.ensureLibraryPackagesLoaded(packageNames, true)
117
+
118
+ const result: ResolvedUnitSource[] = []
119
+
120
+ for (const unitType of unitTypes) {
121
+ const resolvedUnitSource = this.resolvedUnitSources.get(unitType)
122
+
123
+ if (resolvedUnitSource) {
124
+ result.push(resolvedUnitSource)
125
+ } else {
126
+ this.logger.warn(`resolved unit source not found for unit: "%s"`, unitType)
127
+ }
117
128
  }
118
129
 
119
- return this.resolvedUnitSources.get(unitType) ?? null
130
+ return result
120
131
  })
121
132
  }
122
133
 
@@ -128,80 +139,6 @@ export class LocalLibraryBackend implements LibraryBackend {
128
139
  }
129
140
  }
130
141
 
131
- async syncUnitSources(library: LibraryModel): Promise<void> {
132
- const unitsToResolve = new Map<string, UnitModel>()
133
-
134
- for (const component of Object.values(library.components)) {
135
- if (!isUnitModel(component)) {
136
- continue
137
- }
138
-
139
- const existingResolvedSource = this.resolvedUnitSources.get(component.type)
140
- const expectedSource = JSON.stringify(component.source)
141
-
142
- if (existingResolvedSource?.serializedSource !== expectedSource) {
143
- unitsToResolve.set(component.type, component)
144
- }
145
- }
146
-
147
- await this.runSourceResolution(unitsToResolve)
148
- }
149
-
150
- private async runSourceResolution(units: Map<string, UnitModel>): Promise<void> {
151
- const workerPathUrl = importMetaResolve(
152
- `@highstate/backend/source-resolution-worker`,
153
- import.meta.url,
154
- )
155
- const workerPath = fileURLToPath(workerPathUrl)
156
-
157
- const worker = new Worker(workerPath, {
158
- workerData: {
159
- requests: Array.from(units.values()).map(unit => ({
160
- unitType: unit.type,
161
- source: unit.source,
162
- })),
163
- sourceBasePath: this.sourceBasePath,
164
- logLevel: "error",
165
- },
166
- })
167
-
168
- for await (const [event] of on(worker, "message")) {
169
- const eventData = event as {
170
- type: string
171
- results: readonly SourceResolutionResult[]
172
- }
173
-
174
- if (eventData.type !== "result") {
175
- throw new Error(`Unexpected message type '${eventData.type}', expected 'result'`)
176
- }
177
-
178
- for (const result of eventData.results) {
179
- const unit = units.get(result.unitType)
180
- if (!unit) {
181
- this.logger.warn("unit not found for resolved source", { unitType: result.unitType })
182
- continue
183
- }
184
-
185
- const resolvedSource: ResolvedUnitSource = {
186
- unitType: result.unitType,
187
- serializedSource: JSON.stringify(unit.source),
188
- projectPath: result.projectPath,
189
- packageJsonPath: result.packageJsonPath,
190
- allowedDependencies: result.allowedDependencies,
191
- sourceHash: result.sourceHash,
192
- }
193
-
194
- this.resolvedUnitSources.set(result.unitType, resolvedSource)
195
- this.eventEmitter.emit("resolvedUnitSource", resolvedSource)
196
- }
197
-
198
- this.logger.info("unit sources synced")
199
- return
200
- }
201
-
202
- throw new Error("Worker ended without sending the result")
203
- }
204
-
205
142
  async evaluateCompositeInstances(
206
143
  allInstances: InstanceModel[],
207
144
  resolvedInputs: Record<string, Record<string, ResolvedInstanceInput[]>>,
@@ -270,12 +207,16 @@ export class LocalLibraryBackend implements LibraryBackend {
270
207
  return [this.library, this.worker]
271
208
  }
272
209
 
273
- return await this.updateLibrary()
210
+ return await this.reloadLibrary()
274
211
  }
275
212
 
276
- private async updateLibrary(): Promise<[LibraryModel, Worker]> {
277
- this.logger.info("creating library worker")
278
- this.worker = this.createWorker({ modulePaths: this.modulePaths, logLevel: "silent" })
213
+ private async reloadLibrary(): Promise<[LibraryModel, Worker]> {
214
+ this.logger.info("reloading library")
215
+
216
+ this.worker = this.createLibraryWorker({
217
+ modulePaths: this.libraryPackages,
218
+ logLevel: "silent",
219
+ })
279
220
 
280
221
  for await (const [event] of on(this.worker, "message")) {
281
222
  const eventData = event as {
@@ -302,45 +243,31 @@ export class LocalLibraryBackend implements LibraryBackend {
302
243
 
303
244
  this.logger.info("library reloaded")
304
245
 
305
- await this.syncUnitSources(eventData.library)
306
-
307
246
  return [this.library, this.worker]
308
247
  }
309
248
 
310
249
  throw new Error("Worker ended without sending library model")
311
250
  }
312
251
 
313
- private createWorker(workerData: Record<string, unknown>) {
314
- const workerPathUrl = importMetaResolve(`@highstate/backend/library-worker`, import.meta.url)
315
- const workerPath = fileURLToPath(workerPathUrl)
316
-
317
- return new Worker(workerPath, { workerData })
318
- }
319
-
320
- private async updateUnitSourceHashes(path: string): Promise<void> {
321
- const packageJsonPath = await resolvePackageJSON(path)
322
- const packageJson = await readPackageJSON(path)
323
- const library = await this.loadLibrary()
324
-
325
- const manifestPath = resolve(dirname(packageJsonPath), "dist", "highstate.manifest.json")
326
-
327
- let manifest: HighstateManifestJson | undefined
328
- try {
329
- manifest = JSON.parse(await readFile(manifestPath, "utf8")) as HighstateManifestJson
330
- } catch (error) {
331
- this.logger.debug(
332
- { error },
333
- `failed to read highstate manifest for package "%s"`,
334
- packageJson.name,
252
+ private async reloadUnitManifest(libraryPackage: LibraryPackage): Promise<void> {
253
+ const library = this.library
254
+ if (!library) {
255
+ this.logger.warn(
256
+ `library not loaded, cannot reload unit manifest for package: "%s"`,
257
+ libraryPackage.name,
335
258
  )
259
+ return
336
260
  }
337
261
 
262
+ const manifest = await this.readLibraryPackageManifest(libraryPackage)
263
+ const packageJson = await readPackageJSON(libraryPackage.rootPath)
264
+
338
265
  for (const unit of Object.values(library.components)) {
339
266
  if (!isUnitModel(unit)) {
340
267
  continue
341
268
  }
342
269
 
343
- if (unit.source.package !== packageJson.name) {
270
+ if (unit.source.package !== libraryPackage.name) {
344
271
  continue
345
272
  }
346
273
 
@@ -357,47 +284,248 @@ export class LocalLibraryBackend implements LibraryBackend {
357
284
  }
358
285
 
359
286
  const resolvedSource = this.resolvedUnitSources.get(unit.type)
360
- if (!resolvedSource) {
361
- this.logger.warn(`resolved source not found for unit: "%s"`, unit.type)
362
- continue
363
- }
364
287
 
365
288
  const newResolvedSource: ResolvedUnitSource = {
366
- ...resolvedSource,
289
+ unitType: unit.type,
367
290
  sourceHash,
291
+ projectPath: resolve(libraryPackage.rootPath, relativePath),
292
+ allowedDependencies: Object.keys(packageJson.peerDependencies ?? {}),
293
+ }
294
+
295
+ if (
296
+ resolvedSource?.sourceHash === newResolvedSource.sourceHash &&
297
+ resolvedSource?.projectPath === newResolvedSource.projectPath
298
+ ) {
299
+ continue
368
300
  }
369
301
 
370
302
  this.resolvedUnitSources.set(unit.type, newResolvedSource)
371
303
  this.eventEmitter.emit("resolvedUnitSource", newResolvedSource)
372
- this.logger.info(`updated source hash for unit: "%s"`, unit.type)
304
+ this.logger.debug(`updated source for unit: "%s"`, unit.type)
373
305
  }
374
306
  }
375
307
 
376
- static async create(config: z.infer<typeof localLibraryBackendConfig>, logger: Logger) {
377
- const modulePaths: string[] = []
308
+ private async ensureLibraryPackagesLoaded(
309
+ names: string[],
310
+ installIfNotFound = false,
311
+ ): Promise<void> {
312
+ const packagesToLoad = names.filter(name => !this.packages.has(name))
313
+
314
+ if (packagesToLoad.length > 0) {
315
+ await this.loadLibraryPackages(packagesToLoad, installIfNotFound)
316
+ }
317
+ }
318
+
319
+ private async rebuildLibraryPackage(
320
+ libraryPackage: LibraryPackage,
321
+ installDeps = false,
322
+ updateDeps = false,
323
+ rebuiltPackages = new Set<string>(),
324
+ ): Promise<void> {
325
+ if (rebuiltPackages.has(libraryPackage.name)) {
326
+ return
327
+ }
328
+
329
+ rebuiltPackages.add(libraryPackage.name)
330
+
331
+ if (installDeps) {
332
+ this.logger.info(`installing dependencies for package "${libraryPackage.name}"`)
333
+ await installDependencies({ cwd: libraryPackage.rootPath })
334
+ }
335
+
336
+ if (updateDeps) {
337
+ await this.updateLibraryPackageDependencies(libraryPackage)
338
+ }
339
+
340
+ this.logger.info(`rebuilding library package "${libraryPackage.name}" via build script`)
341
+ await runScript("build", { cwd: libraryPackage.rootPath })
342
+
343
+ if (this.libraryPackages.includes(libraryPackage.name)) {
344
+ await this.reloadLibrary()
345
+ } else {
346
+ await this.reloadUnitManifest(libraryPackage)
347
+ }
348
+
349
+ await this.rebuildLibraryPackageDependents(libraryPackage, rebuiltPackages)
350
+ }
351
+
352
+ private async updateLibraryPackageDependencies(libraryPackage: LibraryPackage): Promise<void> {
353
+ const packageJson = await readPackageJSON(libraryPackage.rootPath)
354
+ const parsedName = LocalLibraryBackend.parseDependencyName(libraryPackage.name)
355
+
356
+ const dependencyPackageNames = pipe(
357
+ [packageJson.dependencies, packageJson.devDependencies, packageJson.peerDependencies],
358
+ flatMap(deps => Object.keys(deps ?? {})),
359
+ unique(),
360
+ map(LocalLibraryBackend.parseDependencyName),
361
+ )
362
+
363
+ const sameScopeDependencies = dependencyPackageNames.filter(
364
+ dep => dep.scope === parsedName.scope && dep.name !== parsedName.name,
365
+ )
366
+
367
+ await this.ensureLibraryPackagesLoaded(sameScopeDependencies.map(dep => dep.name))
368
+
369
+ for (const dependency of sameScopeDependencies) {
370
+ const dependencyPackage = this.packages.get(dependency.name)
371
+ if (!dependencyPackage) {
372
+ this.logger.warn(`dependency package not found for graph update: "%s"`, dependency.name)
373
+ continue
374
+ }
375
+
376
+ libraryPackage.dependencies.add(dependency.name)
377
+ dependencyPackage.dependents.add(libraryPackage.name)
378
+ }
379
+ }
380
+
381
+ private async rebuildLibraryPackageDependents(
382
+ libraryPackage: LibraryPackage,
383
+ rebuiltPackages = new Set<string>(),
384
+ ): Promise<void> {
385
+ const promises: Promise<void>[] = []
386
+
387
+ for (const dependent of libraryPackage.dependents) {
388
+ const dependentPackage = this.packages.get(dependent)
389
+ if (!dependentPackage) {
390
+ this.logger.warn(`dependent package not found for rebuild: "%s"`, dependent)
391
+ continue
392
+ }
393
+
394
+ promises.push(this.rebuildLibraryPackage(dependentPackage, false, false, rebuiltPackages))
395
+ }
396
+
397
+ await Promise.all(promises)
398
+ }
399
+
400
+ private static parseDependencyName(this: void, dependency: string) {
401
+ if (dependency.startsWith("@")) {
402
+ const parts = dependency.split("/")
403
+
404
+ return {
405
+ name: dependency,
406
+ scope: parts[0],
407
+ }
408
+ }
409
+
410
+ return {
411
+ name: dependency,
412
+ scope: null,
413
+ }
414
+ }
415
+
416
+ private async readLibraryPackageManifest(
417
+ libraryPackage: LibraryPackage,
418
+ ): Promise<HighstateManifestJson | undefined> {
419
+ const manifestPath = resolve(libraryPackage.rootPath, "dist", "highstate.manifest.json")
420
+
421
+ try {
422
+ const manifest = JSON.parse(await readFile(manifestPath, "utf8")) as HighstateManifestJson
423
+
424
+ return manifest
425
+ } catch (error) {
426
+ this.logger.debug(
427
+ { error },
428
+ `failed to read highstate manifest of package: "%s"`,
429
+ libraryPackage.name,
430
+ )
431
+
432
+ return undefined
433
+ }
434
+ }
435
+
436
+ private async loadLibraryPackages(names: string[], installIfNotFound = false): Promise<void> {
437
+ this.logger.info("loading library packages: %s", names.join(", "))
438
+
439
+ const missingPackages: string[] = []
440
+ const packagesToUpdate: LibraryPackage[] = []
441
+
442
+ const worker = this.createPackageResolutionWorker({ packageNames: names })
443
+ for await (const [event] of on(worker, "message")) {
444
+ const eventData = event as PackageResolutionResponse
445
+
446
+ if (eventData.type !== "result") {
447
+ continue
448
+ }
378
449
 
379
- for (const module of config.HIGHSTATE_BACKEND_LIBRARY_LOCAL_MODULES) {
380
- const url = importMetaResolve(module, import.meta.url)
381
- let path = fileURLToPath(url)
382
- if (basename(path).includes(".")) {
383
- path = dirname(path)
450
+ for (const result of eventData.results) {
451
+ if (result.type === "success") {
452
+ const libraryPackage: LibraryPackage = {
453
+ name: result.packageName,
454
+ rootPath: result.packageRootPath,
455
+ dependencies: new Set(),
456
+ dependents: new Set(),
457
+ }
458
+
459
+ this.packages.set(result.packageName, libraryPackage)
460
+ packagesToUpdate.push(libraryPackage)
461
+
462
+ this.logger.info(`loaded library package: "%s"`, result.packageName)
463
+ } else if (result.type === "not-found") {
464
+ missingPackages.push(result.packageName)
465
+ } else {
466
+ this.logger.error(
467
+ `failed to load library package "%s": %s`,
468
+ result.packageName,
469
+ result.error,
470
+ )
471
+ }
384
472
  }
385
473
 
386
- modulePaths.push(path)
474
+ break
387
475
  }
388
476
 
389
- let sourceBasePath = config.HIGHSTATE_BACKEND_LIBRARY_LOCAL_SOURCE_BASE_PATH
390
- const extraSourceWatchPaths = config.HIGHSTATE_BACKEND_LIBRARY_LOCAL_EXTRA_SOURCE_WATCH_PATHS
391
- if (!sourceBasePath) {
477
+ for (const libraryPackage of packagesToUpdate) {
478
+ await this.updateLibraryPackageDependencies(libraryPackage)
479
+
480
+ if (!this.libraryPackages.includes(libraryPackage.name)) {
481
+ await this.reloadUnitManifest(libraryPackage)
482
+ }
483
+ }
484
+
485
+ if (installIfNotFound && missingPackages.length > 0) {
486
+ this.logger.info("installing missing library packages: %s", missingPackages.join(", "))
487
+ await addDependency(missingPackages)
488
+ await this.loadLibraryPackages(missingPackages)
489
+ }
490
+ }
491
+
492
+ private async handleFileEvent(path: string): Promise<void> {
493
+ await this.lock.acquire(async () => {
494
+ const libraryPackage = this.packages.values().find(pkg => path.startsWith(pkg.rootPath))
495
+
496
+ if (libraryPackage) {
497
+ await this.rebuildLibraryPackage(libraryPackage)
498
+ }
499
+ })
500
+ }
501
+
502
+ private createLibraryWorker(workerData: Record<string, unknown>) {
503
+ const workerPathUrl = importMetaResolve(`@highstate/backend/library-worker`, import.meta.url)
504
+ const workerPath = fileURLToPath(workerPathUrl)
505
+
506
+ return new Worker(workerPath, { workerData })
507
+ }
508
+
509
+ private createPackageResolutionWorker(workerData: PackageResolutionWorkerData) {
510
+ const workerPathUrl = importMetaResolve(
511
+ `@highstate/backend/package-resolution-worker`,
512
+ import.meta.url,
513
+ )
514
+ const workerPath = fileURLToPath(workerPathUrl)
515
+
516
+ return new Worker(workerPath, { workerData })
517
+ }
518
+
519
+ static async create(config: z.infer<typeof localLibraryBackendConfig>, logger: Logger) {
520
+ let watchPaths = config.HIGHSTATE_BACKEND_LIBRARY_LOCAL_WATCH_PATHS
521
+ if (!watchPaths) {
392
522
  const [projectPath] = await resolveMainLocalProject()
393
- sourceBasePath = resolve(projectPath, "units")
394
- extraSourceWatchPaths.push(projectPath)
523
+ watchPaths = [resolve(projectPath, "packages")]
395
524
  }
396
525
 
397
526
  return new LocalLibraryBackend(
398
- modulePaths,
399
- sourceBasePath,
400
- extraSourceWatchPaths,
527
+ config.HIGHSTATE_BACKEND_LIBRARY_LOCAL_PACKAGES,
528
+ watchPaths,
401
529
  logger.child({ backend: "LibraryBackend", service: "LocalLibraryBackend" }),
402
530
  )
403
531
  }
@@ -0,0 +1,70 @@
1
+ import { parentPort, workerData } from "node:worker_threads"
2
+ import { dirname } from "node:path"
3
+ import { findPackageJSON } from "node:module"
4
+ import { realpath } from "node:fs/promises"
5
+ import pino, { type Level } from "pino"
6
+
7
+ export type PackageResolutionWorkerData = {
8
+ packageNames: string[]
9
+ logLevel?: Level
10
+ }
11
+
12
+ export type PackageResult = { packageName: string } & (
13
+ | {
14
+ type: "success"
15
+ packageRootPath: string
16
+ }
17
+ | {
18
+ type: "not-found"
19
+ }
20
+ | {
21
+ type: "error"
22
+ error: string
23
+ }
24
+ )
25
+
26
+ export type PackageResolutionResponse = {
27
+ type: "result"
28
+ results: PackageResult[]
29
+ }
30
+
31
+ const { packageNames, logLevel } = workerData as PackageResolutionWorkerData
32
+
33
+ const logger = pino({ name: "source-resolution-worker", level: logLevel ?? "silent" })
34
+
35
+ const results: PackageResult[] = []
36
+
37
+ for (const packageName of packageNames) {
38
+ try {
39
+ const path = findPackageJSON(packageName, import.meta.url)
40
+ if (!path) {
41
+ throw new Error(`Package "${packageName}" not found`)
42
+ }
43
+
44
+ results.push({
45
+ type: "success",
46
+ packageName,
47
+ packageRootPath: await realpath(dirname(path)),
48
+ })
49
+ } catch (error) {
50
+ logger.error({ error }, `failed to resolve package "%s"`, packageName)
51
+
52
+ if (error instanceof Error && error.message.includes("not found")) {
53
+ results.push({
54
+ type: "not-found",
55
+ packageName,
56
+ })
57
+ } else {
58
+ results.push({
59
+ type: "error",
60
+ packageName,
61
+ error: error instanceof Error ? error.message : String(error),
62
+ })
63
+ }
64
+ }
65
+ }
66
+
67
+ parentPort!.postMessage({
68
+ type: "result",
69
+ results,
70
+ })
@@ -1,10 +1,6 @@
1
- /* eslint-disable @typescript-eslint/no-unsafe-call */
2
- /* eslint-disable @typescript-eslint/no-unsafe-return */
3
- /* eslint-disable @typescript-eslint/no-unsafe-member-access */
4
- /* eslint-disable @typescript-eslint/no-unsafe-assignment */
5
-
6
1
  import type { Logger } from "pino"
7
2
  import type { Jiti } from "jiti"
3
+ import Module from "node:module"
8
4
  import {
9
5
  type Component,
10
6
  type Entity,
@@ -16,8 +12,13 @@ import {
16
12
  import { serializeFunction } from "@pulumi/pulumi/runtime/index.js"
17
13
  import { sha256 } from "crypto-hash"
18
14
 
19
- // eslint-disable-next-line @typescript-eslint/no-require-imports
20
- const Module = require("module")
15
+ declare module "module" {
16
+ // eslint-disable-next-line @typescript-eslint/no-namespace
17
+ namespace Module {
18
+ function _load(request: unknown, parent: unknown, isMain: boolean): unknown
19
+ }
20
+ }
21
+
21
22
  const originalLoad = Module._load
22
23
 
23
24
  Module._load = function (request: unknown, parent: unknown, isMain: boolean) {