@appstrate/validation 1.1.1 → 1.2.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/package.json +1 -1
- package/src/index.ts +14 -48
- package/src/naming.ts +7 -0
- package/src/zip.ts +2 -3
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -26,14 +26,14 @@ export const jsonSchemaObjectSchema = z.object({
|
|
|
26
26
|
propertyOrder: z.array(z.string()).optional(),
|
|
27
27
|
});
|
|
28
28
|
|
|
29
|
-
export const serviceRequirementSchema = z.
|
|
29
|
+
export const serviceRequirementSchema = z.looseObject({
|
|
30
30
|
id: z.string().min(1).regex(SLUG_REGEX, {
|
|
31
31
|
error: "Must be a valid slug (a-z, 0-9, hyphens, no leading/trailing hyphen)",
|
|
32
32
|
}),
|
|
33
33
|
provider: z.string(),
|
|
34
34
|
description: z.string().optional(),
|
|
35
|
-
scopes: z.array(z.string()).optional()
|
|
36
|
-
connectionMode: z.enum(["user", "admin"]).optional()
|
|
35
|
+
scopes: z.array(z.string()).optional(),
|
|
36
|
+
connectionMode: z.enum(["user", "admin"]).optional(),
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
const slugString = z.string().min(1).regex(SLUG_REGEX, {
|
|
@@ -45,9 +45,9 @@ const semverRangeString = z.string().refine((val) => semver.validRange(val) !==
|
|
|
45
45
|
});
|
|
46
46
|
|
|
47
47
|
const registryDependenciesSchema = z
|
|
48
|
-
.
|
|
49
|
-
skills: z.record(z.string(), semverRangeString).optional()
|
|
50
|
-
extensions: z.record(z.string(), semverRangeString).optional()
|
|
48
|
+
.looseObject({
|
|
49
|
+
skills: z.record(z.string(), semverRangeString).optional(),
|
|
50
|
+
extensions: z.record(z.string(), semverRangeString).optional(),
|
|
51
51
|
})
|
|
52
52
|
.optional();
|
|
53
53
|
|
|
@@ -59,7 +59,7 @@ export const scopedNameRegex = /^@[a-z0-9]([a-z0-9-]*[a-z0-9])?\/[a-z0-9]([a-z0-
|
|
|
59
59
|
export const packageTypeEnum = z.enum(["flow", "skill", "extension"]);
|
|
60
60
|
export type PackageType = z.infer<typeof packageTypeEnum>;
|
|
61
61
|
|
|
62
|
-
export const baseManifestSchema = z.
|
|
62
|
+
export const baseManifestSchema = z.looseObject({
|
|
63
63
|
name: z.string().regex(scopedNameRegex, { error: "Must follow the format @scope/package-name" }),
|
|
64
64
|
version: z.string().refine((v) => semver.valid(v) !== null, {
|
|
65
65
|
error: "Must be a valid semver version",
|
|
@@ -82,14 +82,14 @@ export type FlowJsonSchemaProperty = z.infer<typeof jsonSchemaPropertySchema>;
|
|
|
82
82
|
export type FlowJsonSchemaObject = z.infer<typeof jsonSchemaObjectSchema>;
|
|
83
83
|
|
|
84
84
|
// ─────────────────────────────────────────────
|
|
85
|
-
// Shared flow fields
|
|
85
|
+
// Shared flow fields
|
|
86
86
|
// ─────────────────────────────────────────────
|
|
87
87
|
|
|
88
88
|
const flowSharedFields = {
|
|
89
|
-
requires: z.
|
|
89
|
+
requires: z.looseObject({
|
|
90
90
|
services: z.array(serviceRequirementSchema),
|
|
91
|
-
skills: z.array(slugString).optional()
|
|
92
|
-
extensions: z.array(slugString).optional()
|
|
91
|
+
skills: z.array(slugString).optional(),
|
|
92
|
+
extensions: z.array(slugString).optional(),
|
|
93
93
|
}),
|
|
94
94
|
input: z
|
|
95
95
|
.object({
|
|
@@ -107,7 +107,7 @@ const flowSharedFields = {
|
|
|
107
107
|
})
|
|
108
108
|
.optional(),
|
|
109
109
|
execution: z
|
|
110
|
-
.
|
|
110
|
+
.looseObject({
|
|
111
111
|
timeout: z.number().optional(),
|
|
112
112
|
outputRetries: z.number().min(0).max(5).optional(),
|
|
113
113
|
})
|
|
@@ -155,9 +155,8 @@ export function validateManifest(raw: unknown): ValidateManifestResult {
|
|
|
155
155
|
return { valid: false, errors };
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
// No type field —
|
|
159
|
-
|
|
160
|
-
return validateFlowManifest(raw);
|
|
158
|
+
// No type field — require it explicitly
|
|
159
|
+
return { valid: false, errors: ["type: Required field is missing"] };
|
|
161
160
|
}
|
|
162
161
|
|
|
163
162
|
function validateFlowManifest(raw: unknown): ValidateManifestResult {
|
|
@@ -233,39 +232,6 @@ function countParams(paramStr: string): number {
|
|
|
233
232
|
return count;
|
|
234
233
|
}
|
|
235
234
|
|
|
236
|
-
// ─────────────────────────────────────────────
|
|
237
|
-
// Local flow manifest schema — relaxed for strate (no scoped name, optional version)
|
|
238
|
-
// ─────────────────────────────────────────────
|
|
239
|
-
|
|
240
|
-
export const localFlowManifestSchema = z.looseObject({
|
|
241
|
-
$schema: z.string().optional(),
|
|
242
|
-
schemaVersion: z.string(),
|
|
243
|
-
name: slugString,
|
|
244
|
-
displayName: z.string().min(1),
|
|
245
|
-
description: z.string(),
|
|
246
|
-
author: z.string(),
|
|
247
|
-
license: z.string().optional(),
|
|
248
|
-
tags: z.array(z.string()).optional(),
|
|
249
|
-
type: z.string().optional(),
|
|
250
|
-
version: z.string().optional(),
|
|
251
|
-
...flowSharedFields,
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
export type LocalFlowManifest = z.infer<typeof localFlowManifestSchema>;
|
|
255
|
-
|
|
256
|
-
export function validateLocalFlowManifest(raw: unknown): {
|
|
257
|
-
valid: boolean;
|
|
258
|
-
errors: string[];
|
|
259
|
-
manifest?: unknown;
|
|
260
|
-
} {
|
|
261
|
-
const result = localFlowManifestSchema.safeParse(raw);
|
|
262
|
-
if (result.success) {
|
|
263
|
-
return { valid: true, errors: [], manifest: result.data };
|
|
264
|
-
}
|
|
265
|
-
const errors = result.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`);
|
|
266
|
-
return { valid: false, errors };
|
|
267
|
-
}
|
|
268
|
-
|
|
269
235
|
export function validateExtensionSource(source: string): ExtensionValidationResult {
|
|
270
236
|
const errors: string[] = [];
|
|
271
237
|
const warnings: string[] = [];
|
package/src/naming.ts
CHANGED
|
@@ -16,6 +16,13 @@ export function scopedNameToPackageId(scopedName: string): string {
|
|
|
16
16
|
return `${match[1]}--${match[2]}`;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
/** Parse "@scope/name" into { scope, name } or null if invalid */
|
|
20
|
+
export function parseScopedName(scopedName: string): { scope: string; name: string } | null {
|
|
21
|
+
const match = scopedName.match(/^@([^/]+)\/(.+)$/);
|
|
22
|
+
if (!match) return null;
|
|
23
|
+
return { scope: match[1]!, name: match[2]! };
|
|
24
|
+
}
|
|
25
|
+
|
|
19
26
|
/** Convert strate packageId "scope--name" to "@scope/name", or null for local slugs */
|
|
20
27
|
export function packageIdToScopedName(packageId: string): string | null {
|
|
21
28
|
const idx = packageId.indexOf("--");
|
package/src/zip.ts
CHANGED
|
@@ -133,10 +133,9 @@ export function parsePackageZip(zipBuffer: Uint8Array, maxSize?: number): Parsed
|
|
|
133
133
|
);
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
-
const manifest =
|
|
136
|
+
const manifest = manifestRaw as Record<string, unknown>;
|
|
137
137
|
|
|
138
|
-
|
|
139
|
-
const type = (manifest.type as "flow" | "skill" | "extension") ?? "flow";
|
|
138
|
+
const type = manifest.type as "flow" | "skill" | "extension";
|
|
140
139
|
|
|
141
140
|
// Extract primary content based on type
|
|
142
141
|
let content: string;
|