@unieojs/unio-evjs-adapter 0.1.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/README.md +151 -0
- package/dist/build.d.ts +2 -0
- package/dist/build.js +5 -0
- package/dist/build.js.map +1 -0
- package/dist/detect.d.ts +4 -0
- package/dist/detect.js +56 -0
- package/dist/detect.js.map +1 -0
- package/dist/emit.d.ts +2 -0
- package/dist/emit.js +262 -0
- package/dist/emit.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest.d.ts +3 -0
- package/dist/manifest.js +80 -0
- package/dist/manifest.js.map +1 -0
- package/dist/plugin.d.ts +4 -0
- package/dist/plugin.js +28 -0
- package/dist/plugin.js.map +1 -0
- package/dist/types.d.ts +146 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +8 -0
- package/dist/utils.js +52 -0
- package/dist/utils.js.map +1 -0
- package/dist/validate.d.ts +5 -0
- package/dist/validate.js +152 -0
- package/dist/validate.js.map +1 -0
- package/package.json +46 -0
- package/src/build.ts +6 -0
- package/src/detect.ts +74 -0
- package/src/emit.ts +384 -0
- package/src/index.ts +45 -0
- package/src/manifest.ts +108 -0
- package/src/plugin.ts +39 -0
- package/src/types.ts +208 -0
- package/src/utils.ts +61 -0
- package/src/validate.ts +250 -0
package/src/types.ts
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
EvBuildResult,
|
|
3
|
+
EvPlugin,
|
|
4
|
+
EvPluginContext,
|
|
5
|
+
EvPluginHooks,
|
|
6
|
+
} from "@evjs/ev";
|
|
7
|
+
|
|
8
|
+
export type ClientManifest = EvBuildResult["clientManifest"];
|
|
9
|
+
export type ServerManifest = NonNullable<EvBuildResult["serverManifest"]>;
|
|
10
|
+
export type PageManifestEntry = NonNullable<ClientManifest["pages"]>[string];
|
|
11
|
+
export type RouteEntry = NonNullable<ClientManifest["routes"]>[number];
|
|
12
|
+
export type ServerFnEntry = ServerManifest["fns"][string];
|
|
13
|
+
|
|
14
|
+
export type {
|
|
15
|
+
EvBuildResult,
|
|
16
|
+
EvPlugin,
|
|
17
|
+
EvPluginContext,
|
|
18
|
+
EvPluginHooks,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type CapabilityStatus =
|
|
22
|
+
| "supported"
|
|
23
|
+
| "partial"
|
|
24
|
+
| "degraded"
|
|
25
|
+
| "unsupported";
|
|
26
|
+
|
|
27
|
+
export interface UboaCapabilities {
|
|
28
|
+
static: CapabilityStatus;
|
|
29
|
+
routes: CapabilityStatus;
|
|
30
|
+
serverlessFunction: CapabilityStatus;
|
|
31
|
+
serverFunctionIntrospection: CapabilityStatus;
|
|
32
|
+
restRouteIntrospection: CapabilityStatus;
|
|
33
|
+
edgeMiddleware: CapabilityStatus;
|
|
34
|
+
rsc: CapabilityStatus;
|
|
35
|
+
imageOptimization: CapabilityStatus;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface UboaServerFunctionDescriptor {
|
|
39
|
+
name: string;
|
|
40
|
+
runtime: string;
|
|
41
|
+
entry: string;
|
|
42
|
+
handler: string;
|
|
43
|
+
handlerProtocol: string;
|
|
44
|
+
memory: number;
|
|
45
|
+
maxDuration: number;
|
|
46
|
+
environment: Record<string, unknown>;
|
|
47
|
+
bindings: Record<string, unknown>;
|
|
48
|
+
framework: Record<string, unknown>;
|
|
49
|
+
source: Record<string, unknown>;
|
|
50
|
+
[key: string]: unknown;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface ExtendServerFunctionDescriptorContext {
|
|
54
|
+
descriptor: UboaServerFunctionDescriptor;
|
|
55
|
+
serverFunctionName: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface ExtendArtifactsContext {
|
|
59
|
+
artifacts: UboaArtifact[];
|
|
60
|
+
serverFunctionName: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface AfterEmitContext {
|
|
64
|
+
outputDir: string;
|
|
65
|
+
serverFunctionName: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface UnioEvjsAdapterOptions {
|
|
69
|
+
cwd?: string;
|
|
70
|
+
outputDir?: string;
|
|
71
|
+
serverFunctionName?: string;
|
|
72
|
+
serverFunctionEndpoint?: string;
|
|
73
|
+
extendServerFunctionDescriptor?: (
|
|
74
|
+
context: ExtendServerFunctionDescriptorContext,
|
|
75
|
+
) => Record<string, unknown> | Promise<Record<string, unknown>>;
|
|
76
|
+
extendArtifacts?: (
|
|
77
|
+
context: ExtendArtifactsContext,
|
|
78
|
+
) => UboaArtifact[] | Promise<UboaArtifact[]>;
|
|
79
|
+
afterEmit?: (context: AfterEmitContext) => void | Promise<void>;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface DetectResult {
|
|
83
|
+
detected: boolean;
|
|
84
|
+
signals: string[];
|
|
85
|
+
configFiles: string[];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface ValidationResult {
|
|
89
|
+
valid: boolean;
|
|
90
|
+
errors: string[];
|
|
91
|
+
warnings: string[];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
interface UboaRouteBase {
|
|
95
|
+
id: string;
|
|
96
|
+
match: string;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface UboaStaticRoute extends UboaRouteBase {
|
|
100
|
+
type: "static";
|
|
101
|
+
entry: string;
|
|
102
|
+
serverFunction?: never;
|
|
103
|
+
edgeFunction?: never;
|
|
104
|
+
methods?: never;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface UboaServerFunctionRoute extends UboaRouteBase {
|
|
108
|
+
type: "serverFunction";
|
|
109
|
+
serverFunction: string;
|
|
110
|
+
methods?: string[];
|
|
111
|
+
entry?: never;
|
|
112
|
+
edgeFunction?: never;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface UboaEdgeFunctionRoute extends UboaRouteBase {
|
|
116
|
+
type: "edgeFunction";
|
|
117
|
+
edgeFunction: string;
|
|
118
|
+
entry?: never;
|
|
119
|
+
serverFunction?: never;
|
|
120
|
+
methods?: never;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export type UboaRoute =
|
|
124
|
+
| UboaStaticRoute
|
|
125
|
+
| UboaServerFunctionRoute
|
|
126
|
+
| UboaEdgeFunctionRoute;
|
|
127
|
+
|
|
128
|
+
interface UboaMiddlewareBase {
|
|
129
|
+
name: string;
|
|
130
|
+
match: string[];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export interface UboaEdgeMiddleware extends UboaMiddlewareBase {
|
|
134
|
+
runtime: "edge";
|
|
135
|
+
entry: string;
|
|
136
|
+
serverFunction?: never;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export interface UboaServerlessMiddleware extends UboaMiddlewareBase {
|
|
140
|
+
runtime: "serverless";
|
|
141
|
+
serverFunction: string;
|
|
142
|
+
entry?: never;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export type UboaMiddleware =
|
|
146
|
+
| UboaEdgeMiddleware
|
|
147
|
+
| UboaServerlessMiddleware;
|
|
148
|
+
|
|
149
|
+
export type UboaArtifactResourceKind =
|
|
150
|
+
| "assets"
|
|
151
|
+
| "serverFunction"
|
|
152
|
+
| "edgeBundle"
|
|
153
|
+
| "apiSchema";
|
|
154
|
+
|
|
155
|
+
export interface UboaAssetsArtifact {
|
|
156
|
+
id: string;
|
|
157
|
+
resourceKind: "assets";
|
|
158
|
+
path: string;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export interface UboaServerFunctionArtifact {
|
|
162
|
+
id: string;
|
|
163
|
+
resourceKind: "serverFunction";
|
|
164
|
+
path: string;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export interface UboaEdgeBundleArtifact {
|
|
168
|
+
id: string;
|
|
169
|
+
resourceKind: "edgeBundle";
|
|
170
|
+
path: string;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export interface UboaArtifactReference {
|
|
174
|
+
id: string;
|
|
175
|
+
resourceKind: UboaArtifactResourceKind;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export type UboaApiSchemaFormat = "oneapi";
|
|
179
|
+
|
|
180
|
+
export interface UboaApiSchemaArtifact {
|
|
181
|
+
id: string;
|
|
182
|
+
resourceKind: "apiSchema";
|
|
183
|
+
path: string;
|
|
184
|
+
schemaFormat: UboaApiSchemaFormat;
|
|
185
|
+
describes?: UboaArtifactReference;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export type UboaArtifact =
|
|
189
|
+
| UboaAssetsArtifact
|
|
190
|
+
| UboaServerFunctionArtifact
|
|
191
|
+
| UboaEdgeBundleArtifact
|
|
192
|
+
| UboaApiSchemaArtifact;
|
|
193
|
+
|
|
194
|
+
export interface AdapterBuildResult {
|
|
195
|
+
outputDir: string;
|
|
196
|
+
routes: UboaRoute[];
|
|
197
|
+
middleware: UboaMiddleware[];
|
|
198
|
+
artifacts: UboaArtifact[];
|
|
199
|
+
capabilities: UboaCapabilities;
|
|
200
|
+
warnings: string[];
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export interface EmitUboaOptions extends UnioEvjsAdapterOptions {
|
|
204
|
+
clientManifest: ClientManifest;
|
|
205
|
+
serverManifest?: ServerManifest;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export type BuildOptions = EmitUboaOptions;
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
export async function pathExists(filePath: string): Promise<boolean> {
|
|
5
|
+
return fs.access(filePath).then(
|
|
6
|
+
() => true,
|
|
7
|
+
() => false,
|
|
8
|
+
);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function readJson(filePath: string): Promise<unknown> {
|
|
12
|
+
return JSON.parse(await fs.readFile(filePath, "utf8"));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function writeJson(filePath: string, value: unknown): Promise<void> {
|
|
16
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
17
|
+
await fs.writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`, "utf8");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function isRecord(value: unknown): value is Record<string, unknown> {
|
|
21
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function isStringArray(value: unknown): value is string[] {
|
|
25
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function toPosix(filePath: string): string {
|
|
29
|
+
return filePath.split(path.sep).join("/");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function normalizeRoutePath(routePath: string): string {
|
|
33
|
+
if (!routePath || routePath === "/") return "/";
|
|
34
|
+
if (routePath === "*") return "/:path*";
|
|
35
|
+
|
|
36
|
+
const withLeadingSlash = routePath.startsWith("/")
|
|
37
|
+
? routePath
|
|
38
|
+
: `/${routePath}`;
|
|
39
|
+
|
|
40
|
+
return withLeadingSlash
|
|
41
|
+
.split("/")
|
|
42
|
+
.map((segment) => {
|
|
43
|
+
if (segment === "*") return ":path*";
|
|
44
|
+
if (segment.startsWith("$") && segment.length > 1) {
|
|
45
|
+
return `:${segment.slice(1)}`;
|
|
46
|
+
}
|
|
47
|
+
return segment;
|
|
48
|
+
})
|
|
49
|
+
.join("/")
|
|
50
|
+
.replace(/\/+/g, "/");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function routeId(prefix: string, routePath: string): string {
|
|
54
|
+
const slug = routePath
|
|
55
|
+
.replace(/^\/+/, "")
|
|
56
|
+
.replace(/[:*$]+/g, "")
|
|
57
|
+
.replace(/[^A-Za-z0-9]+/g, "-")
|
|
58
|
+
.replace(/^-+|-+$/g, "")
|
|
59
|
+
.toLowerCase();
|
|
60
|
+
return slug ? `${prefix}-${slug}` : `${prefix}-index`;
|
|
61
|
+
}
|
package/src/validate.ts
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
UboaArtifactReference,
|
|
5
|
+
UboaArtifactResourceKind,
|
|
6
|
+
UboaRoute,
|
|
7
|
+
ValidationResult,
|
|
8
|
+
} from "./types.js";
|
|
9
|
+
import { isRecord, pathExists, readJson } from "./utils.js";
|
|
10
|
+
|
|
11
|
+
const ARTIFACT_RESOURCE_KINDS: UboaArtifactResourceKind[] = [
|
|
12
|
+
"assets",
|
|
13
|
+
"serverFunction",
|
|
14
|
+
"edgeBundle",
|
|
15
|
+
"apiSchema",
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
interface ValidArtifact {
|
|
19
|
+
id: string;
|
|
20
|
+
resourceKind: UboaArtifactResourceKind;
|
|
21
|
+
path: string;
|
|
22
|
+
absolutePath: string;
|
|
23
|
+
value: Record<string, unknown>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function artifactKey(resourceKind: string, id: string): string {
|
|
27
|
+
return `${resourceKind}:${id}`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function isArtifactResourceKind(
|
|
31
|
+
value: unknown,
|
|
32
|
+
): value is UboaArtifactResourceKind {
|
|
33
|
+
return (
|
|
34
|
+
typeof value === "string" &&
|
|
35
|
+
ARTIFACT_RESOURCE_KINDS.includes(value as UboaArtifactResourceKind)
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function isSafeRelativePath(value: string): boolean {
|
|
40
|
+
if (!value) return false;
|
|
41
|
+
if (path.isAbsolute(value) || path.win32.isAbsolute(value)) return false;
|
|
42
|
+
return !value.split(/[\\/]+/).includes("..");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function resolveArtifactPath(
|
|
46
|
+
root: string,
|
|
47
|
+
artifactPath: string,
|
|
48
|
+
artifactId: string,
|
|
49
|
+
errors: string[],
|
|
50
|
+
): string | undefined {
|
|
51
|
+
if (!isSafeRelativePath(artifactPath)) {
|
|
52
|
+
errors.push(
|
|
53
|
+
`Artifact ${artifactId} path must stay within .unio/output: ${artifactPath}`,
|
|
54
|
+
);
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return path.resolve(root, artifactPath);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function validateArtifactReference(
|
|
62
|
+
value: unknown,
|
|
63
|
+
label: string,
|
|
64
|
+
errors: string[],
|
|
65
|
+
): UboaArtifactReference | undefined {
|
|
66
|
+
if (!isRecord(value)) {
|
|
67
|
+
errors.push(`${label} must be an object`);
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const { id, resourceKind } = value;
|
|
72
|
+
if (typeof id !== "string" || id.length === 0) {
|
|
73
|
+
errors.push(`${label}.id must be a non-empty string`);
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
if (!isArtifactResourceKind(resourceKind)) {
|
|
77
|
+
errors.push(
|
|
78
|
+
`${label}.resourceKind must be one of ${ARTIFACT_RESOURCE_KINDS.join(", ")}`,
|
|
79
|
+
);
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return { id, resourceKind };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function validateArtifactBase(
|
|
87
|
+
root: string,
|
|
88
|
+
value: unknown,
|
|
89
|
+
index: number,
|
|
90
|
+
errors: string[],
|
|
91
|
+
): ValidArtifact | undefined {
|
|
92
|
+
const label = `artifacts[${index}]`;
|
|
93
|
+
|
|
94
|
+
if (!isRecord(value)) {
|
|
95
|
+
errors.push(`${label} must be an object`);
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const { id, resourceKind, path: artifactPath } = value;
|
|
100
|
+
if (typeof id !== "string" || id.length === 0) {
|
|
101
|
+
errors.push(`${label}.id must be a non-empty string`);
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
if (!isArtifactResourceKind(resourceKind)) {
|
|
105
|
+
errors.push(
|
|
106
|
+
`${label}.resourceKind must be one of ${ARTIFACT_RESOURCE_KINDS.join(", ")}`,
|
|
107
|
+
);
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
if (typeof artifactPath !== "string" || artifactPath.length === 0) {
|
|
111
|
+
errors.push(`${label}.path must be a non-empty string`);
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const absolutePath = resolveArtifactPath(root, artifactPath, id, errors);
|
|
116
|
+
if (!absolutePath) return undefined;
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
id,
|
|
120
|
+
resourceKind,
|
|
121
|
+
path: artifactPath,
|
|
122
|
+
absolutePath,
|
|
123
|
+
value,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async function readRequiredJson<T>(
|
|
128
|
+
root: string,
|
|
129
|
+
file: string,
|
|
130
|
+
errors: string[],
|
|
131
|
+
): Promise<T | undefined> {
|
|
132
|
+
const filePath = path.join(root, file);
|
|
133
|
+
if (!(await pathExists(filePath))) {
|
|
134
|
+
errors.push(`Missing ${file}`);
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
return await readJson(filePath) as T;
|
|
140
|
+
} catch (error) {
|
|
141
|
+
errors.push(`Invalid JSON in ${file}: ${(error as Error).message}`);
|
|
142
|
+
return undefined;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export async function validate(options: {
|
|
147
|
+
cwd: string;
|
|
148
|
+
outputDir?: string;
|
|
149
|
+
}): Promise<ValidationResult> {
|
|
150
|
+
const root = path.resolve(options.cwd, options.outputDir ?? ".unio/output");
|
|
151
|
+
const errors: string[] = [];
|
|
152
|
+
const warnings: string[] = [];
|
|
153
|
+
|
|
154
|
+
if (!(await pathExists(root))) {
|
|
155
|
+
return {
|
|
156
|
+
valid: false,
|
|
157
|
+
errors: [`Missing output directory: ${root}`],
|
|
158
|
+
warnings,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
await readRequiredJson(root, "unio.json", errors);
|
|
163
|
+
const routes = await readRequiredJson<UboaRoute[]>(root, "routes.json", errors);
|
|
164
|
+
await readRequiredJson(root, "middleware.json", errors);
|
|
165
|
+
const artifactsManifest = await readRequiredJson<{ artifacts?: unknown }>(
|
|
166
|
+
root,
|
|
167
|
+
"artifacts.json",
|
|
168
|
+
errors,
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
if (routes && !Array.isArray(routes)) {
|
|
172
|
+
errors.push("routes.json must be an array");
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const artifacts = artifactsManifest?.artifacts;
|
|
176
|
+
if (artifactsManifest && !Array.isArray(artifacts)) {
|
|
177
|
+
errors.push("artifacts.json artifacts must be an array");
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const validArtifacts = Array.isArray(artifacts)
|
|
181
|
+
? artifacts
|
|
182
|
+
.map((artifact, index) =>
|
|
183
|
+
validateArtifactBase(root, artifact, index, errors)
|
|
184
|
+
)
|
|
185
|
+
.filter((artifact): artifact is ValidArtifact => Boolean(artifact))
|
|
186
|
+
: [];
|
|
187
|
+
const artifactKeys = new Set(
|
|
188
|
+
validArtifacts.map((artifact) =>
|
|
189
|
+
artifactKey(artifact.resourceKind, artifact.id)
|
|
190
|
+
),
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
for (const artifact of validArtifacts) {
|
|
194
|
+
if (!(await pathExists(artifact.absolutePath))) {
|
|
195
|
+
errors.push(`Artifact path does not exist: ${artifact.path}`);
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (artifact.resourceKind === "serverFunction") {
|
|
200
|
+
const serverFunctionJson = await readRequiredJson<{ entry?: string }>(
|
|
201
|
+
artifact.absolutePath,
|
|
202
|
+
"serverFunction.json",
|
|
203
|
+
errors,
|
|
204
|
+
);
|
|
205
|
+
if (serverFunctionJson?.entry) {
|
|
206
|
+
const entryPath = path.join(
|
|
207
|
+
artifact.absolutePath,
|
|
208
|
+
serverFunctionJson.entry,
|
|
209
|
+
);
|
|
210
|
+
if (!(await pathExists(entryPath))) {
|
|
211
|
+
errors.push(
|
|
212
|
+
`serverFunction ${artifact.id} entry does not exist: ${serverFunctionJson.entry}`,
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (artifact.resourceKind === "apiSchema") {
|
|
219
|
+
if (artifact.value.schemaFormat !== "oneapi") {
|
|
220
|
+
errors.push(`apiSchema ${artifact.id} schemaFormat must be "oneapi"`);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (artifact.value.describes !== undefined) {
|
|
224
|
+
const reference = validateArtifactReference(
|
|
225
|
+
artifact.value.describes,
|
|
226
|
+
`apiSchema ${artifact.id} describes`,
|
|
227
|
+
errors,
|
|
228
|
+
);
|
|
229
|
+
if (
|
|
230
|
+
reference &&
|
|
231
|
+
!artifactKeys.has(artifactKey(reference.resourceKind, reference.id))
|
|
232
|
+
) {
|
|
233
|
+
const missingArtifact = artifactKey(
|
|
234
|
+
reference.resourceKind,
|
|
235
|
+
reference.id,
|
|
236
|
+
);
|
|
237
|
+
errors.push(
|
|
238
|
+
`apiSchema ${artifact.id} describes missing artifact: ${missingArtifact}`,
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return {
|
|
246
|
+
valid: errors.length === 0,
|
|
247
|
+
errors,
|
|
248
|
+
warnings,
|
|
249
|
+
};
|
|
250
|
+
}
|