@supacloud/compiler 0.51.2 → 0.52.1

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/DELIVERY.md CHANGED
@@ -62,25 +62,48 @@ their kind. HTTP and Job ownership are independent, so one module can contribute
62
62
  HTTP to `api` and Jobs to `jobs`. Two HTTP targets cannot both own the same module.
63
63
  `api` and `jobs` are reserved for their matching workload kinds.
64
64
 
65
- `kind: "function"` declares an Edge Function target instead of an app-module
66
- target. It carries a `function` descriptor (`slug`, `entrypoint`, `verifyJwt`)
67
- and must not declare `modules`. Function targets own no HTTP routes or Jobs, so
68
- the plan surfaces them alongside the module-owned targets:
65
+ `kind: "function"` declares an Edge Function target. It always carries a
66
+ `function` descriptor (`slug`, `entrypoint`, `verifyJwt`). A Function target may
67
+ be standalone or own modules:
68
+
69
+ - **Standalone** (`modules` omitted): the entrypoint is the Function handler
70
+ itself. It may export a default function/object or named `fetch`/`handle`.
71
+ - **Module-backed** (for example `modules: ["reports"]`): the entrypoint is a
72
+ trusted host composition module exporting
73
+ `createDeliveryApplication(modules, lifecycle)`, the same contract used by an
74
+ HTTP host. The selected modules' HTTP routes move to the Function target; their
75
+ Jobs do not. This is the supported way to split a heavy module out of `api`
76
+ without writing a second application by hand.
69
77
 
70
78
  ```ts
71
79
  {
72
80
  version: 1,
73
81
  targets: [
82
+ { name: "reports", kind: "function", modules: ["reports"],
83
+ function: { slug: "app-reports", entrypoint: "functions/reports-host.ts", verifyJwt: true } },
74
84
  { name: "intake-hooks", kind: "function",
75
- function: { slug: "fa-intake-hooks", entrypoint: "functions/intake/index.ts", verifyJwt: false } },
85
+ function: { slug: "app-intake-hooks", entrypoint: "functions/intake/index.ts", verifyJwt: false } },
76
86
  ],
77
87
  }
78
88
  ```
79
89
 
80
- This version only **plans** function targets. The local delivery build still
81
- rejects them with `delivery-function-build-unsupported`; rendering, upload and
82
- activation belong to a later delivery phase (see the project deployment
83
- Function/schedule work). Existing HTTP/Worker configuration is unaffected.
90
+ For a module/feature declaration, the `functions` shorthand expands to the same
91
+ validated Function target. The target name defaults to the slug:
92
+
93
+ ```ts
94
+ {
95
+ version: 1,
96
+ functions: [
97
+ { slug: "app-reports", modules: ["reports"],
98
+ entrypoint: "functions/reports-host.ts", verifyJwt: true },
99
+ ],
100
+ }
101
+ ```
102
+
103
+ Expansion is deterministic and happens before route ownership. Duplicate target
104
+ names, duplicate Function slugs, and modules owned by two HTTP/Function targets
105
+ fail closed. The shorthand does not guess entrypoints or permissions. `api` and
106
+ `jobs` remain reserved for their matching workload kinds.
84
107
 
85
108
  No unused explicit target is accepted. Unknown modules, duplicate ownership,
86
109
  unresolved imports, cyclic dependencies, duplicate job names, and conflicting
@@ -152,7 +175,8 @@ delivery/
152
175
  generated/application.ts
153
176
  bundle/index.js
154
177
  bundle/package.json
155
- bundle/app.manifest.json
178
+ bundle/app.manifest.json # module-backed targets
179
+ bundle/function.json # Function targets only
156
180
  bundle/target.json
157
181
  bundle/assets/...
158
182
  ```
@@ -287,8 +311,10 @@ HOST=127.0.0.1 PORT=3000 bun --no-env-file bundle/index.js
287
311
  ```
288
312
 
289
313
  HTTP objects have `entryKind: "bun-http-application"`; their entry is an executable
290
- that starts the listener, not an import-only module factory. Targets without a
291
- host keep `entryKind: "compiled-module-factory"`. All object kinds still report
314
+ that starts the listener, not an import-only module factory. Function targets
315
+ produce `entryKind: "bun-function-application"` entries exporting a default
316
+ `fetch` handler. Targets without a host keep
317
+ `entryKind: "compiled-module-factory"`. All object kinds still report
292
318
  `deploymentReady: false`: a local build neither activates a service nor verifies
293
319
  its environment, migrations, health, credentials, isolation or recovery.
294
320
  HTTP object identities bind their executable discriminator; existing factory
@@ -310,6 +336,31 @@ and host cleanup; expiry exits nonzero without claiming cleanup succeeded.
310
336
  Startup, listener and shutdown failures use generic messages; the host remains
