@jskit-ai/jskit-cli 0.2.38 → 0.2.40

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jskit-ai/jskit-cli",
3
- "version": "0.2.38",
3
+ "version": "0.2.40",
4
4
  "description": "Bundle and package orchestration CLI for JSKIT apps.",
5
5
  "type": "module",
6
6
  "files": [
@@ -20,8 +20,8 @@
20
20
  "test": "node --test"
21
21
  },
22
22
  "dependencies": {
23
- "@jskit-ai/jskit-catalog": "0.1.38",
24
- "@jskit-ai/kernel": "0.1.30"
23
+ "@jskit-ai/jskit-catalog": "0.1.40",
24
+ "@jskit-ai/kernel": "0.1.32"
25
25
  },
26
26
  "engines": {
27
27
  "node": "20.x"
@@ -16,7 +16,8 @@ import {
16
16
  hashBuffer,
17
17
  fileExists,
18
18
  readFileBufferIfExists,
19
- loadMutationWhenConfigContext
19
+ loadMutationWhenConfigContext,
20
+ resolveAppRelativePathWithinRoot
20
21
  } from "../ioAndMigrations.js";
21
22
  import {
22
23
  copyTemplateFile,
@@ -113,7 +114,7 @@ async function applyFileMutations(
113
114
  mutation,
114
115
  configContext
115
116
  })
116
- : [path.join(appRoot, to)];
117
+ : [resolveAppRelativePathWithinRoot(appRoot, to, `${packageEntry.packageId} files mutation.to`).absolutePath];
117
118
  const hasPrecomputedTemplateContext =
118
119
  precomputedTemplateContextByMutationIndex instanceof Map &&
119
120
  precomputedTemplateContextByMutationIndex.has(mutationIndex);
@@ -224,7 +225,7 @@ async function preflightFileMutationTemplateContexts(
224
225
  mutation,
225
226
  configContext
226
227
  })
227
- : [path.join(appRoot, to)]
228
+ : [resolveAppRelativePathWithinRoot(appRoot, to, `${packageEntry.packageId} files mutation.to`).absolutePath]
228
229
  : [path.join(appRoot, mutation.toDir || "migrations")];
