@jay-framework/plugin-validator 0.22.2 → 0.23.0

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/dist/index.d.ts CHANGED
@@ -153,4 +153,28 @@ declare function lintAddMenuCatalog(items: AddMenuItem[], sourcePath: string): {
153
153
  /** Human-readable fix hints for validate-plugin output. */
154
154
  declare const ADD_MENU_VALIDATION_SUGGESTIONS: Record<string, string>;
155
155
 
156
- export { ADD_MENU_VALIDATION_SUGGESTIONS, type AddMenuBrowseSize, type AddMenuCatalogFile, type AddMenuCatalogLintWarning, type AddMenuInteraction, type AddMenuItem, type AddMenuPresentation, type AddMenuValidationError, type PluginContext, type ValidatePluginOptions, type ValidationError, type ValidationResult, type ValidationWarning, checkComponentPropsAndParams, lintAddMenuCatalog, validateAddMenuCatalogFile, validateAddMenuItem, validatePlugin };
156
+ /**
157
+ * AIditor Project settings contribution schema (materialized YAML).
158
+ * Source of truth for plugin-validator and AIditor runtime discovery.
159
+ */
160
+ type AiditorSettingsRequire = {
161
+ plugin: string;
162
+ status: 'configured';
163
+ };
164
+ type AiditorSettingsFile = {
165
+ label: string;
166
+ route: string;
167
+ pluginName?: string;
168
+ requires?: AiditorSettingsRequire[];
169
+ };
170
+ type AiditorSettingsValidationError = {
171
+ path: string;
172
+ message: string;
173
+ code?: string;
174
+ };
175
+ declare function validateAiditorSettingsFile(raw: unknown, sourcePath: string): {
176
+ file?: AiditorSettingsFile;
177
+ errors: AiditorSettingsValidationError[];
178
+ };
179
+
180
+ export { ADD_MENU_VALIDATION_SUGGESTIONS, type AddMenuBrowseSize, type AddMenuCatalogFile, type AddMenuCatalogLintWarning, type AddMenuInteraction, type AddMenuItem, type AddMenuPresentation, type AddMenuValidationError, type AiditorSettingsFile, type AiditorSettingsRequire, type AiditorSettingsValidationError, type PluginContext, type ValidatePluginOptions, type ValidationError, type ValidationResult, type ValidationWarning, checkComponentPropsAndParams, lintAddMenuCatalog, validateAddMenuCatalogFile, validateAddMenuItem, validateAiditorSettingsFile, validatePlugin };
package/dist/index.js CHANGED
@@ -279,7 +279,7 @@ const FOLDER_PATH_MAX_SEGMENTS = 32;
279
279
  const BLOCKED_TAGS = /<\s*(script|iframe|object|embed)\b[^>]*>[\s\S]*?<\/\s*\1\s*>|<\s*(script|iframe|object|embed)\b[^>]*\/?>/gi;
280
280
  const EVENT_HANDLER_ATTR = /\s+on[a-z]+\s*=\s*("[^"]*"|'[^']*'|[^\s>]+)/gi;
281
281
  const JAVASCRIPT_URL = /\b(href|src|xlink:href)\s*=\s*("|')\s*javascript:/gi;
282
- function isRecord(value) {
282
+ function isRecord$1(value) {
283
283
  return typeof value === "object" && value !== null && !Array.isArray(value);
284
284
  }
285
285
  function byteLengthUtf8(value) {
@@ -349,7 +349,7 @@ function optionalString(obj, field) {
349
349
  function validateInteraction(raw, itemPath, errors) {
350
350
  if (raw === void 0)
351
351
  return void 0;
352
- if (!isRecord(raw)) {
352
+ if (!isRecord$1(raw)) {
353
353
  errors.push({
354
354
  path: itemPath,
355
355
  message: "interaction must be an object",
@@ -374,7 +374,7 @@ function validateInteraction(raw, itemPath, errors) {
374
374
  function validatePresentation(raw, itemPath, errors) {
375
375
  if (raw === void 0)
376
376
  return void 0;
377
- if (!isRecord(raw)) {
377
+ if (!isRecord$1(raw)) {
378
378
  errors.push({
379
379
  path: itemPath,
380
380
  message: "presentation must be an object",
@@ -447,7 +447,7 @@ const BROWSE_SIZES = /* @__PURE__ */ new Set(["large", "medium", "small"]);
447
447
  function validateBrowse(raw, itemPath, errors) {
448
448
  if (raw === void 0)
449
449
  return void 0;
450
- if (!isRecord(raw)) {
450
+ if (!isRecord$1(raw)) {
451
451
  errors.push({
452
452
  path: itemPath,
453
453
  message: "browse must be an object",
@@ -530,7 +530,7 @@ function validateFolderPath(raw, itemPath, errors) {
530
530
  }
531
531
  function validateAddMenuItem(raw, itemPath) {
532
532
  const errors = [];
533
- if (!isRecord(raw)) {
533
+ if (!isRecord$1(raw)) {
534
534
  return {
535
535
  item: null,
536
536
  errors: [
@@ -581,7 +581,7 @@ function validateAddMenuItem(raw, itemPath) {
581
581
  }
582
582
  function validateAddMenuCatalogFile(raw, sourcePath) {
583
583
  const errors = [];
584
- if (!isRecord(raw)) {
584
+ if (!isRecord$1(raw)) {
585
585
  return {
586
586
  file: null,
587
587
  errors: [
@@ -940,7 +940,7 @@ function suggestionForCode(code) {
940
940
  return `See ${CONTRIBUTOR_GUIDE} for schema and validation rules`;
941
941
  return ADD_MENU_VALIDATION_SUGGESTIONS[code] ?? `See ${CONTRIBUTOR_GUIDE} for schema and validation rules`;
942
942
  }
943
- function mapSchemaError(error, catalogPath) {
943
+ function mapSchemaError$1(error, catalogPath) {
944
944
  const code = error.code ?? "catalog-validation-error";
945
945
  return {
946
946
  type: "add-menu-catalog",
@@ -1014,7 +1014,7 @@ async function validateAddMenuCatalogFileAtPath(catalogPath, relPath, result) {
1014
1014
  return;
1015
1015
  }
1016
1016
  const validated = validateAddMenuCatalogFile(parsed, relPath);
1017
- result.errors.push(...validated.errors.map((error) => mapSchemaError(error, relPath)));
1017
+ result.errors.push(...validated.errors.map((error) => mapSchemaError$1(error, relPath)));
1018
1018
  if (!validated.file?.items.length) {
1019
1019
  return;
1020
1020
  }
@@ -1035,6 +1035,173 @@ async function validateAddMenuCatalog(context, result) {
1035
1035
  await validateAddMenuCatalogFileAtPath(catalogPath, relPath, result);
1036
1036
  }
1037
1037
  }
1038
+ function isRecord(value) {
1039
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1040
+ }
1041
+ function parseRequires(raw, filePath) {
1042
+ const errors = [];
1043
+ if (raw === void 0) {
1044
+ return { requires: [], errors };
1045
+ }
1046
+ if (!Array.isArray(raw)) {
1047
+ errors.push({
1048
+ path: `${filePath}.requires`,
1049
+ message: "requires must be an array",
1050
+ code: "settings-requires-type"
1051
+ });
1052
+ return { requires: [], errors };
1053
+ }
1054
+ const requires = [];
1055
+ raw.forEach((entry, index) => {
1056
+ if (!isRecord(entry)) {
1057
+ errors.push({
1058
+ path: `${filePath}.requires[${index}]`,
1059
+ message: "require entry must be an object",
1060
+ code: "settings-require-shape"
1061
+ });
1062
+ return;
1063
+ }
1064
+ if (typeof entry.plugin !== "string" || !entry.plugin.trim()) {
1065
+ errors.push({
1066
+ path: `${filePath}.requires[${index}].plugin`,
1067
+ message: "plugin is required",
1068
+ code: "settings-require-plugin"
1069
+ });
1070
+ return;
1071
+ }
1072
+ if (entry.status !== "configured") {
1073
+ errors.push({
1074
+ path: `${filePath}.requires[${index}].status`,
1075
+ message: 'status must be "configured"',
1076
+ code: "settings-require-status"
1077
+ });
1078
+ return;
1079
+ }
1080
+ requires.push({ plugin: entry.plugin.trim(), status: "configured" });
1081
+ });
1082
+ return { requires, errors };
1083
+ }
1084
+ function validateAiditorSettingsFile(raw, sourcePath) {
1085
+ const errors = [];
1086
+ if (!isRecord(raw)) {
1087
+ return {
1088
+ errors: [
1089
+ {
1090
+ path: sourcePath,
1091
+ message: "settings file must be a YAML object",
1092
+ code: "settings-root-type"
1093
+ }
1094
+ ]
1095
+ };
1096
+ }
1097
+ if (typeof raw.label !== "string" || !raw.label.trim()) {
1098
+ errors.push({
1099
+ path: `${sourcePath}.label`,
1100
+ message: "label is required",
1101
+ code: "settings-label"
1102
+ });
1103
+ }
1104
+ if (typeof raw.route !== "string" || !raw.route.trim()) {
1105
+ errors.push({
1106
+ path: `${sourcePath}.route`,
1107
+ message: "route is required",
1108
+ code: "settings-route"
1109
+ });
1110
+ } else if (!raw.route.startsWith("/")) {
1111
+ errors.push({
1112
+ path: `${sourcePath}.route`,
1113
+ message: "route must start with /",
1114
+ code: "settings-route-format"
1115
+ });
1116
+ }
1117
+ if (raw.pluginName !== void 0 && typeof raw.pluginName !== "string") {
1118
+ errors.push({
1119
+ path: `${sourcePath}.pluginName`,
1120
+ message: "pluginName must be a string",
1121
+ code: "settings-plugin-name-type"
1122
+ });
1123
+ }
1124
+ const { requires, errors: requireErrors } = parseRequires(raw.requires, sourcePath);
1125
+ errors.push(...requireErrors);
1126
+ if (errors.length > 0) {
1127
+ return { errors };
1128
+ }
1129
+ return {
1130
+ file: {
1131
+ label: raw.label.trim(),
1132
+ route: raw.route.trim(),
1133
+ ...typeof raw.pluginName === "string" && raw.pluginName.trim() ? { pluginName: raw.pluginName.trim() } : {},
1134
+ ...requires.length > 0 ? { requires } : {}
1135
+ },
1136
+ errors: []
1137
+ };
1138
+ }
1139
+ const AIDITOR_SETTINGS_TEMPLATE_REL_PATH = "agent-kit/aiditor/settings.template.yaml";
1140
+ function mapSchemaError(error, relPath) {
1141
+ return {
1142
+ type: "schema",
1143
+ message: error.message,
1144
+ location: error.path || relPath,
1145
+ code: error.code
1146
+ };
1147
+ }
1148
+ function validateSettingsTemplateAtPath(catalogPath, relPath, result, manifest) {
1149
+ let parsed;
1150
+ try {
1151
+ parsed = YAML.parse(fs.readFileSync(catalogPath, "utf-8"));
1152
+ } catch (err) {
1153
+ result.errors.push({
1154
+ type: "schema",
1155
+ message: `Invalid YAML in ${relPath}: ${err instanceof Error ? err.message : String(err)}`,
1156
+ location: relPath
1157
+ });
1158
+ return;
1159
+ }
1160
+ const validated = validateAiditorSettingsFile(parsed, relPath);
1161
+ result.errors.push(...validated.errors.map((error) => mapSchemaError(error, relPath)));
1162
+ if (!validated.file) {
1163
+ return;
1164
+ }
1165
+ const routeEntry = manifest.routes?.find((route) => route.path === validated.file.route);
1166
+ if (!routeEntry) {
1167
+ result.warnings.push({
1168
+ type: "schema",
1169
+ message: `settings route "${validated.file.route}" is not declared in plugin.yaml routes[]`,
1170
+ location: relPath,
1171
+ code: "settings-route-missing",
1172
+ suggestion: "Add a matching routes[] entry or fix the route in settings.template.yaml"
1173
+ });
1174
+ } else if (routeEntry.devOnly !== true) {
1175
+ result.warnings.push({
1176
+ type: "schema",
1177
+ message: `settings route "${validated.file.route}" should declare devOnly: true on routes[]`,
1178
+ location: "plugin.yaml routes",
1179
+ code: "settings-route-dev-only",
1180
+ suggestion: "Add devOnly: true when the settings page is dev-server tooling (see Design Log #171)"
1181
+ });
1182
+ }
1183
+ }
1184
+ async function validateAiditorSettings(context, result) {
1185
+ const templatePath = path.join(context.pluginPath, AIDITOR_SETTINGS_TEMPLATE_REL_PATH);
1186
+ if (!fs.existsSync(templatePath)) {
1187
+ return;
1188
+ }
1189
+ validateSettingsTemplateAtPath(
1190
+ templatePath,
1191
+ AIDITOR_SETTINGS_TEMPLATE_REL_PATH,
1192
+ result,
1193
+ context.manifest
1194
+ );
1195
+ if (!context.manifest.agentkit) {
1196
+ result.warnings.push({
1197
+ type: "schema",
1198
+ message: "Plugin ships agent-kit/aiditor/settings.template.yaml but has no agentkit handler",
1199
+ location: "plugin.yaml",
1200
+ code: "settings-missing-agentkit-handler",
1201
+ suggestion: "Declare agentkit in plugin.yaml and materialize to agent-kit/aiditor/settings/<plugin>.yaml on jay-stack agent-kit — see agent-kit/plugin/aiditor-settings-guide.md"
1202
+ });
1203
+ }
1204
+ }
1038
1205
  async function validatePlugin(options = {}) {
1039
1206
  const pluginPath = options.pluginPath || process.cwd();
1040
1207
  if (options.local) {
@@ -1102,6 +1269,7 @@ async function validatePluginPackage(pluginPath, options) {
1102
1269
  await validateDynamicContracts(context, result);
1103
1270
  }
1104
1271
  await validateAddMenuCatalog(context, result);
1272
+ await validateAiditorSettings(context, result);
1105
1273
  result.valid = result.errors.length === 0;
1106
1274
  return result;
1107
1275
  }
@@ -1411,6 +1579,13 @@ async function validateSchema(context, result) {
1411
1579
  if (route.css) {
1412
1580
  validateDocFile(route.css, `route "${route.path}" css`, context, result);
1413
1581
  }
1582
+ if (route.devOnly !== void 0 && typeof route.devOnly !== "boolean") {
1583
+ result.errors.push({
1584
+ type: "schema",
1585
+ message: `Route "${route.path}" devOnly must be a boolean`,
1586
+ location: "plugin.yaml"
1587
+ });
1588
+ }
1414
1589
  });
1415
1590
  }
1416
1591
  }
@@ -1942,5 +2117,6 @@ export {
1942
2117
  lintAddMenuCatalog,
1943
2118
  validateAddMenuCatalogFile,
1944
2119
  validateAddMenuItem,
2120
+ validateAiditorSettingsFile,
1945
2121
  validatePlugin
1946
2122
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jay-framework/plugin-validator",
3
- "version": "0.22.2",
3
+ "version": "0.23.0",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "description": "Validation tool for Jay Stack plugins",
@@ -25,15 +25,14 @@
25
25
  "test:watch": "vitest"
26
26
  },
27
27
  "dependencies": {
28
- "@jay-framework/compiler-jay-html": "^0.22.2",
29
- "@jay-framework/compiler-shared": "^0.22.2",
30
- "@jay-framework/editor-protocol": "^0.22.2",
31
- "@jay-framework/typescript-bridge": "^0.22.2",
28
+ "@jay-framework/compiler-jay-html": "^0.23.0",
29
+ "@jay-framework/compiler-shared": "^0.23.0",
30
+ "@jay-framework/typescript-bridge": "^0.23.0",
32
31
  "chalk": "^4.1.2",
33
32
  "yaml": "^2.3.4"
34
33
  },
35
34
  "devDependencies": {
36
- "@jay-framework/dev-environment": "^0.22.2",
35
+ "@jay-framework/dev-environment": "^0.23.0",
37
36
  "@types/node": "^22.15.21",
38
37
  "rimraf": "^5.0.5",
39
38
  "tsup": "^8.0.1",