@supacloud/compiler 0.17.0 → 0.21.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 CHANGED
@@ -2,10 +2,12 @@
2
2
 
3
3
  ## Local Delivery
4
4
 
5
- `supacloud-compiler plan --json` previews workload targets and route ownership.
5
+ `supacloud-compiler plan --json` previews workload targets, dependency closures,
6
+ route ownership, and required runtime capabilities without writing or deploying.
6
7
  `supacloud-compiler build-delivery --json` creates independent local factory bundles
7
8
  and an atomic inspection manifest, reusing unchanged artifacts without deployment.
8
- See [local delivery](./DELIVERY.md) for configuration, contracts, and limitations.
9
+ See [local delivery](./DELIVERY.md) for validated configuration, AI-facing
10
+ contracts, and the distinction between a topology preview and release evidence.
9
11
 
10
12
  ## Persistent Execution Policy
11
13
 
@@ -20,6 +22,37 @@ recovery suggestions and participate in JSON output and the existing no-write-on
20
22
  gate. These checks validate declared policy, not the implementation of a custom
21
23
  adapter. See [configuration and migration](../../docs/command-migration.md).
22
24
 
25
+ ## Source Migrations
26
+
27
+ The compiler includes deterministic, versioned source migrations for breaking
28
+ framework changes. The command is preview-only unless `--write` is explicit:
29
+
30
+ ```bash
31
+ # Preview files, replacements, and manual conflicts
32
+ bunx supacloud-compiler migrate --root . --json
33
+
34
+ # Apply only after reviewing the preview
35
+ bunx supacloud-compiler migrate --root . --write
36
+ ```
37
+
38
+ Migrations operate on TypeScript ASTs and skip `node_modules`, `dist`, and
39
+ `generated`. Route options are resolved through local constants,
40
+ `defineRouteContract(...)`, namespace properties, named imports, and the
41
+ project's `tsconfig` path/module settings, so a shared contract declaration is
42
+ changed once even when several controllers import it. If the contract is outside
43
+ the selected root/include set, or any file has an ambiguous transformation, the
44
+ command exits non-zero and writes no file. A successful write uses a
45
+ same-directory temporary file followed by replacement for each changed file; it
46
+ is not a version-control rollback mechanism. Review the diff, then run `compile`,
47
+ `check`, and focused tests with the same compiler version. Use version control to
48
+ revert a migration.
49
+
50
+ The current route-contract migration is `route-response-to-responses` (`0.11.0`
51
+ to `0.12.0`): it changes `response: Schema` into
52
+ `responses: { 200: Schema }`. It refuses to guess when `responses` is already
53
+ present. See the [route contract migration guide](../../docs/route-contract-migration.md)
54
+ for the complete upgrade and release sequence.
55
+
23
56
  FA-derived direct-command RPC ownership, contract inspection and POST command
24
57
  protocol migration are documented in `docs/fa-consumer-governance.md` in the
25
58
  repository. `context <module> --json` reports `routeContracts` and standalone
@@ -211,6 +244,7 @@ bunx supacloud-compiler dev
211
244
  | 文件发现 | `**/*.module.ts`、`**/*.ts` |
212
245
  | strict 类型安全门 | 开启 |
213
246
  | typed client | 开启 |
247
+ | OpenAPI 3.1 module | 开启 |
214
248
  | permissions manifest | 开启 |
215
249
  | module boundary preset | `modular-monolith` |
216
250
  | provider tree-shaking | 开启 |