229
230
  const replacements = await resolveTemplateContextReplacementsForMutation({
230
231
  packageEntry,
@@ -1,3 +1,4 @@
1
+ import { normalizePagesRelativeTargetRoot } from "@jskit-ai/kernel/server/support";
1
2
  import { createCliError } from "../shared/cliError.js";
2
3
  import {
3
4
  ensureObject,
@@ -15,6 +16,8 @@ import { loadMutationWhenConfigContext } from "./ioAndMigrations.js";
15
16
 
16
17
  const WORKSPACE_VISIBILITY_LEVELS = Object.freeze(["workspace", "workspace_user"]);
17
18
  const WORKSPACE_VISIBILITY_SET = new Set(WORKSPACE_VISIBILITY_LEVELS);
19
+ const OPTION_VALIDATION_ENABLED_SURFACE_ID = "enabled-surface-id";
20
+ const OPTION_NORMALIZATION_PAGES_RELATIVE_TARGET_ROOT = "pages-relative-target-root";
18
21
 
19
22
  function normalizeSurfaceIdForMutation(value = "") {
20
23
  return String(value || "").trim().toLowerCase();
@@ -100,6 +103,95 @@ function normalizeResolvedOptionValue(value = "") {
100
103
  return String(value || "").trim().toLowerCase();
101
104
  }
102
105
 
106
+ function normalizeResolvedOptionSchemaValue({
107
+ packageEntry,
108
+ optionName = "",
109
+ schema = {},
110
+ value = ""
111
+ } = {}) {
112
+ const normalizedValue = String(value || "").trim();
113
+ if (!normalizedValue) {
114
+ return "";
115
+ }
116
+
117
+ const normalizationType = normalizeResolvedOptionValue(schema?.normalizationType);
118
+ if (!normalizationType) {
119
+ return normalizedValue;
120
+ }
121
+
122
+ if (normalizationType === OPTION_NORMALIZATION_PAGES_RELATIVE_TARGET_ROOT) {
123
+ return normalizePagesRelativeTargetRoot(normalizedValue, {
124
+ context: `package ${String(packageEntry?.packageId || "unknown-package")}`,
125
+ label: `option "${String(optionName || "").trim() || "unknown"}"`
126
+ }).slice("src/pages/".length);
127
+ }
128
+
129
+ throw createCliError(
130
+ `Invalid option normalization type in package ${String(packageEntry?.packageId || "unknown-package")}: ${String(schema?.normalizationType || "").trim()}.`
131
+ );
132
+ }
133
+
134
+ function resolveSchemaValidatedOptionNames(packageEntry = {}, validationType = "", { optionNames = null } = {}) {
135
+ const normalizedValidationType = String(validationType || "").trim().toLowerCase();
136
+ if (!normalizedValidationType) {
137
+ return [];
138
+ }
139
+
140
+ const optionSchemas = ensureObject(packageEntry?.descriptor?.options);
141
+ const candidateOptionNames = Array.isArray(optionNames) && optionNames.length > 0
142
+ ? optionNames
143
+ : Object.keys(optionSchemas);
144
+
145
+ return [
146
+ ...new Set(
147
+ candidateOptionNames.filter((optionName) => {
148
+ const schema = ensureObject(optionSchemas[optionName]);
149
+ return normalizeResolvedOptionValue(schema.validationType) === normalizedValidationType;
150
+ })
151
+ )
152
+ ];
153
+ }
154
+
155
+ function validateEnabledSurfaceOptionValues({
156
+ packageEntry,
157
+ resolvedOptions = {},
158
+ optionNames = null,
159
+ configContext = {}
160
+ } = {}) {
161
+ const validatedOptionNames = resolveSchemaValidatedOptionNames(
162
+ packageEntry,
163
+ OPTION_VALIDATION_ENABLED_SURFACE_ID,
164
+ { optionNames }
165
+ );
166
+ if (validatedOptionNames.length < 1) {
167
+ return;
168
+ }
169
+
170
+ const providedSurfaceOptionNames = validatedOptionNames.filter((optionName) => {
171
+ return normalizeSurfaceIdForMutation(resolvedOptions?.[optionName]).length > 0;
172
+ });
173
+ if (providedSurfaceOptionNames.length < 1) {
174
+ return;
175
+ }
176
+
177
+ const packageId = String(packageEntry?.packageId || "").trim() || "unknown-package";
178
+ const surfaceDefinitions = resolveSurfaceDefinitionsForOptionPolicy(configContext);
179
+ for (const optionName of providedSurfaceOptionNames) {
180
+ const surfaceId = normalizeSurfaceIdForMutation(resolvedOptions?.[optionName]);
181
+ const surfaceDefinition = surfaceDefinitions[surfaceId];
182
+ if (!surfaceDefinition) {
183
+ throw createCliError(
184
+ `Invalid option for package ${packageId}: --${optionName} references unknown surface "${surfaceId}" in config/public.js.`
185
+ );
186
+ }
187
+ if (surfaceDefinition.enabled !== true) {
188
+ throw createCliError(
189
+ `Invalid option for package ${packageId}: --${optionName} references disabled surface "${surfaceId}" in config/public.js.`
190
+ );
191
+ }
192
+ }
193
+ }
194
+
103
195
  function validateSurfaceVisibilityOptionPolicy({
104
196
  packageEntry,
105
197
  resolvedOptions = {},
@@ -159,6 +251,34 @@ async function validateResolvedOptionPolicies({
159
251
  });
160
252
  }
161
253
 
254
+ async function validateOptionValuesForPackage({
255
+ packageEntry,
256
+ resolvedOptions = {},
257
+ appRoot = "",
258
+ optionNames = null
259
+ } = {}) {
260
+ if (!appRoot) {
261
+ return;
262
+ }
263
+
264
+ const validatedOptionNames = resolveSchemaValidatedOptionNames(
265
+ packageEntry,
266
+ OPTION_VALIDATION_ENABLED_SURFACE_ID,
267
+ { optionNames }
268
+ );
269
+ if (validatedOptionNames.length < 1) {
270
+ return;
271
+ }
272
+
273
+ const configContext = await loadMutationWhenConfigContext(appRoot);
274
+ validateEnabledSurfaceOptionValues({
275
+ packageEntry,
276
+ resolvedOptions,
277
+ optionNames: validatedOptionNames,
278
+ configContext
279
+ });
280
+ }
281
+
162
282
  async function resolvePackageOptions(packageEntry, inlineOptions, io, { appRoot = "" } = {}) {
163
283
  const optionSchemas = ensureObject(packageEntry.descriptor.options);
164
284
  const optionNames = Object.keys(optionSchemas);
@@ -211,10 +331,23 @@ async function resolvePackageOptions(packageEntry, inlineOptions, io, { appRoot
211
331
  for (const optionName of optionNames) {
212
332
  const schema = ensureObject(optionSchemas[optionName]);
213
333
  const allowEmpty = schema.allowEmpty === true;
334
+ const assignResolvedOption = (rawValue = "") => {
335
+ const normalizedOptionValue = normalizeResolvedOptionSchemaValue({
336
+ packageEntry,
337
+ optionName,
338
+ schema,
339
+ value: rawValue
340
+ });
341
+ if (normalizedOptionValue || allowEmpty) {
342
+ resolved[optionName] = normalizedOptionValue;
343
+ return true;
344
+ }
345
+ return false;
346
+ };
214
347
  if (hasInlineOption(optionName)) {
215
348
  const inlineValue = String(inlineOptionValues[optionName] || "").trim();
216
349
  if (inlineValue || allowEmpty) {
217
- resolved[optionName] = inlineValue;
350
+ assignResolvedOption(inlineValue);
218
351
  continue;
219
352
  }
220
353
  if (schema.required) {
@@ -226,7 +359,7 @@ async function resolvePackageOptions(packageEntry, inlineOptions, io, { appRoot
226
359
  if (defaultFromConfigPath) {
227
360
  const defaultFromConfigValue = await resolveOptionDefaultFromConfig(defaultFromConfigPath);
228
361
  if (defaultFromConfigValue || allowEmpty) {
229
- resolved[optionName] = defaultFromConfigValue;
362
+ assignResolvedOption(defaultFromConfigValue);
230
363
  continue;
231
364
  }
232
365
  }
@@ -235,31 +368,37 @@ async function resolvePackageOptions(packageEntry, inlineOptions, io, { appRoot
235
368
  if (defaultFromOptionTemplate) {
236
369
  const derivedOptionValue = resolveOptionDefaultFromTemplate(defaultFromOptionTemplate, optionName);
237
370
  if (derivedOptionValue || allowEmpty) {
238
- resolved[optionName] = derivedOptionValue;
371
+ assignResolvedOption(derivedOptionValue);
239
372
  continue;
240
373
  }
241
374
  }
242
375
 
243
376
  if (typeof schema.defaultValue === "string" && schema.defaultValue.trim()) {
244
- resolved[optionName] = schema.defaultValue.trim();
377
+ assignResolvedOption(schema.defaultValue.trim());
245
378
  continue;
246
379
  }
247
380
 
248
381
  if (schema.required) {
249
- resolved[optionName] = await promptForRequiredOption({
382
+ assignResolvedOption(await promptForRequiredOption({
250
383
  ownerType: "package",
251
384
  ownerId: packageEntry.packageId,
252
385
  optionName,
253
386
  optionSchema: schema,
254
387
  stdin: io.stdin,
255
388
  stdout: io.stdout
256
- });
389
+ }));
257
390
  continue;
258
391
  }
259
392
 
260
393
  resolved[optionName] = "";
261
394
  }
262
395
 
396
+ await validateOptionValuesForPackage({
397
+ packageEntry,
398
+ resolvedOptions: resolved,
399
+ appRoot
400
+ });
401
+
263
402
  await validateResolvedOptionPolicies({
264
403
  packageEntry,
265
404
  resolvedOptions: resolved,
@@ -291,9 +430,23 @@ function validateInlineOptionsForPackage(packageEntry, inlineOptions) {
291
430
  );
292
431
  }
293
432
 
433
+ async function validateInlineOptionValuesForPackage(
434
+ packageEntry,
435
+ inlineOptions,
436
+ { appRoot = "", optionNames = null } = {}
437
+ ) {
438
+ await validateOptionValuesForPackage({
439
+ packageEntry,
440
+ resolvedOptions: ensureObject(inlineOptions),
441
+ appRoot,
442
+ optionNames
443
+ });
444
+ }
445
+
294
446
  export {
295
447
  normalizeSurfaceIdForMutation,
296
448
  parseSurfaceIdListForMutation,
297
449
  resolvePackageOptions,
298
- validateInlineOptionsForPackage
450
+ validateInlineOptionsForPackage,
451
+ validateInlineOptionValuesForPackage
299
452
  };