@jskit-ai/jskit-cli 0.2.72 → 0.2.73
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/package.json +4 -4
- package/src/server/cliRuntime/completion.js +3 -1
- package/src/server/cliRuntime/descriptorValidation.js +52 -0
- package/src/server/cliRuntime/mutations/fileMutations.js +18 -14
- package/src/server/cliRuntime/mutations/installMigrationMutation.js +10 -5
- package/src/server/cliRuntime/mutations/textMutations.js +17 -5
- package/src/server/cliRuntime/packageInstallFlow.js +42 -19
- package/src/server/cliRuntime/viteProxy.js +25 -7
- package/src/server/commandHandlers/health.js +88 -15
- package/src/server/commandHandlers/mobile.js +1316 -0
- package/src/server/commandHandlers/mobileCommandCatalog.js +196 -0
- package/src/server/commandHandlers/mobileShellSupport.js +929 -0
- package/src/server/commandHandlers/packageCommands/add.js +415 -2
- package/src/server/commandHandlers/packageCommands/migrations.js +2 -1
- package/src/server/core/argParser.js +6 -0
- package/src/server/core/commandCatalog.js +31 -3
- package/src/server/core/createCommandHandlers.js +3 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/jskit-cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.73",
|
|
4
4
|
"description": "Bundle and package orchestration CLI for JSKIT apps.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
"test": "node --test"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@jskit-ai/jskit-catalog": "0.1.
|
|
24
|
-
"@jskit-ai/kernel": "0.1.
|
|
25
|
-
"@jskit-ai/shell-web": "0.1.
|
|
23
|
+
"@jskit-ai/jskit-catalog": "0.1.72",
|
|
24
|
+
"@jskit-ai/kernel": "0.1.64",
|
|
25
|
+
"@jskit-ai/shell-web": "0.1.63"
|
|
26
26
|
},
|
|
27
27
|
"engines": {
|
|
28
28
|
"node": "20.x"
|
|
@@ -14,10 +14,11 @@ import {
|
|
|
14
14
|
} from "../core/commandCatalog.js";
|
|
15
15
|
|
|
16
16
|
const WRAPPER_COMMANDS = new Set(["npx", "jsx"]);
|
|
17
|
-
const KNOWN_GENERATE_FLAG_OPTIONS = Object.freeze(["dry-run", "run-npm-install", "json", "verbose"]);
|
|
17
|
+
const KNOWN_GENERATE_FLAG_OPTIONS = Object.freeze(["dry-run", "run-npm-install", "devlinks", "json", "verbose"]);
|
|
18
18
|
const BOOLEAN_OPTION_NAMES = new Set([
|
|
19
19
|
"dry-run",
|
|
20
20
|
"run-npm-install",
|
|
21
|
+
"devlinks",
|
|
21
22
|
"full",
|
|
22
23
|
"expanded",
|
|
23
24
|
"details",
|
|
@@ -747,6 +748,7 @@ function buildCommandOptionMeta(command = "", catalogModule) {
|
|
|
747
748
|
const labels = {
|
|
748
749
|
dryRun: "dry-run",
|
|
749
750
|
runNpmInstall: "run-npm-install",
|
|
751
|
+
devlinks: "devlinks",
|
|
750
752
|
full: "full",
|
|
751
753
|
expanded: "expanded",
|
|
752
754
|
details: "details",
|
|
@@ -61,6 +61,54 @@ function validateFileMutationShape(descriptor, descriptorPath) {
|
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
function validateLifecycleHookSpec(spec = {}, descriptorPath, label = "lifecycle hook") {
|
|
65
|
+
const normalized = ensureObject(spec);
|
|
66
|
+
if (Object.keys(normalized).length < 1) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const entrypoint = String(normalized.entrypoint || "").trim();
|
|
71
|
+
if (!entrypoint) {
|
|
72
|
+
throw createCliError(`Invalid package descriptor at ${descriptorPath}: ${label} requires "entrypoint".`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const exportName = String(normalized.export || "").trim() || "default";
|
|
76
|
+
return {
|
|
77
|
+
...normalized,
|
|
78
|
+
entrypoint,
|
|
79
|
+
export: exportName
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function validateLifecycleShape(descriptor, descriptorPath) {
|
|
84
|
+
const lifecycle = ensureObject(ensureObject(descriptor).lifecycle);
|
|
85
|
+
const install = ensureObject(lifecycle.install);
|
|
86
|
+
if (Object.keys(install).length < 1) {
|
|
87
|
+
return lifecycle;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const prepare = validateLifecycleHookSpec(install.prepare, descriptorPath, "lifecycle.install.prepare");
|
|
91
|
+
const finalize = validateLifecycleHookSpec(install.finalize, descriptorPath, "lifecycle.install.finalize");
|
|
92
|
+
|
|
93
|
+
if (install.finalize && typeof install.finalize === "object") {
|
|
94
|
+
const managesNpmInstall = install.finalize.managesNpmInstall;
|
|
95
|
+
if (typeof managesNpmInstall !== "undefined" && typeof managesNpmInstall !== "boolean") {
|
|
96
|
+
throw createCliError(
|
|
97
|
+
`Invalid package descriptor at ${descriptorPath}: lifecycle.install.finalize.managesNpmInstall must be boolean when provided.`
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
...lifecycle,
|
|
104
|
+
install: {
|
|
105
|
+
...install,
|
|
106
|
+
...(prepare ? { prepare } : {}),
|
|
107
|
+
...(finalize ? { finalize } : {})
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
64
112
|
function validatePackageDescriptorShape(descriptor, descriptorPath) {
|
|
65
113
|
const normalized = ensureObject(descriptor);
|
|
66
114
|
const packageId = String(normalized.packageId || "").trim();
|
|
@@ -85,9 +133,11 @@ function validatePackageDescriptorShape(descriptor, descriptorPath) {
|
|
|
85
133
|
}
|
|
86
134
|
|
|
87
135
|
validateFileMutationShape(normalized, descriptorPath);
|
|
136
|
+
const lifecycle = validateLifecycleShape(normalized, descriptorPath);
|
|
88
137
|
|
|
89
138
|
return {
|
|
90
139
|
...normalized,
|
|
140
|
+
lifecycle,
|
|
91
141
|
kind: normalizePackageKind(normalized.kind, descriptorPath)
|
|
92
142
|
};
|
|
93
143
|
}
|
|
@@ -115,11 +165,13 @@ function validateAppLocalPackageDescriptorShape(descriptor, descriptorPath, { ex
|
|
|
115
165
|
}
|
|
116
166
|
|
|
117
167
|
validateFileMutationShape(normalized, descriptorPath);
|
|
168
|
+
const lifecycle = validateLifecycleShape(normalized, descriptorPath);
|
|
118
169
|
|
|
119
170
|
return {
|
|
120
171
|
...normalized,
|
|
121
172
|
packageId,
|
|
122
173
|
version,
|
|
174
|
+
lifecycle,
|
|
123
175
|
kind: normalizePackageKind(normalized.kind, descriptorPath)
|
|
124
176
|
};
|
|
125
177
|
}
|
|
@@ -198,6 +198,7 @@ async function applyFileMutations(
|
|
|
198
198
|
warnings = [],
|
|
199
199
|
existingManagedFiles = [],
|
|
200
200
|
{
|
|
201
|
+
dryRun = false,
|
|
201
202
|
reapplyManagedAppFiles = false
|
|
202
203
|
} = {}
|
|
203
204
|
) {
|
|
@@ -224,18 +225,19 @@ async function applyFileMutations(
|
|
|
224
225
|
for (const preparedMutation of ensureArray(preparedMutations)) {
|
|
225
226
|
const mutation = ensureObject(preparedMutation.mutation);
|
|
226
227
|
const operation = String(preparedMutation.operation || "").trim() || "copy-file";
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
228
|
+
if (operation === "install-migration") {
|
|
229
|
+
await applyInstallMigrationMutation({
|
|
230
|
+
packageEntry,
|
|
231
|
+
preparedMutation,
|
|
232
|
+
appRoot,
|
|
233
|
+
managedMigrations,
|
|
234
|
+
managedMigrationById,
|
|
235
|
+
touchedFiles,
|
|
236
|
+
warnings,
|
|
237
|
+
dryRun
|
|
238
|
+
});
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
239
241
|
|
|
240
242
|
const renderedSourceContent = String(preparedMutation.renderedSourceContent || "");
|
|
241
243
|
const renderedSourceBuffer = Buffer.from(renderedSourceContent, "utf8");
|
|
@@ -280,8 +282,10 @@ async function applyFileMutations(
|
|
|
280
282
|
continue;
|
|
281
283
|
}
|
|
282
284
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
+
if (!dryRun) {
|
|
286
|
+
await mkdir(path.dirname(targetPath), { recursive: true });
|
|
287
|
+
await writeFile(targetPath, renderedSourceContent, "utf8");
|
|
288
|
+
}
|
|
285
289
|
|
|
286
290
|
managedFiles.push({
|
|
287
291
|
path: relativeTargetPath,
|
|
@@ -26,7 +26,8 @@ async function applyInstallMigrationMutation({
|
|
|
26
26
|
managedMigrations,
|
|
27
27
|
managedMigrationById,
|
|
28
28
|
touchedFiles,
|
|
29
|
-
warnings
|
|
29
|
+
warnings,
|
|
30
|
+
dryRun = false
|
|
30
31
|
} = {}) {
|
|
31
32
|
const mutation = ensureObject(preparedMutation?.mutation);
|
|
32
33
|
if (mutation.preserveOnRemove === true) {
|
|
@@ -81,8 +82,10 @@ async function applyInstallMigrationMutation({
|
|
|
81
82
|
}
|
|
82
83
|
|
|
83
84
|
if (!(await fileExists(absolutePath))) {
|
|
84
|
-
|
|
85
|
-
|
|
85
|
+
if (!dryRun) {
|
|
86
|
+
await mkdir(path.dirname(absolutePath), { recursive: true });
|
|
87
|
+
await writeFile(absolutePath, renderedSourceContent, "utf8");
|
|
88
|
+
}
|
|
86
89
|
touchedFiles.add(relativePath);
|
|
87
90
|
}
|
|
88
91
|
|
|
@@ -165,8 +168,10 @@ async function applyInstallMigrationMutation({
|
|
|
165
168
|
|
|
166
169
|
const relativePath = targetPath.relativePath;
|
|
167
170
|
const absolutePath = targetPath.absolutePath;
|
|
168
|
-
|
|
169
|
-
|
|
171
|
+
if (!dryRun) {
|
|
172
|
+
await mkdir(path.dirname(absolutePath), { recursive: true });
|
|
173
|
+
await writeFile(absolutePath, renderedSourceContent, "utf8");
|
|
174
|
+
}
|
|
170
175
|
touchedFiles.add(relativePath);
|
|
171
176
|
|
|
172
177
|
const nextManagedRecord = {
|
|
@@ -35,7 +35,15 @@ const PRE_FILE_CONFIG_MUTATION_TARGETS = new Set([
|
|
|
35
35
|
"config/server.js"
|
|
36
36
|
]);
|
|
37
37
|
|
|
38
|
-
async function applyTextMutations(
|
|
38
|
+
async function applyTextMutations(
|
|
39
|
+
packageEntry,
|
|
40
|
+
appRoot,
|
|
41
|
+
textMutations,
|
|
42
|
+
options,
|
|
43
|
+
managedText,
|
|
44
|
+
touchedFiles,
|
|
45
|
+
{ dryRun = false } = {}
|
|
46
|
+
) {
|
|
39
47
|
for (const mutation of textMutations) {
|
|
40
48
|
const when = normalizeMutationWhen(mutation?.when);
|
|
41
49
|
const configContext = when?.config ? await loadMutationWhenConfigContext(appRoot) : {};
|
|
@@ -69,8 +77,10 @@ async function applyTextMutations(packageEntry, appRoot, textMutations, options,
|
|
|
69
77
|
const resolvedValue = interpolateOptionValue(mutation?.value || "", options, packageEntry.packageId, resolvedKey);
|
|
70
78
|
const upserted = upsertEnvValue(previousContent, resolvedKey, resolvedValue);
|
|
71
79
|
|
|
72
|
-
|
|
73
|
-
|
|
80
|
+
if (!dryRun) {
|
|
81
|
+
await mkdir(path.dirname(absoluteFile), { recursive: true });
|
|
82
|
+
await writeFile(absoluteFile, upserted.content, "utf8");
|
|
83
|
+
}
|
|
74
84
|
|
|
75
85
|
const recordKey = `${relativeFile}::${String(mutation?.id || resolvedKey)}`;
|
|
76
86
|
managedText[recordKey] = {
|
|
@@ -136,8 +146,10 @@ async function applyTextMutations(packageEntry, appRoot, textMutations, options,
|
|
|
136
146
|
continue;
|
|
137
147
|
}
|
|
138
148
|
|
|
139
|
-
|
|
140
|
-
|
|
149
|
+
if (!dryRun) {
|
|
150
|
+
await mkdir(path.dirname(absoluteFile), { recursive: true });
|
|
151
|
+
await writeFile(absoluteFile, appended.content, "utf8");
|
|
152
|
+
}
|
|
141
153
|
|
|
142
154
|
const recordKey = `${relativeFile}::${mutationId}`;
|
|
143
155
|
managedText[recordKey] = {
|
|
@@ -146,7 +146,8 @@ async function applyPackagePositioning({
|
|
|
146
146
|
packageOptions,
|
|
147
147
|
appRoot,
|
|
148
148
|
lock,
|
|
149
|
-
touchedFiles
|
|
149
|
+
touchedFiles,
|
|
150
|
+
dryRun = false
|
|
150
151
|
}) {
|
|
151
152
|
const existingInstall = ensureObject(lock.installedPackages[packageEntry.packageId]);
|
|
152
153
|
if (Object.keys(existingInstall).length < 1) {
|
|
@@ -196,7 +197,10 @@ async function applyPackagePositioning({
|
|
|
196
197
|
[],
|
|
197
198
|
touchedFiles,
|
|
198
199
|
[],
|
|
199
|
-
nextManaged.files
|
|
200
|
+
nextManaged.files,
|
|
201
|
+
{
|
|
202
|
+
dryRun
|
|
203
|
+
}
|
|
200
204
|
);
|
|
201
205
|
}
|
|
202
206
|
if (positioningMutations.text.length > 0) {
|
|
@@ -206,7 +210,10 @@ async function applyPackagePositioning({
|
|
|
206
210
|
positioningMutations.text,
|
|
207
211
|
packageOptions,
|
|
208
212
|
appliedManagedText,
|
|
209
|
-
touchedFiles
|
|
213
|
+
touchedFiles,
|
|
214
|
+
{
|
|
215
|
+
dryRun
|
|
216
|
+
}
|
|
210
217
|
);
|
|
211
218
|
}
|
|
212
219
|
|
|
@@ -250,7 +257,8 @@ async function applyPackageMigrationsOnly({
|
|
|
250
257
|
packageOptions,
|
|
251
258
|
appRoot,
|
|
252
259
|
lock,
|
|
253
|
-
touchedFiles
|
|
260
|
+
touchedFiles,
|
|
261
|
+
dryRun = false
|
|
254
262
|
}) {
|
|
255
263
|
const existingInstall = ensureObject(lock.installedPackages[packageEntry.packageId]);
|
|
256
264
|
if (Object.keys(existingInstall).length < 1) {
|
|
@@ -303,7 +311,10 @@ async function applyPackageMigrationsOnly({
|
|
|
303
311
|
nextManaged.migrations,
|
|
304
312
|
touchedFiles,
|
|
305
313
|
mutationWarnings,
|
|
306
|
-
nextManaged.files
|
|
314
|
+
nextManaged.files,
|
|
315
|
+
{
|
|
316
|
+
dryRun
|
|
317
|
+
}
|
|
307
318
|
);
|
|
308
319
|
}
|
|
309
320
|
|
|
@@ -333,7 +344,8 @@ async function applyPackageInstall({
|
|
|
333
344
|
lock,
|
|
334
345
|
packageRegistry,
|
|
335
346
|
touchedFiles,
|
|
336
|
-
reportTemplateFetchStatus = null
|
|
347
|
+
reportTemplateFetchStatus = null,
|
|
348
|
+
dryRun = false
|
|
337
349
|
}) {
|
|
338
350
|
const existingInstall = ensureObject(lock.installedPackages[packageEntry.packageId]);
|
|
339
351
|
const existingManaged = ensureObject(existingInstall.managed);
|
|
@@ -376,7 +388,10 @@ async function applyPackageInstall({
|
|
|
376
388
|
preFileTextMutations,
|
|
377
389
|
packageOptions,
|
|
378
390
|
managedRecord.managed.text,
|
|
379
|
-
touchedFiles
|
|
391
|
+
touchedFiles,
|
|
392
|
+
{
|
|
393
|
+
dryRun
|
|
394
|
+
}
|
|
380
395
|
);
|
|
381
396
|
}
|
|
382
397
|
|
|
@@ -391,7 +406,8 @@ async function applyPackageInstall({
|
|
|
391
406
|
appRoot,
|
|
392
407
|
packageId: packageEntry.packageId,
|
|
393
408
|
managedViteChanges: ensureObject(existingManaged.vite),
|
|
394
|
-
touchedFiles
|
|
409
|
+
touchedFiles,
|
|
410
|
+
dryRun
|
|
395
411
|
});
|
|
396
412
|
|
|
397
413
|
const mutationDependencies = ensureObject(mutations.dependencies);
|
|
@@ -499,15 +515,16 @@ async function applyPackageInstall({
|
|
|
499
515
|
packageEntryForMutations,
|
|
500
516
|
appRoot,
|
|
501
517
|
preparedFileMutations,
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
518
|
+
managedRecord.managed.files,
|
|
519
|
+
managedRecord.managed.migrations,
|
|
520
|
+
touchedFiles,
|
|
521
|
+
mutationWarnings,
|
|
522
|
+
ensureArray(existingManaged.files),
|
|
523
|
+
{
|
|
524
|
+
dryRun,
|
|
525
|
+
reapplyManagedAppFiles: Object.keys(existingInstall).length > 0
|
|
526
|
+
}
|
|
527
|
+
);
|
|
511
528
|
|
|
512
529
|
await applyTextMutations(
|
|
513
530
|
packageEntryForMutations,
|
|
@@ -515,7 +532,10 @@ async function applyPackageInstall({
|
|
|
515
532
|
postFileTextMutations,
|
|
516
533
|
packageOptions,
|
|
517
534
|
managedRecord.managed.text,
|
|
518
|
-
touchedFiles
|
|
535
|
+
touchedFiles,
|
|
536
|
+
{
|
|
537
|
+
dryRun
|
|
538
|
+
}
|
|
519
539
|
);
|
|
520
540
|
|
|
521
541
|
await applyViteMutations(
|
|
@@ -524,7 +544,10 @@ async function applyPackageInstall({
|
|
|
524
544
|
ensureObject(mutations.vite),
|
|
525
545
|
packageOptions,
|
|
526
546
|
managedRecord.managed.vite,
|
|
527
|
-
touchedFiles
|
|
547
|
+
touchedFiles,
|
|
548
|
+
{
|
|
549
|
+
dryRun
|
|
550
|
+
}
|
|
528
551
|
);
|
|
529
552
|
|
|
530
553
|
mutationWarnings.push(...await collectInstallWarnings({
|
|
@@ -144,14 +144,16 @@ async function loadViteDevProxyConfig(appRoot, { context = "vite proxy config" }
|
|
|
144
144
|
});
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
-
async function writeViteDevProxyConfig(appRoot, config = {}, touchedFiles = null) {
|
|
147
|
+
async function writeViteDevProxyConfig(appRoot, config = {}, touchedFiles = null, { dryRun = false } = {}) {
|
|
148
148
|
const absolutePath = resolveViteDevProxyConfigAbsolutePath(appRoot);
|
|
149
149
|
const relativePath = normalizeRelativePath(appRoot, absolutePath);
|
|
150
150
|
const normalizedConfig = normalizeViteDevProxyConfig(config);
|
|
151
151
|
|
|
152
152
|
if (normalizedConfig.entries.length < 1) {
|
|
153
153
|
if (await fileExists(absolutePath)) {
|
|
154
|
-
|
|
154
|
+
if (!dryRun) {
|
|
155
|
+
await rm(absolutePath);
|
|
156
|
+
}
|
|
155
157
|
if (touchedFiles && typeof touchedFiles.add === "function") {
|
|
156
158
|
touchedFiles.add(relativePath);
|
|
157
159
|
}
|
|
@@ -159,7 +161,9 @@ async function writeViteDevProxyConfig(appRoot, config = {}, touchedFiles = null
|
|
|
159
161
|
return;
|
|
160
162
|
}
|
|
161
163
|
|
|
162
|
-
|
|
164
|
+
if (!dryRun) {
|
|
165
|
+
await writeJsonFile(absolutePath, normalizedConfig);
|
|
166
|
+
}
|
|
163
167
|
if (touchedFiles && typeof touchedFiles.add === "function") {
|
|
164
168
|
touchedFiles.add(relativePath);
|
|
165
169
|
}
|
|
@@ -181,7 +185,15 @@ function normalizeViteProxyMutationRecord(value = {}) {
|
|
|
181
185
|
});
|
|
182
186
|
}
|
|
183
187
|
|
|
184
|
-
async function applyViteMutations(
|
|
188
|
+
async function applyViteMutations(
|
|
189
|
+
packageEntry,
|
|
190
|
+
appRoot,
|
|
191
|
+
viteMutations,
|
|
192
|
+
options,
|
|
193
|
+
managedVite,
|
|
194
|
+
touchedFiles,
|
|
195
|
+
{ dryRun = false } = {}
|
|
196
|
+
) {
|
|
185
197
|
const mutations = ensureArray(ensureObject(viteMutations).proxy).map((entry) => normalizeViteProxyMutationRecord(entry));
|
|
186
198
|
if (mutations.length < 1) {
|
|
187
199
|
return;
|
|
@@ -291,10 +303,16 @@ async function applyViteMutations(packageEntry, appRoot, viteMutations, options,
|
|
|
291
303
|
return;
|
|
292
304
|
}
|
|
293
305
|
|
|
294
|
-
await writeViteDevProxyConfig(appRoot, nextConfig, touchedFiles);
|
|
306
|
+
await writeViteDevProxyConfig(appRoot, nextConfig, touchedFiles, { dryRun });
|
|
295
307
|
}
|
|
296
308
|
|
|
297
|
-
async function removeManagedViteProxyEntries({
|
|
309
|
+
async function removeManagedViteProxyEntries({
|
|
310
|
+
appRoot,
|
|
311
|
+
packageId,
|
|
312
|
+
managedViteChanges = {},
|
|
313
|
+
touchedFiles = null,
|
|
314
|
+
dryRun = false
|
|
315
|
+
} = {}) {
|
|
298
316
|
const managedChanges = Object.values(ensureObject(managedViteChanges))
|
|
299
317
|
.map((entry) => ensureObject(entry))
|
|
300
318
|
.filter((entry) => String(entry.op || "").trim() === "upsert-vite-proxy");
|
|
@@ -339,7 +357,7 @@ async function removeManagedViteProxyEntries({ appRoot, packageId, managedViteCh
|
|
|
339
357
|
return;
|
|
340
358
|
}
|
|
341
359
|
|
|
342
|
-
await writeViteDevProxyConfig(appRoot, nextConfig, touchedFiles);
|
|
360
|
+
await writeViteDevProxyConfig(appRoot, nextConfig, touchedFiles, { dryRun });
|
|
343
361
|
}
|
|
344
362
|
|
|
345
363
|
export {
|
|
@@ -603,7 +603,9 @@ function createHealthCommands(ctx = {}) {
|
|
|
603
603
|
packagePath,
|
|
604
604
|
tableName,
|
|
605
605
|
provenance: String(entry.provenance || "").trim().toLowerCase(),
|
|
606
|
-
ownerKind: String(entry.ownerKind || "").trim().toLowerCase()
|
|
606
|
+
ownerKind: String(entry.ownerKind || "").trim().toLowerCase(),
|
|
607
|
+
providerEntrypoint: String(entry.providerEntrypoint || "").trim(),
|
|
608
|
+
ownershipFilter: normalizeDbIdentifier(entry.ownershipFilter)
|
|
607
609
|
});
|
|
608
610
|
}
|
|
609
611
|
|
|
@@ -969,7 +971,7 @@ function createHealthCommands(ctx = {}) {
|
|
|
969
971
|
.join(", ");
|
|
970
972
|
}
|
|
971
973
|
|
|
972
|
-
async function resolveAppLocalCrudOwnershipFilters({ appRoot, appLocalRegistry }) {
|
|
974
|
+
async function resolveAppLocalCrudOwnershipFilters({ appRoot, appLocalRegistry, issues }) {
|
|
973
975
|
const ownershipByTable = new Map();
|
|
974
976
|
const packageEntries = sortStrings([...appLocalRegistry.keys()])
|
|
975
977
|
.map((packageId) => appLocalRegistry.get(packageId))
|
|
@@ -980,26 +982,73 @@ function createHealthCommands(ctx = {}) {
|
|
|
980
982
|
continue;
|
|
981
983
|
}
|
|
982
984
|
|
|
983
|
-
const
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
985
|
+
const serverProviderEntries = resolveServerProviderEntries(packageEntry);
|
|
986
|
+
const providerInfoByEntrypoint = new Map();
|
|
987
|
+
for (const providerEntry of serverProviderEntries) {
|
|
988
|
+
if (!(await fileExists(providerEntry.absolutePath))) {
|
|
989
|
+
continue;
|
|
990
|
+
}
|
|
987
991
|
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
+
const sourceText = await readFile(providerEntry.absolutePath, "utf8");
|
|
993
|
+
const match = CRUD_OWNERSHIP_FILTER_LITERAL_PATTERN.exec(sourceText);
|
|
994
|
+
if (!match) {
|
|
995
|
+
continue;
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
const ownershipFilter = normalizeDbIdentifier(match[1]);
|
|
999
|
+
providerInfoByEntrypoint.set(providerEntry.entrypoint, {
|
|
1000
|
+
providerPath: normalizeRelativePath(appRoot, providerEntry.absolutePath),
|
|
1001
|
+
ownershipFilter,
|
|
1002
|
+
requiredOwnerKinds: resolveRequiredOwnerKindsFromOwnershipFilter(ownershipFilter)
|
|
1003
|
+
});
|
|
992
1004
|
}
|
|
993
|
-
const ownershipFilter = normalizeDbIdentifier(match[1]);
|
|
994
|
-
const requiredOwnerKinds = resolveRequiredOwnerKindsFromOwnershipFilter(ownershipFilter);
|
|
995
1005
|
|
|
996
1006
|
for (const ownershipEntry of normalizeOwnedTableEntries(packageEntry)) {
|
|
1007
|
+
const descriptorPath = `${resolvePackageDisplayPath(packageEntry)}/package.descriptor.mjs`;
|
|
1008
|
+
let providerInfo = null;
|
|
1009
|
+
if (ownershipEntry.providerEntrypoint) {
|
|
1010
|
+
providerInfo = providerInfoByEntrypoint.get(ownershipEntry.providerEntrypoint) || null;
|
|
1011
|
+
if (!providerInfo) {
|
|
1012
|
+
issues.push(
|
|
1013
|
+
`${descriptorPath}: [crud-ownership:provider-unresolved] table "${ownershipEntry.tableName}" points at providerEntrypoint "${ownershipEntry.providerEntrypoint}" but doctor could not resolve an ownershipFilter from that provider. Make sure the file exists and declares a literal CRUD_MODULE_CONFIG.ownershipFilter.`
|
|
1014
|
+
);
|
|
1015
|
+
}
|
|
1016
|
+
} else if (serverProviderEntries.length === 1) {
|
|
1017
|
+
providerInfo = providerInfoByEntrypoint.get(serverProviderEntries[0].entrypoint) || null;
|
|
1018
|
+
if (!providerInfo) {
|
|
1019
|
+
issues.push(
|
|
1020
|
+
`${descriptorPath}: [crud-ownership:provider-unresolved] table "${ownershipEntry.tableName}" relies on the package's only server provider, but doctor could not resolve a literal CRUD_MODULE_CONFIG.ownershipFilter from "${serverProviderEntries[0].entrypoint}".`
|
|
1021
|
+
);
|
|
1022
|
+
}
|
|
1023
|
+
} else if (serverProviderEntries.length > 1) {
|
|
1024
|
+
issues.push(
|
|
1025
|
+
`${descriptorPath}: [crud-ownership:missing-provider-entrypoint] table "${ownershipEntry.tableName}" is claimed by a multi-provider CRUD package but does not declare metadata.jskit.tableOwnership.tables[].providerEntrypoint. Point it at the owning provider so doctor can verify the real ownershipFilter.`
|
|
1026
|
+
);
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
if (
|
|
1030
|
+
ownershipEntry.ownershipFilter &&
|
|
1031
|
+
providerInfo?.ownershipFilter &&
|
|
1032
|
+
ownershipEntry.ownershipFilter !== providerInfo.ownershipFilter
|
|
1033
|
+
) {
|
|
1034
|
+
issues.push(
|
|
1035
|
+
`${providerInfo.providerPath}: [crud-ownership:ownership-filter-mismatch] metadata declares ownershipFilter "${ownershipEntry.ownershipFilter}" for live table "${ownershipEntry.tableName}" but provider code uses "${providerInfo.ownershipFilter}". Update the provider or metadata so doctor verifies the real contract.`
|
|
1036
|
+
);
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
const ownershipFilter = providerInfo?.ownershipFilter || "";
|
|
1040
|
+
if (!ownershipFilter) {
|
|
1041
|
+
continue;
|
|
1042
|
+
}
|
|
1043
|
+
|
|
997
1044
|
ownershipByTable.set(ownershipEntry.tableName, {
|
|
998
1045
|
tableName: ownershipEntry.tableName,
|
|
999
1046
|
ownershipFilter,
|
|
1000
|
-
requiredOwnerKinds,
|
|
1047
|
+
requiredOwnerKinds: providerInfo?.requiredOwnerKinds || new Set(),
|
|
1001
1048
|
packagePath: resolvePackageDisplayPath(packageEntry),
|
|
1002
|
-
providerPath:
|
|
1049
|
+
providerPath:
|
|
1050
|
+
providerInfo?.providerPath ||
|
|
1051
|
+
normalizeRelativePath(appRoot, resolvePrimaryServerProviderPath(packageEntry))
|
|
1003
1052
|
});
|
|
1004
1053
|
}
|
|
1005
1054
|
}
|
|
@@ -1149,6 +1198,29 @@ function createHealthCommands(ctx = {}) {
|
|
|
1149
1198
|
return "";
|
|
1150
1199
|
}
|
|
1151
1200
|
|
|
1201
|
+
function resolveServerProviderEntries(packageEntry) {
|
|
1202
|
+
const rootDir = String(packageEntry?.rootDir || "").trim();
|
|
1203
|
+
if (!rootDir) {
|
|
1204
|
+
return [];
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
const providers = ensureArray(ensureObject(ensureObject(packageEntry?.descriptor).runtime).server?.providers);
|
|
1208
|
+
const resolved = [];
|
|
1209
|
+
for (const rawProvider of providers) {
|
|
1210
|
+
const provider = ensureObject(rawProvider);
|
|
1211
|
+
const entrypoint = String(provider.entrypoint || "").trim();
|
|
1212
|
+
if (!entrypoint || entrypoint.includes("*")) {
|
|
1213
|
+
continue;
|
|
1214
|
+
}
|
|
1215
|
+
resolved.push({
|
|
1216
|
+
entrypoint,
|
|
1217
|
+
absolutePath: path.resolve(rootDir, entrypoint)
|
|
1218
|
+
});
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
return resolved;
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1152
1224
|
function resolvePackageDisplayPath(packageEntry) {
|
|
1153
1225
|
const relativeDir = String(packageEntry?.relativeDir || "").trim();
|
|
1154
1226
|
if (relativeDir) {
|
|
@@ -1517,7 +1589,8 @@ function createHealthCommands(ctx = {}) {
|
|
|
1517
1589
|
});
|
|
1518
1590
|
const crudOwnershipByTable = await resolveAppLocalCrudOwnershipFilters({
|
|
1519
1591
|
appRoot,
|
|
1520
|
-
appLocalRegistry
|
|
1592
|
+
appLocalRegistry,
|
|
1593
|
+
issues
|
|
1521
1594
|
});
|
|
1522
1595
|
const exceptionEntriesByName = new Map();
|
|
1523
1596
|
for (const entry of exceptionConfig.exceptions) {
|