311
337
  responsible for sanitizing its own logging and responses.
312
338
 
339
+ ## Runnable Functions
340
+
341
+ Module-backed Function targets use the same host contract as HTTP targets:
342
+ export `createDeliveryApplication(modules, lifecycle)` from the declared
343
+ entrypoint. The generated bundle exports a default `{ fetch(request) }` object.
344
+ The host is created lazily once per Function isolate and the host owns its
345
+ identity, database, governance, and error policy. The compiler does not provide
346
+ an anonymous or in-memory fallback.
347
+
348
+ Standalone Function targets do not require a compiler application graph. Their
349
+ declared entrypoint is imported and its default export (or the module namespace
350
+ when there is no default) becomes the Function handler. Use this for an existing
351
+ fetch handler that has no module-owned HTTP routes.
352
+
353
+ Function bundles include `bundle/function.json` with the attested slug,
354
+ entrypoint, and `verifyJwt` declaration. Module-backed Functions also include
355
+ the normal application manifest, execution context, and development metadata;
356
+ standalone Functions do not. Function objects carry
357
+ `entryKind: "bun-function-application"` in their immutable identity.
358
+
359
+ This stage builds local Function artifacts only. Application release upload still
360
+ rejects a delivery manifest containing Function targets, so they cannot be
361
+ silently started as application HTTP services. Creating the Edge Function
362
+ version, activation, and receipt remains a separate trusted release operation.
363
+
313
364
  ## Runnable Workers
314
365
 
315
366
  Job targets can explicitly bind a worker host:
package/README.md CHANGED
@@ -594,6 +594,42 @@ parameters、requestBody、responses、securitySchemes 以及 `x-supacloud` 路
594
594
  因此不会维护第二份 API contract。默认包含 bearer JWT scheme;项目可在
595
595
  `openApi` 配置中补充文档信息、servers 和其他显式 security schemes。
596
596
 
597
+ 内存受限的业务进程可配置 `openApi: { snapshots: false }`,只生成
598
+ `createOpenApiDocument` 和 `serializeOpenApiDocument` 工厂,不导出
599
+ `OPENAPI_DOCUMENT` / `OPENAPI_JSON`,也不在模块加载时转换完整文档或序列化 JSON。
600
+ 例如:
601
+
602
+ ```ts
603
+ export default defineSupacloudConfig({
604
+ generateOpenApi: true,
605
+ openApi: { title: "Orders API", version: "1.0.0", snapshots: false },
606
+ });
607
+ ```
608
+
609
+ 重新生成后,可将工厂传给可选文档插件,按请求创建文档:
610
+
611
+ ```ts
612
+ import { createOpenApiDocument } from "./generated/openapi";
613
+
614
+ const documentation = {
615
+ openApi: { document: createOpenApiDocument },
616
+ };
617
+ // createApplication({ ..., documentation })
618
+ ```
619
+
620
+ 该模式仍加载文档所引用的 schema 和路由定义,但不会缓存生成的文档对象或 JSON
621
+ 字符串。`serializeOpenApiDocument()` 每次调用都会创建并序列化一份新文档;传入
622
+ 显式 document 时直接序列化该对象。应用路由的响应校验不受影响。
623
+
624
+ 需要彻底移出业务进程时,在构建/发布阶段使用下方 `openapi-export` 导出静态
625
+ `openapi.json`,由静态服务或独立文档入口提供,业务入口不再导入生成的 OpenAPI
626
+ 模块。GraphQL SDL 同样可直接发布已有 `schema.graphql` 文件;生成的 GraphQL
627
+ 查询和结果校验应继续保留。部署必须显式携带这些静态文件,单文件函数 bundle
628
+ 不能假设请求时可访问源码目录。
629
+
630
+ `snapshots` 默认仍为 `true`,保持旧导出兼容。快照初始化带有 pure 注解,支持该
631
+ 注解的打包器可移除未使用的快照;未打包导入默认模块仍会创建两份快照。
632
+
597
633
  `OPENAPI_JSON` 是运行时快照;需要提交独立 `openapi.json` 时,在应用已经能加载
598
634
  生成模块的运行时调用 `exportGeneratedOpenApiJson()` 或直接写出该字符串。编译器
599
635
  不会为了生成 JSON 执行应用 schema。`readOpenApiJson()` 和
@@ -625,7 +661,8 @@ supacloud-compiler openapi-export ./generated/openapi.ts ./generated/openapi.jso
625
661
  supacloud-compiler openapi-diff ./contracts/openapi.base.json ./generated/openapi.json --json
