@jskit-ai/kernel 0.1.147 → 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 });
@@ -238,7 +238,7 @@ const GENERATED_UI_SOURCE_CONTRACT_PROFILES = Object.freeze({
238
238
  }),
239
239
  Object.freeze({
240
240
  id: "medium-viewport",
241
- pattern: /\b768\b/,
241
+ pattern: /\b(?:768|1024)\b/,
242
242
  message: "Generated UI smoke tests must include tablet-ish medium width."
243
243
  }),
244
244
  Object.freeze({
@@ -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"]
@@ -206,3 +206,13 @@ test("generated UI responsive smoke profile requires compact medium expanded che
206
206
  /missing:medium-viewport/
207
207
  );
208
208
  });
209
+
210
+ test("generated UI responsive smoke profile accepts a Vuetify md viewport", () => {
211
+ assert.doesNotThrow(() => assertGeneratedUiSourceContract(
212
+ "390 1024 1280 scrollWidth toBeGreaterThanOrEqual(48) generated-ui-screen",
213
+ {
214
+ profile: "responsive-smoke",
215
+ sourceName: "tests/e2e/example.spec.ts"
216
+ }
217
+ ));
218
+ });
@@ -5,6 +5,7 @@ import {
5
5
  normalizeSingleSchemaDefinition,
6
6
  resolveSchemaTransportSchemaDefinition
7
7
  } from "./schemaDefinitions.js";
8
+ import { validateSchemaPayload } from "./schemaPayloadValidation.js";
8
9
 
9
10
  test("normalizeSingleSchemaDefinition validates mode eagerly", () => {
10
11
  const definition = {
@@ -49,3 +50,41 @@ test("resolveSchemaTransportSchemaDefinition still resolves valid definitions",
49
50
  assert.equal(transportSchema.additionalProperties, false);
50
51
  assert.equal(transportSchema.properties.name.type, "string");
51
52
  });
53
+
54
+ test("temporal schemas preserve strict JSON strings", () => {
55
+ const definition = {
56
+ schema: createSchema({
57
+ serviceDate: { type: "date", required: true },
58
+ openingTime: { type: "time", required: true },
59
+ scheduledAt: { type: "dateTime", required: true }
60
+ }),
61
+ mode: "replace"
62
+ };
63
+ const payload = {
64
+ serviceDate: "2026-08-13",
65
+ openingTime: "07:08:09.123",
66
+ scheduledAt: "2026-08-13T07:08:09.123+08:00"
67
+ };
68
+
69
+ assert.deepEqual(validateSchemaPayload(definition, payload), payload);
70
+ assert.throws(
71
+ () => validateSchemaPayload(definition, {
72
+ serviceDate: new Date("2026-08-13T00:00:00.000Z"),
73
+ openingTime: new Date("2026-08-13T07:08:09.123Z"),
74
+ scheduledAt: new Date("2026-08-13T07:08:09.123Z")
75
+ }),
76
+ /Schema validation failed\./
77
+ );
78
+ });
79
+
80
+ test("removed timestamp schemas fail instead of silently changing meaning", () => {
81
+ const definition = {
82
+ schema: createSchema({ occurredAt: { type: "timestamp" } }),
83
+ mode: "replace"
84
+ };
85
+
86
+ assert.throws(
87
+ () => validateSchemaPayload(definition, { occurredAt: 1 }),
88
+ /No casting function for type: timestamp/
89
+ );
90
+ });