@supacloud/compiler 0.51.2 → 0.52.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/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
  }
@@ -16260,10 +16265,27 @@ function parseDeliveryOptions(value) {
16260
16265
  throw new DeliveryConfigurationError;
16261
16266
  }
16262
16267
  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;
16268
+ const invalidShape = target.kind === "function" ? target.function === undefined || target.compute !== undefined : target.modules === undefined || target.function !== undefined;
16264
16269
  if (invalidShape)
16265
16270
  throw new DeliveryConfigurationError;
16266
16271
  }
16272
+ const targetNames = new Set((value.targets ?? []).map((target) => target.name));
16273
+ const functionSlugs = new Set;
16274
+ for (const target of value.targets ?? []) {
16275
+ if (target.function !== undefined && functionSlugs.has(target.function.slug)) {
16276
+ throw new DeliveryConfigurationError;
16277
+ }
16278
+ if (target.function !== undefined)
16279
+ functionSlugs.add(target.function.slug);
16280
+ }
16281
+ for (const declared of value.functions ?? []) {
16282
+ const name = declared.name ?? declared.slug;
16283
+ if (targetNames.has(name) || functionSlugs.has(declared.slug)) {
16284
+ throw new DeliveryConfigurationError;
16285
+ }
16286
+ targetNames.add(name);
16287
+ functionSlugs.add(declared.slug);
16288
+ }
16267
16289
  const migrations = value.build?.migrations ?? [];
16268
16290
  try {
16269
16291
  validateWorkerExecution(value.execution?.groups ?? []);
@@ -16273,14 +16295,30 @@ function parseDeliveryOptions(value) {
16273
16295
  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
16296
  throw new DeliveryConfigurationError;
16275
16297
  }
16276
- return value;
16298
+ if (value.functions === undefined || value.functions.length === 0)
16299
+ return value;
16300
+ const targets = [...value.targets ?? []];
16301
+ for (const declared of value.functions) {
16302
+ targets.push({
16303
+ name: declared.name ?? declared.slug,
16304
+ kind: "function",
16305
+ modules: [...declared.modules],
16306
+ function: {
16307
+ slug: declared.slug,
16308
+ entrypoint: declared.entrypoint,
16309
+ verifyJwt: declared.verifyJwt
16310
+ }
16311
+ });
16312
+ }
16313
+ const { functions: _expandedFunctions, ...rest } = value;
16314
+ return { ...rest, targets };
16277
16315
  }
16278
16316
  function parseDeliveryPlanResult(value) {
16279
16317
  if (!Check2(DeliveryPlanResultSchema, value))
16280
16318
  throw new Error("Invalid delivery plan result.");
16281
16319
  return value;
16282
16320
  }
