@jskit-ai/kernel 0.1.4
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/README.md +24 -0
- package/_testable/index.js +4 -0
- package/client/appConfig.js +33 -0
- package/client/componentInteraction.js +51 -0
- package/client/componentInteraction.test.js +111 -0
- package/client/descriptorSections.js +75 -0
- package/client/index.d.ts +70 -0
- package/client/index.js +3 -0
- package/client/logging.js +38 -0
- package/client/moduleBootstrap.js +670 -0
- package/client/moduleBootstrap.test.js +403 -0
- package/client/shellBootstrap.js +233 -0
- package/client/shellBootstrap.test.js +185 -0
- package/client/shellRouting.js +321 -0
- package/client/shellRouting.test.js +113 -0
- package/client/vite/clientBootstrapPlugin.js +259 -0
- package/client/vite/clientBootstrapPlugin.test.js +563 -0
- package/client/vite/index.js +3 -0
- package/internal/node/fileSystem.js +21 -0
- package/internal/node/installedPackageDescriptor.js +104 -0
- package/package.json +43 -0
- package/server/actions/ActionRuntimeServiceProvider.js +309 -0
- package/server/actions/ActionRuntimeServiceProvider.test.js +551 -0
- package/server/actions/index.js +8 -0
- package/server/container/ContainerCoreServiceProvider.js +27 -0
- package/server/container/index.js +10 -0
- package/server/exportPolicy.test.js +68 -0
- package/server/http/HttpFastifyServiceProvider.js +25 -0
- package/server/http/_testable/index.js +2 -0
- package/server/http/index.js +1 -0
- package/server/http/lib/controller.js +183 -0
- package/server/http/lib/controller.test.js +143 -0
- package/server/http/lib/errors.js +12 -0
- package/server/http/lib/httpRuntime.js +82 -0
- package/server/http/lib/index.js +18 -0
- package/server/http/lib/kernel.js +15 -0
- package/server/http/lib/kernel.test.js +880 -0
- package/server/http/lib/middlewareRuntime.js +149 -0
- package/server/http/lib/requestActionExecutor.js +258 -0
- package/server/http/lib/requestScope.js +59 -0
- package/server/http/lib/routeRegistration.js +165 -0
- package/server/http/lib/routeSupport.js +45 -0
- package/server/http/lib/routeValidator.js +469 -0
- package/server/http/lib/routeValidator.test.js +474 -0
- package/server/http/lib/router.js +206 -0
- package/server/kernel/KernelCoreServiceProvider.js +27 -0
- package/server/kernel/index.js +10 -0
- package/server/platform/PlatformServerRuntimeServiceProvider.js +30 -0
- package/server/platform/index.js +5 -0
- package/server/platform/providerRuntime/descriptorCatalog.js +170 -0
- package/server/platform/providerRuntime/helpers.js +45 -0
- package/server/platform/providerRuntime/lockfile.js +27 -0
- package/server/platform/providerRuntime/providerLoader.js +283 -0
- package/server/platform/providerRuntime.js +142 -0
- package/server/platform/providerRuntime.test.js +217 -0
- package/server/platform/runtime.js +40 -0
- package/server/platform/surfaceRuntime.js +150 -0
- package/server/platform/surfaceRuntime.test.js +136 -0
- package/server/registries/actionSurfaceSourceRegistry.js +150 -0
- package/server/registries/bootstrapPayloadContributorRegistry.js +41 -0
- package/server/registries/domainEventListenerRegistry.js +61 -0
- package/server/registries/index.js +36 -0
- package/server/registries/primitives.js +63 -0
- package/server/registries/routeVisibilityResolverRegistry.js +87 -0
- package/server/registries/serviceRegistrationRegistry.js +431 -0
- package/server/runtime/ServerRuntimeCoreServiceProvider.js +65 -0
- package/server/runtime/ServerRuntimeCoreServiceProvider.test.js +53 -0
- package/server/runtime/apiRoutePolicyParity.test.js +109 -0
- package/server/runtime/apiRouteRegistration.js +65 -0
- package/server/runtime/bootBootstrapRoutes.js +46 -0
- package/server/runtime/bootBootstrapRoutes.test.js +79 -0
- package/server/runtime/bootstrapContributors.test.js +114 -0
- package/server/runtime/canonicalJson.js +74 -0
- package/server/runtime/composition.js +142 -0
- package/server/runtime/domainEvents.test.js +114 -0
- package/server/runtime/domainRules.js +50 -0
- package/server/runtime/domainRules.test.js +87 -0
- package/server/runtime/entityChangeEvents.js +182 -0
- package/server/runtime/entityChangeEvents.test.js +211 -0
- package/server/runtime/errors.js +68 -0
- package/server/runtime/errors.test.js +73 -0
- package/server/runtime/fastifyBootstrap.js +372 -0
- package/server/runtime/fastifyBootstrap.test.js +194 -0
- package/server/runtime/index.js +6 -0
- package/server/runtime/integers.js +13 -0
- package/server/runtime/moduleConfig.js +269 -0
- package/server/runtime/moduleConfig.test.js +141 -0
- package/server/runtime/pagination.js +13 -0
- package/server/runtime/realtimeNormalization.js +21 -0
- package/server/runtime/requestUrl.js +38 -0
- package/server/runtime/routeUtils.js +20 -0
- package/server/runtime/runtimeAssembly.js +113 -0
- package/server/runtime/runtimeKernel.js +55 -0
- package/server/runtime/securityAudit.js +269 -0
- package/server/runtime/securityAudit.test.js +41 -0
- package/server/runtime/serviceAuthorization.js +113 -0
- package/server/runtime/serviceAuthorization.test.js +100 -0
- package/server/runtime/serviceRegistration.test.js +197 -0
- package/server/support/SupportCoreServiceProvider.js +25 -0
- package/server/support/appConfig.js +37 -0
- package/server/support/appConfig.test.js +94 -0
- package/server/support/defaultMissingHandler.js +7 -0
- package/server/support/index.js +2 -0
- package/server/support/routePolicyConfig.js +51 -0
- package/server/support/symlinkSafeRequire.js +78 -0
- package/server/support/symlinkSafeRequire.test.js +27 -0
- package/server/surface/SurfaceRoutingServiceProvider.js +27 -0
- package/server/surface/index.js +19 -0
- package/shared/actions/actionContributorHelpers.js +34 -0
- package/shared/actions/actionContributorHelpers.test.js +16 -0
- package/shared/actions/actionDefinitions.js +488 -0
- package/shared/actions/actionDefinitions.test.js +212 -0
- package/shared/actions/audit.js +7 -0
- package/shared/actions/executionContext.js +97 -0
- package/shared/actions/executionContext.test.js +66 -0
- package/shared/actions/idempotency.js +62 -0
- package/shared/actions/index.js +2 -0
- package/shared/actions/observability.js +10 -0
- package/shared/actions/pipeline.js +287 -0
- package/shared/actions/policies.js +342 -0
- package/shared/actions/policies.test.js +233 -0
- package/shared/actions/registry.js +187 -0
- package/shared/actions/registry.test.js +381 -0
- package/shared/actions/requestMeta.js +36 -0
- package/shared/actions/textNormalization.js +3 -0
- package/shared/actions/withActionDefaults.js +34 -0
- package/shared/index.js +2 -0
- package/shared/runtime/application.js +323 -0
- package/shared/runtime/container.js +261 -0
- package/shared/runtime/containerErrors.js +22 -0
- package/shared/runtime/index.js +18 -0
- package/shared/runtime/kernelErrors.js +20 -0
- package/shared/runtime/serviceProvider.js +13 -0
- package/shared/support/formatDateTime.js +10 -0
- package/shared/support/formatDateTime.test.js +15 -0
- package/shared/support/index.js +14 -0
- package/shared/support/linkPath.js +67 -0
- package/shared/support/linkPath.test.js +35 -0
- package/shared/support/normalize.js +116 -0
- package/shared/support/normalize.test.js +48 -0
- package/shared/support/packageDescriptor.test.js +121 -0
- package/shared/support/permissions.js +50 -0
- package/shared/support/pickOwnProperties.js +17 -0
- package/shared/support/pickOwnProperties.test.js +25 -0
- package/shared/support/policies.js +11 -0
- package/shared/support/queryPath.js +33 -0
- package/shared/support/queryPath.test.js +19 -0
- package/shared/support/queryResilience.js +34 -0
- package/shared/support/queryResilience.test.js +33 -0
- package/shared/support/returnToPath.js +153 -0
- package/shared/support/returnToPath.test.js +123 -0
- package/shared/support/sorting.js +15 -0
- package/shared/support/tokens.js +23 -0
- package/shared/support/tokens.test.js +17 -0
- package/shared/support/visibility.js +56 -0
- package/shared/support/visibility.test.js +45 -0
- package/shared/surface/apiPaths.js +84 -0
- package/shared/surface/escapeRegExp.js +5 -0
- package/shared/surface/index.js +6 -0
- package/shared/surface/paths.js +273 -0
- package/shared/surface/registry.js +135 -0
- package/shared/surface/registry.test.js +44 -0
- package/shared/surface/runtime.js +357 -0
- package/shared/surface/runtime.test.js +319 -0
- package/shared/validators/createCursorListValidator.js +42 -0
- package/shared/validators/createCursorListValidator.test.js +34 -0
- package/shared/validators/cursorPaginationQueryValidator.js +31 -0
- package/shared/validators/cursorPaginationQueryValidator.test.js +21 -0
- package/shared/validators/index.js +12 -0
- package/shared/validators/inputNormalization.js +13 -0
- package/shared/validators/mergeObjectSchemas.js +31 -0
- package/shared/validators/mergeObjectSchemas.test.js +67 -0
- package/shared/validators/mergeValidators.js +89 -0
- package/shared/validators/mergeValidators.test.js +116 -0
- package/shared/validators/nestValidator.js +53 -0
- package/shared/validators/nestValidator.test.js +60 -0
- package/shared/validators/recordIdParamsValidator.js +36 -0
- package/shared/validators/recordIdParamsValidator.test.js +20 -0
- package/shared/validators/resourceRequiredMetadata.js +41 -0
- package/shared/validators/resourceRequiredMetadata.test.js +49 -0
- package/test/barrelExposure.test.js +106 -0
- package/test/dynamicImportPolicy.test.js +89 -0
- package/test/exportsContract.test.js +168 -0
- package/test/routeInputContractGuard.test.js +78 -0
- package/test/surfaceIndependence.test.js +109 -0
|
@@ -0,0 +1,563 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { mkdtemp, mkdir, writeFile } from "node:fs/promises";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import test from "node:test";
|
|
6
|
+
import {
|
|
7
|
+
CLIENT_BOOTSTRAP_RESOLVED_ID,
|
|
8
|
+
CLIENT_BOOTSTRAP_VIRTUAL_ID,
|
|
9
|
+
createJskitClientBootstrapPlugin,
|
|
10
|
+
createVirtualModuleSource,
|
|
11
|
+
resolveLocalScopeOptimizeExcludeSpecifiers,
|
|
12
|
+
resolveClientOptimizeIncludeSpecifiers,
|
|
13
|
+
resolveClientOptimizeExcludeSpecifiers,
|
|
14
|
+
resolveLocalScopePackageIds,
|
|
15
|
+
resolveInstalledClientPackageIds,
|
|
16
|
+
resolveInstalledClientModules
|
|
17
|
+
} from "./clientBootstrapPlugin.js";
|
|
18
|
+
|
|
19
|
+
async function writeJson(filePath, value) {
|
|
20
|
+
await writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`, "utf8");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function writeDescriptor(filePath, descriptor) {
|
|
24
|
+
await writeFile(filePath, `export default ${JSON.stringify(descriptor, null, 2)};\n`, "utf8");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
test("createVirtualModuleSource renders deterministic client module imports", () => {
|
|
28
|
+
const source = createVirtualModuleSource([
|
|
29
|
+
{
|
|
30
|
+
packageId: "@z/pkg",
|
|
31
|
+
descriptorUiRoutes: [{ id: "z.route", path: "/z", scope: "global", componentKey: "z-view" }],
|
|
32
|
+
descriptorClientProviders: [{ export: "ZProvider", entrypoint: "src/client/providers/ZProvider.js" }]
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
packageId: "@a/pkg",
|
|
36
|
+
descriptorUiRoutes: [{ id: "a.route", path: "/a", scope: "global", componentKey: "a-view" }],
|
|
37
|
+
descriptorClientProviders: [{ export: "AProvider", entrypoint: "src/client/providers/AProvider.js" }]
|
|
38
|
+
}
|
|
39
|
+
]);
|
|
40
|
+
|
|
41
|
+
assert.match(source, /import \* as clientModule0 from "@a\/pkg\/client";/);
|
|
42
|
+
assert.match(source, /import \* as clientModule1 from "@z\/pkg\/client";/);
|
|
43
|
+
assert.match(source, /descriptorUiRoutes: \[\{"id":"a\.route","path":"\/a","scope":"global","componentKey":"a-view"\}\]/);
|
|
44
|
+
assert.match(source, /descriptorClientProviders: \[\{"export":"AProvider","entrypoint":"src\/client\/providers\/AProvider\.js"\}\]/);
|
|
45
|
+
assert.match(source, /descriptorUiRoutes: \[\{"id":"z\.route","path":"\/z","scope":"global","componentKey":"z-view"\}\]/);
|
|
46
|
+
assert.match(source, /descriptorClientProviders: \[\{"export":"ZProvider","entrypoint":"src\/client\/providers\/ZProvider\.js"\}\]/);
|
|
47
|
+
assert.match(source, /bootClientModules/);
|
|
48
|
+
assert.match(source, /installedClientModules/);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("resolveClientOptimizeExcludeSpecifiers excludes local/app-local package roots and client/shared subpaths", () => {
|
|
52
|
+
const exclude = resolveClientOptimizeExcludeSpecifiers([
|
|
53
|
+
{
|
|
54
|
+
packageId: "@z/pkg",
|
|
55
|
+
sourceType: "packages-directory",
|
|
56
|
+
descriptorUiRoutes: []
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
packageId: "@a/pkg",
|
|
60
|
+
sourceType: "app-local-package",
|
|
61
|
+
descriptorUiRoutes: []
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
packageId: "@b/pkg",
|
|
65
|
+
sourceType: "local-package",
|
|
66
|
+
descriptorUiRoutes: []
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
packageId: "@c/pkg",
|
|
70
|
+
sourceType: "npm",
|
|
71
|
+
descriptorUiRoutes: []
|
|
72
|
+
}
|
|
73
|
+
]);
|
|
74
|
+
|
|
75
|
+
assert.deepEqual(exclude, [
|
|
76
|
+
"@a/pkg",
|
|
77
|
+
"@a/pkg/client",
|
|
78
|
+
"@a/pkg/shared",
|
|
79
|
+
"@b/pkg",
|
|
80
|
+
"@b/pkg/client",
|
|
81
|
+
"@b/pkg/shared"
|
|
82
|
+
]);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("resolveClientOptimizeIncludeSpecifiers includes only non-local package clients", () => {
|
|
86
|
+
const include = resolveClientOptimizeIncludeSpecifiers([
|
|
87
|
+
{
|
|
88
|
+
packageId: "@z/pkg",
|
|
89
|
+
sourceType: "packages-directory",
|
|
90
|
+
descriptorUiRoutes: []
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
packageId: "@a/pkg",
|
|
94
|
+
sourceType: "app-local-package",
|
|
95
|
+
descriptorUiRoutes: []
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
packageId: "@b/pkg",
|
|
99
|
+
sourceType: "local-package",
|
|
100
|
+
descriptorUiRoutes: []
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
packageId: "@c/pkg",
|
|
104
|
+
sourceType: "npm",
|
|
105
|
+
descriptorUiRoutes: []
|
|
106
|
+
}
|
|
107
|
+
]);
|
|
108
|
+
|
|
109
|
+
assert.deepEqual(include, ["@c/pkg/client", "@z/pkg/client"]);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("resolveLocalScopeOptimizeExcludeSpecifiers expands @local package ids to root/client/shared", () => {
|
|
113
|
+
const exclude = resolveLocalScopeOptimizeExcludeSpecifiers(["@local/app", "@local/feature"]);
|
|
114
|
+
assert.deepEqual(exclude, [
|
|
115
|
+
"@local/app",
|
|
116
|
+
"@local/app/client",
|
|
117
|
+
"@local/app/shared",
|
|
118
|
+
"@local/feature",
|
|
119
|
+
"@local/feature/client",
|
|
120
|
+
"@local/feature/shared"
|
|
121
|
+
]);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test("resolveInstalledClientPackageIds returns only installed packages with a client export", async () => {
|
|
125
|
+
const tempRoot = await mkdtemp(path.join(os.tmpdir(), "jskit-client-bootstrap-"));
|
|
126
|
+
const appRoot = tempRoot;
|
|
127
|
+
|
|
128
|
+
await mkdir(path.join(appRoot, ".jskit"), { recursive: true });
|
|
129
|
+
await writeJson(path.join(appRoot, ".jskit", "lock.json"), {
|
|
130
|
+
lockVersion: 1,
|
|
131
|
+
installedPackages: {
|
|
132
|
+
"@example/has-client": {},
|
|
133
|
+
"@example/no-client": {}
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
await mkdir(path.join(appRoot, "node_modules", "@example", "has-client"), { recursive: true });
|
|
138
|
+
await writeJson(path.join(appRoot, "node_modules", "@example", "has-client", "package.json"), {
|
|
139
|
+
name: "@example/has-client",
|
|
140
|
+
exports: {
|
|
141
|
+
"./client": "./src/client/index.js"
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
await mkdir(path.join(appRoot, "node_modules", "@example", "no-client"), { recursive: true });
|
|
146
|
+
await writeJson(path.join(appRoot, "node_modules", "@example", "no-client", "package.json"), {
|
|
147
|
+
name: "@example/no-client",
|
|
148
|
+
exports: {
|
|
149
|
+
"./server": "./src/server/index.js"
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
const packageIds = await resolveInstalledClientPackageIds({
|
|
154
|
+
appRoot,
|
|
155
|
+
lockPath: ".jskit/lock.json"
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
assert.deepEqual(packageIds, ["@example/has-client"]);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test("resolveInstalledClientModules returns installed modules with client exports", async () => {
|
|
162
|
+
const tempRoot = await mkdtemp(path.join(os.tmpdir(), "jskit-client-bootstrap-modules-"));
|
|
163
|
+
|
|
164
|
+
await mkdir(path.join(tempRoot, ".jskit"), { recursive: true });
|
|
165
|
+
await writeJson(path.join(tempRoot, ".jskit", "lock.json"), {
|
|
166
|
+
lockVersion: 1,
|
|
167
|
+
installedPackages: {
|
|
168
|
+
"@example/has-client": {}
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
const packageRoot = path.join(tempRoot, "node_modules", "@example", "has-client");
|
|
173
|
+
await mkdir(packageRoot, { recursive: true });
|
|
174
|
+
await writeJson(path.join(packageRoot, "package.json"), {
|
|
175
|
+
name: "@example/has-client",
|
|
176
|
+
exports: {
|
|
177
|
+
"./client": "./src/client/index.js"
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
await writeDescriptor(path.join(packageRoot, "package.descriptor.mjs"), {
|
|
181
|
+
runtime: {
|
|
182
|
+
client: {
|
|
183
|
+
providers: [
|
|
184
|
+
{
|
|
185
|
+
entrypoint: "src/client/providers/HasClientProvider.js",
|
|
186
|
+
export: "HasClientProvider"
|
|
187
|
+
}
|
|
188
|
+
]
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
metadata: {
|
|
192
|
+
client: {
|
|
193
|
+
optimizeDeps: {
|
|
194
|
+
include: ["mime-match"]
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
ui: {
|
|
198
|
+
routes: [
|
|
199
|
+
{
|
|
200
|
+
id: "auth.default-login-2",
|
|
201
|
+
path: "/auth/default-login-2",
|
|
202
|
+
scope: "global",
|
|
203
|
+
componentKey: "auth-login",
|
|
204
|
+
autoRegister: true
|
|
205
|
+
}
|
|
206
|
+
]
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
const modules = await resolveInstalledClientModules({
|
|
211
|
+
appRoot: tempRoot,
|
|
212
|
+
lockPath: ".jskit/lock.json"
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
assert.equal(modules.length, 1);
|
|
216
|
+
assert.equal(modules[0].packageId, "@example/has-client");
|
|
217
|
+
assert.equal(modules[0].sourceType, "");
|
|
218
|
+
assert.equal(Array.isArray(modules[0].descriptorUiRoutes), true);
|
|
219
|
+
assert.equal(modules[0].descriptorUiRoutes.length, 1);
|
|
220
|
+
assert.equal(modules[0].descriptorUiRoutes[0].id, "auth.default-login-2");
|
|
221
|
+
assert.equal(Array.isArray(modules[0].descriptorClientProviders), true);
|
|
222
|
+
assert.equal(modules[0].descriptorClientProviders.length, 1);
|
|
223
|
+
assert.equal(modules[0].descriptorClientProviders[0].export, "HasClientProvider");
|
|
224
|
+
assert.equal(Array.isArray(modules[0].descriptorClientOptimizeIncludeSpecifiers), true);
|
|
225
|
+
assert.deepEqual(modules[0].descriptorClientOptimizeIncludeSpecifiers, ["mime-match"]);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
test("resolveInstalledClientModules resolves descriptor via source.packagePath", async () => {
|
|
229
|
+
const tempRoot = await mkdtemp(path.join(os.tmpdir(), "jskit-client-bootstrap-package-path-"));
|
|
230
|
+
|
|
231
|
+
await mkdir(path.join(tempRoot, ".jskit"), { recursive: true });
|
|
232
|
+
await writeJson(path.join(tempRoot, ".jskit", "lock.json"), {
|
|
233
|
+
lockVersion: 1,
|
|
234
|
+
installedPackages: {
|
|
235
|
+
"@example/has-client": {
|
|
236
|
+
source: {
|
|
237
|
+
type: "local-package",
|
|
238
|
+
packagePath: "packages/has-client"
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
const packageRoot = path.join(tempRoot, "node_modules", "@example", "has-client");
|
|
245
|
+
await mkdir(packageRoot, { recursive: true });
|
|
246
|
+
await writeJson(path.join(packageRoot, "package.json"), {
|
|
247
|
+
name: "@example/has-client",
|
|
248
|
+
exports: {
|
|
249
|
+
"./client": "./src/client/index.js"
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
await mkdir(path.join(tempRoot, "packages", "has-client"), { recursive: true });
|
|
253
|
+
await writeDescriptor(path.join(tempRoot, "packages", "has-client", "package.descriptor.mjs"), {
|
|
254
|
+
runtime: {
|
|
255
|
+
client: {
|
|
256
|
+
providers: [
|
|
257
|
+
{
|
|
258
|
+
entrypoint: "src/client/providers/LocalClientProvider.js",
|
|
259
|
+
export: "LocalClientProvider"
|
|
260
|
+
}
|
|
261
|
+
]
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
metadata: {
|
|
265
|
+
ui: {
|
|
266
|
+
routes: [
|
|
267
|
+
{
|
|
268
|
+
id: "local.route",
|
|
269
|
+
path: "/local",
|
|
270
|
+
scope: "global",
|
|
271
|
+
componentKey: "local-view",
|
|
272
|
+
autoRegister: true
|
|
273
|
+
}
|
|
274
|
+
]
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
const modules = await resolveInstalledClientModules({
|
|
280
|
+
appRoot: tempRoot,
|
|
281
|
+
lockPath: ".jskit/lock.json"
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
assert.equal(modules.length, 1);
|
|
285
|
+
assert.equal(modules[0].packageId, "@example/has-client");
|
|
286
|
+
assert.equal(modules[0].sourceType, "local-package");
|
|
287
|
+
assert.equal(modules[0].descriptorUiRoutes.length, 1);
|
|
288
|
+
assert.equal(modules[0].descriptorUiRoutes[0].id, "local.route");
|
|
289
|
+
assert.equal(modules[0].descriptorClientProviders.length, 1);
|
|
290
|
+
assert.equal(modules[0].descriptorClientProviders[0].export, "LocalClientProvider");
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
test("resolveLocalScopePackageIds reads @local packages from lock and package.json", async () => {
|
|
294
|
+
const tempRoot = await mkdtemp(path.join(os.tmpdir(), "jskit-client-bootstrap-local-scope-"));
|
|
295
|
+
await mkdir(path.join(tempRoot, ".jskit"), { recursive: true });
|
|
296
|
+
await writeJson(path.join(tempRoot, ".jskit", "lock.json"), {
|
|
297
|
+
lockVersion: 1,
|
|
298
|
+
installedPackages: {
|
|
299
|
+
"@local/main": {
|
|
300
|
+
source: {
|
|
301
|
+
type: "packages-directory"
|
|
302
|
+
}
|
|
303
|
+
},
|
|
304
|
+
"@example/remote": {
|
|
305
|
+
source: {
|
|
306
|
+
type: "packages-directory"
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
await writeJson(path.join(tempRoot, "package.json"), {
|
|
312
|
+
name: "fixture-app",
|
|
313
|
+
dependencies: {
|
|
314
|
+
"@local/feature": "file:packages/feature",
|
|
315
|
+
"@example/remote": "^1.0.0"
|
|
316
|
+
},
|
|
317
|
+
devDependencies: {
|
|
318
|
+
"@local/dev-only": "file:packages/dev-only"
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
const packageIds = await resolveLocalScopePackageIds({
|
|
323
|
+
appRoot: tempRoot,
|
|
324
|
+
lockPath: ".jskit/lock.json"
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
assert.deepEqual(packageIds, ["@local/dev-only", "@local/feature", "@local/main"]);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
test("createJskitClientBootstrapPlugin resolves and loads virtual module", async () => {
|
|
331
|
+
const tempRoot = await mkdtemp(path.join(os.tmpdir(), "jskit-client-bootstrap-plugin-"));
|
|
332
|
+
const previousCwd = process.cwd();
|
|
333
|
+
|
|
334
|
+
try {
|
|
335
|
+
await mkdir(path.join(tempRoot, ".jskit"), { recursive: true });
|
|
336
|
+
await writeJson(path.join(tempRoot, ".jskit", "lock.json"), {
|
|
337
|
+
lockVersion: 1,
|
|
338
|
+
installedPackages: {
|
|
339
|
+
"@example/has-client": {
|
|
340
|
+
source: {
|
|
341
|
+
type: "packages-directory"
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
const packageRoot = path.join(tempRoot, "node_modules", "@example", "has-client");
|
|
348
|
+
await mkdir(packageRoot, { recursive: true });
|
|
349
|
+
await writeJson(path.join(packageRoot, "package.json"), {
|
|
350
|
+
name: "@example/has-client",
|
|
351
|
+
exports: {
|
|
352
|
+
"./client": "./src/client/index.js"
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
process.chdir(tempRoot);
|
|
356
|
+
const plugin = createJskitClientBootstrapPlugin();
|
|
357
|
+
|
|
358
|
+
const resolvedId = plugin.resolveId(CLIENT_BOOTSTRAP_VIRTUAL_ID);
|
|
359
|
+
assert.equal(resolvedId, CLIENT_BOOTSTRAP_RESOLVED_ID);
|
|
360
|
+
|
|
361
|
+
const source = await plugin.load(CLIENT_BOOTSTRAP_RESOLVED_ID);
|
|
362
|
+
assert.match(String(source || ""), /@example\/has-client\/client/);
|
|
363
|
+
assert.match(String(source || ""), /bootInstalledClientModules/);
|
|
364
|
+
} finally {
|
|
365
|
+
process.chdir(previousCwd);
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
test("createJskitClientBootstrapPlugin config excludes installed client package specifiers from optimizeDeps", async () => {
|
|
370
|
+
const tempRoot = await mkdtemp(path.join(os.tmpdir(), "jskit-client-bootstrap-config-"));
|
|
371
|
+
const previousCwd = process.cwd();
|
|
372
|
+
|
|
373
|
+
try {
|
|
374
|
+
await mkdir(path.join(tempRoot, ".jskit"), { recursive: true });
|
|
375
|
+
await writeJson(path.join(tempRoot, ".jskit", "lock.json"), {
|
|
376
|
+
lockVersion: 1,
|
|
377
|
+
installedPackages: {
|
|
378
|
+
"@example/has-client": {
|
|
379
|
+
source: {
|
|
380
|
+
type: "packages-directory"
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
const packageRoot = path.join(tempRoot, "node_modules", "@example", "has-client");
|
|
387
|
+
await mkdir(packageRoot, { recursive: true });
|
|
388
|
+
await writeJson(path.join(packageRoot, "package.json"), {
|
|
389
|
+
name: "@example/has-client",
|
|
390
|
+
exports: {
|
|
391
|
+
"./client": "./src/client/index.js"
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
process.chdir(tempRoot);
|
|
396
|
+
const plugin = createJskitClientBootstrapPlugin();
|
|
397
|
+
const result = await plugin.config({
|
|
398
|
+
optimizeDeps: {
|
|
399
|
+
include: ["a"],
|
|
400
|
+
exclude: ["already/excluded"]
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
assert.equal(Array.isArray(result?.optimizeDeps?.exclude), true);
|
|
405
|
+
assert.deepEqual(result.optimizeDeps.exclude, ["already/excluded"]);
|
|
406
|
+
assert.deepEqual(result.optimizeDeps.include, ["@example/has-client/client", "a"]);
|
|
407
|
+
assert.deepEqual(result.resolve.dedupe, ["@tanstack/vue-query", "vue", "vue-router", "vuetify"]);
|
|
408
|
+
} finally {
|
|
409
|
+
process.chdir(previousCwd);
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
test("createJskitClientBootstrapPlugin config excludes local package roots and client/shared subpaths", async () => {
|
|
414
|
+
const tempRoot = await mkdtemp(path.join(os.tmpdir(), "jskit-client-bootstrap-config-local-"));
|
|
415
|
+
const previousCwd = process.cwd();
|
|
416
|
+
|
|
417
|
+
try {
|
|
418
|
+
await mkdir(path.join(tempRoot, ".jskit"), { recursive: true });
|
|
419
|
+
await writeJson(path.join(tempRoot, ".jskit", "lock.json"), {
|
|
420
|
+
lockVersion: 1,
|
|
421
|
+
installedPackages: {
|
|
422
|
+
"@example/local-client": {
|
|
423
|
+
source: {
|
|
424
|
+
type: "local-package"
|
|
425
|
+
}
|
|
426
|
+
},
|
|
427
|
+
"@example/remote-client": {
|
|
428
|
+
source: {
|
|
429
|
+
type: "packages-directory"
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
const localPackageRoot = path.join(tempRoot, "node_modules", "@example", "local-client");
|
|
436
|
+
await mkdir(localPackageRoot, { recursive: true });
|
|
437
|
+
await writeJson(path.join(localPackageRoot, "package.json"), {
|
|
438
|
+
name: "@example/local-client",
|
|
439
|
+
exports: {
|
|
440
|
+
"./client": "./src/client/index.js"
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
await writeDescriptor(path.join(localPackageRoot, "package.descriptor.mjs"), {
|
|
444
|
+
metadata: {
|
|
445
|
+
client: {
|
|
446
|
+
optimizeDeps: {
|
|
447
|
+
include: ["mime-match"]
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
const remotePackageRoot = path.join(tempRoot, "node_modules", "@example", "remote-client");
|
|
454
|
+
await mkdir(remotePackageRoot, { recursive: true });
|
|
455
|
+
await writeJson(path.join(remotePackageRoot, "package.json"), {
|
|
456
|
+
name: "@example/remote-client",
|
|
457
|
+
exports: {
|
|
458
|
+
"./client": "./src/client/index.js"
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
process.chdir(tempRoot);
|
|
463
|
+
const plugin = createJskitClientBootstrapPlugin();
|
|
464
|
+
const result = await plugin.config({});
|
|
465
|
+
|
|
466
|
+
assert.deepEqual(result.optimizeDeps.exclude, [
|
|
467
|
+
"@example/local-client",
|
|
468
|
+
"@example/local-client/client",
|
|
469
|
+
"@example/local-client/shared"
|
|
470
|
+
]);
|
|
471
|
+
assert.deepEqual(result.optimizeDeps.include, ["@example/remote-client/client", "mime-match"]);
|
|
472
|
+
assert.deepEqual(result.resolve.dedupe, ["@tanstack/vue-query", "vue", "vue-router", "vuetify"]);
|
|
473
|
+
} finally {
|
|
474
|
+
process.chdir(previousCwd);
|
|
475
|
+
}
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
test("createJskitClientBootstrapPlugin config preserves user resolve fields and merges dedupe", async () => {
|
|
479
|
+
const tempRoot = await mkdtemp(path.join(os.tmpdir(), "jskit-client-bootstrap-config-resolve-"));
|
|
480
|
+
const previousCwd = process.cwd();
|
|
481
|
+
|
|
482
|
+
try {
|
|
483
|
+
await mkdir(path.join(tempRoot, ".jskit"), { recursive: true });
|
|
484
|
+
await writeJson(path.join(tempRoot, ".jskit", "lock.json"), {
|
|
485
|
+
lockVersion: 1,
|
|
486
|
+
installedPackages: {}
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
process.chdir(tempRoot);
|
|
490
|
+
const plugin = createJskitClientBootstrapPlugin();
|
|
491
|
+
const result = await plugin.config({
|
|
492
|
+
resolve: {
|
|
493
|
+
alias: {
|
|
494
|
+
"@": "/tmp/app/src"
|
|
495
|
+
},
|
|
496
|
+
dedupe: ["vue", "custom-lib"]
|
|
497
|
+
}
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
assert.deepEqual(result.resolve.alias, {
|
|
501
|
+
"@": "/tmp/app/src"
|
|
502
|
+
});
|
|
503
|
+
assert.deepEqual(result.resolve.dedupe, ["@tanstack/vue-query", "custom-lib", "vue", "vue-router", "vuetify"]);
|
|
504
|
+
} finally {
|
|
505
|
+
process.chdir(previousCwd);
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
test("createJskitClientBootstrapPlugin config excludes all @local scoped packages from lock and package.json", async () => {
|
|
510
|
+
const tempRoot = await mkdtemp(path.join(os.tmpdir(), "jskit-client-bootstrap-local-scope-config-"));
|
|
511
|
+
const previousCwd = process.cwd();
|
|
512
|
+
|
|
513
|
+
try {
|
|
514
|
+
await mkdir(path.join(tempRoot, ".jskit"), { recursive: true });
|
|
515
|
+
await writeJson(path.join(tempRoot, ".jskit", "lock.json"), {
|
|
516
|
+
lockVersion: 1,
|
|
517
|
+
installedPackages: {
|
|
518
|
+
"@local/main": {
|
|
519
|
+
source: {
|
|
520
|
+
type: "packages-directory"
|
|
521
|
+
}
|
|
522
|
+
},
|
|
523
|
+
"@example/remote-client": {
|
|
524
|
+
source: {
|
|
525
|
+
type: "packages-directory"
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
await writeJson(path.join(tempRoot, "package.json"), {
|
|
531
|
+
name: "fixture-app",
|
|
532
|
+
dependencies: {
|
|
533
|
+
"@local/feature": "file:packages/feature",
|
|
534
|
+
"@example/remote-client": "^1.0.0"
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
const remotePackageRoot = path.join(tempRoot, "node_modules", "@example", "remote-client");
|
|
539
|
+
await mkdir(remotePackageRoot, { recursive: true });
|
|
540
|
+
await writeJson(path.join(remotePackageRoot, "package.json"), {
|
|
541
|
+
name: "@example/remote-client",
|
|
542
|
+
exports: {
|
|
543
|
+
"./client": "./src/client/index.js"
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
process.chdir(tempRoot);
|
|
548
|
+
const plugin = createJskitClientBootstrapPlugin();
|
|
549
|
+
const result = await plugin.config({});
|
|
550
|
+
|
|
551
|
+
assert.deepEqual(result.optimizeDeps.exclude, [
|
|
552
|
+
"@local/feature",
|
|
553
|
+
"@local/feature/client",
|
|
554
|
+
"@local/feature/shared",
|
|
555
|
+
"@local/main",
|
|
556
|
+
"@local/main/client",
|
|
557
|
+
"@local/main/shared"
|
|
558
|
+
]);
|
|
559
|
+
assert.deepEqual(result.optimizeDeps.include, ["@example/remote-client/client"]);
|
|
560
|
+
} finally {
|
|
561
|
+
process.chdir(previousCwd);
|
|
562
|
+
}
|
|
563
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { access, constants as fsConstants, readFile } from "node:fs/promises";
|
|
2
|
+
|
|
3
|
+
async function fileExists(filePath) {
|
|
4
|
+
try {
|
|
5
|
+
await access(filePath, fsConstants.F_OK);
|
|
6
|
+
return true;
|
|
7
|
+
} catch {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async function readJsonFile(filePath, fallback = {}) {
|
|
13
|
+
try {
|
|
14
|
+
const source = await readFile(filePath, "utf8");
|
|
15
|
+
return JSON.parse(source);
|
|
16
|
+
} catch {
|
|
17
|
+
return fallback;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { fileExists, readJsonFile };
|
|
@@ -0,0 +1,104 @@
|
|
|
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 resolveDescriptorCandidatePaths({ appRoot, packageId, installedPackageState }) {
|
|
7
|
+
const normalizedAppRoot = String(appRoot || "").trim();
|
|
8
|
+
if (!normalizedAppRoot) {
|
|
9
|
+
throw new TypeError("resolveDescriptorCandidatePaths requires appRoot.");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const normalizedPackageId = String(packageId || "").trim();
|
|
13
|
+
if (!normalizedPackageId) {
|
|
14
|
+
throw new TypeError("resolveDescriptorCandidatePaths requires packageId.");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const source = normalizeObject(installedPackageState?.source);
|
|
18
|
+
const descriptorPathFromSource = String(source.descriptorPath || "").trim();
|
|
19
|
+
const packagePathFromSource = String(source.packagePath || "").trim();
|
|
20
|
+
const jskitRoot = path.join(normalizedAppRoot, "node_modules", "@jskit-ai", "jskit-cli");
|
|
21
|
+
|
|
22
|
+
const candidatePaths = [
|
|
23
|
+
path.resolve(normalizedAppRoot, "node_modules", normalizedPackageId, "package.descriptor.mjs")
|
|
24
|
+
];
|
|
25
|
+
if (packagePathFromSource) {
|
|
26
|
+
candidatePaths.push(path.resolve(normalizedAppRoot, packagePathFromSource, "package.descriptor.mjs"));
|
|
27
|
+
}
|
|
28
|
+
if (descriptorPathFromSource) {
|
|
29
|
+
candidatePaths.push(path.resolve(normalizedAppRoot, descriptorPathFromSource));
|
|
30
|
+
candidatePaths.push(path.resolve(jskitRoot, descriptorPathFromSource));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const uniqueCandidatePaths = [];
|
|
34
|
+
const seenPaths = new Set();
|
|
35
|
+
for (const candidatePath of candidatePaths) {
|
|
36
|
+
const normalizedCandidatePath = String(candidatePath || "").trim();
|
|
37
|
+
if (!normalizedCandidatePath || seenPaths.has(normalizedCandidatePath)) {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
seenPaths.add(normalizedCandidatePath);
|
|
41
|
+
uniqueCandidatePaths.push(normalizedCandidatePath);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return uniqueCandidatePaths;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function resolveDescriptorPathForInstalledPackage({ appRoot, packageId, installedPackageState, required = false }) {
|
|
48
|
+
const candidatePaths = resolveDescriptorCandidatePaths({
|
|
49
|
+
appRoot,
|
|
50
|
+
packageId,
|
|
51
|
+
installedPackageState
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
for (const candidatePath of candidatePaths) {
|
|
55
|
+
if (await fileExists(candidatePath)) {
|
|
56
|
+
return candidatePath;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (required === true) {
|
|
61
|
+
throw new Error(`Unable to resolve package descriptor for ${String(packageId || "").trim()}.`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return "";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function normalizeDescriptorPayload(descriptorModule) {
|
|
68
|
+
return normalizeObject(descriptorModule?.default);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function loadInstalledPackageDescriptor({ appRoot, packageId, installedPackageState, required = false }) {
|
|
72
|
+
const descriptorPath = await resolveDescriptorPathForInstalledPackage({
|
|
73
|
+
appRoot,
|
|
74
|
+
packageId,
|
|
75
|
+
installedPackageState,
|
|
76
|
+
required
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
if (!descriptorPath) {
|
|
80
|
+
return Object.freeze({
|
|
81
|
+
descriptorPath: "",
|
|
82
|
+
descriptor: Object.freeze({})
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
const descriptorModule = await import(pathToFileURL(descriptorPath).href);
|
|
88
|
+
return Object.freeze({
|
|
89
|
+
descriptorPath,
|
|
90
|
+
descriptor: normalizeDescriptorPayload(descriptorModule)
|
|
91
|
+
});
|
|
92
|
+
} catch (error) {
|
|
93
|
+
if (required === true) {
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return Object.freeze({
|
|
98
|
+
descriptorPath: "",
|
|
99
|
+
descriptor: Object.freeze({})
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export { loadInstalledPackageDescriptor, resolveDescriptorPathForInstalledPackage };
|