@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 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.15.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;
@@ -14,6 +14,7 @@ export interface GenerateOptions {
14
14
  export interface RenderedArtifacts {
15
15
  applicationCode: string;
16
16
  manifestJson: string;
17
+ contractsManifestJson: string;
17
18
  clientCode?: string;
18
19
  openApiCode?: string;
19
20
  permissionsCode?: string;
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.15.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.23.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",