626
662
  ```
627
663
 
628
- `openapi-export` 在运行时加载生成的 `openapi.ts` 并原子地写出独立 JSON;它不会在
664
+ `openapi-export` 在运行时加载生成的 `openapi.ts` 并原子地写出独立 JSON;也支持
665
+ `snapshots: false` 的模块,此时调用 `createOpenApiDocument()`。它不会在
629
666
  编译阶段执行应用 schema。可用 `--space 0` 到 `--space 10` 控制缩进,重复执行不会
630
667
  改写内容不变的文件。当前只承诺 JSON 输出,YAML 转换由发布流水线按需处理。
631
668
 
package/dist/cli.js CHANGED
@@ -3438,6 +3438,7 @@ ${procedureMethods.join(`
3438
3438
  function renderOpenApi(graph, options) {
3439
3439
  const rootDir = options?.rootDir ?? process.cwd();
3440
3440
  const outDir = options?.outDir ?? process.cwd();
3441
+ const snapshots = options?.openApi?.snapshots !== false;
3441
3442
  const imports = new ImportManager;
3442
3443
  const schemaLocals = new Map;
3443
3444
  const schemaComponents = new Map;
@@ -3933,14 +3934,18 @@ function renderOpenApi(graph, options) {
3933
3934
  " };",
3934
3935
  "}",
3935
3936
  "",
3936
- "export const OPENAPI_DOCUMENT = createOpenApiDocument();",
3937
- "",
3938
- "export function serializeOpenApiDocument(document: OpenApiDocument = OPENAPI_DOCUMENT, space = 2): string {",
3937
+ ...snapshots ? [
3938
+ "export const OPENAPI_DOCUMENT = /* @__PURE__ */ createOpenApiDocument();",
3939
+ ""
3940
+ ] : [],
3941
+ `export function serializeOpenApiDocument(document: OpenApiDocument = ${snapshots ? "OPENAPI_DOCUMENT" : "createOpenApiDocument()"}, space = 2): string {`,
3939
3942
  ' return JSON.stringify(document, null, space) + "\\n";',
3940
3943
  "}",
3941
3944
  "",
3942
- "export const OPENAPI_JSON = serializeOpenApiDocument();",
3943
- ""
3945
+ ...snapshots ? [
3946
+ "export const OPENAPI_JSON = /* @__PURE__ */ serializeOpenApiDocument();",
3947
+ ""
3948
+ ] : []
3944
3949
  ].join(`
3945
3950
  `);
3946
3951
  }