16283
- var objectOptions, reference2, references, kind2, isolation, mode2, DeliveryFilePathSchema, functionTarget, DeliveryOptionsSchema, DeliveryDiagnosticSchema, routeSchema, jobSchema, DeliveryTargetSchema, DeliveryPlanSchema, DeliveryPlanResultSchema, DeliveryConfigurationError;
16321
+ var objectOptions, reference2, references, kind2, isolation, targetName, functionSlug, mode2, DeliveryFilePathSchema, functionTarget, automaticFunction, DeliveryOptionsSchema, DeliveryDiagnosticSchema, routeSchema, jobSchema, DeliveryTargetSchema, DeliveryPlanSchema, DeliveryPlanResultSchema, DeliveryConfigurationError;
16284
16322
  var init_delivery_schema = __esm(() => {
16285
16323
  init_build();
16286
16324
  init_value2();
@@ -16290,13 +16328,22 @@ var init_delivery_schema = __esm(() => {
16290
16328
  references = _Array_(reference2, { uniqueItems: true });
16291
16329
  kind2 = Union([Literal("api"), Literal("jobs"), Literal("webhook"), Literal("function")]);
16292
16330
  isolation = Union([Literal("shared"), Literal("process")]);
16331
+ targetName = String2({ pattern: "^[a-z][a-z0-9-]{0,62}$" });
16332
+ functionSlug = String2({ pattern: "^[a-z][a-z0-9-]{0,62}$" });
16293
16333
  mode2 = Union([Literal("required"), Literal("none")]);
16294
16334
  DeliveryFilePathSchema = String2({
16295
16335
  maxLength: 512,
16296
16336
  pattern: "^(?:[A-Za-z0-9_-][A-Za-z0-9_.-]*/)*[A-Za-z0-9_-][A-Za-z0-9_.-]*$"
16297
16337
  });
16298
16338
  functionTarget = _Object_({
16299
- slug: String2({ pattern: "^[a-z][a-z0-9-]{0,62}$" }),
16339
+ slug: functionSlug,
16340
+ entrypoint: DeliveryFilePathSchema,
16341
+ verifyJwt: Boolean2()
16342
+ }, objectOptions);
16343
+ automaticFunction = _Object_({
16344
+ name: Optional(targetName),
16345
+ slug: functionSlug,
16346
+ modules: _Array_(reference2, { minItems: 1, uniqueItems: true }),
16300
16347
  entrypoint: DeliveryFilePathSchema,
16301
16348
  verifyJwt: Boolean2()
16302
16349
  }, objectOptions);
@@ -16304,7 +16351,7 @@ var init_delivery_schema = __esm(() => {
16304
16351
  version: Literal(1),
16305
16352
  execution: Optional(WorkerExecutionSchema),
16306
16353
  targets: Optional(_Array_(_Object_({
16307
- name: String2({ pattern: "^[a-z][a-z0-9-]{0,62}$" }),
16354
+ name: targetName,
16308
16355
  kind: kind2,
16309
16356
  modules: Optional(_Array_(reference2, { minItems: 1, uniqueItems: true })),
16310
16357
  isolation: Optional(isolation),
@@ -16312,6 +16359,7 @@ var init_delivery_schema = __esm(() => {
16312
16359
  compute: Optional(ComputeResourcesSchema),
16313
16360
  function: Optional(functionTarget)
16314
16361
  }, objectOptions))),
16362
+ functions: Optional(_Array_(automaticFunction, { maxItems: 32 })),
16315
16363
  runtime: Optional(_Object_({
16316
16364
  processIsolation: Boolean2(),
16317
16365
  durableQueue: Boolean2(),
@@ -16616,7 +16664,7 @@ function parseDeliveryBuildManifest(value) {
16616
16664
  }
16617
16665
  for (const object of value.objects) {
16618
16666
  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") {
16667
+ 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
16668
  throw new Error("Delivery executable kind does not match its target.");
16621
16669
  }
16622
16670
  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 +16708,7 @@ var init_delivery_build_schema = __esm(() => {
16660
16708
  Literal("compiled-module-factory"),
16661
16709
  Literal("bun-http-application"),
16662
16710
  Literal("bun-worker-application"),
16711
+ Literal("bun-function-application"),
16663
16712
  Literal("go-worker-application"),
16664
16713
  Literal("scriptc-worker-application")
16665
16714
  ]),
@@ -17104,7 +17153,7 @@ var init_application_release = __esm(() => {
17104
17153
  });
17105
17154
 
17106
17155
  // node_modules/@supacloud/delivery/src/application-configuration.ts
17107
- var ApplicationConfigurationIdSchema, targetName, variableName2, kind3, hosts, bunVersion, ApplicationConfigurationSchema, ApplicationConfigurationWriteSchema, ApplicationConfigurationViewSchema, APPLICATION_RESERVED_ENVIRONMENT_NAMES;
17156
+ var ApplicationConfigurationIdSchema, targetName2, variableName2, kind3, hosts, bunVersion, ApplicationConfigurationSchema, ApplicationConfigurationWriteSchema, ApplicationConfigurationViewSchema, APPLICATION_RESERVED_ENVIRONMENT_NAMES;
17108
17157
  var init_application_configuration = __esm(() => {
17109
17158
  init_build();
17110
17159
  init_value2();
@@ -17112,7 +17161,7 @@ var init_application_configuration = __esm(() => {
17112
17161
  ApplicationConfigurationIdSchema = String2({
17113
17162
  pattern: "^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$"
17114
17163
  });
17115
- targetName = String2({ pattern: "^[a-z][a-z0-9-]{0,62}$" });
17164
+ targetName2 = String2({ pattern: "^[a-z][a-z0-9-]{0,62}$" });
17116
17165
  variableName2 = String2({ pattern: "^[A-Z][A-Z0-9_]{0,127}$" });
17117
17166
  kind3 = Union([Literal("http"), Literal("worker")]);
17118
17167
  hosts = _Array_(String2({ minLength: 1, maxLength: 253 }), { maxItems: 32, uniqueItems: true });
@@ -17120,7 +17169,7 @@ var init_application_configuration = __esm(() => {
17120
17169
  ApplicationConfigurationSchema = _Object_({
17121
17170
  bun_version: bunVersion,
17122
17171
  targets: _Array_(_Object_({
17123
- name: targetName,
17172
+ name: targetName2,
17124
17173
  kind: kind3,
17125
17174
  hosts,
17126
17175
  environment: Record(variableName2, String2({ maxLength: 16384 }), {
@@ -17143,7 +17192,7 @@ var init_application_configuration = __esm(() => {
17143
17192
  created_at: String2(),
17144
17193
  bun_version: bunVersion,
17145
17194
  targets: _Array_(_Object_({
17146
- name: targetName,
17195
+ name: targetName2,
17147
17196
  kind: kind3,
17148
17197
  hosts,
17149
17198
  environment_names: _Array_(variableName2, { maxItems: 128, uniqueItems: true })
@@ -23991,7 +24040,7 @@ function createDeliveryPlan(graph, input, validation) {
23991
24040
  name: declaration.name,
23992
24041
  kind: "function",
23993
24042
  isolation: "shared",
23994
- roots: [],
24043
+ roots: sorted(declaration.modules ?? []),
23995
24044
  modules: [],
23996
24045
  routes: [],
23997
24046
  jobs: [],
@@ -24780,6 +24829,53 @@ interface DeliveryWorkerHost {
24780
24829
  `;
24781
24830
  }
24782
24831
 
24832
+ // src/delivery-function.ts
24833
+ function renderDeliveryFunctionEntry(generatedDirectory, hostPath) {
24834
+ const specifier = relativeImportPath(generatedDirectory, hostPath);
24835
+ return `
24836
+ import * as deliveryFunctionHostModule from ${JSON.stringify(specifier)};
24837
+
24838
+ type DeliveryFunctionHost = {
24839
+ fetch(request: Request): Response | Promise<Response>;
24840
+ };
24841
+
24842
+ const createDeliveryFunctionHost = (deliveryFunctionHostModule as {
24843
+ createDeliveryApplication?: (
24844
+ modules: ReturnType<typeof createCompiledModules>,
24845
+ lifecycle: { signal: AbortSignal },
24846
+ ) => DeliveryFunctionHost | Promise<DeliveryFunctionHost>;
24847
+ }).createDeliveryApplication;
24848
+ if (typeof createDeliveryFunctionHost !== "function") {
24849
+ throw new Error("Invalid delivery Function host.");
24850
+ }
24851
+ const deliveryCreateHost = createDeliveryFunctionHost;
24852
+
24853
+ const deliveryFunctionLifecycle = new AbortController();
24854
+ let deliveryFunctionHost: Promise<DeliveryFunctionHost> | undefined;
24855
+ function deliveryHost(): Promise<DeliveryFunctionHost> {
24856
+ return deliveryFunctionHost ??= Promise.resolve(
24857
+ deliveryCreateHost(createCompiledModules(), { signal: deliveryFunctionLifecycle.signal }),
24858
+ );
24859
+ }
24860
+
24861
+ export default {
24862
+ async fetch(request: Request): Promise<Response> {
24863
+ return (await deliveryHost()).fetch(request);
24864
+ },
24865
+ };
24866
+ `;
24867
+ }
24868
+ function renderStandaloneFunctionEntry(generatedDirectory, entrypointPath) {
24869
+ const specifier = relativeImportPath(generatedDirectory, entrypointPath);
24870
+ return `
24871
+ import * as deliveryFunctionEntry from ${JSON.stringify(specifier)};
24872
+
24873
+ const deliveryFunctionDefault: unknown = (deliveryFunctionEntry as { default?: unknown }).default
24874
+ ?? deliveryFunctionEntry;
24875
+ export default deliveryFunctionDefault;
24876
+ `;
24877
+ }
24878
+
24783
24879
  // src/delivery-migrations.ts
24784
24880
  init_delivery_files2();
24785
24881
  import { resolve as resolve16 } from "node:path";
@@ -24875,14 +24971,6 @@ async function buildDeliveryProject(options, delivery) {
24875
24971
  const planned = createDeliveryPlan({ ...checked.graph, diagnostics: checked.diagnostics }, settings, options);
24876
24972
  if (!planned.ok)
24877
24973
  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
24974
  if (options.graphql && checked.mismatches.some((mismatch) => mismatch.startsWith("graphql"))) {
24887
24975
  throw new Error("Refresh GraphQL artifacts with compile before building independent targets.");
24888
24976
  }
@@ -24912,6 +25000,17 @@ async function buildDeliveryProject(options, delivery) {
24912
25000
  hosts.set(app.target, { path, kind, hash: digest(await readOwned(sourceRoot, path)) });
24913
25001
  }
24914
25002
  }
25003
+ for (const target of planned.plan.targets) {
25004
+ if (target.kind !== "function" || target.modules.length === 0 || target.function === undefined)
25005
+ continue;
25006
+ if (hosts.has(target.name))
25007
+ throw new Error("Duplicate delivery application host.");
25008
+ const path = resolve17(sourceRoot, target.function.entrypoint);
25009
+ if (inside(generatedRoot, path) || !/\.[cm]?ts$/.test(path)) {
25010
+ throw new Error("Function hosts require application TypeScript source.");
25011
+ }
25012
+ hosts.set(target.name, { path, kind: "function", hash: digest(await readOwned(sourceRoot, path)) });
25013
+ }
24915
25014
  phase = "migration-inputs";
24916
25015
  const migrations = await prepareDeliveryMigrations(project, generatedRoot, settings.build?.migrations);
24917
25016
  phase = "output-ownership";
@@ -24966,19 +25065,38 @@ async function buildDeliveryProject(options, delivery) {
24966
25065
  const stage = await mkdtemp(join10(objectRoot, ".build-"));
24967
25066
  temporary.push(stage);
24968
25067
  const generatedDirectory = join10(objectRoot, "0".repeat(64), "generated");
24969
- const rendered = renderDeliveryTarget(checked.graph, target, {
25068
+ const host = hosts.get(target.name);
25069
+ const standaloneFunction = target.kind === "function" && target.modules.length === 0;
25070
+ const rendered = standaloneFunction ? undefined : renderDeliveryTarget(checked.graph, target, {
24970
25071
  ...options,
24971
25072
  rootDir: sourceRoot,
24972
25073
  outDir: generatedDirectory
24973
25074
  });
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
- };
25075
+ let entryKind;
25076
+ let applicationCode;
25077
+ let generated;
25078
+ if (standaloneFunction) {
25079
+ if (target.function === undefined)
25080
+ throw new Error("Function target requires an entrypoint descriptor.");
25081
+ const entrypointPath = resolve17(sourceRoot, target.function.entrypoint);
25082
+ if (inside(generatedRoot, entrypointPath) || !/\.[cm]?ts$/.test(entrypointPath)) {
25083
+ throw new Error("Standalone Function entrypoints require application TypeScript source.");
25084
+ }
25085
+ entryKind = "bun-function-application";
25086
+ applicationCode = renderStandaloneFunctionEntry(generatedDirectory, entrypointPath);
25087
+ generated = { "function.ts": applicationCode };
25088
+ } else {
25089
+ if (target.kind === "function" && host?.kind !== "function") {
25090
+ throw new Error("Module-backed Function targets require an entrypoint host.");
25091
+ }
25092
+ entryKind = target.kind === "function" ? "bun-function-application" : host?.kind === "http" ? "bun-http-application" : host?.kind === "worker" ? "bun-worker-application" : "compiled-module-factory";
25093
+ applicationCode = rendered.applicationCode + (host?.kind === "http" ? renderDeliveryHttpEntry(generatedDirectory, host.path) : host?.kind === "worker" ? renderDeliveryWorkerEntry(generatedDirectory, host.path) : host?.kind === "function" ? renderDeliveryFunctionEntry(generatedDirectory, host.path) : "");
25094
+ generated = {
25095
+ "application.ts": applicationCode,
25096
+ ...rendered.clientCode === undefined ? {} : { "client.ts": rendered.clientCode },
25097
+ ...rendered.permissionsCode === undefined ? {} : { "permissions.ts": rendered.permissionsCode }
25098
+ };
25099
+ }
24982
25100
  const unsafe = scanGeneratedArtifacts(generated, true);
24983
25101
  if (unsafe.some((item) => item.severity === "error"))
24984
25102
  return failed(unsafe);
@@ -25021,9 +25139,14 @@ async function buildDeliveryProject(options, delivery) {
25021
25139
  for (const [path, bytes] of bundled.files)
25022
25140
  await writeArtifact(stage, path, bytes);
25023
25141
  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);
25142
+ if (target.kind === "function" && target.function !== undefined) {
25143
+ await writeArtifact(stage, "bundle/function.json", canonical2(target.function));
25144
+ }
25145
+ if (rendered !== undefined) {
25146
+ await writeArtifact(stage, "bundle/app.manifest.json", rendered.manifestJson);
25147
+ await writeArtifact(stage, "bundle/execution-context.json", rendered.executionSnapshot);
25148
+ await writeArtifact(stage, "bundle/application-development.json", rendered.applicationDevelopment);
25149
+ }
25027
25150
  for (const [path, bytes] of migrations.files)
25028
25151
  await writeArtifact(stage, path, bytes);
25029
25152
  await writeArtifact(stage, "bundle/target.json", canonical2({
@@ -25157,7 +25280,7 @@ import { readFileSync as readFileSync4 } from "node:fs";
25157
25280
  import { resolve as resolve18 } from "node:path";
25158
25281
 
25159
25282
  // src/migration-app-version.ts
25160
- var MIGRATION_APP_VERSION = "0.33.0";
25283
+ var MIGRATION_APP_VERSION = "0.34.0";
25161
25284
 
25162
25285
  // src/migration-elysia-version.ts
25163
25286
  var MIGRATION_ELYSIA_VERSION = "0.36.2";
@@ -26039,7 +26162,11 @@ async function loadGeneratedOpenApiDocument(modulePath) {
26039
26162
  const loaded = await import(moduleUrl.href);
26040
26163
  if (!isRecord(loaded))
26041
26164
  throw new OpenApiDocumentError;
26042
- return parseOpenApiDocument(loaded["OPENAPI_DOCUMENT"]);
26165
+ if ("OPENAPI_DOCUMENT" in loaded)
26166
+ return parseOpenApiDocument(loaded["OPENAPI_DOCUMENT"]);
26167
+ if (typeof loaded["createOpenApiDocument"] !== "function")
26168
+ throw new OpenApiDocumentError;
26169
+ return parseOpenApiDocument(await loaded["createOpenApiDocument"]());
26043
26170
  } catch (error) {
26044
26171
  if (error instanceof OpenApiDocumentError)
26045
26172
  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;
@@ -10129,10 +10129,27 @@ function parseDeliveryOptions(value) {
10129
10129
  throw new DeliveryConfigurationError;
10130
10130
  }
10131
10131
  for (const target of value.targets ?? []) {
10132
- const invalidShape = target.kind === "function" ? target.function === undefined || target.modules !== undefined || target.compute !== undefined : target.modules === undefined || target.function !== undefined;
10132
+ const invalidShape = target.kind === "function" ? target.function === undefined || target.compute !== undefined : target.modules === undefined || target.function !== undefined;
10133
10133
  if (invalidShape)
10134
10134
  throw new DeliveryConfigurationError;
10135
10135
  }
10136
+ const targetNames = new Set((value.targets ?? []).map((target) => target.name));
10137
+ const functionSlugs = new Set;
10138
+ for (const target of value.targets ?? []) {
10139
+ if (target.function !== undefined && functionSlugs.has(target.function.slug)) {
10140
+ throw new DeliveryConfigurationError;
10141
+ }
10142
+ if (target.function !== undefined)
10143
+ functionSlugs.add(target.function.slug);
10144
+ }
10145
+ for (const declared of value.functions ?? []) {
10146
+ const name = declared.name ?? declared.slug;
10147
+ if (targetNames.has(name) || functionSlugs.has(declared.slug)) {
10148
+ throw new DeliveryConfigurationError;
10149
+ }
10150
+ targetNames.add(name);
10151
+ functionSlugs.add(declared.slug);
10152
+ }
10136
10153
  const migrations = value.build?.migrations ?? [];
10137
10154
  try {
10138
10155
  validateWorkerExecution(value.execution?.groups ?? []);
@@ -10142,14 +10159,30 @@ function parseDeliveryOptions(value) {
10142
10159
  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"))) {
10143
10160
  throw new DeliveryConfigurationError;
10144
10161
  }
10145
- return value;
10162
+ if (value.functions === undefined || value.functions.length === 0)
10163
+ return value;
10164
+ const targets = [...value.targets ?? []];
10165
+ for (const declared of value.functions) {
10166
+ targets.push({
10167
+ name: declared.name ?? declared.slug,
10168
+ kind: "function",
10169
+ modules: [...declared.modules],
10170
+ function: {
10171
+ slug: declared.slug,
10172
+ entrypoint: declared.entrypoint,
10173
+ verifyJwt: declared.verifyJwt
10174
+ }
10175
+ });
10176
+ }
10177
+ const { functions: _expandedFunctions, ...rest } = value;
10178
+ return { ...rest, targets };
10146
10179
  }
10147
10180
  function parseDeliveryPlanResult(value) {
10148
10181
  if (!Check2(DeliveryPlanResultSchema, value))
10149
10182
  throw new Error("Invalid delivery plan result.");
10150
10183
  return value;
10151
10184
  }
10152
- var objectOptions, reference, references, kind, isolation, mode, DeliveryFilePathSchema, functionTarget, DeliveryOptionsSchema, DeliveryDiagnosticSchema, routeSchema, jobSchema, DeliveryTargetSchema, DeliveryPlanSchema, DeliveryPlanResultSchema, DeliveryConfigurationError;
10185
+ var objectOptions, reference, references, kind, isolation, targetName, functionSlug, mode, DeliveryFilePathSchema, functionTarget, automaticFunction, DeliveryOptionsSchema, DeliveryDiagnosticSchema, routeSchema, jobSchema, DeliveryTargetSchema, DeliveryPlanSchema, DeliveryPlanResultSchema, DeliveryConfigurationError;
10153
10186
  var init_delivery_schema = __esm(() => {
10154
10187
  init_build2();
10155
10188
  init_value2();
@@ -10159,13 +10192,22 @@ var init_delivery_schema = __esm(() => {
10159
10192
  references = _Array_(reference, { uniqueItems: true });
10160
10193
  kind = Union([Literal("api"), Literal("jobs"), Literal("webhook"), Literal("function")]);
10161
10194
  isolation = Union([Literal("shared"), Literal("process")]);
10195
+ targetName = String2({ pattern: "^[a-z][a-z0-9-]{0,62}$" });
10196
+ functionSlug = String2({ pattern: "^[a-z][a-z0-9-]{0,62}$" });
10162
10197
  mode = Union([Literal("required"), Literal("none")]);
10163
10198
  DeliveryFilePathSchema = String2({
10164
10199
  maxLength: 512,
10165
10200
  pattern: "^(?:[A-Za-z0-9_-][A-Za-z0-9_.-]*/)*[A-Za-z0-9_-][A-Za-z0-9_.-]*$"
10166
10201
  });
10167
10202
  functionTarget = _Object_({
10168
- slug: String2({ pattern: "^[a-z][a-z0-9-]{0,62}$" }),
10203
+ slug: functionSlug,
10204
+ entrypoint: DeliveryFilePathSchema,
10205
+ verifyJwt: Boolean2()
10206
+ }, objectOptions);
10207
+ automaticFunction = _Object_({
10208
+ name: Optional(targetName),
10209
+ slug: functionSlug,
10210
+ modules: _Array_(reference, { minItems: 1, uniqueItems: true }),
10169
10211
  entrypoint: DeliveryFilePathSchema,
10170
10212
  verifyJwt: Boolean2()
10171
10213
  }, objectOptions);
@@ -10173,7 +10215,7 @@ var init_delivery_schema = __esm(() => {
10173
10215
  version: Literal(1),
10174
10216
  execution: Optional(WorkerExecutionSchema),
10175
10217
  targets: Optional(_Array_(_Object_({
10176
- name: String2({ pattern: "^[a-z][a-z0-9-]{0,62}$" }),
10218
+ name: targetName,
10177
10219
  kind,
10178
10220
  modules: Optional(_Array_(reference, { minItems: 1, uniqueItems: true })),
10179
10221
  isolation: Optional(isolation),
@@ -10181,6 +10223,7 @@ var init_delivery_schema = __esm(() => {
10181
10223
  compute: Optional(ComputeResourcesSchema),
10182
10224
  function: Optional(functionTarget)
10183
10225
  }, objectOptions))),
10226
+ functions: Optional(_Array_(automaticFunction, { maxItems: 32 })),
10184
10227
  runtime: Optional(_Object_({
10185
10228
  processIsolation: Boolean2(),
10186
10229
  durableQueue: Boolean2(),
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Wrap a module-backed Function target in the host-owned application factory.
3
+ * The generated application is already in scope as `createCompiledModules`.
4
+ */
5
+ export declare function renderDeliveryFunctionEntry(generatedDirectory: string, hostPath: string): string;
6
+ /** Re-export a standalone Edge Function entrypoint without requiring a compiler app graph. */
7
+ export declare function renderStandaloneFunctionEntry(generatedDirectory: string, entrypointPath: string): string;
package/dist/index.js CHANGED
@@ -3437,6 +3437,7 @@ ${procedureMethods.join(`
3437
3437
  function renderOpenApi(graph, options) {
3438
3438
  const rootDir = options?.rootDir ?? process.cwd();
3439
3439
  const outDir = options?.outDir ?? process.cwd();
3440
+ const snapshots = options?.openApi?.snapshots !== false;
3440
3441
  const imports = new ImportManager;
3441
3442
  const schemaLocals = new Map;
3442
3443
  const schemaComponents = new Map;
@@ -3932,14 +3933,18 @@ function renderOpenApi(graph, options) {
3932
3933
  " };",
3933
3934
  "}",
3934
3935
  "",
3935
- "export const OPENAPI_DOCUMENT = createOpenApiDocument();",
3936
- "",
3937
- "export function serializeOpenApiDocument(document: OpenApiDocument = OPENAPI_DOCUMENT, space = 2): string {",
3936
+ ...snapshots ? [
3937
+ "export const OPENAPI_DOCUMENT = /* @__PURE__ */ createOpenApiDocument();",
3938
+ ""
3939
+ ] : [],
3940
+ `export function serializeOpenApiDocument(document: OpenApiDocument = ${snapshots ? "OPENAPI_DOCUMENT" : "createOpenApiDocument()"}, space = 2): string {`,
3938
3941
  ' return JSON.stringify(document, null, space) + "\\n";',
3939
3942
  "}",
3940
3943
  "",
3941
- "export const OPENAPI_JSON = serializeOpenApiDocument();",
3942
- ""
3944
+ ...snapshots ? [
3945
+ "export const OPENAPI_JSON = /* @__PURE__ */ serializeOpenApiDocument();",
3946
+ ""
3947
+ ] : []
3943
3948
  ].join(`
3944
3949
  `);
3945
3950
  }
@@ -15049,10 +15054,27 @@ function parseDeliveryOptions(value) {
15049
15054
  throw new DeliveryConfigurationError;
15050
15055
  }
15051
15056
  for (const target of value.targets ?? []) {
15052
- const invalidShape = target.kind === "function" ? target.function === undefined || target.modules !== undefined || target.compute !== undefined : target.modules === undefined || target.function !== undefined;
15057
+ const invalidShape = target.kind === "function" ? target.function === undefined || target.compute !== undefined : target.modules === undefined || target.function !== undefined;
15053
15058
  if (invalidShape)
15054
15059
  throw new DeliveryConfigurationError;
15055
15060
  }
15061
+ const targetNames = new Set((value.targets ?? []).map((target) => target.name));
15062
+ const functionSlugs = new Set;
15063
+ for (const target of value.targets ?? []) {
15064
+ if (target.function !== undefined && functionSlugs.has(target.function.slug)) {
15065
+ throw new DeliveryConfigurationError;
15066
+ }
15067
+ if (target.function !== undefined)
15068
+ functionSlugs.add(target.function.slug);
15069
+ }
15070
+ for (const declared of value.functions ?? []) {
15071
+ const name = declared.name ?? declared.slug;
15072
+ if (targetNames.has(name) || functionSlugs.has(declared.slug)) {
15073
+ throw new DeliveryConfigurationError;
15074
+ }
15075
+ targetNames.add(name);
15076
+ functionSlugs.add(declared.slug);
15077
+ }
15056
15078
  const migrations = value.build?.migrations ?? [];
15057
15079
  try {
15058
15080
  validateWorkerExecution(value.execution?.groups ?? []);
@@ -15062,14 +15084,30 @@ function parseDeliveryOptions(value) {
15062
15084
  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"))) {
15063
15085
  throw new DeliveryConfigurationError;
15064
15086
  }
15065
- return value;
15087
+ if (value.functions === undefined || value.functions.length === 0)
15088
+ return value;
15089
+ const targets = [...value.targets ?? []];
15090
+ for (const declared of value.functions) {
15091
+ targets.push({
15092
+ name: declared.name ?? declared.slug,
15093
+ kind: "function",
15094
+ modules: [...declared.modules],
15095
+ function: {
15096
+ slug: declared.slug,
15097
+ entrypoint: declared.entrypoint,
15098
+ verifyJwt: declared.verifyJwt
15099
+ }
15100
+ });
15101
+ }
15102
+ const { functions: _expandedFunctions, ...rest } = value;
15103
+ return { ...rest, targets };
15066
15104
  }
15067
15105
  function parseDeliveryPlanResult(value) {
15068
15106
  if (!Check2(DeliveryPlanResultSchema, value))
15069
15107
  throw new Error("Invalid delivery plan result.");
15070
15108
  return value;
15071
15109
  }
15072
- var objectOptions, reference, references, kind, isolation, mode, DeliveryFilePathSchema, functionTarget, DeliveryOptionsSchema, DeliveryDiagnosticSchema, routeSchema, jobSchema, DeliveryTargetSchema, DeliveryPlanSchema, DeliveryPlanResultSchema, DeliveryConfigurationError;
15110
+ var objectOptions, reference, references, kind, isolation, targetName, functionSlug, mode, DeliveryFilePathSchema, functionTarget, automaticFunction, DeliveryOptionsSchema, DeliveryDiagnosticSchema, routeSchema, jobSchema, DeliveryTargetSchema, DeliveryPlanSchema, DeliveryPlanResultSchema, DeliveryConfigurationError;
15073
15111
  var init_delivery_schema = __esm(() => {
15074
15112
  init_build();
15075
15113
  init_value2();
@@ -15079,13 +15117,22 @@ var init_delivery_schema = __esm(() => {
15079
15117
  references = _Array_(reference, { uniqueItems: true });
15080
15118
  kind = Union([Literal("api"), Literal("jobs"), Literal("webhook"), Literal("function")]);
15081
15119
  isolation = Union([Literal("shared"), Literal("process")]);
15120
+ targetName = String2({ pattern: "^[a-z][a-z0-9-]{0,62}$" });
15121
+ functionSlug = String2({ pattern: "^[a-z][a-z0-9-]{0,62}$" });
15082
15122
  mode = Union([Literal("required"), Literal("none")]);
15083
15123
  DeliveryFilePathSchema = String2({
15084
15124
  maxLength: 512,
15085
15125
  pattern: "^(?:[A-Za-z0-9_-][A-Za-z0-9_.-]*/)*[A-Za-z0-9_-][A-Za-z0-9_.-]*$"
15086
15126
  });
15087
15127
  functionTarget = _Object_({
15088
- slug: String2({ pattern: "^[a-z][a-z0-9-]{0,62}$" }),
15128
+ slug: functionSlug,
15129
+ entrypoint: DeliveryFilePathSchema,
15130
+ verifyJwt: Boolean2()
15131
+ }, objectOptions);
15132
+ automaticFunction = _Object_({
15133
+ name: Optional(targetName),
15134
+ slug: functionSlug,
15135
+ modules: _Array_(reference, { minItems: 1, uniqueItems: true }),
15089
15136
  entrypoint: DeliveryFilePathSchema,
15090
15137
  verifyJwt: Boolean2()
15091
15138
  }, objectOptions);
@@ -15093,7 +15140,7 @@ var init_delivery_schema = __esm(() => {
15093
15140
  version: Literal(1),
15094
15141
  execution: Optional(WorkerExecutionSchema),
15095
15142
  targets: Optional(_Array_(_Object_({
15096
- name: String2({ pattern: "^[a-z][a-z0-9-]{0,62}$" }),
15143
+ name: targetName,
15097
15144
  kind,
15098
15145
  modules: Optional(_Array_(reference, { minItems: 1, uniqueItems: true })),
15099
15146
  isolation: Optional(isolation),
@@ -15101,6 +15148,7 @@ var init_delivery_schema = __esm(() => {
15101
15148
  compute: Optional(ComputeResourcesSchema),
15102
15149
  function: Optional(functionTarget)
15103
15150
  }, objectOptions))),
15151
+ functions: Optional(_Array_(automaticFunction, { maxItems: 32 })),
15104
15152
  runtime: Optional(_Object_({
15105
15153
  processIsolation: Boolean2(),
15106
15154
  durableQueue: Boolean2(),
@@ -15405,7 +15453,7 @@ function parseDeliveryBuildManifest(value) {
15405
15453
  }
15406
15454
  for (const object of value.objects) {
15407
15455
  const target = value.plan.targets.find((target) => target.name === object.name);
15408
- if (["bun-worker-application", "go-worker-application", "scriptc-worker-application"].includes(object.entryKind) && target?.kind !== "jobs" || object.entryKind === "bun-http-application" && target?.kind === "jobs") {
15456
+ 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") {
15409
15457
  throw new Error("Delivery executable kind does not match its target.");
15410
15458
  }
15411
15459
  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) {
@@ -15449,6 +15497,7 @@ var init_delivery_build_schema = __esm(() => {
15449
15497
  Literal("compiled-module-factory"),
15450
15498
  Literal("bun-http-application"),
15451
15499
  Literal("bun-worker-application"),
15500
+ Literal("bun-function-application"),
15452
15501
  Literal("go-worker-application"),
15453
15502
  Literal("scriptc-worker-application")
15454
15503
  ]),
@@ -16522,7 +16571,7 @@ var init_application_release = __esm(() => {
16522
16571
  });
16523
16572
 
16524
16573
  // node_modules/@supacloud/delivery/src/application-configuration.ts
16525
- var ApplicationConfigurationIdSchema, targetName, variableName2, kind3, hosts, bunVersion, ApplicationConfigurationSchema, ApplicationConfigurationWriteSchema, ApplicationConfigurationViewSchema, APPLICATION_RESERVED_ENVIRONMENT_NAMES;
16574
+ var ApplicationConfigurationIdSchema, targetName2, variableName2, kind3, hosts, bunVersion, ApplicationConfigurationSchema, ApplicationConfigurationWriteSchema, ApplicationConfigurationViewSchema, APPLICATION_RESERVED_ENVIRONMENT_NAMES;
16526
16575
  var init_application_configuration = __esm(() => {
16527
16576
  init_build();
16528
16577
  init_value2();
@@ -16530,7 +16579,7 @@ var init_application_configuration = __esm(() => {
16530
16579
  ApplicationConfigurationIdSchema = String2({
16531
16580
  pattern: "^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$"
16532
16581
  });
16533
- targetName = String2({ pattern: "^[a-z][a-z0-9-]{0,62}$" });
16582
+ targetName2 = String2({ pattern: "^[a-z][a-z0-9-]{0,62}$" });
16534
16583
  variableName2 = String2({ pattern: "^[A-Z][A-Z0-9_]{0,127}$" });
16535
16584
  kind3 = Union([Literal("http"), Literal("worker")]);
16536
16585
  hosts = _Array_(String2({ minLength: 1, maxLength: 253 }), { maxItems: 32, uniqueItems: true });
@@ -16538,7 +16587,7 @@ var init_application_configuration = __esm(() => {
16538
16587
  ApplicationConfigurationSchema = _Object_({
16539
16588
  bun_version: bunVersion,
16540
16589
  targets: _Array_(_Object_({
16541
- name: targetName,
16590
+ name: targetName2,
16542
16591
  kind: kind3,
16543
16592
  hosts,
16544
16593
  environment: Record(variableName2, String2({ maxLength: 16384 }), {
@@ -16561,7 +16610,7 @@ var init_application_configuration = __esm(() => {
16561
16610
  created_at: String2(),
16562
16611
  bun_version: bunVersion,
16563
16612
  targets: _Array_(_Object_({
16564
- name: targetName,
16613
+ name: targetName2,
16565
16614
  kind: kind3,
16566
16615
  hosts,
16567
16616
  environment_names: _Array_(variableName2, { maxItems: 128, uniqueItems: true })
@@ -22990,7 +23039,7 @@ function createDeliveryPlan(graph, input, validation) {
22990
23039
  name: declaration.name,
22991
23040
  kind: "function",
22992
23041
  isolation: "shared",
22993
- roots: [],
23042
+ roots: sorted(declaration.modules ?? []),
22994
23043
  modules: [],
22995
23044
  routes: [],
22996
23045
  jobs: [],
@@ -23778,6 +23827,53 @@ interface DeliveryWorkerHost {
23778
23827
  `;
23779
23828
  }
23780
23829
 
23830
+ // src/delivery-function.ts
23831
+ function renderDeliveryFunctionEntry(generatedDirectory, hostPath) {
23832
+ const specifier = relativeImportPath(generatedDirectory, hostPath);
23833
+ return `
23834
+ import * as deliveryFunctionHostModule from ${JSON.stringify(specifier)};
23835
+
23836
+ type DeliveryFunctionHost = {
23837
+ fetch(request: Request): Response | Promise<Response>;
23838
+ };
23839
+
23840
+ const createDeliveryFunctionHost = (deliveryFunctionHostModule as {
23841
+ createDeliveryApplication?: (
23842
+ modules: ReturnType<typeof createCompiledModules>,
23843
+ lifecycle: { signal: AbortSignal },
23844
+ ) => DeliveryFunctionHost | Promise<DeliveryFunctionHost>;
23845
+ }).createDeliveryApplication;
23846
+ if (typeof createDeliveryFunctionHost !== "function") {
23847
+ throw new Error("Invalid delivery Function host.");
23848
+ }
23849
+ const deliveryCreateHost = createDeliveryFunctionHost;
23850
+
23851
+ const deliveryFunctionLifecycle = new AbortController();
23852
+ let deliveryFunctionHost: Promise<DeliveryFunctionHost> | undefined;
23853
+ function deliveryHost(): Promise<DeliveryFunctionHost> {
23854
+ return deliveryFunctionHost ??= Promise.resolve(
23855
+ deliveryCreateHost(createCompiledModules(), { signal: deliveryFunctionLifecycle.signal }),
23856
+ );
23857
+ }
23858
+
23859
+ export default {
23860
+ async fetch(request: Request): Promise<Response> {
23861
+ return (await deliveryHost()).fetch(request);
23862
+ },
23863
+ };
23864
+ `;
23865
+ }
23866
+ function renderStandaloneFunctionEntry(generatedDirectory, entrypointPath) {
23867
+ const specifier = relativeImportPath(generatedDirectory, entrypointPath);
23868
+ return `
23869
+ import * as deliveryFunctionEntry from ${JSON.stringify(specifier)};
23870
+
23871
+ const deliveryFunctionDefault: unknown = (deliveryFunctionEntry as { default?: unknown }).default
23872
+ ?? deliveryFunctionEntry;
23873
+ export default deliveryFunctionDefault;
23874
+ `;
23875
+ }
23876
+
23781
23877
  // src/delivery-migrations.ts
23782
23878
  init_delivery_files2();
23783
23879
  import { resolve as resolve12 } from "node:path";
@@ -23873,14 +23969,6 @@ async function buildDeliveryProject(options, delivery) {
23873
23969
  const planned = createDeliveryPlan({ ...checked.graph, diagnostics: checked.diagnostics }, settings, options);
23874
23970
  if (!planned.ok)
23875
23971
  return failed(planned.diagnostics);
23876
- if (planned.plan.targets.some((target) => target.kind === "function")) {
23877
- return failed([{
23878
- severity: "error",
23879
- code: "delivery-function-build-unsupported",
23880
- message: "Function target rendering is not implemented yet; the delivery build only produces HTTP and Worker application objects.",
23881
- suggestion: "Declare HTTP/Worker application targets for now; Function and schedule delivery is tracked separately."
23882
- }]);
23883
- }
23884
23972
  if (options.graphql && checked.mismatches.some((mismatch) => mismatch.startsWith("graphql"))) {
23885
23973
  throw new Error("Refresh GraphQL artifacts with compile before building independent targets.");
23886
23974
  }
@@ -23910,6 +23998,17 @@ async function buildDeliveryProject(options, delivery) {
23910
23998
  hosts.set(app.target, { path, kind, hash: digest(await readOwned(sourceRoot, path)) });
23911
23999
  }
23912
24000
  }
24001
+ for (const target of planned.plan.targets) {
24002
+ if (target.kind !== "function" || target.modules.length === 0 || target.function === undefined)
24003
+ continue;
24004
+ if (hosts.has(target.name))
24005
+ throw new Error("Duplicate delivery application host.");
24006
+ const path = resolve13(sourceRoot, target.function.entrypoint);
24007
+ if (inside(generatedRoot, path) || !/\.[cm]?ts$/.test(path)) {
24008
+ throw new Error("Function hosts require application TypeScript source.");
24009
+ }
24010
+ hosts.set(target.name, { path, kind: "function", hash: digest(await readOwned(sourceRoot, path)) });
24011
+ }
23913
24012
  phase = "migration-inputs";
23914
24013
  const migrations = await prepareDeliveryMigrations(project, generatedRoot, settings.build?.migrations);
23915
24014
  phase = "output-ownership";
@@ -23964,19 +24063,38 @@ async function buildDeliveryProject(options, delivery) {
23964
24063
  const stage = await mkdtemp(join9(objectRoot, ".build-"));
23965
24064
  temporary.push(stage);
23966
24065
  const generatedDirectory = join9(objectRoot, "0".repeat(64), "generated");
23967
- const rendered = renderDeliveryTarget(checked.graph, target, {
24066
+ const host = hosts.get(target.name);
24067
+ const standaloneFunction = target.kind === "function" && target.modules.length === 0;
24068
+ const rendered = standaloneFunction ? undefined : renderDeliveryTarget(checked.graph, target, {
23968
24069
  ...options,
23969
24070
  rootDir: sourceRoot,
23970
24071
  outDir: generatedDirectory
23971
24072
  });
23972
- const host = hosts.get(target.name);
23973
- const entryKind = host?.kind === "http" ? "bun-http-application" : host?.kind === "worker" ? "bun-worker-application" : "compiled-module-factory";
23974
- const applicationCode = rendered.applicationCode + (host?.kind === "http" ? renderDeliveryHttpEntry(generatedDirectory, host.path) : host?.kind === "worker" ? renderDeliveryWorkerEntry(generatedDirectory, host.path) : "");
23975
- const generated = {
23976
- "application.ts": applicationCode,
23977
- ...rendered.clientCode === undefined ? {} : { "client.ts": rendered.clientCode },
23978
- ...rendered.permissionsCode === undefined ? {} : { "permissions.ts": rendered.permissionsCode }
23979
- };
24073
+ let entryKind;
24074
+ let applicationCode;
24075
+ let generated;
24076
+ if (standaloneFunction) {
24077
+ if (target.function === undefined)
24078
+ throw new Error("Function target requires an entrypoint descriptor.");
24079
+ const entrypointPath = resolve13(sourceRoot, target.function.entrypoint);
24080
+ if (inside(generatedRoot, entrypointPath) || !/\.[cm]?ts$/.test(entrypointPath)) {
24081
+ throw new Error("Standalone Function entrypoints require application TypeScript source.");
24082
+ }
24083
+ entryKind = "bun-function-application";
24084
+ applicationCode = renderStandaloneFunctionEntry(generatedDirectory, entrypointPath);
24085
+ generated = { "function.ts": applicationCode };
24086
+ } else {
24087
+ if (target.kind === "function" && host?.kind !== "function") {
24088
+ throw new Error("Module-backed Function targets require an entrypoint host.");
24089
+ }
24090
+ entryKind = target.kind === "function" ? "bun-function-application" : host?.kind === "http" ? "bun-http-application" : host?.kind === "worker" ? "bun-worker-application" : "compiled-module-factory";
24091
+ applicationCode = rendered.applicationCode + (host?.kind === "http" ? renderDeliveryHttpEntry(generatedDirectory, host.path) : host?.kind === "worker" ? renderDeliveryWorkerEntry(generatedDirectory, host.path) : host?.kind === "function" ? renderDeliveryFunctionEntry(generatedDirectory, host.path) : "");
24092
+ generated = {
24093
+ "application.ts": applicationCode,
24094
+ ...rendered.clientCode === undefined ? {} : { "client.ts": rendered.clientCode },
24095
+ ...rendered.permissionsCode === undefined ? {} : { "permissions.ts": rendered.permissionsCode }
24096
+ };
24097
+ }
23980
24098
  const unsafe = scanGeneratedArtifacts(generated, true);
23981
24099
  if (unsafe.some((item) => item.severity === "error"))
23982
24100
  return failed(unsafe);
@@ -24019,9 +24137,14 @@ async function buildDeliveryProject(options, delivery) {
24019
24137
  for (const [path, bytes] of bundled.files)
24020
24138
  await writeArtifact(stage, path, bytes);
24021
24139
  await writeArtifact(stage, "bundle/package.json", canonical2({ type: "module", private: true }));
24022
- await writeArtifact(stage, "bundle/app.manifest.json", rendered.manifestJson);
24023
- await writeArtifact(stage, "bundle/execution-context.json", rendered.executionSnapshot);
24024
- await writeArtifact(stage, "bundle/application-development.json", rendered.applicationDevelopment);
24140
+ if (target.kind === "function" && target.function !== undefined) {
24141
+ await writeArtifact(stage, "bundle/function.json", canonical2(target.function));
24142
+ }
24143
+ if (rendered !== undefined) {
24144
+ await writeArtifact(stage, "bundle/app.manifest.json", rendered.manifestJson);
24145
+ await writeArtifact(stage, "bundle/execution-context.json", rendered.executionSnapshot);
24146
+ await writeArtifact(stage, "bundle/application-development.json", rendered.applicationDevelopment);
24147
+ }
24025
24148
  for (const [path, bytes] of migrations.files)
24026
24149
  await writeArtifact(stage, path, bytes);
24027
24150
  await writeArtifact(stage, "bundle/target.json", canonical2({
@@ -24839,7 +24962,7 @@ import { readFileSync as readFileSync4 } from "node:fs";
24839
24962
  import { resolve as resolve17 } from "node:path";
24840
24963
 
24841
24964
  // src/migration-app-version.ts
24842
- var MIGRATION_APP_VERSION = "0.33.0";
24965
+ var MIGRATION_APP_VERSION = "0.34.0";
24843
24966
 
24844
24967
  // src/migration-elysia-version.ts
24845
24968
  var MIGRATION_ELYSIA_VERSION = "0.36.2";
@@ -25720,7 +25843,11 @@ async function loadGeneratedOpenApiDocument(modulePath) {
25720
25843
  const loaded = await import(moduleUrl.href);
25721
25844
  if (!isRecord(loaded))
25722
25845
  throw new OpenApiDocumentError;
25723
- return parseOpenApiDocument(loaded["OPENAPI_DOCUMENT"]);
25846
+ if ("OPENAPI_DOCUMENT" in loaded)
25847
+ return parseOpenApiDocument(loaded["OPENAPI_DOCUMENT"]);
25848
+ if (typeof loaded["createOpenApiDocument"] !== "function")
25849
+ throw new OpenApiDocumentError;
25850
+ return parseOpenApiDocument(await loaded["createOpenApiDocument"]());
25724
25851
  } catch (error) {
25725
25852
  if (error instanceof OpenApiDocumentError)
25726
25853
  throw error;
@@ -1 +1 @@
1
- export declare const MIGRATION_APP_VERSION = "0.33.0";
1
+ export declare const MIGRATION_APP_VERSION = "0.34.0";
package/dist/types.d.ts CHANGED
@@ -552,6 +552,8 @@ export type OpenApiSecurityScheme = {
552
552
  description?: string;
553
553
  };
554
554
  export interface OpenApiOptions {
555
+ /** Emit eager document and JSON snapshots for compatibility. Set false for factory-only imports. Defaults to true. */
556
+ snapshots?: boolean;
555
557
  title?: string;
556
558
  version?: string;
557
559
  description?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supacloud/compiler",
3
- "version": "0.51.2",
3
+ "version": "0.52.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",
@@ -58,7 +58,7 @@
58
58
  },
59
59
  "dependencies": {
60
60
  "@supacloud/db": "0.20.1",
61
- "@supacloud/delivery": "0.21.1",
61
+ "@supacloud/delivery": "0.22.0",
62
62
  "typebox": "1.4.2",
63
63
  "@graphql-codegen/core": "^6.2.2",
64
64
  "@graphql-codegen/typed-document-node": "^7.2.0",