@jay-framework/plugin-validator 0.22.2 → 0.23.1
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 +25 -1
- package/dist/index.js +248 -136
- package/package.json +5 -6
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
|
-
|
|
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
|
@@ -93,17 +93,13 @@ function collectLocalInterfaces(sourceFile) {
|
|
|
93
93
|
function collectContractImportedTypes(sourceFile) {
|
|
94
94
|
const contractTypes = /* @__PURE__ */ new Set();
|
|
95
95
|
for (const statement of sourceFile.statements) {
|
|
96
|
-
if (!ts.isImportDeclaration(statement))
|
|
97
|
-
continue;
|
|
96
|
+
if (!ts.isImportDeclaration(statement)) continue;
|
|
98
97
|
const moduleSpecifier = statement.moduleSpecifier;
|
|
99
|
-
if (!ts.isStringLiteral(moduleSpecifier))
|
|
100
|
-
continue;
|
|
98
|
+
if (!ts.isStringLiteral(moduleSpecifier)) continue;
|
|
101
99
|
const modulePath = moduleSpecifier.text;
|
|
102
|
-
if (!modulePath.includes(".jay-contract"))
|
|
103
|
-
continue;
|
|
100
|
+
if (!modulePath.includes(".jay-contract")) continue;
|
|
104
101
|
const importClause = statement.importClause;
|
|
105
|
-
if (!importClause)
|
|
106
|
-
continue;
|
|
102
|
+
if (!importClause) continue;
|
|
107
103
|
const namedBindings = importClause.namedBindings;
|
|
108
104
|
if (namedBindings && ts.isNamedImports(namedBindings)) {
|
|
109
105
|
for (const element of namedBindings.elements) {
|
|
@@ -139,8 +135,7 @@ function visitNode(node, result) {
|
|
|
139
135
|
}
|
|
140
136
|
if (node.arguments.length > 0) {
|
|
141
137
|
const arg = node.arguments[0];
|
|
142
|
-
if (ts.isIdentifier(arg))
|
|
143
|
-
;
|
|
138
|
+
if (ts.isIdentifier(arg)) ;
|
|
144
139
|
}
|
|
145
140
|
}
|
|
146
141
|
}
|
|
@@ -162,10 +157,8 @@ function extractTypeNames(typeNode) {
|
|
|
162
157
|
return [];
|
|
163
158
|
}
|
|
164
159
|
function checkPropsConsistency(propsTypeName, localInterfaces, contractImportedTypes, contract, contractName, contractPath, sourcePath, errors, warnings) {
|
|
165
|
-
if (FRAMEWORK_PROP_TYPES.has(propsTypeName))
|
|
166
|
-
|
|
167
|
-
if (contractImportedTypes.has(propsTypeName))
|
|
168
|
-
return;
|
|
160
|
+
if (FRAMEWORK_PROP_TYPES.has(propsTypeName)) return;
|
|
161
|
+
if (contractImportedTypes.has(propsTypeName)) return;
|
|
169
162
|
const prefix = `[${contractName}]`;
|
|
170
163
|
const iface = localInterfaces.get(propsTypeName);
|
|
171
164
|
if (!iface) {
|
|
@@ -180,8 +173,7 @@ function checkPropsConsistency(propsTypeName, localInterfaces, contractImportedT
|
|
|
180
173
|
return;
|
|
181
174
|
}
|
|
182
175
|
const ownProperties = iface.properties;
|
|
183
|
-
if (ownProperties.length === 0)
|
|
184
|
-
return;
|
|
176
|
+
if (ownProperties.length === 0) return;
|
|
185
177
|
if (!contract.props || contract.props.length === 0) {
|
|
186
178
|
errors.push({
|
|
187
179
|
type: "contract-invalid",
|
|
@@ -225,10 +217,8 @@ function checkParamsConsistency(paramsTypeNames, localInterfaces, contractImport
|
|
|
225
217
|
suggestion: `Add a params section to the contract (e.g., params: { slug: string })`
|
|
226
218
|
});
|
|
227
219
|
for (const typeName of paramsTypeNames) {
|
|
228
|
-
if (FRAMEWORK_PROP_TYPES.has(typeName))
|
|
229
|
-
|
|
230
|
-
if (contractImportedTypes.has(typeName))
|
|
231
|
-
continue;
|
|
220
|
+
if (FRAMEWORK_PROP_TYPES.has(typeName)) continue;
|
|
221
|
+
if (contractImportedTypes.has(typeName)) continue;
|
|
232
222
|
const iface = localInterfaces.get(typeName);
|
|
233
223
|
if (iface) {
|
|
234
224
|
const ownProps = iface.properties;
|
|
@@ -241,13 +231,10 @@ function checkParamsConsistency(paramsTypeNames, localInterfaces, contractImport
|
|
|
241
231
|
return;
|
|
242
232
|
}
|
|
243
233
|
for (const typeName of paramsTypeNames) {
|
|
244
|
-
if (FRAMEWORK_PROP_TYPES.has(typeName))
|
|
245
|
-
|
|
246
|
-
if (contractImportedTypes.has(typeName))
|
|
247
|
-
continue;
|
|
234
|
+
if (FRAMEWORK_PROP_TYPES.has(typeName)) continue;
|
|
235
|
+
if (contractImportedTypes.has(typeName)) continue;
|
|
248
236
|
const iface = localInterfaces.get(typeName);
|
|
249
|
-
if (!iface)
|
|
250
|
-
continue;
|
|
237
|
+
if (!iface) continue;
|
|
251
238
|
const ownProperties = iface.properties;
|
|
252
239
|
const contractParamNames = new Set(contract.params.map((p) => p.name));
|
|
253
240
|
for (const prop of ownProperties) {
|
|
@@ -279,7 +266,7 @@ const FOLDER_PATH_MAX_SEGMENTS = 32;
|
|
|
279
266
|
const BLOCKED_TAGS = /<\s*(script|iframe|object|embed)\b[^>]*>[\s\S]*?<\/\s*\1\s*>|<\s*(script|iframe|object|embed)\b[^>]*\/?>/gi;
|
|
280
267
|
const EVENT_HANDLER_ATTR = /\s+on[a-z]+\s*=\s*("[^"]*"|'[^']*'|[^\s>]+)/gi;
|
|
281
268
|
const JAVASCRIPT_URL = /\b(href|src|xlink:href)\s*=\s*("|')\s*javascript:/gi;
|
|
282
|
-
function isRecord(value) {
|
|
269
|
+
function isRecord$1(value) {
|
|
283
270
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
284
271
|
}
|
|
285
272
|
function byteLengthUtf8(value) {
|
|
@@ -339,17 +326,14 @@ function requiredString(obj, field, itemPath, errors, code) {
|
|
|
339
326
|
}
|
|
340
327
|
function optionalString(obj, field) {
|
|
341
328
|
const value = obj[field];
|
|
342
|
-
if (value === void 0)
|
|
343
|
-
|
|
344
|
-
if (typeof value !== "string")
|
|
345
|
-
return void 0;
|
|
329
|
+
if (value === void 0) return void 0;
|
|
330
|
+
if (typeof value !== "string") return void 0;
|
|
346
331
|
const trimmed = value.trim();
|
|
347
332
|
return trimmed.length > 0 ? trimmed : void 0;
|
|
348
333
|
}
|
|
349
334
|
function validateInteraction(raw, itemPath, errors) {
|
|
350
|
-
if (raw === void 0)
|
|
351
|
-
|
|
352
|
-
if (!isRecord(raw)) {
|
|
335
|
+
if (raw === void 0) return void 0;
|
|
336
|
+
if (!isRecord$1(raw)) {
|
|
353
337
|
errors.push({
|
|
354
338
|
path: itemPath,
|
|
355
339
|
message: "interaction must be an object",
|
|
@@ -372,9 +356,8 @@ function validateInteraction(raw, itemPath, errors) {
|
|
|
372
356
|
};
|
|
373
357
|
}
|
|
374
358
|
function validatePresentation(raw, itemPath, errors) {
|
|
375
|
-
if (raw === void 0)
|
|
376
|
-
|
|
377
|
-
if (!isRecord(raw)) {
|
|
359
|
+
if (raw === void 0) return void 0;
|
|
360
|
+
if (!isRecord$1(raw)) {
|
|
378
361
|
errors.push({
|
|
379
362
|
path: itemPath,
|
|
380
363
|
message: "presentation must be an object",
|
|
@@ -393,14 +376,12 @@ function validatePresentation(raw, itemPath, errors) {
|
|
|
393
376
|
}
|
|
394
377
|
if (type === "image") {
|
|
395
378
|
const src = requiredString(raw, "src", itemPath, errors, "presentation-missing-src");
|
|
396
|
-
if (!src)
|
|
397
|
-
return void 0;
|
|
379
|
+
if (!src) return void 0;
|
|
398
380
|
return { type: "image", src };
|
|
399
381
|
}
|
|
400
382
|
if (type === "gif") {
|
|
401
383
|
const src = requiredString(raw, "src", itemPath, errors, "presentation-missing-src");
|
|
402
|
-
if (!src)
|
|
403
|
-
return void 0;
|
|
384
|
+
if (!src) return void 0;
|
|
404
385
|
return {
|
|
405
386
|
type: "gif",
|
|
406
387
|
src,
|
|
@@ -445,9 +426,8 @@ function validatePresentation(raw, itemPath, errors) {
|
|
|
445
426
|
}
|
|
446
427
|
const BROWSE_SIZES = /* @__PURE__ */ new Set(["large", "medium", "small"]);
|
|
447
428
|
function validateBrowse(raw, itemPath, errors) {
|
|
448
|
-
if (raw === void 0)
|
|
449
|
-
|
|
450
|
-
if (!isRecord(raw)) {
|
|
429
|
+
if (raw === void 0) return void 0;
|
|
430
|
+
if (!isRecord$1(raw)) {
|
|
451
431
|
errors.push({
|
|
452
432
|
path: itemPath,
|
|
453
433
|
message: "browse must be an object",
|
|
@@ -470,8 +450,7 @@ function validateBrowse(raw, itemPath, errors) {
|
|
|
470
450
|
return { size: sizeRaw };
|
|
471
451
|
}
|
|
472
452
|
function validateFolderPath(raw, itemPath, errors) {
|
|
473
|
-
if (raw === void 0)
|
|
474
|
-
return void 0;
|
|
453
|
+
if (raw === void 0) return void 0;
|
|
475
454
|
if (!Array.isArray(raw)) {
|
|
476
455
|
errors.push({
|
|
477
456
|
path: `${itemPath}.folderPath`,
|
|
@@ -530,7 +509,7 @@ function validateFolderPath(raw, itemPath, errors) {
|
|
|
530
509
|
}
|
|
531
510
|
function validateAddMenuItem(raw, itemPath) {
|
|
532
511
|
const errors = [];
|
|
533
|
-
if (!isRecord(raw)) {
|
|
512
|
+
if (!isRecord$1(raw)) {
|
|
534
513
|
return {
|
|
535
514
|
item: null,
|
|
536
515
|
errors: [
|
|
@@ -581,7 +560,7 @@ function validateAddMenuItem(raw, itemPath) {
|
|
|
581
560
|
}
|
|
582
561
|
function validateAddMenuCatalogFile(raw, sourcePath) {
|
|
583
562
|
const errors = [];
|
|
584
|
-
if (!isRecord(raw)) {
|
|
563
|
+
if (!isRecord$1(raw)) {
|
|
585
564
|
return {
|
|
586
565
|
file: null,
|
|
587
566
|
errors: [
|
|
@@ -609,8 +588,7 @@ function validateAddMenuCatalogFile(raw, sourcePath) {
|
|
|
609
588
|
raw.items.forEach((entry, index) => {
|
|
610
589
|
const result = validateAddMenuItem(entry, `${sourcePath}.items[${index}]`);
|
|
611
590
|
errors.push(...result.errors);
|
|
612
|
-
if (result.item)
|
|
613
|
-
items.push(result.item);
|
|
591
|
+
if (result.item) items.push(result.item);
|
|
614
592
|
});
|
|
615
593
|
if (items.length === 0 && errors.length > 0) {
|
|
616
594
|
return { file: null, errors };
|
|
@@ -618,11 +596,9 @@ function validateAddMenuCatalogFile(raw, sourcePath) {
|
|
|
618
596
|
return { file: { items }, errors };
|
|
619
597
|
}
|
|
620
598
|
function normalizeAddMenuPresentation(item) {
|
|
621
|
-
if (item.presentation)
|
|
622
|
-
return item.presentation;
|
|
599
|
+
if (item.presentation) return item.presentation;
|
|
623
600
|
const thumbnail = item.thumbnail?.trim();
|
|
624
|
-
if (!thumbnail)
|
|
625
|
-
return void 0;
|
|
601
|
+
if (!thumbnail) return void 0;
|
|
626
602
|
if (/\.gif$/i.test(thumbnail)) {
|
|
627
603
|
return { type: "gif", src: thumbnail };
|
|
628
604
|
}
|
|
@@ -633,15 +609,12 @@ function normalizeAddMenuBrowseSize(item) {
|
|
|
633
609
|
}
|
|
634
610
|
function hasSingleRootDiv(html) {
|
|
635
611
|
const trimmed = html.trim();
|
|
636
|
-
if (!trimmed.startsWith("<div"))
|
|
637
|
-
return false;
|
|
612
|
+
if (!trimmed.startsWith("<div")) return false;
|
|
638
613
|
const openMatch = trimmed.match(/^<div\b[^>]*>/i);
|
|
639
|
-
if (!openMatch)
|
|
640
|
-
return false;
|
|
614
|
+
if (!openMatch) return false;
|
|
641
615
|
const afterOpen = trimmed.slice(openMatch[0].length);
|
|
642
616
|
const closeIdx = afterOpen.lastIndexOf("</div>");
|
|
643
|
-
if (closeIdx < 0)
|
|
644
|
-
return false;
|
|
617
|
+
if (closeIdx < 0) return false;
|
|
645
618
|
const tail = afterOpen.slice(closeIdx + "</div>".length).trim();
|
|
646
619
|
return tail.length === 0;
|
|
647
620
|
}
|
|
@@ -717,8 +690,7 @@ function lintHtmlFragment(item, sourcePath) {
|
|
|
717
690
|
}
|
|
718
691
|
function lintGifPoster(item, sourcePath) {
|
|
719
692
|
const presentation = normalizeAddMenuPresentation(item);
|
|
720
|
-
if (presentation?.type !== "gif" || presentation.poster?.trim())
|
|
721
|
-
return [];
|
|
693
|
+
if (presentation?.type !== "gif" || presentation.poster?.trim()) return [];
|
|
722
694
|
return [
|
|
723
695
|
catalogWarning(
|
|
724
696
|
"gif-missing-poster",
|
|
@@ -729,10 +701,8 @@ function lintGifPoster(item, sourcePath) {
|
|
|
729
701
|
];
|
|
730
702
|
}
|
|
731
703
|
function lintBrowseLargeWithoutPresentation(item, sourcePath) {
|
|
732
|
-
if (normalizeAddMenuBrowseSize(item) !== "large")
|
|
733
|
-
|
|
734
|
-
if (normalizeAddMenuPresentation(item))
|
|
735
|
-
return [];
|
|
704
|
+
if (normalizeAddMenuBrowseSize(item) !== "large") return [];
|
|
705
|
+
if (normalizeAddMenuPresentation(item)) return [];
|
|
736
706
|
return [
|
|
737
707
|
catalogWarning(
|
|
738
708
|
"browse-large-without-presentation",
|
|
@@ -805,8 +775,7 @@ function resolveModulePath$1(basePath) {
|
|
|
805
775
|
return void 0;
|
|
806
776
|
}
|
|
807
777
|
function collectTypeScriptFiles(dir, depth = 0) {
|
|
808
|
-
if (depth > 4)
|
|
809
|
-
return [];
|
|
778
|
+
if (depth > 4) return [];
|
|
810
779
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
811
780
|
const files = [];
|
|
812
781
|
for (const entry of entries) {
|
|
@@ -825,8 +794,7 @@ function resolveHandlerSourceFile(pluginPath, handlerRef, isNpmPackage) {
|
|
|
825
794
|
}
|
|
826
795
|
const searchRoots = isNpmPackage ? [path.join(pluginPath, "lib"), path.join(pluginPath, "dist")] : [pluginPath];
|
|
827
796
|
for (const root of searchRoots) {
|
|
828
|
-
if (!fs.existsSync(root))
|
|
829
|
-
continue;
|
|
797
|
+
if (!fs.existsSync(root)) continue;
|
|
830
798
|
for (const file of collectTypeScriptFiles(root)) {
|
|
831
799
|
const content = fs.readFileSync(file, "utf-8");
|
|
832
800
|
const definesHandler = new RegExp(
|
|
@@ -846,8 +814,7 @@ function resolveHandlerSourceFile(pluginPath, handlerRef, isNpmPackage) {
|
|
|
846
814
|
if (reExportMatch) {
|
|
847
815
|
const importSpec = reExportMatch[1].replace(/\.js$/, "");
|
|
848
816
|
const resolved = resolveModulePath$1(path.resolve(path.dirname(file), importSpec));
|
|
849
|
-
if (resolved)
|
|
850
|
-
return resolved;
|
|
817
|
+
if (resolved) return resolved;
|
|
851
818
|
}
|
|
852
819
|
}
|
|
853
820
|
}
|
|
@@ -857,8 +824,7 @@ function extractBalancedBlock(source, openBraceIndex) {
|
|
|
857
824
|
let depth = 0;
|
|
858
825
|
for (let index = openBraceIndex; index < source.length; index++) {
|
|
859
826
|
const char = source[index];
|
|
860
|
-
if (char === "{")
|
|
861
|
-
depth++;
|
|
827
|
+
if (char === "{") depth++;
|
|
862
828
|
else if (char === "}") {
|
|
863
829
|
depth--;
|
|
864
830
|
if (depth === 0) {
|
|
@@ -878,8 +844,7 @@ function findFunctionBodyOpenBrace(source, searchFrom) {
|
|
|
878
844
|
if (parenMatch?.index !== void 0) {
|
|
879
845
|
candidates.push(searchFrom + parenMatch.index + parenMatch[0].length - 1);
|
|
880
846
|
}
|
|
881
|
-
if (candidates.length === 0)
|
|
882
|
-
return -1;
|
|
847
|
+
if (candidates.length === 0) return -1;
|
|
883
848
|
return Math.min(...candidates);
|
|
884
849
|
}
|
|
885
850
|
function extractFunctionBody(source, functionName) {
|
|
@@ -896,11 +861,9 @@ function extractFunctionBody(source, functionName) {
|
|
|
896
861
|
];
|
|
897
862
|
for (const pattern of patterns) {
|
|
898
863
|
const match = pattern.exec(source);
|
|
899
|
-
if (!match)
|
|
900
|
-
continue;
|
|
864
|
+
if (!match) continue;
|
|
901
865
|
const braceIndex = findFunctionBodyOpenBrace(source, match.index);
|
|
902
|
-
if (braceIndex === -1)
|
|
903
|
-
continue;
|
|
866
|
+
if (braceIndex === -1) continue;
|
|
904
867
|
return extractBalancedBlock(source, braceIndex);
|
|
905
868
|
}
|
|
906
869
|
return null;
|
|
@@ -913,11 +876,9 @@ function extractDefaultExportFunctionBody(source) {
|
|
|
913
876
|
];
|
|
914
877
|
for (const pattern of patterns) {
|
|
915
878
|
const match = pattern.exec(source);
|
|
916
|
-
if (!match)
|
|
917
|
-
continue;
|
|
879
|
+
if (!match) continue;
|
|
918
880
|
const braceIndex = findFunctionBodyOpenBrace(source, match.index);
|
|
919
|
-
if (braceIndex === -1)
|
|
920
|
-
continue;
|
|
881
|
+
if (braceIndex === -1) continue;
|
|
921
882
|
return extractBalancedBlock(source, braceIndex);
|
|
922
883
|
}
|
|
923
884
|
return null;
|
|
@@ -927,8 +888,7 @@ function handlerBodyWritesAddMenuCatalog(body) {
|
|
|
927
888
|
}
|
|
928
889
|
function resolveSetupHandlerFunctionBody(pluginPath, handlerRef, isNpmPackage) {
|
|
929
890
|
const sourceFile = resolveHandlerSourceFile(pluginPath, handlerRef, isNpmPackage);
|
|
930
|
-
if (!sourceFile)
|
|
931
|
-
return null;
|
|
891
|
+
if (!sourceFile) return null;
|
|
932
892
|
const source = fs.readFileSync(sourceFile, "utf-8");
|
|
933
893
|
if (isRelativeHandlerRef(handlerRef)) {
|
|
934
894
|
return extractDefaultExportFunctionBody(source) ?? extractFunctionBody(source, "setup") ?? extractFunctionBody(source, handlerRef);
|
|
@@ -936,11 +896,10 @@ function resolveSetupHandlerFunctionBody(pluginPath, handlerRef, isNpmPackage) {
|
|
|
936
896
|
return extractFunctionBody(source, handlerRef);
|
|
937
897
|
}
|
|
938
898
|
function suggestionForCode(code) {
|
|
939
|
-
if (!code)
|
|
940
|
-
return `See ${CONTRIBUTOR_GUIDE} for schema and validation rules`;
|
|
899
|
+
if (!code) return `See ${CONTRIBUTOR_GUIDE} for schema and validation rules`;
|
|
941
900
|
return ADD_MENU_VALIDATION_SUGGESTIONS[code] ?? `See ${CONTRIBUTOR_GUIDE} for schema and validation rules`;
|
|
942
901
|
}
|
|
943
|
-
function mapSchemaError(error, catalogPath) {
|
|
902
|
+
function mapSchemaError$1(error, catalogPath) {
|
|
944
903
|
const code = error.code ?? "catalog-validation-error";
|
|
945
904
|
return {
|
|
946
905
|
type: "add-menu-catalog",
|
|
@@ -967,8 +926,7 @@ function pluginShipsAddMenuCatalog(context) {
|
|
|
967
926
|
);
|
|
968
927
|
}
|
|
969
928
|
function validateAddMenuAgentKitHandler(context, result) {
|
|
970
|
-
if (!pluginShipsAddMenuCatalog(context))
|
|
971
|
-
return;
|
|
929
|
+
if (!pluginShipsAddMenuCatalog(context)) return;
|
|
972
930
|
const agentKitHandler = context.manifest.agentkit;
|
|
973
931
|
if (!agentKitHandler) {
|
|
974
932
|
result.warnings.push({
|
|
@@ -980,15 +938,13 @@ function validateAddMenuAgentKitHandler(context, result) {
|
|
|
980
938
|
});
|
|
981
939
|
}
|
|
982
940
|
const setupHandler = typeof context.manifest.setup === "string" ? context.manifest.setup : void 0;
|
|
983
|
-
if (!setupHandler)
|
|
984
|
-
return;
|
|
941
|
+
if (!setupHandler) return;
|
|
985
942
|
const setupBody = resolveSetupHandlerFunctionBody(
|
|
986
943
|
context.pluginPath,
|
|
987
944
|
setupHandler,
|
|
988
945
|
context.isNpmPackage
|
|
989
946
|
);
|
|
990
|
-
if (!setupBody || !handlerBodyWritesAddMenuCatalog(setupBody))
|
|
991
|
-
return;
|
|
947
|
+
if (!setupBody || !handlerBodyWritesAddMenuCatalog(setupBody)) return;
|
|
992
948
|
result.warnings.push({
|
|
993
949
|
type: "add-menu-catalog",
|
|
994
950
|
code: "add-menu-legacy-setup-handler",
|
|
@@ -1014,7 +970,7 @@ async function validateAddMenuCatalogFileAtPath(catalogPath, relPath, result) {
|
|
|
1014
970
|
return;
|
|
1015
971
|
}
|
|
1016
972
|
const validated = validateAddMenuCatalogFile(parsed, relPath);
|
|
1017
|
-
result.errors.push(...validated.errors.map((error) => mapSchemaError(error, relPath)));
|
|
973
|
+
result.errors.push(...validated.errors.map((error) => mapSchemaError$1(error, relPath)));
|
|
1018
974
|
if (!validated.file?.items.length) {
|
|
1019
975
|
return;
|
|
1020
976
|
}
|
|
@@ -1030,11 +986,177 @@ async function validateAddMenuCatalog(context, result) {
|
|
|
1030
986
|
validateAddMenuAgentKitHandler(context, result);
|
|
1031
987
|
for (const relPath of ADD_MENU_CATALOG_REL_PATHS) {
|
|
1032
988
|
const catalogPath = path.join(context.pluginPath, relPath);
|
|
1033
|
-
if (!fs.existsSync(catalogPath))
|
|
1034
|
-
continue;
|
|
989
|
+
if (!fs.existsSync(catalogPath)) continue;
|
|
1035
990
|
await validateAddMenuCatalogFileAtPath(catalogPath, relPath, result);
|
|
1036
991
|
}
|
|
1037
992
|
}
|
|
993
|
+
function isRecord(value) {
|
|
994
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
995
|
+
}
|
|
996
|
+
function parseRequires(raw, filePath) {
|
|
997
|
+
const errors = [];
|
|
998
|
+
if (raw === void 0) {
|
|
999
|
+
return { requires: [], errors };
|
|
1000
|
+
}
|
|
1001
|
+
if (!Array.isArray(raw)) {
|
|
1002
|
+
errors.push({
|
|
1003
|
+
path: `${filePath}.requires`,
|
|
1004
|
+
message: "requires must be an array",
|
|
1005
|
+
code: "settings-requires-type"
|
|
1006
|
+
});
|
|
1007
|
+
return { requires: [], errors };
|
|
1008
|
+
}
|
|
1009
|
+
const requires = [];
|
|
1010
|
+
raw.forEach((entry, index) => {
|
|
1011
|
+
if (!isRecord(entry)) {
|
|
1012
|
+
errors.push({
|
|
1013
|
+
path: `${filePath}.requires[${index}]`,
|
|
1014
|
+
message: "require entry must be an object",
|
|
1015
|
+
code: "settings-require-shape"
|
|
1016
|
+
});
|
|
1017
|
+
return;
|
|
1018
|
+
}
|
|
1019
|
+
if (typeof entry.plugin !== "string" || !entry.plugin.trim()) {
|
|
1020
|
+
errors.push({
|
|
1021
|
+
path: `${filePath}.requires[${index}].plugin`,
|
|
1022
|
+
message: "plugin is required",
|
|
1023
|
+
code: "settings-require-plugin"
|
|
1024
|
+
});
|
|
1025
|
+
return;
|
|
1026
|
+
}
|
|
1027
|
+
if (entry.status !== "configured") {
|
|
1028
|
+
errors.push({
|
|
1029
|
+
path: `${filePath}.requires[${index}].status`,
|
|
1030
|
+
message: 'status must be "configured"',
|
|
1031
|
+
code: "settings-require-status"
|
|
1032
|
+
});
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
requires.push({ plugin: entry.plugin.trim(), status: "configured" });
|
|
1036
|
+
});
|
|
1037
|
+
return { requires, errors };
|
|
1038
|
+
}
|
|
1039
|
+
function validateAiditorSettingsFile(raw, sourcePath) {
|
|
1040
|
+
const errors = [];
|
|
1041
|
+
if (!isRecord(raw)) {
|
|
1042
|
+
return {
|
|
1043
|
+
errors: [
|
|
1044
|
+
{
|
|
1045
|
+
path: sourcePath,
|
|
1046
|
+
message: "settings file must be a YAML object",
|
|
1047
|
+
code: "settings-root-type"
|
|
1048
|
+
}
|
|
1049
|
+
]
|
|
1050
|
+
};
|
|
1051
|
+
}
|
|
1052
|
+
if (typeof raw.label !== "string" || !raw.label.trim()) {
|
|
1053
|
+
errors.push({
|
|
1054
|
+
path: `${sourcePath}.label`,
|
|
1055
|
+
message: "label is required",
|
|
1056
|
+
code: "settings-label"
|
|
1057
|
+
});
|
|
1058
|
+
}
|
|
1059
|
+
if (typeof raw.route !== "string" || !raw.route.trim()) {
|
|
1060
|
+
errors.push({
|
|
1061
|
+
path: `${sourcePath}.route`,
|
|
1062
|
+
message: "route is required",
|
|
1063
|
+
code: "settings-route"
|
|
1064
|
+
});
|
|
1065
|
+
} else if (!raw.route.startsWith("/")) {
|
|
1066
|
+
errors.push({
|
|
1067
|
+
path: `${sourcePath}.route`,
|
|
1068
|
+
message: "route must start with /",
|
|
1069
|
+
code: "settings-route-format"
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1072
|
+
if (raw.pluginName !== void 0 && typeof raw.pluginName !== "string") {
|
|
1073
|
+
errors.push({
|
|
1074
|
+
path: `${sourcePath}.pluginName`,
|
|
1075
|
+
message: "pluginName must be a string",
|
|
1076
|
+
code: "settings-plugin-name-type"
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
const { requires, errors: requireErrors } = parseRequires(raw.requires, sourcePath);
|
|
1080
|
+
errors.push(...requireErrors);
|
|
1081
|
+
if (errors.length > 0) {
|
|
1082
|
+
return { errors };
|
|
1083
|
+
}
|
|
1084
|
+
return {
|
|
1085
|
+
file: {
|
|
1086
|
+
label: raw.label.trim(),
|
|
1087
|
+
route: raw.route.trim(),
|
|
1088
|
+
...typeof raw.pluginName === "string" && raw.pluginName.trim() ? { pluginName: raw.pluginName.trim() } : {},
|
|
1089
|
+
...requires.length > 0 ? { requires } : {}
|
|
1090
|
+
},
|
|
1091
|
+
errors: []
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1094
|
+
const AIDITOR_SETTINGS_TEMPLATE_REL_PATH = "agent-kit/aiditor/settings.template.yaml";
|
|
1095
|
+
function mapSchemaError(error, relPath) {
|
|
1096
|
+
return {
|
|
1097
|
+
type: "schema",
|
|
1098
|
+
message: error.message,
|
|
1099
|
+
location: error.path || relPath,
|
|
1100
|
+
code: error.code
|
|
1101
|
+
};
|
|
1102
|
+
}
|
|
1103
|
+
function validateSettingsTemplateAtPath(catalogPath, relPath, result, manifest) {
|
|
1104
|
+
let parsed;
|
|
1105
|
+
try {
|
|
1106
|
+
parsed = YAML.parse(fs.readFileSync(catalogPath, "utf-8"));
|
|
1107
|
+
} catch (err) {
|
|
1108
|
+
result.errors.push({
|
|
1109
|
+
type: "schema",
|
|
1110
|
+
message: `Invalid YAML in ${relPath}: ${err instanceof Error ? err.message : String(err)}`,
|
|
1111
|
+
location: relPath
|
|
1112
|
+
});
|
|
1113
|
+
return;
|
|
1114
|
+
}
|
|
1115
|
+
const validated = validateAiditorSettingsFile(parsed, relPath);
|
|
1116
|
+
result.errors.push(...validated.errors.map((error) => mapSchemaError(error, relPath)));
|
|
1117
|
+
if (!validated.file) {
|
|
1118
|
+
return;
|
|
1119
|
+
}
|
|
1120
|
+
const routeEntry = manifest.routes?.find((route) => route.path === validated.file.route);
|
|
1121
|
+
if (!routeEntry) {
|
|
1122
|
+
result.warnings.push({
|
|
1123
|
+
type: "schema",
|
|
1124
|
+
message: `settings route "${validated.file.route}" is not declared in plugin.yaml routes[]`,
|
|
1125
|
+
location: relPath,
|
|
1126
|
+
code: "settings-route-missing",
|
|
1127
|
+
suggestion: "Add a matching routes[] entry or fix the route in settings.template.yaml"
|
|
1128
|
+
});
|
|
1129
|
+
} else if (routeEntry.devOnly !== true) {
|
|
1130
|
+
result.warnings.push({
|
|
1131
|
+
type: "schema",
|
|
1132
|
+
message: `settings route "${validated.file.route}" should declare devOnly: true on routes[]`,
|
|
1133
|
+
location: "plugin.yaml routes",
|
|
1134
|
+
code: "settings-route-dev-only",
|
|
1135
|
+
suggestion: "Add devOnly: true when the settings page is dev-server tooling (see Design Log #171)"
|
|
1136
|
+
});
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
async function validateAiditorSettings(context, result) {
|
|
1140
|
+
const templatePath = path.join(context.pluginPath, AIDITOR_SETTINGS_TEMPLATE_REL_PATH);
|
|
1141
|
+
if (!fs.existsSync(templatePath)) {
|
|
1142
|
+
return;
|
|
1143
|
+
}
|
|
1144
|
+
validateSettingsTemplateAtPath(
|
|
1145
|
+
templatePath,
|
|
1146
|
+
AIDITOR_SETTINGS_TEMPLATE_REL_PATH,
|
|
1147
|
+
result,
|
|
1148
|
+
context.manifest
|
|
1149
|
+
);
|
|
1150
|
+
if (!context.manifest.agentkit) {
|
|
1151
|
+
result.warnings.push({
|
|
1152
|
+
type: "schema",
|
|
1153
|
+
message: "Plugin ships agent-kit/aiditor/settings.template.yaml but has no agentkit handler",
|
|
1154
|
+
location: "plugin.yaml",
|
|
1155
|
+
code: "settings-missing-agentkit-handler",
|
|
1156
|
+
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"
|
|
1157
|
+
});
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1038
1160
|
async function validatePlugin(options = {}) {
|
|
1039
1161
|
const pluginPath = options.pluginPath || process.cwd();
|
|
1040
1162
|
if (options.local) {
|
|
@@ -1102,6 +1224,7 @@ async function validatePluginPackage(pluginPath, options) {
|
|
|
1102
1224
|
await validateDynamicContracts(context, result);
|
|
1103
1225
|
}
|
|
1104
1226
|
await validateAddMenuCatalog(context, result);
|
|
1227
|
+
await validateAiditorSettings(context, result);
|
|
1105
1228
|
result.valid = result.errors.length === 0;
|
|
1106
1229
|
return result;
|
|
1107
1230
|
}
|
|
@@ -1411,6 +1534,13 @@ async function validateSchema(context, result) {
|
|
|
1411
1534
|
if (route.css) {
|
|
1412
1535
|
validateDocFile(route.css, `route "${route.path}" css`, context, result);
|
|
1413
1536
|
}
|
|
1537
|
+
if (route.devOnly !== void 0 && typeof route.devOnly !== "boolean") {
|
|
1538
|
+
result.errors.push({
|
|
1539
|
+
type: "schema",
|
|
1540
|
+
message: `Route "${route.path}" devOnly must be a boolean`,
|
|
1541
|
+
location: "plugin.yaml"
|
|
1542
|
+
});
|
|
1543
|
+
}
|
|
1414
1544
|
});
|
|
1415
1545
|
}
|
|
1416
1546
|
}
|
|
@@ -1480,16 +1610,14 @@ async function validateSchema(context, result) {
|
|
|
1480
1610
|
}
|
|
1481
1611
|
function checkExportExists(exportName, context) {
|
|
1482
1612
|
const packageJsonPath = path.join(context.pluginPath, "package.json");
|
|
1483
|
-
if (!fs.existsSync(packageJsonPath))
|
|
1484
|
-
return true;
|
|
1613
|
+
if (!fs.existsSync(packageJsonPath)) return true;
|
|
1485
1614
|
let mainPath;
|
|
1486
1615
|
try {
|
|
1487
1616
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
1488
1617
|
if (packageJson.exports?.["."]) {
|
|
1489
1618
|
const entry = packageJson.exports["."];
|
|
1490
1619
|
const entryPath = typeof entry === "string" ? entry : entry.default || entry.import;
|
|
1491
|
-
if (entryPath)
|
|
1492
|
-
mainPath = path.join(context.pluginPath, entryPath);
|
|
1620
|
+
if (entryPath) mainPath = path.join(context.pluginPath, entryPath);
|
|
1493
1621
|
}
|
|
1494
1622
|
if (!mainPath && packageJson.main) {
|
|
1495
1623
|
mainPath = path.join(context.pluginPath, packageJson.main);
|
|
@@ -1497,8 +1625,7 @@ function checkExportExists(exportName, context) {
|
|
|
1497
1625
|
} catch {
|
|
1498
1626
|
return true;
|
|
1499
1627
|
}
|
|
1500
|
-
if (!mainPath || !fs.existsSync(mainPath))
|
|
1501
|
-
return true;
|
|
1628
|
+
if (!mainPath || !fs.existsSync(mainPath)) return true;
|
|
1502
1629
|
try {
|
|
1503
1630
|
const content = fs.readFileSync(mainPath, "utf-8");
|
|
1504
1631
|
const patterns = [
|
|
@@ -1558,8 +1685,7 @@ function resolveContractFile(contractSpec, context) {
|
|
|
1558
1685
|
const resolvedPath = typeof exportValue === "string" ? exportValue : exportValue.default || exportValue.import || exportValue.require;
|
|
1559
1686
|
if (resolvedPath) {
|
|
1560
1687
|
const fullPath = path.join(context.pluginPath, resolvedPath);
|
|
1561
|
-
if (fs.existsSync(fullPath))
|
|
1562
|
-
return fullPath;
|
|
1688
|
+
if (fs.existsSync(fullPath)) return fullPath;
|
|
1563
1689
|
}
|
|
1564
1690
|
}
|
|
1565
1691
|
}
|
|
@@ -1568,8 +1694,7 @@ function resolveContractFile(contractSpec, context) {
|
|
|
1568
1694
|
}
|
|
1569
1695
|
for (const dir of ["dist", "lib", ""]) {
|
|
1570
1696
|
const candidate = path.join(context.pluginPath, dir, contractSpec);
|
|
1571
|
-
if (fs.existsSync(candidate))
|
|
1572
|
-
return candidate;
|
|
1697
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
1573
1698
|
}
|
|
1574
1699
|
return void 0;
|
|
1575
1700
|
} else {
|
|
@@ -1657,8 +1782,7 @@ function hasExportModifier(node) {
|
|
|
1657
1782
|
function resolveModulePath(basePath) {
|
|
1658
1783
|
for (const ext of ["", ".ts", ".js", "/index.ts", "/index.js"]) {
|
|
1659
1784
|
const candidate = basePath + ext;
|
|
1660
|
-
if (fs.existsSync(candidate))
|
|
1661
|
-
return candidate;
|
|
1785
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
1662
1786
|
}
|
|
1663
1787
|
return void 0;
|
|
1664
1788
|
}
|
|
@@ -1668,10 +1792,8 @@ function resolveComponentSourcePath(componentName, context) {
|
|
|
1668
1792
|
const entryFile = resolveModulePath(entryBase);
|
|
1669
1793
|
const libEntryFile = !entryFile ? resolveModulePath(path.join(context.pluginPath, "lib", modulePath)) : void 0;
|
|
1670
1794
|
const sourceEntry = entryFile || libEntryFile;
|
|
1671
|
-
if (!sourceEntry)
|
|
1672
|
-
|
|
1673
|
-
if (!sourceEntry.endsWith(".ts"))
|
|
1674
|
-
return void 0;
|
|
1795
|
+
if (!sourceEntry) return void 0;
|
|
1796
|
+
if (!sourceEntry.endsWith(".ts")) return void 0;
|
|
1675
1797
|
let sourceCode;
|
|
1676
1798
|
try {
|
|
1677
1799
|
sourceCode = fs.readFileSync(sourceEntry, "utf-8");
|
|
@@ -1687,12 +1809,9 @@ function resolveComponentSourcePath(componentName, context) {
|
|
|
1687
1809
|
);
|
|
1688
1810
|
const starReexportModules = [];
|
|
1689
1811
|
for (const statement of sourceFile.statements) {
|
|
1690
|
-
if (!ts.isExportDeclaration(statement))
|
|
1691
|
-
|
|
1692
|
-
if (!statement.moduleSpecifier)
|
|
1693
|
-
continue;
|
|
1694
|
-
if (!ts.isStringLiteral(statement.moduleSpecifier))
|
|
1695
|
-
continue;
|
|
1812
|
+
if (!ts.isExportDeclaration(statement)) continue;
|
|
1813
|
+
if (!statement.moduleSpecifier) continue;
|
|
1814
|
+
if (!ts.isStringLiteral(statement.moduleSpecifier)) continue;
|
|
1696
1815
|
const moduleSpec = statement.moduleSpecifier.text;
|
|
1697
1816
|
const exportClause = statement.exportClause;
|
|
1698
1817
|
if (!exportClause) {
|
|
@@ -1710,12 +1829,10 @@ function resolveComponentSourcePath(componentName, context) {
|
|
|
1710
1829
|
}
|
|
1711
1830
|
}
|
|
1712
1831
|
for (const moduleSpec of starReexportModules) {
|
|
1713
|
-
if (!moduleSpec.startsWith("."))
|
|
1714
|
-
continue;
|
|
1832
|
+
if (!moduleSpec.startsWith(".")) continue;
|
|
1715
1833
|
const resolvedBase = path.resolve(path.dirname(sourceEntry), moduleSpec);
|
|
1716
1834
|
const resolvedPath = resolveModulePath(resolvedBase);
|
|
1717
|
-
if (!resolvedPath || !resolvedPath.endsWith(".ts"))
|
|
1718
|
-
continue;
|
|
1835
|
+
if (!resolvedPath || !resolvedPath.endsWith(".ts")) continue;
|
|
1719
1836
|
try {
|
|
1720
1837
|
const modSource = fs.readFileSync(resolvedPath, "utf-8");
|
|
1721
1838
|
const modFile = ts.createSourceFile(
|
|
@@ -1748,16 +1865,12 @@ function resolveContractPath(contract, context) {
|
|
|
1748
1865
|
}
|
|
1749
1866
|
async function checkComponentContractConsistency(contract, context, result) {
|
|
1750
1867
|
const componentName = contract.component;
|
|
1751
|
-
if (!componentName)
|
|
1752
|
-
return;
|
|
1868
|
+
if (!componentName) return;
|
|
1753
1869
|
const sourcePath = resolveComponentSourcePath(componentName, context);
|
|
1754
|
-
if (!sourcePath)
|
|
1755
|
-
|
|
1756
|
-
if (!sourcePath.endsWith(".ts"))
|
|
1757
|
-
return;
|
|
1870
|
+
if (!sourcePath) return;
|
|
1871
|
+
if (!sourcePath.endsWith(".ts")) return;
|
|
1758
1872
|
const contractPath = resolveContractPath(contract, context);
|
|
1759
|
-
if (!contractPath)
|
|
1760
|
-
return;
|
|
1873
|
+
if (!contractPath) return;
|
|
1761
1874
|
let contractContent;
|
|
1762
1875
|
try {
|
|
1763
1876
|
contractContent = await fs.promises.readFile(contractPath, "utf-8");
|
|
@@ -1765,8 +1878,7 @@ async function checkComponentContractConsistency(contract, context, result) {
|
|
|
1765
1878
|
return;
|
|
1766
1879
|
}
|
|
1767
1880
|
const parsed = parseContract(contractContent, path.basename(contractPath));
|
|
1768
|
-
if (parsed.validations.length > 0)
|
|
1769
|
-
return;
|
|
1881
|
+
if (parsed.validations.length > 0) return;
|
|
1770
1882
|
let sourceCode;
|
|
1771
1883
|
try {
|
|
1772
1884
|
sourceCode = await fs.promises.readFile(sourcePath, "utf-8");
|
|
@@ -1885,8 +1997,7 @@ async function validatePackageJson(context, result) {
|
|
|
1885
1997
|
}
|
|
1886
1998
|
async function validateDynamicContracts(context, result) {
|
|
1887
1999
|
const { dynamic_contracts } = context.manifest;
|
|
1888
|
-
if (!dynamic_contracts)
|
|
1889
|
-
return;
|
|
2000
|
+
if (!dynamic_contracts) return;
|
|
1890
2001
|
const dynamicConfigs = Array.isArray(dynamic_contracts) ? dynamic_contracts : [dynamic_contracts];
|
|
1891
2002
|
for (const config of dynamicConfigs) {
|
|
1892
2003
|
const prefix = config.prefix || "(unknown)";
|
|
@@ -1942,5 +2053,6 @@ export {
|
|
|
1942
2053
|
lintAddMenuCatalog,
|
|
1943
2054
|
validateAddMenuCatalogFile,
|
|
1944
2055
|
validateAddMenuItem,
|
|
2056
|
+
validateAiditorSettingsFile,
|
|
1945
2057
|
validatePlugin
|
|
1946
2058
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jay-framework/plugin-validator",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.1",
|
|
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.
|
|
29
|
-
"@jay-framework/compiler-shared": "^0.
|
|
30
|
-
"@jay-framework/
|
|
31
|
-
"@jay-framework/typescript-bridge": "^0.22.2",
|
|
28
|
+
"@jay-framework/compiler-jay-html": "^0.23.1",
|
|
29
|
+
"@jay-framework/compiler-shared": "^0.23.1",
|
|
30
|
+
"@jay-framework/typescript-bridge": "^0.23.1",
|
|
32
31
|
"chalk": "^4.1.2",
|
|
33
32
|
"yaml": "^2.3.4"
|
|
34
33
|
},
|
|
35
34
|
"devDependencies": {
|
|
36
|
-
"@jay-framework/dev-environment": "^0.
|
|
35
|
+
"@jay-framework/dev-environment": "^0.23.1",
|
|
37
36
|
"@types/node": "^22.15.21",
|
|
38
37
|
"rimraf": "^5.0.5",
|
|
39
38
|
"tsup": "^8.0.1",
|