@supacloud/compiler 0.23.0 → 0.24.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/cli.js +94 -4
- package/dist/contract-manifest.d.ts +51 -0
- package/dist/generate.d.ts +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +96 -4
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -78,6 +78,83 @@ function findClosestMatch(target, candidates) {
|
|
|
78
78
|
}
|
|
79
79
|
var REQUEST_CONTEXT_TOKEN_NAME = "supacloud.request-context", JOB_CONTEXT_TOKEN_NAME = "supacloud.job-context";
|
|
80
80
|
|
|
81
|
+
// src/contract-manifest.ts
|
|
82
|
+
function buildContractManifest(graph, artifacts) {
|
|
83
|
+
const commands = [];
|
|
84
|
+
const queries = [];
|
|
85
|
+
const routes = [];
|
|
86
|
+
const permissions = new Set;
|
|
87
|
+
const rpc = [];
|
|
88
|
+
const events = [];
|
|
89
|
+
const fixtures = new Set;
|
|
90
|
+
for (const module of graph.modules) {
|
|
91
|
+
for (const command of module.commands) {
|
|
92
|
+
if (command.permission)
|
|
93
|
+
permissions.add(command.permission);
|
|
94
|
+
if (command.rpc)
|
|
95
|
+
rpc.push({ command: command.name, adapter: command.rpc });
|
|
96
|
+
if (command.audit)
|
|
97
|
+
events.push({ name: command.audit, source: "command.audit", command: command.name });
|
|
98
|
+
commands.push({
|
|
99
|
+
name: command.name,
|
|
100
|
+
className: command.className,
|
|
101
|
+
module: module.name,
|
|
102
|
+
...command.permission === undefined ? {} : { permission: command.permission },
|
|
103
|
+
...command.rpc === undefined ? {} : { rpc: command.rpc },
|
|
104
|
+
transaction: command.transaction,
|
|
105
|
+
idempotency: command.idempotency,
|
|
106
|
+
...command.audit === undefined ? {} : { auditEvent: command.audit }
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
for (const query of module.queries) {
|
|
110
|
+
queries.push({ name: query.name, className: query.className, module: module.name });
|
|
111
|
+
}
|
|
112
|
+
for (const controller of module.controllers) {
|
|
113
|
+
for (const route of controller.routes) {
|
|
114
|
+
const command = route.command ? module.commands.find((candidate) => candidate.className === route.command) : undefined;
|
|
115
|
+
if (command?.permission)
|
|
116
|
+
permissions.add(command.permission);
|
|
117
|
+
if (route.contract?.evidence)
|
|
118
|
+
fixtures.add(route.contract.evidence);
|
|
119
|
+
routes.push({
|
|
120
|
+
method: route.method,
|
|
121
|
+
path: joinRoutePaths(controller.path, route.path),
|
|
122
|
+
module: module.name,
|
|
123
|
+
controller: controller.className,
|
|
124
|
+
handler: route.handler,
|
|
125
|
+
...route.command === undefined ? {} : { command: route.command },
|
|
126
|
+
...command?.permission === undefined ? {} : { permission: command.permission },
|
|
127
|
+
requestSchemas: Object.fromEntries(["body", "params", "query", "headers", "cookie"].flatMap((key) => route[key] === undefined ? [] : [[key, route[key]]])),
|
|
128
|
+
...route.response === undefined ? {} : { responseSchema: route.response },
|
|
129
|
+
...route.contract?.evidence === undefined ? {} : { evidence: route.contract.evidence }
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
commands.sort((left, right) => left.name.localeCompare(right.name));
|
|
135
|
+
queries.sort((left, right) => left.name.localeCompare(right.name));
|
|
136
|
+
routes.sort((left, right) => `${left.method} ${left.path}`.localeCompare(`${right.method} ${right.path}`));
|
|
137
|
+
rpc.sort((left, right) => left.command.localeCompare(right.command));
|
|
138
|
+
events.sort((left, right) => left.name.localeCompare(right.name));
|
|
139
|
+
return {
|
|
140
|
+
version: 1,
|
|
141
|
+
commands,
|
|
142
|
+
queries,
|
|
143
|
+
routes,
|
|
144
|
+
permissions: [...permissions].sort(),
|
|
145
|
+
rpc,
|
|
146
|
+
events,
|
|
147
|
+
fixtures: [...fixtures].sort(),
|
|
148
|
+
artifacts: {
|
|
149
|
+
...artifacts,
|
|
150
|
+
sdk: "generated-client",
|
|
151
|
+
openapiDocument: "generated",
|
|
152
|
+
permissionManifest: "generated"
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
var init_contract_manifest = () => {};
|
|
157
|
+
|
|
81
158
|
// src/generate.ts
|
|
82
159
|
import { createHash as createHash4 } from "node:crypto";
|
|
83
160
|
import { access, mkdir, rename, unlink, writeFile } from "node:fs/promises";
|
|
@@ -219,9 +296,16 @@ function renderApplication(graph, options) {
|
|
|
219
296
|
const clientCode = options.generateClient ? renderClient(graph, options) : undefined;
|
|
220
297
|
const openApiCode = options.generateOpenApi ? renderOpenApi(graph, options) : undefined;
|
|
221
298
|
const permissionsCode = options.generatePermissions ? renderPermissions(graph) : undefined;
|
|
299
|
+
const contractsManifest = buildContractManifest(graph, {
|
|
300
|
+
client: clientCode !== undefined,
|
|
301
|
+
openapi: openApiCode !== undefined,
|
|
302
|
+
permissions: permissionsCode !== undefined
|
|
303
|
+
});
|
|
222
304
|
return {
|
|
223
305
|
applicationCode: code,
|
|
224
306
|
manifestJson: JSON.stringify(manifest, null, 2) + `
|
|
307
|
+
`,
|
|
308
|
+
contractsManifestJson: JSON.stringify(contractsManifest, null, 2) + `
|
|
225
309
|
`,
|
|
226
310
|
...clientCode === undefined ? {} : { clientCode },
|
|
227
311
|
...openApiCode === undefined ? {} : { openApiCode },
|
|
@@ -233,9 +317,11 @@ async function generateApplication(graph, options) {
|
|
|
233
317
|
await mkdir(options.outDir, { recursive: true });
|
|
234
318
|
const applicationPath = join2(options.outDir, "application.ts");
|
|
235
319
|
const manifestPath = join2(options.outDir, "app.manifest.json");
|
|
320
|
+
const contractsManifestPath = join2(options.outDir, "contracts.manifest.json");
|
|
236
321
|
const writeCandidates = [
|
|
237
322
|
{ path: applicationPath, content: rendered.applicationCode },
|
|
238
|
-
{ path: manifestPath, content: rendered.manifestJson }
|
|
323
|
+
{ path: manifestPath, content: rendered.manifestJson },
|
|
324
|
+
{ path: contractsManifestPath, content: rendered.contractsManifestJson }
|
|
239
325
|
];
|
|
240
326
|
if (rendered.clientCode) {
|
|
241
327
|
writeCandidates.push({ path: join2(options.outDir, "client.ts"), content: rendered.clientCode });
|
|
@@ -2096,7 +2182,9 @@ function destroyScopeInstances(
|
|
|
2096
2182
|
scopeDestructions.set(scope, destruction);
|
|
2097
2183
|
return destruction;
|
|
2098
2184
|
}`;
|
|
2099
|
-
var init_generate = () => {
|
|
2185
|
+
var init_generate = __esm(() => {
|
|
2186
|
+
init_contract_manifest();
|
|
2187
|
+
});
|
|
2100
2188
|
|
|
2101
2189
|
// src/graphql-client.ts
|
|
2102
2190
|
var GRAPHQL_CLIENT_SOURCE = `
|
|
@@ -6985,6 +7073,7 @@ async function compileProject(options) {
|
|
|
6985
7073
|
"client.ts": rendered.clientCode,
|
|
6986
7074
|
"openapi.ts": rendered.openApiCode,
|
|
6987
7075
|
"permissions.ts": rendered.permissionsCode,
|
|
7076
|
+
"contracts.manifest.json": rendered.contractsManifestJson,
|
|
6988
7077
|
"graphql.ts": graphql.files["graphql.ts"],
|
|
6989
7078
|
"graphql.documents.ts": graphql.files["graphql.documents.ts"]
|
|
6990
7079
|
}, options.strict ?? false));
|
|
@@ -7054,7 +7143,8 @@ async function checkProject(options) {
|
|
|
7054
7143
|
const expectedFiles = {
|
|
7055
7144
|
...graphql.files,
|
|
7056
7145
|
"application.ts": rendered.applicationCode,
|
|
7057
|
-
"app.manifest.json": rendered.manifestJson
|
|
7146
|
+
"app.manifest.json": rendered.manifestJson,
|
|
7147
|
+
"contracts.manifest.json": rendered.contractsManifestJson
|
|
7058
7148
|
};
|
|
7059
7149
|
if (rendered.clientCode) {
|
|
7060
7150
|
expectedFiles["client.ts"] = rendered.clientCode;
|
|
@@ -12205,7 +12295,7 @@ function compilerVersion() {
|
|
|
12205
12295
|
}
|
|
12206
12296
|
function migrationDependencies() {
|
|
12207
12297
|
return {
|
|
12208
|
-
"@supacloud/app": "0.
|
|
12298
|
+
"@supacloud/app": "0.16.0",
|
|
12209
12299
|
"@supacloud/compiler": compilerVersion(),
|
|
12210
12300
|
"@supacloud/elysia": "0.18.0",
|
|
12211
12301
|
elysia: "1.4.30",
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { ApplicationGraph } from "./types";
|
|
2
|
+
export interface ContractManifest {
|
|
3
|
+
version: 1;
|
|
4
|
+
commands: Array<{
|
|
5
|
+
name: string;
|
|
6
|
+
className: string;
|
|
7
|
+
module: string;
|
|
8
|
+
permission?: string;
|
|
9
|
+
rpc?: string;
|
|
10
|
+
transaction: "required" | "none";
|
|
11
|
+
idempotency: "required" | "none";
|
|
12
|
+
auditEvent?: string;
|
|
13
|
+
}>;
|
|
14
|
+
queries: Array<{
|
|
15
|
+
name: string;
|
|
16
|
+
className: string;
|
|
17
|
+
module: string;
|
|
18
|
+
}>;
|
|
19
|
+
routes: Array<{
|
|
20
|
+
method: string;
|
|
21
|
+
path: string;
|
|
22
|
+
module: string;
|
|
23
|
+
controller: string;
|
|
24
|
+
handler: string;
|
|
25
|
+
command?: string;
|
|
26
|
+
permission?: string;
|
|
27
|
+
requestSchemas: Partial<Record<"body" | "params" | "query" | "headers" | "cookie", string>>;
|
|
28
|
+
responseSchema?: string;
|
|
29
|
+
evidence?: string;
|
|
30
|
+
}>;
|
|
31
|
+
permissions: string[];
|
|
32
|
+
rpc: Array<{
|
|
33
|
+
command: string;
|
|
34
|
+
adapter: string;
|
|
35
|
+
}>;
|
|
36
|
+
events: Array<{
|
|
37
|
+
name: string;
|
|
38
|
+
source: string;
|
|
39
|
+
command: string;
|
|
40
|
+
}>;
|
|
41
|
+
fixtures: string[];
|
|
42
|
+
artifacts: {
|
|
43
|
+
client: boolean;
|
|
44
|
+
openapi: boolean;
|
|
45
|
+
permissions: boolean;
|
|
46
|
+
sdk: "generated-client";
|
|
47
|
+
openapiDocument: "generated";
|
|
48
|
+
permissionManifest: "generated";
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
export declare function buildContractManifest(graph: ApplicationGraph, artifacts: Pick<ContractManifest["artifacts"], "client" | "openapi" | "permissions">): ContractManifest;
|
package/dist/generate.d.ts
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -22,6 +22,8 @@ export { TraitCompiler } from "./traits";
|
|
|
22
22
|
export type { TraitCompilation, TraitHandler, TraitKind, TraitRecord } from "./traits";
|
|
23
23
|
export { generateApplication, renderApplication, renderClient, renderOpenApi } from "./generate";
|
|
24
24
|
export type { GenerateOptions, RenderedArtifacts } from "./generate";
|
|
25
|
+
export { buildContractManifest } from "./contract-manifest";
|
|
26
|
+
export type { ContractManifest } from "./contract-manifest";
|
|
25
27
|
export { diffOpenApiDocuments, exportGeneratedOpenApiJson, formatOpenApiDiff, loadGeneratedOpenApiDocument, parseOpenApiDocument, readOpenApiJson, serializeOpenApiJson, writeOpenApiJson, OpenApiDocumentError, } from "./openapi-tools";
|
|
26
28
|
export type { OpenApiDiffChange, OpenApiDiffResult, OpenApiDocument, OpenApiExportOptions, OpenApiJsonWriteResult, OpenApiObject, } from "./openapi-tools";
|
|
27
29
|
export type { ContextPack, DoctorResult, ExecutionPlan } from "./inspect";
|
package/dist/index.js
CHANGED
|
@@ -77,6 +77,83 @@ function findClosestMatch(target, candidates) {
|
|
|
77
77
|
}
|
|
78
78
|
var REQUEST_CONTEXT_TOKEN_NAME = "supacloud.request-context", JOB_CONTEXT_TOKEN_NAME = "supacloud.job-context";
|
|
79
79
|
|
|
80
|
+
// src/contract-manifest.ts
|
|
81
|
+
function buildContractManifest(graph, artifacts) {
|
|
82
|
+
const commands = [];
|
|
83
|
+
const queries = [];
|
|
84
|
+
const routes = [];
|
|
85
|
+
const permissions = new Set;
|
|
86
|
+
const rpc = [];
|
|
87
|
+
const events = [];
|
|
88
|
+
const fixtures = new Set;
|
|
89
|
+
for (const module of graph.modules) {
|
|
90
|
+
for (const command of module.commands) {
|
|
91
|
+
if (command.permission)
|
|
92
|
+
permissions.add(command.permission);
|
|
93
|
+
if (command.rpc)
|
|
94
|
+
rpc.push({ command: command.name, adapter: command.rpc });
|
|
95
|
+
if (command.audit)
|
|
96
|
+
events.push({ name: command.audit, source: "command.audit", command: command.name });
|
|
97
|
+
commands.push({
|
|
98
|
+
name: command.name,
|
|
99
|
+
className: command.className,
|
|
100
|
+
module: module.name,
|
|
101
|
+
...command.permission === undefined ? {} : { permission: command.permission },
|
|
102
|
+
...command.rpc === undefined ? {} : { rpc: command.rpc },
|
|
103
|
+
transaction: command.transaction,
|
|
104
|
+
idempotency: command.idempotency,
|
|
105
|
+
...command.audit === undefined ? {} : { auditEvent: command.audit }
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
for (const query of module.queries) {
|
|
109
|
+
queries.push({ name: query.name, className: query.className, module: module.name });
|
|
110
|
+
}
|
|
111
|
+
for (const controller of module.controllers) {
|
|
112
|
+
for (const route of controller.routes) {
|
|
113
|
+
const command = route.command ? module.commands.find((candidate) => candidate.className === route.command) : undefined;
|
|
114
|
+
if (command?.permission)
|
|
115
|
+
permissions.add(command.permission);
|
|
116
|
+
if (route.contract?.evidence)
|
|
117
|
+
fixtures.add(route.contract.evidence);
|
|
118
|
+
routes.push({
|
|
119
|
+
method: route.method,
|
|
120
|
+
path: joinRoutePaths(controller.path, route.path),
|
|
121
|
+
module: module.name,
|
|
122
|
+
controller: controller.className,
|
|
123
|
+
handler: route.handler,
|
|
124
|
+
...route.command === undefined ? {} : { command: route.command },
|
|
125
|
+
...command?.permission === undefined ? {} : { permission: command.permission },
|
|
126
|
+
requestSchemas: Object.fromEntries(["body", "params", "query", "headers", "cookie"].flatMap((key) => route[key] === undefined ? [] : [[key, route[key]]])),
|
|
127
|
+
...route.response === undefined ? {} : { responseSchema: route.response },
|
|
128
|
+
...route.contract?.evidence === undefined ? {} : { evidence: route.contract.evidence }
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
commands.sort((left, right) => left.name.localeCompare(right.name));
|
|
134
|
+
queries.sort((left, right) => left.name.localeCompare(right.name));
|
|
135
|
+
routes.sort((left, right) => `${left.method} ${left.path}`.localeCompare(`${right.method} ${right.path}`));
|
|
136
|
+
rpc.sort((left, right) => left.command.localeCompare(right.command));
|
|
137
|
+
events.sort((left, right) => left.name.localeCompare(right.name));
|
|
138
|
+
return {
|
|
139
|
+
version: 1,
|
|
140
|
+
commands,
|
|
141
|
+
queries,
|
|
142
|
+
routes,
|
|
143
|
+
permissions: [...permissions].sort(),
|
|
144
|
+
rpc,
|
|
145
|
+
events,
|
|
146
|
+
fixtures: [...fixtures].sort(),
|
|
147
|
+
artifacts: {
|
|
148
|
+
...artifacts,
|
|
149
|
+
sdk: "generated-client",
|
|
150
|
+
openapiDocument: "generated",
|
|
151
|
+
permissionManifest: "generated"
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
var init_contract_manifest = () => {};
|
|
156
|
+
|
|
80
157
|
// src/generate.ts
|
|
81
158
|
import { createHash as createHash4 } from "node:crypto";
|
|
82
159
|
import { access, mkdir, rename, unlink, writeFile } from "node:fs/promises";
|
|
@@ -218,9 +295,16 @@ function renderApplication(graph, options) {
|
|
|
218
295
|
const clientCode = options.generateClient ? renderClient(graph, options) : undefined;
|
|
219
296
|
const openApiCode = options.generateOpenApi ? renderOpenApi(graph, options) : undefined;
|
|
220
297
|
const permissionsCode = options.generatePermissions ? renderPermissions(graph) : undefined;
|
|
298
|
+
const contractsManifest = buildContractManifest(graph, {
|
|
299
|
+
client: clientCode !== undefined,
|
|
300
|
+
openapi: openApiCode !== undefined,
|
|
301
|
+
permissions: permissionsCode !== undefined
|
|
302
|
+
});
|
|
221
303
|
return {
|
|
222
304
|
applicationCode: code,
|
|
223
305
|
manifestJson: JSON.stringify(manifest, null, 2) + `
|
|
306
|
+
`,
|
|
307
|
+
contractsManifestJson: JSON.stringify(contractsManifest, null, 2) + `
|
|
224
308
|
`,
|
|
225
309
|
...clientCode === undefined ? {} : { clientCode },
|
|
226
310
|
...openApiCode === undefined ? {} : { openApiCode },
|
|
@@ -232,9 +316,11 @@ async function generateApplication(graph, options) {
|
|
|
232
316
|
await mkdir(options.outDir, { recursive: true });
|
|
233
317
|
const applicationPath = join2(options.outDir, "application.ts");
|
|
234
318
|
const manifestPath = join2(options.outDir, "app.manifest.json");
|
|
319
|
+
const contractsManifestPath = join2(options.outDir, "contracts.manifest.json");
|
|
235
320
|
const writeCandidates = [
|
|
236
321
|
{ path: applicationPath, content: rendered.applicationCode },
|
|
237
|
-
{ path: manifestPath, content: rendered.manifestJson }
|
|
322
|
+
{ path: manifestPath, content: rendered.manifestJson },
|
|
323
|
+
{ path: contractsManifestPath, content: rendered.contractsManifestJson }
|
|
238
324
|
];
|
|
239
325
|
if (rendered.clientCode) {
|
|
240
326
|
writeCandidates.push({ path: join2(options.outDir, "client.ts"), content: rendered.clientCode });
|
|
@@ -2095,7 +2181,9 @@ function destroyScopeInstances(
|
|
|
2095
2181
|
scopeDestructions.set(scope, destruction);
|
|
2096
2182
|
return destruction;
|
|
2097
2183
|
}`;
|
|
2098
|
-
var init_generate = () => {
|
|
2184
|
+
var init_generate = __esm(() => {
|
|
2185
|
+
init_contract_manifest();
|
|
2186
|
+
});
|
|
2099
2187
|
|
|
2100
2188
|
// src/graphql-client.ts
|
|
2101
2189
|
var GRAPHQL_CLIENT_SOURCE = `
|
|
@@ -6982,6 +7070,7 @@ async function compileProject(options) {
|
|
|
6982
7070
|
"client.ts": rendered.clientCode,
|
|
6983
7071
|
"openapi.ts": rendered.openApiCode,
|
|
6984
7072
|
"permissions.ts": rendered.permissionsCode,
|
|
7073
|
+
"contracts.manifest.json": rendered.contractsManifestJson,
|
|
6985
7074
|
"graphql.ts": graphql.files["graphql.ts"],
|
|
6986
7075
|
"graphql.documents.ts": graphql.files["graphql.documents.ts"]
|
|
6987
7076
|
}, options.strict ?? false));
|
|
@@ -7051,7 +7140,8 @@ async function checkProject(options) {
|
|
|
7051
7140
|
const expectedFiles = {
|
|
7052
7141
|
...graphql.files,
|
|
7053
7142
|
"application.ts": rendered.applicationCode,
|
|
7054
|
-
"app.manifest.json": rendered.manifestJson
|
|
7143
|
+
"app.manifest.json": rendered.manifestJson,
|
|
7144
|
+
"contracts.manifest.json": rendered.contractsManifestJson
|
|
7055
7145
|
};
|
|
7056
7146
|
if (rendered.clientCode) {
|
|
7057
7147
|
expectedFiles["client.ts"] = rendered.clientCode;
|
|
@@ -11775,7 +11865,7 @@ function compilerVersion() {
|
|
|
11775
11865
|
}
|
|
11776
11866
|
function migrationDependencies() {
|
|
11777
11867
|
return {
|
|
11778
|
-
"@supacloud/app": "0.
|
|
11868
|
+
"@supacloud/app": "0.16.0",
|
|
11779
11869
|
"@supacloud/compiler": compilerVersion(),
|
|
11780
11870
|
"@supacloud/elysia": "0.18.0",
|
|
11781
11871
|
elysia: "1.4.30",
|
|
@@ -12477,6 +12567,7 @@ function exportGraphDot(graph) {
|
|
|
12477
12567
|
|
|
12478
12568
|
// src/index.ts
|
|
12479
12569
|
init_generate();
|
|
12570
|
+
init_contract_manifest();
|
|
12480
12571
|
|
|
12481
12572
|
// src/openapi-tools.ts
|
|
12482
12573
|
import { mkdir as mkdir4, readFile as readFile10, rename as rename6, unlink as unlink3, writeFile as writeFile5 } from "node:fs/promises";
|
|
@@ -13143,6 +13234,7 @@ export {
|
|
|
13143
13234
|
TraitCompiler,
|
|
13144
13235
|
analyzeProject,
|
|
13145
13236
|
applyDiagnosticFix,
|
|
13237
|
+
buildContractManifest,
|
|
13146
13238
|
buildDeliveryProject,
|
|
13147
13239
|
camelName,
|
|
13148
13240
|
checkProject,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@supacloud/compiler",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.0",
|
|
4
4
|
"description": "Static compiler for @supacloud/app metadata: builds the application graph from AST, validates it, and generates reflection-free factory code",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|