@jskit-ai/kernel 0.1.148 → 0.1.149

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.
@@ -8,75 +8,56 @@ import { createProviderRuntimeFromApp } from "./providerRuntime.js";
8
8
 
9
9
  async function createTestAppRoot(prefix) {
10
10
  const appRoot = await mkdtemp(path.join(tmpdir(), prefix));
11
- await mkdir(path.join(appRoot, ".jskit"), { recursive: true });
12
11
  await writeFile(
13
- path.join(appRoot, ".jskit", "lock.json"),
14
- `${JSON.stringify({ lockVersion: 1, installedPackages: {} }, null, 2)}\n`,
12
+ path.join(appRoot, "package.json"),
13
+ `${JSON.stringify({ name: "fixture-app", private: true, type: "module" }, null, 2)}\n`,
15
14
  "utf8"
16
15
  );
17
16
  return appRoot;
18
17
  }
19
18
 
20
- test("createProviderRuntimeFromApp discovers package providers from descriptor discover entries", async () => {
19
+ async function declareLocalPackage(appRoot, packageId, packagePath, jskit = {}) {
20
+ await writeFile(
21
+ path.join(appRoot, "package.json"),
22
+ `${JSON.stringify({
23
+ name: "fixture-app",
24
+ private: true,
25
+ type: "module",
26
+ dependencies: {
27
+ [packageId]: `file:${packagePath}`
28
+ }
29
+ }, null, 2)}\n`,
30
+ "utf8"
31
+ );
32
+ const packageRoot = path.join(appRoot, packagePath);
33
+ await mkdir(packageRoot, { recursive: true });
34
+ await writeFile(
35
+ path.join(packageRoot, "package.json"),
36
+ `${JSON.stringify({
37
+ name: packageId,
38
+ version: "0.1.0",
39
+ description: "Local example package",
40
+ type: "module",
41
+ jskit
42
+ }, null, 2)}\n`,
43
+ "utf8"
44
+ );
45
+ }
46
+
47
+ test("createProviderRuntimeFromApp discovers package providers from package metadata", async () => {
21
48
  const appRoot = await createTestAppRoot("kernel-provider-runtime-discover-");
22
49
  try {
23
50
  await mkdir(path.join(appRoot, "packages", "local-example", "src", "server", "providers"), { recursive: true });
24
- await writeFile(
25
- path.join(appRoot, ".jskit", "lock.json"),
26
- `${JSON.stringify(
27
- {
28
- lockVersion: 1,
29
- installedPackages: {
30
- "@local/example": {
31
- packageId: "@local/example",
32
- version: "0.1.0",
33
- source: {
34
- type: "local-package",
35
- packagePath: "packages/local-example"
36
- },
37
- managed: {
38
- packageJson: {
39
- dependencies: {},
40
- devDependencies: {},
41
- scripts: {}
42
- },
43
- text: {},
44
- files: []
45
- },
46
- options: {},
47
- installedAt: "2026-01-01T00:00:00.000Z"
48
- }
49
- }
50
- },
51
- null,
52
- 2
53
- )}\n`,
54
- "utf8"
55
- );
56
- await writeFile(
57
- path.join(appRoot, "packages", "local-example", "package.descriptor.mjs"),
58
- [
59
- "export default Object.freeze({",
60
- " packageVersion: 1,",
61
- " packageId: \"@local/example\",",
62
- " version: \"0.1.0\",",
63
- " description: \"Local example package\",",
64
- " dependsOn: [],",
65
- " capabilities: {",
66
- " provides: [],",
67
- " requires: []",
68
- " },",
69
- " runtime: {",
70
- " server: {",
71
- " providers: [",
72
- " { discover: { dir: \"src/server/providers\", pattern: \"*Provider.js\" } }",
73
- " ]",
74
- " }",
75
- " }",
76
- "});"
77
- ].join("\n"),
78
- "utf8"
79
- );
51
+ await declareLocalPackage(appRoot, "@local/example", "packages/local-example", {
52
+ capabilities: { provides: [], requires: [] },
53
+ runtime: {
54
+ server: {
55
+ providers: [
56
+ { discover: { dir: "src/server/providers", pattern: "*Provider.js" } }
57
+ ]
58
+ }
59
+ }
60
+ });
80
61
  await writeFile(
81
62
  path.join(appRoot, "packages", "local-example", "src", "server", "providers", "AlphaProvider.js"),
82
63
  [
@@ -145,64 +126,14 @@ test("createProviderRuntimeFromApp ignores app-local src/server/providers folder
145
126
  }
146
127
  });
147
128
 
148
- test("createProviderRuntimeFromApp resolves descriptor using source.packagePath", async () => {
129
+ test("createProviderRuntimeFromApp resolves package metadata from a file dependency", async () => {
149
130
  const appRoot = await createTestAppRoot("kernel-provider-runtime-local-package-");
150
131
  try {
151
132
  await mkdir(path.join(appRoot, "packages", "local-example"), { recursive: true });
152
- await writeFile(
153
- path.join(appRoot, ".jskit", "lock.json"),
154
- `${JSON.stringify(
155
- {
156
- lockVersion: 1,
157
- installedPackages: {
158
- "@local/example": {
159
- packageId: "@local/example",
160
- version: "0.1.0",
161
- source: {
162
- type: "local-package",
163
- packagePath: "packages/local-example"
164
- },
165
- managed: {
166
- packageJson: {
167
- dependencies: {},
168
- devDependencies: {},
169
- scripts: {}
170
- },
171
- text: {},
172
- files: []
173
- },
174
- options: {},
175
- installedAt: "2026-01-01T00:00:00.000Z"
176
- }
177
- }
178
- },
179
- null,
180
- 2
181
- )}\n`,
182
- "utf8"
183
- );
184
- await writeFile(
185
- path.join(appRoot, "packages", "local-example", "package.descriptor.mjs"),
186
- [
187
- "export default Object.freeze({",
188
- " packageVersion: 1,",
189
- " packageId: \"@local/example\",",
190
- " version: \"0.1.0\",",
191
- " description: \"Local example package\",",
192
- " dependsOn: [],",
193
- " capabilities: {",
194
- " provides: [],",
195
- " requires: []",
196
- " },",
197
- " runtime: {",
198
- " server: {",
199
- " providers: []",
200
- " }",
201
- " }",
202
- "});"
203
- ].join("\n"),
204
- "utf8"
205
- );
133
+ await declareLocalPackage(appRoot, "@local/example", "packages/local-example", {
134
+ capabilities: { provides: [], requires: [] },
135
+ runtime: { server: { providers: [] } }
136
+ });
206
137
 
207
138
  const runtime = await createProviderRuntimeFromApp({
208
139
  appRoot,
@@ -220,62 +151,16 @@ test("createProviderRuntimeFromApp wires fastify onClose to provider shutdown ex
220
151
  const appRoot = await createTestAppRoot("kernel-provider-runtime-fastify-close-");
221
152
  try {
222
153
  await mkdir(path.join(appRoot, "packages", "local-example", "src", "server", "providers"), { recursive: true });
223
- await writeFile(
224
- path.join(appRoot, ".jskit", "lock.json"),
225
- `${JSON.stringify(
226
- {
227
- lockVersion: 1,
228
- installedPackages: {
229
- "@local/example": {
230
- packageId: "@local/example",
231
- version: "0.1.0",
232
- source: {
233
- type: "local-package",
234
- packagePath: "packages/local-example"
235
- },
236
- managed: {
237
- packageJson: {
238
- dependencies: {},
239
- devDependencies: {},
240
- scripts: {}
241
- },
242
- text: {},
243
- files: []
244
- },
245
- options: {},
246
- installedAt: "2026-01-01T00:00:00.000Z"
247
- }
248
- }
249
- },
250
- null,
251
- 2
252
- )}\n`,
253
- "utf8"
254
- );
255
- await writeFile(
256
- path.join(appRoot, "packages", "local-example", "package.descriptor.mjs"),
257
- [
258
- "export default Object.freeze({",
259
- " packageVersion: 1,",
260
- " packageId: \"@local/example\",",
261
- " version: \"0.1.0\",",
262
- " description: \"Local example package\",",
263
- " dependsOn: [],",
264
- " capabilities: {",
265
- " provides: [],",
266
- " requires: []",
267
- " },",
268
- " runtime: {",
269
- " server: {",
270
- " providers: [",
271
- " { discover: { dir: \"src/server/providers\", pattern: \"*Provider.js\" } }",
272
- " ]",
273
- " }",
274
- " }",
275
- "});"
276
- ].join("\n"),
277
- "utf8"
278
- );
154
+ await declareLocalPackage(appRoot, "@local/example", "packages/local-example", {
155
+ capabilities: { provides: [], requires: [] },
156
+ runtime: {
157
+ server: {
158
+ providers: [
159
+ { discover: { dir: "src/server/providers", pattern: "*Provider.js" } }
160
+ ]
161
+ }
162
+ }
163
+ });
279
164
  await writeFile(
280
165
  path.join(appRoot, "packages", "local-example", "src", "server", "providers", "CloseAwareProvider.js"),
281
166
  [
@@ -68,7 +68,7 @@ test("shouldServePathForSurface allows api and matching enabled surfaces", () =>
68
68
  );
69
69
  });
70
70
 
71
- test("shouldServePathForSurface allows descriptor-declared global ui paths", () => {
71
+ test("shouldServePathForSurface allows packageMetadata-declared global ui paths", () => {
72
72
  const surfaceRuntime = createFakeSurfaceRuntime();
73
73
  assert.equal(
74
74
  shouldServePathForSurface({
@@ -2,6 +2,12 @@ export { symlinkSafeRequire } from "./symlinkSafeRequire.js";
2
2
  export { resolveAppConfig, resolveMobileConfig, resolveClientAssetMode, resolveMobileCallbackUrls } from "./appConfig.js";
3
3
  export { loadAppConfigFromAppRoot, loadAppConfigFromModuleUrl } from "./appConfigFiles.js";
4
4
  export { importFreshModuleFromAbsolutePath } from "./importFreshModuleFromAbsolutePath.js";
5
+ export {
6
+ collectPackageDependencyIds,
7
+ collectRootDependencySpecifiers,
8
+ createPackageMetadata,
9
+ discoverInstalledPackages
10
+ } from "../../internal/node/installedPackages.js";
5
11
  export { resolveRequiredAppRoot, toPosixPath } from "./path.js";
6
12
  export {
7
13
  DEFAULT_PAGE_LINK_COMPONENT_TOKEN,
@@ -1,7 +1,7 @@
1
1
  import path from "node:path";
2
2
  import { readdir, readFile } from "node:fs/promises";
3
3
  import { pathToFileURL } from "node:url";
4
- import { loadInstalledPackageDescriptor } from "../../internal/node/installedPackageDescriptor.js";
4
+ import { discoverInstalledPackages } from "../../internal/node/installedPackages.js";
5
5
  import { normalizeObject, normalizeText } from "../../shared/support/normalize.js";
6
6
  import { resolveRequiredAppRoot, toPosixPath } from "./path.js";
7
7
  import {
@@ -15,7 +15,6 @@ import {
15
15
  } from "../../shared/support/shellLayoutTargets.js";
16
16
 
17
17
  const VUE_DISCOVERY_IGNORED_ERROR_CODES = new Set(["ENOENT", "ENOTDIR", "EACCES", "EPERM"]);
18
- const LOCK_FILE_RELATIVE_PATH = ".jskit/lock.json";
19
18
  const PLACEMENT_TOPOLOGY_RELATIVE_PATH = "src/placementTopology.js";
20
19
  const ROUTE_TAG_PATTERN = /<route\b([^>]*)>([\s\S]*?)<\/route>/g;
21
20
  const ATTRIBUTE_PATTERN = /([:@]?[A-Za-z_][A-Za-z0-9_-]*)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'))?/g;
@@ -174,33 +173,10 @@ async function collectVueFilePaths(rootDirectoryPath) {
174
173
  return files.sort((left, right) => left.localeCompare(right));
175
174
  }
176
175
 
177
- async function readInstalledPackageStates(appRoot) {
178
- const lockPath = path.resolve(appRoot, LOCK_FILE_RELATIVE_PATH);
179
- let lockSource = "";
180
- try {
181
- lockSource = await readFile(lockPath, "utf8");
182
- } catch (error) {
183
- const errorCode = normalizeText(error?.code).toUpperCase();
184
- if (errorCode === "ENOENT") {
185
- return {};
186
- }
187
- throw error;
188
- }
189
-
190
- let lockPayload = {};
191
- try {
192
- lockPayload = JSON.parse(lockSource);
193
- } catch (error) {
194
- throw new Error(`Invalid JSON in ${LOCK_FILE_RELATIVE_PATH}.`);
195
- }
196
-
197
- return normalizeObject(lockPayload.installedPackages);
198
- }
199
-
200
176
  function normalizePackageOutletTarget({
201
177
  packageId = "",
202
178
  outlet = {},
203
- descriptorPath = ""
179
+ manifestPath = ""
204
180
  } = {}) {
205
181
  const normalizedPackageId = normalizeText(packageId);
206
182
  if (!normalizedPackageId) {
@@ -218,7 +194,7 @@ function normalizePackageOutletTarget({
218
194
  const source = normalizeText(outletRecord.source);
219
195
  const sourcePath = source
220
196
  ? `package:${normalizedPackageId}:${toPosixPath(source)}`
221
- : `package:${normalizedPackageId}${descriptorPath ? `:${toPosixPath(descriptorPath)}` : ""}`;
197
+ : `package:${normalizedPackageId}${manifestPath ? `:${toPosixPath(manifestPath)}` : ""}`;
222
198
 
223
199
  return Object.freeze({
224
200
  ...normalizedTarget,
@@ -229,19 +205,13 @@ function normalizePackageOutletTarget({
229
205
  }
230
206
 
231
207
  async function collectInstalledPackageOutletTargets(appRoot) {
232
- const installedPackageStates = await readInstalledPackageStates(appRoot);
233
- const packageIds = Object.keys(installedPackageStates).sort((left, right) => left.localeCompare(right));
208
+ const installedPackages = await discoverInstalledPackages({ appRoot });
234
209
  const targets = [];
235
210
 
236
- for (const packageId of packageIds) {
237
- const installedPackageState = normalizeObject(installedPackageStates[packageId]);
238
- const descriptorRecord = await loadInstalledPackageDescriptor({
239
- appRoot,
240
- packageId,
241
- installedPackageState
242
- });
243
- const descriptor = normalizeObject(descriptorRecord.descriptor);
244
- const metadata = normalizeObject(descriptor.metadata);
211
+ for (const installedPackage of installedPackages) {
212
+ const packageId = installedPackage.packageId;
213
+ const packageMetadata = normalizeObject(installedPackage.packageMetadata);
214
+ const metadata = normalizeObject(packageMetadata.metadata);
245
215
  const ui = normalizeObject(metadata.ui);
246
216
  const placements = normalizeObject(ui.placements);
247
217
  const outlets = Array.isArray(placements.outlets) ? placements.outlets : [];
@@ -249,7 +219,7 @@ async function collectInstalledPackageOutletTargets(appRoot) {
249
219
  const normalizedTarget = normalizePackageOutletTarget({
250
220
  packageId,
251
221
  outlet,
252
- descriptorPath: descriptorRecord.descriptorPath
222
+ manifestPath: installedPackage.manifestPath
253
223
  });
254
224
  if (normalizedTarget) {
255
225
  targets.push(normalizedTarget);
@@ -297,19 +267,13 @@ async function loadAppPlacementTopology(appRoot) {
297
267
  }
298
268
 
299
269
  async function collectInstalledPackagePlacementTopology(appRoot) {
300
- const installedPackageStates = await readInstalledPackageStates(appRoot);
301
- const packageIds = Object.keys(installedPackageStates).sort((left, right) => left.localeCompare(right));
270
+ const installedPackages = await discoverInstalledPackages({ appRoot });
302
271
  const placements = [];
303
272
 
304
- for (const packageId of packageIds) {
305
- const installedPackageState = normalizeObject(installedPackageStates[packageId]);
306
- const descriptorRecord = await loadInstalledPackageDescriptor({
307
- appRoot,
308
- packageId,
309
- installedPackageState
310
- });
311
- const descriptor = normalizeObject(descriptorRecord.descriptor);
312
- const metadata = normalizeObject(descriptor.metadata);
273
+ for (const installedPackage of installedPackages) {
274
+ const packageId = installedPackage.packageId;
275
+ const packageMetadata = normalizeObject(installedPackage.packageMetadata);
276
+ const metadata = normalizeObject(packageMetadata.metadata);
313
277
  const ui = normalizeObject(metadata.ui);
314
278
  const placementsMeta = normalizeObject(ui.placements);
315
279
  const topology = placementsMeta.topology;
@@ -324,7 +288,7 @@ async function collectInstalledPackagePlacementTopology(appRoot) {
324
288
  placements.push(
325
289
  withTopologySource(
326
290
  placement,
327
- `package:${packageId}${descriptorRecord.descriptorPath ? `:${toPosixPath(descriptorRecord.descriptorPath)}` : ""}`
291
+ `package:${packageId}${installedPackage.manifestPath ? `:${toPosixPath(installedPackage.manifestPath)}` : ""}`
328
292
  )
329
293
  );
330
294
  }
@@ -71,18 +71,13 @@ test("discoverShellOutletTargetsFromApp includes installed package placement out
71
71
  );
72
72
  await writeFileInApp(
73
73
  appRoot,
74
- ".jskit/lock.json",
74
+ "package.json",
75
75
  `${JSON.stringify(
76
76
  {
77
- lockVersion: 1,
78
- installedPackages: {
79
- "@example/users-web": {
80
- packageId: "@example/users-web",
81
- source: {
82
- type: "npm-installed-package",
83
- descriptorPath: "node_modules/@example/users-web/package.descriptor.mjs"
84
- }
85
- }
77
+ name: "fixture-app",
78
+ private: true,
79
+ dependencies: {
80
+ "@example/users-web": "1.0.0"
86
81
  }
87
82
  },
88
83
  null,
@@ -91,23 +86,25 @@ test("discoverShellOutletTargetsFromApp includes installed package placement out
91
86
  );
92
87
  await writeFileInApp(
93
88
  appRoot,
94
- "node_modules/@example/users-web/package.descriptor.mjs",
95
- `export default {
96
- packageId: "@example/users-web",
97
- metadata: {
98
- ui: {
99
- placements: {
100
- outlets: [
101
- {
102
- target: "admin-cog:primary-menu",
103
- source: "src/client/components/UsersWorkspaceToolsWidget.vue"
89
+ "node_modules/@example/users-web/package.json",
90
+ `${JSON.stringify({
91
+ name: "@example/users-web",
92
+ version: "1.0.0",
93
+ jskit: {
94
+ metadata: {
95
+ ui: {
96
+ placements: {
97
+ outlets: [
98
+ {
99
+ target: "admin-cog:primary-menu",
100
+ source: "src/client/components/UsersWorkspaceToolsWidget.vue"
101
+ }
102
+ ]
103
+ }
104
+ }
104
105
  }
105
- ]
106
- }
107
- }
108
- }
109
- };
110
- `
106
+ }
107
+ }, null, 2)}\n`
111
108
  );
112
109
 
113
110
  const discovered = await discoverShellOutletTargetsFromApp({ appRoot });
@@ -15,7 +15,7 @@ import {
15
15
  shouldCreateGeneratedUiNavigationLink
16
16
  } from "./generatedUiContract.js";
17
17
 
18
- test("generated UI navigation role metadata is descriptor-ready", () => {
18
+ test("generated UI navigation role metadata is package-ready", () => {
19
19
  assert.deepEqual(
20
20
  GENERATED_UI_NAVIGATION_ROLE_VALUES,
21
21
  ["primary", "secondary", "utility", "detail", "workflow", "none"]
@@ -1,125 +0,0 @@
1
- import path from "node:path";
2
- import { pathToFileURL } from "node:url";
3
- import { normalizeObject } from "../../shared/support/normalize.js";
4
- import { fileExists } from "./fileSystem.js";
5
-
6
- function resolveNodeModulesDescriptorCandidatePaths({ appRoot, packageId }) {
7
- const packageDescriptorPath = path.join(packageId, "package.descriptor.mjs");
8
- const candidatePaths = [];
9
- let currentRoot = path.resolve(appRoot);
10
-
11
- while (true) {
12
- const nodeModulesRoot =
13
- path.basename(currentRoot) === "node_modules" ? currentRoot : path.join(currentRoot, "node_modules");
14
- candidatePaths.push(path.join(nodeModulesRoot, packageDescriptorPath));
15
-
16
- const parentRoot = path.dirname(currentRoot);
17
- if (parentRoot === currentRoot) {
18
- break;
19
- }
20
- currentRoot = parentRoot;
21
- }
22
-
23
- return candidatePaths;
24
- }
25
-
26
- function resolveDescriptorCandidatePaths({ appRoot, packageId, installedPackageState }) {
27
- const normalizedAppRoot = String(appRoot || "").trim();
28
- if (!normalizedAppRoot) {
29
- throw new TypeError("resolveDescriptorCandidatePaths requires appRoot.");
30
- }
31
-
32
- const normalizedPackageId = String(packageId || "").trim();
33
- if (!normalizedPackageId) {
34
- throw new TypeError("resolveDescriptorCandidatePaths requires packageId.");
35
- }
36
-
37
- const source = normalizeObject(installedPackageState?.source);
38
- const descriptorPathFromSource = String(source.descriptorPath || "").trim();
39
- const packagePathFromSource = String(source.packagePath || "").trim();
40
- const jskitRoot = path.join(normalizedAppRoot, "node_modules", "@jskit-ai", "jskit-cli");
41
-
42
- const candidatePaths = resolveNodeModulesDescriptorCandidatePaths({
43
- appRoot: normalizedAppRoot,
44
- packageId: normalizedPackageId
45
- });
46
- if (packagePathFromSource) {
47
- candidatePaths.push(path.resolve(normalizedAppRoot, packagePathFromSource, "package.descriptor.mjs"));
48
- }
49
- if (descriptorPathFromSource) {
50
- candidatePaths.push(path.resolve(normalizedAppRoot, descriptorPathFromSource));
51
- candidatePaths.push(path.resolve(jskitRoot, descriptorPathFromSource));
52
- }
53
-
54
- const uniqueCandidatePaths = [];
55
- const seenPaths = new Set();
56
- for (const candidatePath of candidatePaths) {
57
- const normalizedCandidatePath = String(candidatePath || "").trim();
58
- if (!normalizedCandidatePath || seenPaths.has(normalizedCandidatePath)) {
59
- continue;
60
- }
61
- seenPaths.add(normalizedCandidatePath);
62
- uniqueCandidatePaths.push(normalizedCandidatePath);
63
- }
64
-
65
- return uniqueCandidatePaths;
66
- }
67
-
68
- async function resolveDescriptorPathForInstalledPackage({ appRoot, packageId, installedPackageState, required = false }) {
69
- const candidatePaths = resolveDescriptorCandidatePaths({
70
- appRoot,
71
- packageId,
72
- installedPackageState
73
- });
74
-
75
- for (const candidatePath of candidatePaths) {
76
- if (await fileExists(candidatePath)) {
77
- return candidatePath;
78
- }
79
- }
80
-
81
- if (required === true) {
82
- throw new Error(`Unable to resolve package descriptor for ${String(packageId || "").trim()}.`);
83
- }
84
-
85
- return "";
86
- }
87
-
88
- function normalizeDescriptorPayload(descriptorModule) {
89
- return normalizeObject(descriptorModule?.default);
90
- }
91
-
92
- async function loadInstalledPackageDescriptor({ appRoot, packageId, installedPackageState, required = false }) {
93
- const descriptorPath = await resolveDescriptorPathForInstalledPackage({
94
- appRoot,
95
- packageId,
96
- installedPackageState,
97
- required
98
- });
99
-
100
- if (!descriptorPath) {
101
- return Object.freeze({
102
- descriptorPath: "",
103
- descriptor: Object.freeze({})
104
- });
105
- }
106
-
107
- try {
108
- const descriptorModule = await import(pathToFileURL(descriptorPath).href);
109
- return Object.freeze({
110
- descriptorPath,
111
- descriptor: normalizeDescriptorPayload(descriptorModule)
112
- });
113
- } catch (error) {
114
- if (required === true) {
115
- throw error;
116
- }
117
-
118
- return Object.freeze({
119
- descriptorPath: "",
120
- descriptor: Object.freeze({})
121
- });
122
- }
123
- }
124
-
125
- export { loadInstalledPackageDescriptor, resolveDescriptorPathForInstalledPackage };
@@ -1,27 +0,0 @@
1
- import path from "node:path";
2
- import { readFile } from "node:fs/promises";
3
- import { fileExists } from "../../../internal/node/fileSystem.js";
4
-
5
- async function readLockFromApp({ appRoot, lockPath = ".jskit/lock.json" } = {}) {
6
- if (!appRoot || typeof appRoot !== "string") {
7
- throw new TypeError("readLockFromApp requires appRoot.");
8
- }
9
-
10
- const absoluteLockPath = path.resolve(appRoot, String(lockPath || ".jskit/lock.json"));
11
- if (!(await fileExists(absoluteLockPath))) {
12
- throw new Error(`Lock file not found: ${absoluteLockPath}`);
13
- }
14
-
15
- const source = await readFile(absoluteLockPath, "utf8");
16
- const lock = JSON.parse(source);
17
- if (!lock || typeof lock !== "object") {
18
- throw new TypeError(`Invalid lock file payload at ${absoluteLockPath}.`);
19
- }
20
-
21
- return {
22
- lock,
23
- lockPath: absoluteLockPath
24
- };
25
- }
26
-
27
- export { readLockFromApp };