@appstrate/validation 1.0.0 → 1.0.2
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/dependencies.ts +5 -7
- package/src/index.ts +24 -46
- package/src/naming.ts +24 -3
package/package.json
CHANGED
package/src/dependencies.ts
CHANGED
|
@@ -6,20 +6,18 @@ export interface DepEntry {
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
export function extractDependencies(manifest: Record<string, unknown>): DepEntry[] {
|
|
9
|
-
const
|
|
9
|
+
const registryDependencies = manifest.registryDependencies as
|
|
10
10
|
| {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
extensions?: Record<string, string>;
|
|
14
|
-
};
|
|
11
|
+
skills?: Record<string, string>;
|
|
12
|
+
extensions?: Record<string, string>;
|
|
15
13
|
}
|
|
16
14
|
| undefined;
|
|
17
15
|
|
|
18
|
-
if (!
|
|
16
|
+
if (!registryDependencies) return [];
|
|
19
17
|
|
|
20
18
|
const deps: DepEntry[] = [];
|
|
21
19
|
|
|
22
|
-
const { skills = {}, extensions = {} } =
|
|
20
|
+
const { skills = {}, extensions = {} } = registryDependencies;
|
|
23
21
|
|
|
24
22
|
for (const [fullName, versionRange] of Object.entries(skills)) {
|
|
25
23
|
const { scope, name } = parseScopedName(fullName);
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import semver from "semver";
|
|
3
|
+
import { SLUG_REGEX } from "./naming.ts";
|
|
3
4
|
|
|
4
|
-
export
|
|
5
|
+
export { SLUG_REGEX };
|
|
5
6
|
|
|
6
7
|
const flowFieldTypeEnum = z.enum(["string", "number", "boolean", "array", "object", "file"]);
|
|
7
8
|
|
|
@@ -73,20 +74,14 @@ export const baseManifestSchema = z.object({
|
|
|
73
74
|
export type BaseManifest = z.infer<typeof baseManifestSchema>;
|
|
74
75
|
|
|
75
76
|
// ─────────────────────────────────────────────
|
|
76
|
-
//
|
|
77
|
+
// Shared flow fields — used by both flowManifestSchema and localFlowManifestSchema
|
|
77
78
|
// ─────────────────────────────────────────────
|
|
78
79
|
|
|
79
|
-
|
|
80
|
-
$schema: z.string().optional(),
|
|
81
|
-
schemaVersion: z.string(),
|
|
82
|
-
displayName: z.string().min(1),
|
|
83
|
-
author: z.string(),
|
|
84
|
-
tags: z.array(z.string()).optional(),
|
|
80
|
+
const flowSharedFields = {
|
|
85
81
|
requires: z.object({
|
|
86
82
|
services: z.array(serviceRequirementSchema),
|
|
87
83
|
skills: z.array(slugString).optional().default([]),
|
|
88
84
|
extensions: z.array(slugString).optional().default([]),
|
|
89
|
-
registryDependencies: registryDependenciesSchema,
|
|
90
85
|
}),
|
|
91
86
|
input: z
|
|
92
87
|
.object({
|
|
@@ -109,6 +104,19 @@ export const flowManifestSchema = baseManifestSchema.extend({
|
|
|
109
104
|
outputRetries: z.number().min(0).max(5).optional(),
|
|
110
105
|
})
|
|
111
106
|
.optional(),
|
|
107
|
+
} as const;
|
|
108
|
+
|
|
109
|
+
// ─────────────────────────────────────────────
|
|
110
|
+
// Flow manifest schema — extends base with flow-specific fields
|
|
111
|
+
// ─────────────────────────────────────────────
|
|
112
|
+
|
|
113
|
+
export const flowManifestSchema = baseManifestSchema.extend({
|
|
114
|
+
$schema: z.string().optional(),
|
|
115
|
+
schemaVersion: z.string(),
|
|
116
|
+
displayName: z.string().min(1),
|
|
117
|
+
author: z.string(),
|
|
118
|
+
tags: z.array(z.string()).optional(),
|
|
119
|
+
...flowSharedFields,
|
|
112
120
|
});
|
|
113
121
|
|
|
114
122
|
export type FlowManifest = z.infer<typeof flowManifestSchema>;
|
|
@@ -121,11 +129,11 @@ export type ManifestSchema = FlowManifest;
|
|
|
121
129
|
// Unified validateManifest — dispatches by type
|
|
122
130
|
// ─────────────────────────────────────────────
|
|
123
131
|
|
|
124
|
-
export
|
|
125
|
-
valid:
|
|
126
|
-
errors: string[];
|
|
127
|
-
|
|
128
|
-
|
|
132
|
+
export type ValidateManifestResult =
|
|
133
|
+
| { valid: true; errors: []; manifest: BaseManifest | FlowManifest }
|
|
134
|
+
| { valid: false; errors: string[]; manifest?: undefined };
|
|
135
|
+
|
|
136
|
+
export function validateManifest(raw: unknown): ValidateManifestResult {
|
|
129
137
|
// First, check if it has a type field to dispatch
|
|
130
138
|
if (raw && typeof raw === "object" && "type" in raw) {
|
|
131
139
|
const obj = raw as Record<string, unknown>;
|
|
@@ -148,11 +156,7 @@ export function validateManifest(raw: unknown): {
|
|
|
148
156
|
return validateFlowManifest(raw);
|
|
149
157
|
}
|
|
150
158
|
|
|
151
|
-
function validateFlowManifest(raw: unknown): {
|
|
152
|
-
valid: boolean;
|
|
153
|
-
errors: string[];
|
|
154
|
-
manifest?: unknown;
|
|
155
|
-
} {
|
|
159
|
+
function validateFlowManifest(raw: unknown): ValidateManifestResult {
|
|
156
160
|
const result = flowManifestSchema.safeParse(raw);
|
|
157
161
|
if (result.success) {
|
|
158
162
|
return { valid: true, errors: [], manifest: result.data };
|
|
@@ -240,33 +244,7 @@ export const localFlowManifestSchema = z.looseObject({
|
|
|
240
244
|
tags: z.array(z.string()).optional(),
|
|
241
245
|
type: z.string().optional(),
|
|
242
246
|
version: z.string().optional(),
|
|
243
|
-
|
|
244
|
-
services: z.array(serviceRequirementSchema),
|
|
245
|
-
skills: z.array(slugString).optional().default([]),
|
|
246
|
-
extensions: z.array(slugString).optional().default([]),
|
|
247
|
-
registryDependencies: registryDependenciesSchema,
|
|
248
|
-
}),
|
|
249
|
-
input: z
|
|
250
|
-
.object({
|
|
251
|
-
schema: jsonSchemaObjectSchema,
|
|
252
|
-
})
|
|
253
|
-
.optional(),
|
|
254
|
-
output: z
|
|
255
|
-
.object({
|
|
256
|
-
schema: jsonSchemaObjectSchema,
|
|
257
|
-
})
|
|
258
|
-
.optional(),
|
|
259
|
-
config: z
|
|
260
|
-
.object({
|
|
261
|
-
schema: jsonSchemaObjectSchema,
|
|
262
|
-
})
|
|
263
|
-
.optional(),
|
|
264
|
-
execution: z
|
|
265
|
-
.object({
|
|
266
|
-
timeout: z.number().optional(),
|
|
267
|
-
outputRetries: z.number().min(0).max(5).optional(),
|
|
268
|
-
})
|
|
269
|
-
.optional(),
|
|
247
|
+
...flowSharedFields,
|
|
270
248
|
});
|
|
271
249
|
|
|
272
250
|
export type LocalFlowManifest = z.infer<typeof localFlowManifestSchema>;
|
package/src/naming.ts
CHANGED
|
@@ -1,11 +1,32 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export { SLUG_REGEX };
|
|
1
|
+
export const SLUG_REGEX = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/;
|
|
4
2
|
|
|
5
3
|
export function normalizeScope(scope: string): string {
|
|
4
|
+
if (!scope) throw new Error("Scope cannot be empty");
|
|
6
5
|
return scope.startsWith("@") ? scope : `@${scope}`;
|
|
7
6
|
}
|
|
8
7
|
|
|
9
8
|
export function stripScope(scope: string): string {
|
|
10
9
|
return scope.startsWith("@") ? scope.slice(1) : scope;
|
|
11
10
|
}
|
|
11
|
+
|
|
12
|
+
/** Convert "@scope/name" to strate packageId "scope--name" */
|
|
13
|
+
export function scopedNameToPackageId(scopedName: string): string {
|
|
14
|
+
const match = scopedName.match(/^@([a-z0-9][a-z0-9-]*[a-z0-9]?)\/([a-z0-9][a-z0-9-]*[a-z0-9]?)$/);
|
|
15
|
+
if (!match) throw new Error(`Invalid scoped package name: ${scopedName}`);
|
|
16
|
+
return `${match[1]}--${match[2]}`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Convert strate packageId "scope--name" to "@scope/name", or null for local slugs */
|
|
20
|
+
export function packageIdToScopedName(packageId: string): string | null {
|
|
21
|
+
const idx = packageId.indexOf("--");
|
|
22
|
+
if (idx === -1) return null;
|
|
23
|
+
const scope = packageId.slice(0, idx);
|
|
24
|
+
const name = packageId.slice(idx + 2);
|
|
25
|
+
if (!scope || !name) return null;
|
|
26
|
+
return `@${scope}/${name}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Convert separated scope + name to strate packageId */
|
|
30
|
+
export function depEntryToPackageId(depScope: string, depName: string): string {
|
|
31
|
+
return `${stripScope(depScope)}--${depName}`;
|
|
32
|
+
}
|