@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.
- package/client/moduleBootstrap.js +54 -54
- package/client/moduleBootstrap.test.js +12 -12
- package/client/{descriptorSections.js → packageMetadataSections.js} +15 -15
- package/client/vite/clientBootstrapPlugin.js +67 -100
- package/client/vite/clientBootstrapPlugin.test.js +177 -238
- package/internal/node/installedPackages.js +283 -0
- package/package.json +1 -1
- package/server/platform/providerRuntime/{descriptorCatalog.js → packageCatalog.js} +32 -47
- package/server/platform/providerRuntime/providerLoader.js +18 -18
- package/server/platform/providerRuntime.js +15 -22
- package/server/platform/providerRuntime.test.js +56 -171
- package/server/platform/surfaceRuntime.test.js +1 -1
- package/server/support/index.js +6 -0
- package/server/support/shellOutlets.js +15 -51
- package/server/support/shellOutlets.test.js +23 -26
- package/shared/support/generatedUiContract.js +1 -1
- package/shared/support/generatedUiContract.test.js +11 -1
- package/shared/validators/schemaDefinitions.test.js +39 -0
- package/internal/node/installedPackageDescriptor.js +0 -125
- package/server/platform/providerRuntime/lockfile.js +0 -27
- package/shared/support/packageDescriptor.test.js +0 -147
|
@@ -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, ".
|
|
14
|
-
`${JSON.stringify({
|
|
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
|
-
|
|
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
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
{
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
|
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
|
|
153
|
-
|
|
154
|
-
|
|
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
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
{
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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
|
|
71
|
+
test("shouldServePathForSurface allows packageMetadata-declared global ui paths", () => {
|
|
72
72
|
const surfaceRuntime = createFakeSurfaceRuntime();
|
|
73
73
|
assert.equal(
|
|
74
74
|
shouldServePathForSurface({
|
package/server/support/index.js
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
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}${
|
|
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
|
|
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
|
|
237
|
-
const
|
|
238
|
-
const
|
|
239
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
305
|
-
const
|
|
306
|
-
const
|
|
307
|
-
|
|
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}${
|
|
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
|
-
".
|
|
74
|
+
"package.json",
|
|
75
75
|
`${JSON.stringify(
|
|
76
76
|
{
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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.
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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: /\
|
|
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
|
|
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
|
+
});
|