@@ -12084,12 +12089,18 @@ var init_time = __esm(() => {
12084
12089
 
12085
12090
  // node_modules/typebox/build/format/date_time.mjs
12086
12091
  function IsDateTime(value) {
12087
- const dateTime = value.split(/T/i);
12088
- return dateTime.length === 2 && IsDate2(dateTime[0]) && IsTime(dateTime[1]);
12092
+ const index = value.indexOf("[");
12093
+ const datetime = index === -1 ? value : value.slice(0, index);
12094
+ if (index !== -1 && !DATE_TIME_SUFFIX.test(value.slice(index)))
12095
+ return false;
12096
+ const split = datetime.split(/T/i);
12097
+ return split.length === 2 && IsDate2(split[0]) && IsTime(split[1]);
12089
12098
  }
12099
+ var DATE_TIME_SUFFIX;
12090
12100
  var init_date_time = __esm(() => {
12091
12101
  init_date();
12092
12102
  init_time();
12103
+ DATE_TIME_SUFFIX = /^(?:\[!?(?:(?!\.{1,2}[/\]])[A-Za-z._][A-Za-z._0-9+-]{0,13}(?:\/(?!\.{1,2}[/\]])[A-Za-z._][A-Za-z._0-9+-]{0,13})*|[+-](?:[01][0-9]|2[0-3]):[0-5][0-9])\](?:\[!?[a-z_][a-z_0-9-]*=[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*\])*|(?:\[!?[a-z_][a-z_0-9-]*=[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*\])*)$/;
12093
12104
  });
12094
12105
 
12095
12106
  // node_modules/typebox/build/format/duration.mjs
@@ -16260,10 +16271,27 @@ function parseDeliveryOptions(value) {
16260
16271
  throw new DeliveryConfigurationError;
16261
16272
  }
16262
16273
  for (const target of value.targets ?? []) {
16263
- const invalidShape = target.kind === "function" ? target.function === undefined || target.modules !== undefined || target.compute !== undefined : target.modules === undefined || target.function !== undefined;
16274
+ const invalidShape = target.kind === "function" ? target.function === undefined || target.compute !== undefined : target.modules === undefined || target.function !== undefined;
16264
16275
  if (invalidShape)
16265
16276
  throw new DeliveryConfigurationError;
16266
16277
  }
16278
+ const targetNames = new Set((value.targets ?? []).map((target) => target.name));
16279
+ const functionSlugs = new Set;
16280
+ for (const target of value.targets ?? []) {
16281
+ if (target.function !== undefined && functionSlugs.has(target.function.slug)) {
16282
+ throw new DeliveryConfigurationError;
16283
+ }
16284
+ if (target.function !== undefined)
16285
+ functionSlugs.add(target.function.slug);
16286
+ }
16287
+ for (const declared of value.functions ?? []) {
16288
+ const name = declared.name ?? declared.slug;
16289
+ if (targetNames.has(name) || functionSlugs.has(declared.slug)) {
16290
+ throw new DeliveryConfigurationError;
16291
+ }
16292
+ targetNames.add(name);
16293
+ functionSlugs.add(declared.slug);
16294
+ }
16267
16295
  const migrations = value.build?.migrations ?? [];
16268
16296
  try {
16269
16297
  validateWorkerExecution(value.execution?.groups ?? []);
@@ -16273,14 +16301,30 @@ function parseDeliveryOptions(value) {
16273
16301
  if (new Set(migrations.map((item) => item.version)).size !== migrations.length || new Set(migrations.map((item) => item.source)).size !== migrations.length || migrations.some((item) => BigInt(item.version) > 9223372036854775807n || !/\.sql$/i.test(item.source) || item.source.split("/").includes("node_modules"))) {
16274
16302
  throw new DeliveryConfigurationError;
16275
16303
  }
16276
- return value;
16304
+ if (value.functions === undefined || value.functions.length === 0)
16305
+ return value;
16306
+ const targets = [...value.targets ?? []];
16307
+ for (const declared of value.functions) {
16308
+ targets.push({
16309
+ name: declared.name ?? declared.slug,
16310
+ kind: "function",
16311
+ modules: [...declared.modules],
16312
+ function: {
16313
+ slug: declared.slug,
16314
+ entrypoint: declared.entrypoint,
16315
+ verifyJwt: declared.verifyJwt
16316
+ }
16317
+ });
16318
+ }
16319
+ const { functions: _expandedFunctions, ...rest } = value;
16320
+ return { ...rest, targets };
16277
16321
  }
16278
16322
  function parseDeliveryPlanResult(value) {
16279
16323
  if (!Check2(DeliveryPlanResultSchema, value))
16280
16324
  throw new Error("Invalid delivery plan result.");
16281
16325
  return value;
16282
16326
  }
16283
- var objectOptions, reference2, references, kind2, isolation, mode2, DeliveryFilePathSchema, functionTarget, DeliveryOptionsSchema, DeliveryDiagnosticSchema, routeSchema, jobSchema, DeliveryTargetSchema, DeliveryPlanSchema, DeliveryPlanResultSchema, DeliveryConfigurationError;
16327
+ var objectOptions, reference2, references, kind2, isolation, targetName, functionSlug, mode2, DeliveryFilePathSchema, functionTarget, automaticFunction, DeliveryOptionsSchema, DeliveryDiagnosticSchema, routeSchema, jobSchema, DeliveryTargetSchema, DeliveryPlanSchema, DeliveryPlanResultSchema, DeliveryConfigurationError;
16284
16328
  var init_delivery_schema = __esm(() => {
16285
16329
  init_build();
16286
16330
  init_value2();
@@ -16290,13 +16334,22 @@ var init_delivery_schema = __esm(() => {
16290
16334
  references = _Array_(reference2, { uniqueItems: true });
16291
16335
  kind2 = Union([Literal("api"), Literal("jobs"), Literal("webhook"), Literal("function")]);
16292
16336
  isolation = Union([Literal("shared"), Literal("process")]);
16337
+ targetName = String2({ pattern: "^[a-z][a-z0-9-]{0,62}$" });
16338
+ functionSlug = String2({ pattern: "^[a-z][a-z0-9-]{0,62}$" });
16293
16339
  mode2 = Union([Literal("required"), Literal("none")]);
16294
16340
  DeliveryFilePathSchema = String2({
16295
16341
  maxLength: 512,
16296
16342
  pattern: "^(?:[A-Za-z0-9_-][A-Za-z0-9_.-]*/)*[A-Za-z0-9_-][A-Za-z0-9_.-]*$"
16297
16343
  });
16298
16344
  functionTarget = _Object_({
16299
- slug: String2({ pattern: "^[a-z][a-z0-9-]{0,62}$" }),
16345
+ slug: functionSlug,
16346
+ entrypoint: DeliveryFilePathSchema,
16347
+ verifyJwt: Boolean2()
16348
+ }, objectOptions);
16349
+ automaticFunction = _Object_({
16350
+ name: Optional(targetName),
16351
+ slug: functionSlug,
16352
+ modules: _Array_(reference2, { minItems: 1, uniqueItems: true }),
16300
16353
  entrypoint: DeliveryFilePathSchema,
16301
16354
  verifyJwt: Boolean2()
16302
16355
  }, objectOptions);
@@ -16304,7 +16357,7 @@ var init_delivery_schema = __esm(() => {
16304
16357
  version: Literal(1),
16305
16358
  execution: Optional(WorkerExecutionSchema),
16306
16359
  targets: Optional(_Array_(_Object_({
16307
- name: String2({ pattern: "^[a-z][a-z0-9-]{0,62}$" }),
16360
+ name: targetName,
16308
16361
  kind: kind2,
16309
16362
  modules: Optional(_Array_(reference2, { minItems: 1, uniqueItems: true })),
16310
16363
  isolation: Optional(isolation),
@@ -16312,6 +16365,7 @@ var init_delivery_schema = __esm(() => {
16312
16365
  compute: Optional(ComputeResourcesSchema),
16313
16366
  function: Optional(functionTarget)
16314
16367
  }, objectOptions))),
16368
+ functions: Optional(_Array_(automaticFunction, { maxItems: 32 })),
16315
16369
  runtime: Optional(_Object_({
16316
16370
  processIsolation: Boolean2(),
16317
16371
  durableQueue: Boolean2(),
@@ -16616,7 +16670,7 @@ function parseDeliveryBuildManifest(value) {
16616
16670
  }
16617
16671
  for (const object of value.objects) {
16618
16672
  const target = value.plan.targets.find((target) => target.name === object.name);
16619
- if (["bun-worker-application", "go-worker-application", "scriptc-worker-application"].includes(object.entryKind) && target?.kind !== "jobs" || object.entryKind === "bun-http-application" && target?.kind === "jobs") {
16673
+ if (["bun-worker-application", "go-worker-application", "scriptc-worker-application"].includes(object.entryKind) && target?.kind !== "jobs" || object.entryKind === "bun-http-application" && target?.kind === "jobs" || object.entryKind === "bun-function-application" && target?.kind !== "function" || target?.kind === "function" && object.entryKind !== "bun-function-application") {
16620
16674
  throw new Error("Delivery executable kind does not match its target.");
16621
16675
  }
16622
16676
  if (new Set(object.files.map((file) => file.path)).size !== object.files.length || !object.files.some((file) => file.path === object.entrypoint) || deliveryObjectDigest(object) !== object.objectId) {
@@ -16660,6 +16714,7 @@ var init_delivery_build_schema = __esm(() => {
16660
16714
  Literal("compiled-module-factory"),
16661
16715
  Literal("bun-http-application"),
16662
16716
  Literal("bun-worker-application"),
16717
+ Literal("bun-function-application"),
16663
16718
  Literal("go-worker-application"),
16664
16719
  Literal("scriptc-worker-application")
16665
16720
  ]),
@@ -17104,7 +17159,7 @@ var init_application_release = __esm(() => {
17104
17159
  });
17105
17160
 
17106
17161
  // node_modules/@supacloud/delivery/src/application-configuration.ts
17107
- var ApplicationConfigurationIdSchema, targetName, variableName2, kind3, hosts, bunVersion, ApplicationConfigurationSchema, ApplicationConfigurationWriteSchema, ApplicationConfigurationViewSchema, APPLICATION_RESERVED_ENVIRONMENT_NAMES;
17162
+ var ApplicationConfigurationIdSchema, targetName2, variableName2, kind3, hosts, bunVersion, ApplicationConfigurationSchema, ApplicationConfigurationWriteSchema, ApplicationConfigurationViewSchema, APPLICATION_RESERVED_ENVIRONMENT_NAMES;
17108
17163
  var init_application_configuration = __esm(() => {
17109
17164
  init_build();
17110
17165
  init_value2();
@@ -17112,7 +17167,7 @@ var init_application_configuration = __esm(() => {
17112
17167
  ApplicationConfigurationIdSchema = String2({
17113
17168
  pattern: "^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$"
17114
17169
  });
17115
- targetName = String2({ pattern: "^[a-z][a-z0-9-]{0,62}$" });
17170
+ targetName2 = String2({ pattern: "^[a-z][a-z0-9-]{0,62}$" });
17116
17171
  variableName2 = String2({ pattern: "^[A-Z][A-Z0-9_]{0,127}$" });
17117
17172
  kind3 = Union([Literal("http"), Literal("worker")]);
17118
17173
  hosts = _Array_(String2({ minLength: 1, maxLength: 253 }), { maxItems: 32, uniqueItems: true });
@@ -17120,7 +17175,7 @@ var init_application_configuration = __esm(() => {
17120
17175
  ApplicationConfigurationSchema = _Object_({
17121
17176
  bun_version: bunVersion,
17122
17177
  targets: _Array_(_Object_({
17123
- name: targetName,
17178
+ name: targetName2,
17124
17179
  kind: kind3,
17125
17180
  hosts,
17126
17181
  environment: Record(variableName2, String2({ maxLength: 16384 }), {
@@ -17143,7 +17198,7 @@ var init_application_configuration = __esm(() => {
17143
17198
  created_at: String2(),
17144
17199
  bun_version: bunVersion,
17145
17200
  targets: _Array_(_Object_({
17146
- name: targetName,
17201
+ name: targetName2,
17147
17202
  kind: kind3,
17148
17203
  hosts,
17149
17204
  environment_names: _Array_(variableName2, { maxItems: 128, uniqueItems: true })
@@ -17930,6 +17985,19 @@ var init_delivery_migration_archive = __esm(() => {
17930
17985
  // node_modules/@supacloud/delivery/src/delivery-migration-plan.ts
17931
17986
  var init_delivery_migration_plan = () => {};
17932
17987
 
17988
+ // node_modules/@supacloud/delivery/src/release-contract.ts
17989
+ var ALLOWED_PHASE_TRANSITIONS;
17990
+ var init_release_contract = __esm(() => {
17991
+ ALLOWED_PHASE_TRANSITIONS = new Map([
17992
+ ["planned", ["mutating", "rollback-required"]],
17993
+ ["mutating", ["applied", "rollback-required"]],
17994
+ ["applied", ["reconciled", "rollback-required"]],
17995
+ ["reconciled", []],
17996
+ ["rollback-required", ["rolled-back", "reconciled"]],
17997
+ ["rolled-back", []]
17998
+ ]);
17999
+ });
18000
+
17933
18001
  // node_modules/@supacloud/delivery/src/index.ts
17934
18002
  var init_src = __esm(() => {
17935
18003
  init_delivery_artifact();
@@ -17953,6 +18021,7 @@ var init_src = __esm(() => {
17953
18021
  init_delivery_build_schema();
17954
18022
  init_delivery_migration_archive();
17955
18023
  init_delivery_migration_plan();
18024
+ init_release_contract();
17956
18025
  });
17957
18026
 
17958
18027
  // src/delivery-migration-archive.ts
@@ -23991,7 +24060,7 @@ function createDeliveryPlan(graph, input, validation) {
23991
24060
  name: declaration.name,
23992
24061
  kind: "function",
23993
24062
  isolation: "shared",
23994
- roots: [],
24063
+ roots: sorted(declaration.modules ?? []),
23995
24064
  modules: [],
23996
24065
  routes: [],
23997
24066
  jobs: [],
@@ -24780,6 +24849,53 @@ interface DeliveryWorkerHost {
24780
24849
  `;
24781
24850
  }
24782
24851
 
24852
+ // src/delivery-function.ts
24853
+ function renderDeliveryFunctionEntry(generatedDirectory, hostPath) {
24854
+ const specifier = relativeImportPath(generatedDirectory, hostPath);
24855
+ return `
24856
+ import * as deliveryFunctionHostModule from ${JSON.stringify(specifier)};
24857
+
24858
+ type DeliveryFunctionHost = {
24859
+ fetch(request: Request): Response | Promise<Response>;
24860
+ };
24861
+
24862
+ const createDeliveryFunctionHost = (deliveryFunctionHostModule as {
24863
+ createDeliveryApplication?: (
24864
+ modules: ReturnType<typeof createCompiledModules>,
24865
+ lifecycle: { signal: AbortSignal },
24866
+ ) => DeliveryFunctionHost | Promise<DeliveryFunctionHost>;
24867
+ }).createDeliveryApplication;
24868
+ if (typeof createDeliveryFunctionHost !== "function") {
24869
+ throw new Error("Invalid delivery Function host.");
24870
+ }
24871
+ const deliveryCreateHost = createDeliveryFunctionHost;
24872
+
24873
+ const deliveryFunctionLifecycle = new AbortController();
24874
+ let deliveryFunctionHost: Promise<DeliveryFunctionHost> | undefined;
24875
+ function deliveryHost(): Promise<DeliveryFunctionHost> {
24876
+ return deliveryFunctionHost ??= Promise.resolve(
24877
+ deliveryCreateHost(createCompiledModules(), { signal: deliveryFunctionLifecycle.signal }),
24878
+ );
24879
+ }
24880
+
24881
+ export default {
24882
+ async fetch(request: Request): Promise<Response> {
24883
+ return (await deliveryHost()).fetch(request);
24884
+ },
24885
+ };
24886
+ `;
24887
+ }
24888
+ function renderStandaloneFunctionEntry(generatedDirectory, entrypointPath) {
24889
+ const specifier = relativeImportPath(generatedDirectory, entrypointPath);
24890
+ return `
24891
+ import * as deliveryFunctionEntry from ${JSON.stringify(specifier)};
24892
+
24893
+ const deliveryFunctionDefault: unknown = (deliveryFunctionEntry as { default?: unknown }).default
24894
+ ?? deliveryFunctionEntry;
24895
+ export default deliveryFunctionDefault;
24896
+ `;
24897
+ }
24898
+
24783
24899
  // src/delivery-migrations.ts
24784
24900
  init_delivery_files2();
24785
24901
  import { resolve as resolve16 } from "node:path";
@@ -24875,14 +24991,6 @@ async function buildDeliveryProject(options, delivery) {
24875
24991
  const planned = createDeliveryPlan({ ...checked.graph, diagnostics: checked.diagnostics }, settings, options);
24876
24992
  if (!planned.ok)
24877
24993
  return failed(planned.diagnostics);
24878
- if (planned.plan.targets.some((target) => target.kind === "function")) {
24879
- return failed([{
24880
- severity: "error",
24881
- code: "delivery-function-build-unsupported",
24882
- message: "Function target rendering is not implemented yet; the delivery build only produces HTTP and Worker application objects.",
24883
- suggestion: "Declare HTTP/Worker application targets for now; Function and schedule delivery is tracked separately."
24884
- }]);
24885
- }
24886
24994
  if (options.graphql && checked.mismatches.some((mismatch) => mismatch.startsWith("graphql"))) {
24887
24995
  throw new Error("Refresh GraphQL artifacts with compile before building independent targets.");
24888
24996
  }
@@ -24912,6 +25020,17 @@ async function buildDeliveryProject(options, delivery) {
24912
25020
  hosts.set(app.target, { path, kind, hash: digest(await readOwned(sourceRoot, path)) });
24913
25021
  }
24914
25022
  }
25023
+ for (const target of planned.plan.targets) {
25024
+ if (target.kind !== "function" || target.modules.length === 0 || target.function === undefined)
25025
+ continue;
25026
+ if (hosts.has(target.name))
25027
+ throw new Error("Duplicate delivery application host.");
25028
+ const path = resolve17(sourceRoot, target.function.entrypoint);
25029
+ if (inside(generatedRoot, path) || !/\.[cm]?ts$/.test(path)) {
25030
+ throw new Error("Function hosts require application TypeScript source.");
25031
+ }
25032
+ hosts.set(target.name, { path, kind: "function", hash: digest(await readOwned(sourceRoot, path)) });
25033
+ }
24915
25034
  phase = "migration-inputs";
24916
25035
  const migrations = await prepareDeliveryMigrations(project, generatedRoot, settings.build?.migrations);
24917
25036
  phase = "output-ownership";
@@ -24966,19 +25085,38 @@ async function buildDeliveryProject(options, delivery) {
24966
25085
  const stage = await mkdtemp(join10(objectRoot, ".build-"));
24967
25086
  temporary.push(stage);
24968
25087
  const generatedDirectory = join10(objectRoot, "0".repeat(64), "generated");
24969
- const rendered = renderDeliveryTarget(checked.graph, target, {
25088
+ const host = hosts.get(target.name);
25089
+ const standaloneFunction = target.kind === "function" && target.modules.length === 0;
25090
+ const rendered = standaloneFunction ? undefined : renderDeliveryTarget(checked.graph, target, {
24970
25091
  ...options,
24971
25092
  rootDir: sourceRoot,
24972
25093
  outDir: generatedDirectory
24973
25094
  });
24974
- const host = hosts.get(target.name);
24975
- const entryKind = host?.kind === "http" ? "bun-http-application" : host?.kind === "worker" ? "bun-worker-application" : "compiled-module-factory";
24976
- const applicationCode = rendered.applicationCode + (host?.kind === "http" ? renderDeliveryHttpEntry(generatedDirectory, host.path) : host?.kind === "worker" ? renderDeliveryWorkerEntry(generatedDirectory, host.path) : "");
24977
- const generated = {
24978
- "application.ts": applicationCode,
24979
- ...rendered.clientCode === undefined ? {} : { "client.ts": rendered.clientCode },
24980
- ...rendered.permissionsCode === undefined ? {} : { "permissions.ts": rendered.permissionsCode }
24981
- };
25095
+ let entryKind;
25096
+ let applicationCode;
25097
+ let generated;
25098
+ if (standaloneFunction) {
25099
+ if (target.function === undefined)
25100
+ throw new Error("Function target requires an entrypoint descriptor.");
25101
+ const entrypointPath = resolve17(sourceRoot, target.function.entrypoint);
25102
+ if (inside(generatedRoot, entrypointPath) || !/\.[cm]?ts$/.test(entrypointPath)) {
25103
+ throw new Error("Standalone Function entrypoints require application TypeScript source.");
25104
+ }
25105
+ entryKind = "bun-function-application";
25106
+ applicationCode = renderStandaloneFunctionEntry(generatedDirectory, entrypointPath);
25107
+ generated = { "function.ts": applicationCode };
25108
+ } else {
25109
+ if (target.kind === "function" && host?.kind !== "function") {
25110
+ throw new Error("Module-backed Function targets require an entrypoint host.");
25111
+ }
25112
+ entryKind = target.kind === "function" ? "bun-function-application" : host?.kind === "http" ? "bun-http-application" : host?.kind === "worker" ? "bun-worker-application" : "compiled-module-factory";
25113
+ applicationCode = rendered.applicationCode + (host?.kind === "http" ? renderDeliveryHttpEntry(generatedDirectory, host.path) : host?.kind === "worker" ? renderDeliveryWorkerEntry(generatedDirectory, host.path) : host?.kind === "function" ? renderDeliveryFunctionEntry(generatedDirectory, host.path) : "");
25114
+ generated = {
25115
+ "application.ts": applicationCode,
25116
+ ...rendered.clientCode === undefined ? {} : { "client.ts": rendered.clientCode },
25117
+ ...rendered.permissionsCode === undefined ? {} : { "permissions.ts": rendered.permissionsCode }
25118
+ };
25119
+ }
24982
25120
  const unsafe = scanGeneratedArtifacts(generated, true);
24983
25121
  if (unsafe.some((item) => item.severity === "error"))
24984
25122
  return failed(unsafe);
@@ -25021,9 +25159,14 @@ async function buildDeliveryProject(options, delivery) {
25021
25159
  for (const [path, bytes] of bundled.files)
25022
25160
  await writeArtifact(stage, path, bytes);
25023
25161
  await writeArtifact(stage, "bundle/package.json", canonical2({ type: "module", private: true }));
25024
- await writeArtifact(stage, "bundle/app.manifest.json", rendered.manifestJson);
25025
- await writeArtifact(stage, "bundle/execution-context.json", rendered.executionSnapshot);
25026
- await writeArtifact(stage, "bundle/application-development.json", rendered.applicationDevelopment);
25162
+ if (target.kind === "function" && target.function !== undefined) {
25163
+ await writeArtifact(stage, "bundle/function.json", canonical2(target.function));
25164
+ }
25165
+ if (rendered !== undefined) {
25166
+ await writeArtifact(stage, "bundle/app.manifest.json", rendered.manifestJson);
25167
+ await writeArtifact(stage, "bundle/execution-context.json", rendered.executionSnapshot);
25168
+ await writeArtifact(stage, "bundle/application-development.json", rendered.applicationDevelopment);
25169
+ }
25027
25170
  for (const [path, bytes] of migrations.files)
25028
25171
  await writeArtifact(stage, path, bytes);
25029
25172
  await writeArtifact(stage, "bundle/target.json", canonical2({
@@ -25157,10 +25300,10 @@ import { readFileSync as readFileSync4 } from "node:fs";
25157
25300
  import { resolve as resolve18 } from "node:path";
25158
25301
 
25159
25302
  // src/migration-app-version.ts
25160
- var MIGRATION_APP_VERSION = "0.33.0";
25303
+ var MIGRATION_APP_VERSION = "0.34.1";
25161
25304
 
25162
25305
  // src/migration-elysia-version.ts
25163
- var MIGRATION_ELYSIA_VERSION = "0.36.2";
25306
+ var MIGRATION_ELYSIA_VERSION = "0.36.3";
25164
25307
 
25165
25308
  // src/migration-policy.ts
25166
25309
  var cachedCompilerVersion;
@@ -26039,7 +26182,11 @@ async function loadGeneratedOpenApiDocument(modulePath) {
26039
26182
  const loaded = await import(moduleUrl.href);
26040
26183
  if (!isRecord(loaded))
26041
26184
  throw new OpenApiDocumentError;
26042
- return parseOpenApiDocument(loaded["OPENAPI_DOCUMENT"]);
26185
+ if ("OPENAPI_DOCUMENT" in loaded)
26186
+ return parseOpenApiDocument(loaded["OPENAPI_DOCUMENT"]);
26187
+ if (typeof loaded["createOpenApiDocument"] !== "function")
26188
+ throw new OpenApiDocumentError;
26189
+ return parseOpenApiDocument(await loaded["createOpenApiDocument"]());
26043
26190
  } catch (error) {
26044
26191
  if (error instanceof OpenApiDocumentError)
26045
26192
  throw error;
@@ -57,6 +57,13 @@ declare const requestSchema: Type.TObject<{
57
57
  verifyJwt: Type.TBoolean;
58
58
  }>>;
59
59
  }>>>;
60
+ functions: Type.TOptional<Type.TArray<Type.TObject<{
61
+ name: Type.TOptional<Type.TString>;
62
+ slug: Type.TString;
63
+ modules: Type.TArray<Type.TString>;
64
+ entrypoint: Type.TString;
65
+ verifyJwt: Type.TBoolean;
66
+ }>>>;
60
67
  runtime: Type.TOptional<Type.TObject<{
61
68
  processIsolation: Type.TBoolean;
62
69
  durableQueue: Type.TBoolean;