@@ -225,6 +259,11 @@ export default defineSupacloudConfig({
225
259
  outDir: "generated",
226
260
  strict: true,
227
261
  generateClient: true,
262
+ generateOpenApi: true,
263
+ openApi: {
264
+ title: "Orders API",
265
+ version: "1.0.0",
266
+ },
228
267
  generatePermissions: true,
229
268
  moduleBoundaryPreset: "modular-monolith",
230
269
  commandCapabilities: {
@@ -241,6 +280,37 @@ export default defineSupacloudConfig({
241
280
  `commandCapabilities` 用于声明运行时实际支持的命令治理能力;命令声明了
242
281
  `permission`、`audit` 或 `idempotency` 时,若对应能力关闭,编译器会失败。
243
282
 
283
+ ## OpenAPI 与 Client Generator
284
+
285
+ 编译器从同一份 `ApplicationGraph` 生成 `client.ts` 和 `openapi.ts`,不引入
286
+ 反射或第二套路由注册。路由装饰器中显式声明的 TypeBox `body`、`params`、
287
+ `query`、`response` schema 会被静态导入;没有 schema 的字段保持为
288
+ `unknown`,不会从 TypeScript 类型推断出未经验证的运行时协议。
289
+
290
+ `client.ts` 提供路由方法、路径参数检查、请求类型和 `API_ROUTES`。已声明
291
+ 响应 schema 的方法不传 decoder 也会按 HTTP status 自动选择并校验内置 schema;
292
+ 传入 `ResponseDecoder<T>` 时,decoder 接收已经通过 schema 校验/规范化的值,
293
+ 可安全做日期、金额等业务转换。没有响应 schema 的方法仍返回原始 `unknown`,
294
+ 除非调用方显式提供 decoder。
295
+
296
+ `openapi.ts` 导出 `OPENAPI_DOCUMENT`、`OPENAPI_JSON` 和
297
+ `createOpenApiDocument()`。它包含 OpenAPI 3.1 路径、参数、请求体、响应、
298
+ 错误协议、默认 bearer security scheme,以及 `x-supacloud` 中的模块、命令、
299
+ 权限和静态 contract 元数据。文档只描述编译器发现的 HTTP routes;文件和
300
+ 流式响应仍由宿主运行时负责传输。
301
+
302
+ ```bash
303
+ # 导出可提交或交给文档工具的 JSON
304
+ bunx supacloud-compiler openapi-export generated/openapi.ts openapi.json
305
+
306
+ # 在 CI 中阻止破坏性 contract 变更
307
+ bunx supacloud-compiler openapi-diff openapi-baseline.json openapi.json --json
308
+ ```
309
+
310
+ `openapi-diff` 会检查路径/操作、参数必填性、请求体、响应状态和 schema 的
311
+ 枚举、属性与 required 变化;命令失败时返回非零退出码。基线文件由应用
312
+ 负责版本管理,生成的 `openapi.ts` 则由普通 `compile`/`check` 漂移检查维护。
313
+
244
314
  ## API
245
315
 
246
316
  ```ts
@@ -339,9 +409,88 @@ IDE 和 AI agent 做状态机漂移检查。
339
409
  - AOP 只支持静态边界:`ModuleOptions.aspects`、`RouteOptions.aspects`、`CommandOptions.aspects` 和 `JobOptions.aspects` 必须是显式数组字面量,元素必须是可解析的函数标识符。生成器会直接 import aspect 并生成固定顺序的 onion chain,不使用 Proxy、Reflect 扫描、动态 pointcut 或运行时注册。
340
410
  - 执行顺序为 `module -> route -> command -> commandGovernance -> handler`;Job 使用 `module -> job -> executor -> run/execute`,并在 finally 中销毁 job scope。
341
411
  - services 对象的 key 为 token 名的 camelCase:`CaseService → caseService`、`CASE_REPOSITORY → caseRepository`、`LOGGER → logger`。
342
- - controller 描述静态给出:`{ path, serviceKey, scope, routes: [{ method, path, handler, body?, params?, query?, response? }] }`,schema 直接引用 import 进来的对象。
412
+ - controller 描述静态给出:`{ path, serviceKey, scope, routes: [{ method, path, handler, body?, params?, query?, headers?, cookie?, response?, responses? }] }`,schema 直接引用 import 进来的对象。
413
+ - `client.ts` 在启用 `generateClient` 时生成:包含 `API_ROUTES`、`API_SCHEMAS`、类型化请求选项和显式响应 decoder 入口。
414
+ - `openapi.ts` 在启用 `generateOpenApi` 时生成:包含 OpenAPI 3.1 文档模块和可序列化 JSON;`check` 会将它纳入生成物漂移检查。
343
415
  - 严格生成模式会对 `application.ts`、可选的 `client.ts` 和 `permissions.ts` 做 AST 扫描,禁止生成 `any`。
344
416
 
417
+ `<outDir>/client.ts` 提供按 Controller 分组的 Fetch client。路径参数会从
418
+ controller 和 route 的完整路径合并推导;声明了 `response` 或 `responses` 的
419
+ route 会自动按 HTTP status 执行内置 response decoder,并返回 schema 推导的
420
+ 类型。显式 decoder 仍可用于覆盖自定义转换;没有响应 schema 的 route 返回
421
+ `unknown`。`headers`、`cookie` 和多状态 `responses` 会同步进入客户端和
422
+ OpenAPI。`buildRouteUrl` 和 `createApiClient` 可直接复用,也支持动态 headers
423
+ 和请求拦截器。
424
+
425
+ ### Migration from manual decoders
426
+
427
+ 旧版本要求调用方为每个有响应 schema 的 route 传入 decoder。升级后删除该
428
+ decoder 即可;需要保留自定义转换时,将它作为第二个参数传入。旧的单一
429
+ `response: Schema` 当前作为迁移桥接仍可编译,但新代码必须迁移到
430
+ `responses: { 200: Schema }` 或实际的状态映射;该桥接字段不保证在下一次破坏性
431
+ 版本继续保留。Management API 的契约注册表
432
+ 由实际 Elysia `app.routes` 投影生成,不应再维护平行的路由清单。
433
+
434
+ 完整的破坏性升级步骤(包括 headers、cookie、客户端 decoder、OpenAPI 和生成物
435
+ 刷新)见 [route contract migration guide](../../docs/route-contract-migration.md)。
436
+
437
+ `<outDir>/openapi.ts` 是无额外运行时依赖的 OpenAPI 3.1 module,导出
438
+ `OPENAPI_DOCUMENT`、`OPENAPI_JSON`、`createOpenApiDocument` 和
439
+ `serializeOpenApiDocument`。它在运行时读取同一组 TypeBox schema,生成 paths、
440
+ parameters、requestBody、responses、securitySchemes 以及 `x-supacloud` 路由元数据,
441
+ 因此不会维护第二份 API contract。默认包含 bearer JWT scheme;项目可在
442
+ `openApi` 配置中补充文档信息、servers 和其他显式 security schemes。
443
+
444
+ `OPENAPI_JSON` 是运行时快照;需要提交独立 `openapi.json` 时,在应用已经能加载
445
+ 生成模块的运行时调用 `exportGeneratedOpenApiJson()` 或直接写出该字符串。编译器
446
+ 不会为了生成 JSON 执行应用 schema。`readOpenApiJson()` 和
447
+ `diffOpenApiDocuments()` 可用于构建发布门禁:
448
+
449
+ ```ts
450
+ import {
451
+ exportGeneratedOpenApiJson,
452
+ diffOpenApiDocuments,
453
+ readOpenApiJson,
454
+ } from "@supacloud/compiler";
455
+
456
+ await exportGeneratedOpenApiJson({
457
+ modulePath: "./generated/openapi.ts",
458
+ outputPath: "./generated/openapi.json",
459
+ });
460
+
461
+ const diff = diffOpenApiDocuments(
462
+ await readOpenApiJson("./contracts/openapi.base.json"),
463
+ await readOpenApiJson("./generated/openapi.json"),
464
+ );
465
+ if (!diff.ok) throw new Error("OpenAPI breaking change");
466
+ ```
467
+
468
+ 也可以直接在 CI 中运行:
469
+
470
+ ```bash
471
+ supacloud-compiler openapi-export ./generated/openapi.ts ./generated/openapi.json
472
+ supacloud-compiler openapi-diff ./contracts/openapi.base.json ./generated/openapi.json --json
473
+ ```
474
+
475
+ `openapi-export` 在运行时加载生成的 `openapi.ts` 并原子地写出独立 JSON;它不会在
476
+ 编译阶段执行应用 schema。可用 `--space 0` 到 `--space 10` 控制缩进,重复执行不会
477
+ 改写内容不变的文件。当前只承诺 JSON 输出,YAML 转换由发布流水线按需处理。
478
+
479
+ diff 默认阻止路径/操作/参数/响应删除、请求约束收紧、响应字段收窄或安全要求新增;
480
+ 新增可选参数、路径、响应和组件会标记为 non-breaking。它是保守的合同门禁,不替代
481
+ 应用端的业务兼容性测试。
482
+
483
+ ```ts
484
+ export default defineSupacloudConfig({
485
+ generateClient: true,
486
+ generateOpenApi: true,
487
+ openApi: { title: "Orders API", version: "1.0.0" },
488
+ });
489
+ ```
490
+
491
+ 用 `--no-client` 或 `--no-openapi` 关闭对应产物;`compile` 和 `check` 会同时检查
492
+ 已生成的 `client.ts`、`openapi.ts` 是否与当前 ApplicationGraph 漂移。
493
+
345
494
  `<outDir>/app.manifest.json`:`{ version: 1, modules, externalTokens }`,供 CLI graph/explain 使用。
346
495
 
347
496
  ## 诊断码
@@ -452,8 +601,8 @@ bun run build
452
601
 
453
602
  ## Route Contract Policy
454
603
 
455
- Enable `requireRouteContracts: true` in `defineSupacloudConfig(...)` or
456
- `CompileOptions` to report `route-contract-required` errors in both compile and
604
+ Project configuration defaults to `requireRouteContracts: true`. Low-level
605
+ `CompileOptions` callers can set it explicitly to report `route-contract-required` errors in both compile and
457
606
  check (including JSON diagnostics). Changing this option invalidates incremental
458
607
  results. Combine it with `writeOnError: false` when programmatic compilation must
459
608
  not emit files on errors.
@@ -463,10 +612,20 @@ query, and response declarations. Required inputs are detected from handler
463
612
  bindings and controller/route path parameters. Responses always require an
464
613
  explicit declaration, including intentional void contracts.
465
614
 
466
- This checks declaration coverage only, not schema quality, handler/schema type
615
+ This checks declaration coverage and rejects known opaque schemas, not full schema quality, handler/schema type
467
616
  equivalence, or database authorization. It deliberately does not auto-fix missing
468
617
  schemas with `unknown` placeholders. Consumers must define the actual contracts
469
- and test decoding separately. The policy defaults to false for existing projects.
618
+ and test decoding separately. Native output must be classified; delegated
619
+ validation and native transports require a `contract.evidence` test reference.
620
+ The report still sets `verified: false`, since a reference does not prove execution.
621
+
622
+ The source type gate now includes TypeScript syntactic/semantic diagnostics and
623
+ rejects production `@ts-ignore`, `@ts-nocheck` and `@ts-expect-error`. A source-directory root resolves
624
+ the enclosing tsconfig. When this gate is enabled, incremental compilation
625
+ rechecks types instead of returning an unchecked cached result.
626
+
627
+ Generated route calls require all path parameters and a decoder for typed
628
+ responses. See [type safety and migration](../../docs/type-safety.md).
470
629
 
471
630
  ## License
472
631