@supacloud/compiler 0.45.1 → 0.46.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,6 +62,26 @@ 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:
69
+
70
+ ```ts
71
+ {
72
+ version: 1,
73
+ targets: [
74
+ { name: "intake-hooks", kind: "function",
75
+ function: { slug: "fa-intake-hooks", entrypoint: "functions/intake/index.ts", verifyJwt: false } },
76
+ ],
77
+ }
78
+ ```
79
+
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.
84
+
65
85
  No unused explicit target is accepted. Unknown modules, duplicate ownership,
66
86
  unresolved imports, cyclic dependencies, duplicate job names, and conflicting
67
87
  method/path patterns reject the plan. Parameter-name aliases such as `/:id` and
package/README.md CHANGED
@@ -2,16 +2,18 @@
2
2
 
3
3
  ## Enforced Database Governance
4
4
 
5
- Existing compiler callers retain `off`. Newly generated `command` starters set
6
- `databaseGovernance: { mode: "check" }`: compile/check/dev run the existing
7
- `@supacloud/db` source-boundary and contract-freshness gate offline, including
8
- incremental cache hits. Missing adoption files or stale contracts block generation
9
- even with `writeOnError`; checks never generate contracts. This mode does not
10
- import database modules, lint their SQL or connect to a database.
5
+ Existing compiler callers retain `off` as a compatibility migration exception.
6
+ Newly generated `command` starters set
7
+ `databaseGovernance: { mode: "enforce", moduleFile: "db/modules.ts" }`:
8
+ compile/check/build run the existing `@supacloud/db` source-boundary and
9
+ contract-freshness gate, SQL lint and live Catalog reconciliation, including
10
+ incremental cache hits. Missing adoption files, stale contracts, SQL findings or
11
+ Catalog drift block generation even with `writeOnError`; checks never generate
12
+ contracts or migrations.
11
13
  The minimal/http/edge starters remain database-free.
12
14
 
13
- For SQL lint and live Catalog reconciliation, declare the real database modules
14
- and explicitly set the following in `supacloud.config.ts`:
15
+ For an existing database project, adopt the governed path by declaring the real
16
+ database modules and explicitly setting the following in `supacloud.config.ts`:
15
17
 
16
18
  ```ts
17
19
  export default {
@@ -26,10 +28,12 @@ export default {
26
28
  ```
27
29
 
28
30
  `projectRoot` is relative to the configuration directory (or `rootDir` when
29
- calling `compileProject` / `checkProject` directly). The default mode is `off`.
30
- Use a read-only PostgreSQL credential in the named environment variable, targeting
31
- an explicitly selected local or test database. The compiler does not load `.env`
32
- files for this worker, generate migrations, repair contracts, or apply SQL.
31
+ calling `compileProject` / `checkProject` directly). Use a read-only PostgreSQL
32
+ credential in the named environment variable, targeting an explicitly selected
33
+ local or test database. The compiler does not load `.env` files for this worker,
34
+ generate migrations, repair contracts, or apply SQL. Setting `mode: "off"` is
35
+ reserved for an explicit migration exception and should not be used in a new
36
+ database starter.
33
37
 
34
38
  In `enforce` mode, compile/check/dev must pass the existing
35
39
  `@supacloud/db` source-boundary and contract-freshness check, SQL lint and live
package/dist/cli.js CHANGED
@@ -808,12 +808,15 @@ function validateGraph(graph, options = false) {
808
808
  if (typeof options === "object" && options.commandCapabilities) {
809
809
  const hostCaps = options.commandCapabilities;
810
810
  const rpcCaps = command.rpc && Object.hasOwn(hostCaps.rpc ?? {}, command.rpc) ? hostCaps.rpc?.[command.rpc] : undefined;
811
- if (hostCaps.requirePersistentAdapters && (command.rpc !== undefined && (!rpcCaps?.boundary || rpcCaps.audit !== true || rpcCaps.idempotency !== true) || hostCaps.permission !== true || !command.permission || !command.audit || command.idempotency !== "required" || command.rpc !== undefined && rpcCaps?.boundary === "database" && (rpcCaps.transaction !== true || command.transaction !== "required") || command.rpc === undefined && (hostCaps.audit !== true || hostCaps.idempotency !== true || hostCaps.transaction !== true || command.transaction !== "required"))) {
811
+ if (hostCaps.requirePersistentAdapters && (command.rpc !== undefined && (!rpcCaps?.boundary || rpcCaps.audit !== true || rpcCaps.idempotency !== true) || hostCaps.permission !== true || !command.permission || !command.audit || command.idempotency !== "required" || command.rpc !== undefined && rpcCaps?.boundary === "database" && (rpcCaps.transaction !== true || command.transaction !== "required") || command.rpc === undefined && (hostCaps.audit !== true || hostCaps.idempotency !== true || hostCaps.transaction !== true || command.transaction !== "required") || command.rpc !== undefined && rpcCaps?.boundary === "external" && rpcCaps.reconcile !== true)) {
812
812
  error("command-persistence-required", `Command ${command.name} requires an explicit persistent adapter, permission, audit and idempotency policy.`, module.file, module.line, "Register a named database/external adapter, enable permission checks and declare permission, audit and required idempotency on the command.");
813
813
  }
814
814
  if (rpcCaps?.boundary === "external" && (command.transaction === "required" || rpcCaps.transaction === true)) {
815
815
  error("command-external-transaction", `Command ${command.name} calls an external service; use durable intent and reconciliation, not transaction: 'required'.`, module.file, module.line, "Use transaction: 'none' with createExternalCommand; recover with read-only reconciliation. Do not claim a database transaction covers a remote request.");
816
816
  }
817
+ if (rpcCaps?.boundary === "external" && rpcCaps.reconcile !== true) {
818
+ error("command-reconcile-required", `Command ${command.name} uses an external adapter without a receipt reconciliation capability.`, module.file, module.line, "Declare reconcile: true only when the adapter can look up the same idempotency key and resolve unknown outcomes without redispatching.");
819
+ }
817
820
  const caps = command.rpc ? {
818
821
  permission: hostCaps.permission,
819
822
  audit: rpcCaps?.audit === true,
@@ -1230,6 +1233,7 @@ var init_validate = __esm(() => {
1230
1233
  "invalid-job-timeout": { code: "SC4015", docsUrl: "https://supacloud.dev/errors/SC4015" },
1231
1234
  "invalid-job-attempts": { code: "SC4016", docsUrl: "https://supacloud.dev/errors/SC4016" },
1232
1235
  "invalid-job-idempotency": { code: "SC4017", docsUrl: "https://supacloud.dev/errors/SC4017" },
1236
+ "job-retry-idempotency-required": { code: "SC4027", docsUrl: "https://supacloud.dev/errors/SC4027" },
1233
1237
  "invalid-job-mode": { code: "SC4018", docsUrl: "https://supacloud.dev/errors/SC4018" },
1234
1238
  "invalid-job-schema": { code: "SC4019", docsUrl: "https://supacloud.dev/errors/SC4019" },
1235
1239
  "command-persistence-required": { code: "SC4020", docsUrl: "https://supacloud.dev/errors/SC4020" },
@@ -1239,6 +1243,7 @@ var init_validate = __esm(() => {
1239
1243
  "command-idempotency-unsupported": { code: "SC4024", docsUrl: "https://supacloud.dev/errors/SC4024" },
1240
1244
  "command-transaction-rpc-only": { code: "SC4025", docsUrl: "https://supacloud.dev/errors/SC4025" },
1241
1245
  "command-transaction-unsupported": { code: "SC4026", docsUrl: "https://supacloud.dev/errors/SC4026" },
1246
+ "command-reconcile-required": { code: "SC4028", docsUrl: "https://supacloud.dev/errors/SC4028" },
1242
1247
  "invalid-route-contract": { code: "SC3020", docsUrl: "https://supacloud.dev/errors/SC3020" },
1243
1248
  "route-contract-required": { code: "SC3021", docsUrl: "https://supacloud.dev/errors/SC3021" },
1244
1249
  "route-contract-unverified": { code: "SC3022", docsUrl: "https://supacloud.dev/errors/SC3022" },
@@ -4592,7 +4597,7 @@ function createContextPack(graph, subject) {
4592
4597
  }
4593
4598
  }
4594
4599
  }
4595
- const modules = graph.modules.filter((module) => selected.has(module.name));
4600
+ const modules = graph.modules.filter((module) => selected.has(module.name)).map(sanitizeContextModule);
4596
4601
  const queryDocuments = graph.graphql?.documents.filter((file) => modules.some((module) => {
4597
4602
  const path = relative5(dirname4(module.file), file);
4598
4603
  return path !== ".." && !path.startsWith(`..${sep5}`);
@@ -4634,7 +4639,12 @@ function createContextPack(graph, subject) {
4634
4639
  singleResult: "async-await",
4635
4640
  eventComposition: "rxjs",
4636
4641
  angularHost: "@supacloud/app/angular",
4637
- cancellation: "explicit-owner-signal"
4642
+ cancellation: "explicit-owner-signal",
4643
+ readTransport: "postgrest-supabase-js",
4644
+ writeTransport: "command-rpc",
4645
+ writeRetry: "disabled",
4646
+ externalOutcome: "receipt-reconcile",
4647
+ databaseSource: "drizzle-append-only"
4638
4648
  },
4639
4649
  relatedModules: {
4640
4650
  imports: subjectModule.imports.filter((name) => selected.has(name)),
@@ -4642,6 +4652,44 @@ function createContextPack(graph, subject) {
4642
4652
  }
4643
4653
  };
4644
4654
  }
4655
+ function sanitizeContextModule(module) {
4656
+ const sanitizeAspect = (aspect) => ({
4657
+ ...aspect,
4658
+ expression: "[omitted]"
4659
+ });
4660
+ return {
4661
+ ...module,
4662
+ providers: module.providers.map((provider) => ({
4663
+ ...provider,
4664
+ ...provider.useValueExpr === undefined ? {} : { useValueExpr: "[omitted]" },
4665
+ ...provider.useFactoryName === undefined ? {} : { useFactoryName: "[omitted]" },
4666
+ ...provider.functionalInjects === undefined ? {} : {
4667
+ functionalInjects: provider.functionalInjects.map((inject) => ({ ...inject, expression: "[omitted]" }))
4668
+ }
4669
+ })),
4670
+ ...module.aspects === undefined ? {} : { aspects: module.aspects.map(sanitizeAspect) },
4671
+ controllers: module.controllers.map((controller) => ({
4672
+ ...controller,
4673
+ ...controller.functionalInjects === undefined ? {} : {
4674
+ functionalInjects: controller.functionalInjects.map((inject) => ({ ...inject, expression: "[omitted]" }))
4675
+ },
4676
+ routes: controller.routes.map((route) => ({
4677
+ ...route,
4678
+ ...route.aspects === undefined ? {} : { aspects: route.aspects.map(sanitizeAspect) }
4679
+ }))
4680
+ })),
4681
+ commands: module.commands.map((command) => ({
4682
+ ...command,
4683
+ ...command.aspects === undefined ? {} : { aspects: command.aspects.map(sanitizeAspect) }
4684
+ })),
4685
+ ...module.jobs === undefined ? {} : {
4686
+ jobs: module.jobs.map((job) => ({
4687
+ ...job,
4688
+ ...job.aspects === undefined ? {} : { aspects: job.aspects.map(sanitizeAspect) }
4689
+ }))
4690
+ }
4691
+ };
4692
+ }
4645
4693
  function resolveContextModule(graph, subject) {
4646
4694
  const exact = graph.modules.find((module) => module.name === subject);
4647
4695
  if (exact)
@@ -15683,6 +15731,11 @@ function parseDeliveryOptions(value) {
15683
15731
  if (value.targets?.some((target) => target.compute && target.isolation === "shared")) {
15684
15732
  throw new DeliveryConfigurationError;
15685
15733
  }
15734
+ for (const target of value.targets ?? []) {
15735
+ const invalidShape = target.kind === "function" ? target.function === undefined || target.modules !== undefined || target.compute !== undefined : target.modules === undefined || target.function !== undefined;
15736
+ if (invalidShape)
15737
+ throw new DeliveryConfigurationError;
15738
+ }
15686
15739
  const migrations = value.build?.migrations ?? [];
15687
15740
  try {
15688
15741
  validateWorkerExecution(value.execution?.groups ?? []);
@@ -15699,7 +15752,7 @@ function parseDeliveryPlanResult(value) {
15699
15752
  throw new Error("Invalid delivery plan result.");
15700
15753
  return value;
15701
15754
  }
15702
- var objectOptions, reference2, references, kind2, isolation, DeliveryFilePathSchema, DeliveryOptionsSchema, DeliveryDiagnosticSchema, routeSchema, jobSchema, DeliveryTargetSchema, DeliveryPlanSchema, DeliveryPlanResultSchema, DeliveryConfigurationError;
15755
+ var objectOptions, reference2, references, kind2, isolation, DeliveryFilePathSchema, functionTarget, DeliveryOptionsSchema, DeliveryDiagnosticSchema, routeSchema, jobSchema, DeliveryTargetSchema, DeliveryPlanSchema, DeliveryPlanResultSchema, DeliveryConfigurationError;
15703
15756
  var init_delivery_schema = __esm(() => {
15704
15757
  init_build();
15705
15758
  init_value2();
@@ -15707,22 +15760,28 @@ var init_delivery_schema = __esm(() => {
15707
15760
  objectOptions = { additionalProperties: false };
15708
15761
  reference2 = String2({ minLength: 1, maxLength: 128, pattern: "^[A-Za-z][A-Za-z0-9_.:/-]*$" });
15709
15762
  references = _Array_(reference2, { uniqueItems: true });
15710
- kind2 = Union([Literal("api"), Literal("jobs"), Literal("webhook")]);
15763
+ kind2 = Union([Literal("api"), Literal("jobs"), Literal("webhook"), Literal("function")]);
15711
15764
  isolation = Union([Literal("shared"), Literal("process")]);
15712
15765
  DeliveryFilePathSchema = String2({
15713
15766
  maxLength: 512,
15714
15767
  pattern: "^(?:[A-Za-z0-9_-][A-Za-z0-9_.-]*/)*[A-Za-z0-9_-][A-Za-z0-9_.-]*$"
15715
15768
  });
15769
+ functionTarget = _Object_({
15770
+ slug: String2({ pattern: "^[a-z][a-z0-9-]{0,62}$" }),
15771
+ entrypoint: DeliveryFilePathSchema,
15772
+ verifyJwt: Boolean2()
15773
+ }, objectOptions);
15716
15774
  DeliveryOptionsSchema = _Object_({
15717
15775
  version: Literal(1),
15718
15776
  execution: Optional(WorkerExecutionSchema),
15719
15777
  targets: Optional(_Array_(_Object_({
15720
15778
  name: String2({ pattern: "^[a-z][a-z0-9-]{0,62}$" }),
15721
15779
  kind: kind2,
15722
- modules: _Array_(reference2, { minItems: 1, uniqueItems: true }),
15780
+ modules: Optional(_Array_(reference2, { minItems: 1, uniqueItems: true })),
15723
15781
  isolation: Optional(isolation),
15724
15782
  capabilities: Optional(references),
15725
- compute: Optional(ComputeResourcesSchema)
15783
+ compute: Optional(ComputeResourcesSchema),
15784
+ function: Optional(functionTarget)
15726
15785
  }, objectOptions))),
15727
15786
  runtime: Optional(_Object_({
15728
15787
  processIsolation: Boolean2(),
@@ -15800,6 +15859,7 @@ var init_delivery_schema = __esm(() => {
15800
15859
  routes: _Array_(routeSchema),
15801
15860
  jobs: _Array_(jobSchema),
15802
15861
  externalTokens: _Array_(String2()),
15862
+ function: Optional(functionTarget),
15803
15863
  requirements: _Object_({
15804
15864
  processIsolation: Boolean2(),
15805
15865
  durableQueue: Boolean2(),
@@ -21143,9 +21203,25 @@ import * as ts9 from "@typescript/typescript6";
21143
21203
  var SOURCE_SEMANTIC_DIAGNOSTIC_CODES = {
21144
21204
  "source-unsafe-any": { errorCode: "SC6009", docsUrl: "https://supacloud.dev/errors/SC6009" },
21145
21205
  "source-floating-promise": { errorCode: "SC6010", docsUrl: "https://supacloud.dev/errors/SC6010" },
21146
- "source-non-exhaustive-switch": { errorCode: "SC6011", docsUrl: "https://supacloud.dev/errors/SC6011" }
21206
+ "source-non-exhaustive-switch": { errorCode: "SC6011", docsUrl: "https://supacloud.dev/errors/SC6011" },
21207
+ "source-alternative-library": { errorCode: "SC6012", docsUrl: "https://supacloud.dev/errors/SC6012" }
21147
21208
  };
21148
- function scanSourceSemantics(source, checker, file, rootDir) {
21209
+ var DEFAULT_ALTERNATIVE_LIBRARIES = [
21210
+ "@apollo/client",
21211
+ "@urql/core",
21212
+ "graphql-request",
21213
+ "kefir",
21214
+ "most",
21215
+ "@pgflow",
21216
+ "pgflow",
21217
+ "pg_durable",
21218
+ "react-relay",
21219
+ "redux-observable",
21220
+ "urql",
21221
+ "wonka",
21222
+ "xstream"
21223
+ ];
21224
+ function scanSourceSemantics(source, checker, file, rootDir, options = {}) {
21149
21225
  const diagnostics = [];
21150
21226
  const report = (code, node, message) => {
21151
21227
  diagnostics.push({
@@ -21157,6 +21233,16 @@ function scanSourceSemantics(source, checker, file, rootDir) {
21157
21233
  line: source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1
21158
21234
  });
21159
21235
  };
21236
+ const allowedLibraries = new Set(options.allowAlternativeLibraries ?? []);
21237
+ for (const statement of source.statements) {
21238
+ if (!ts9.isImportDeclaration(statement) || !ts9.isStringLiteral(statement.moduleSpecifier))
21239
+ continue;
21240
+ const moduleName = statement.moduleSpecifier.text;
21241
+ const alternative = DEFAULT_ALTERNATIVE_LIBRARIES.find((name) => moduleName === name || moduleName.startsWith(`${name}/`));
21242
+ if (alternative && !allowedLibraries.has(alternative) && !allowedLibraries.has(moduleName)) {
21243
+ report("source-alternative-library", statement.moduleSpecifier, `Production source imports alternative library "${moduleName}". Use the governed Supabase client/PostgREST transport and RxJS event composition by default, or allow this package explicitly in typeSafety.allowAlternativeLibraries for a documented boundary.`);
21244
+ }
21245
+ }
21160
21246
  const unsafe = (value, target) => {
21161
21247
  if (isUnknownBoundary(target, checker) || containsAny(target, checker, new Set, rootDir))
21162
21248
  return;
@@ -21437,7 +21523,7 @@ function scanProductionSource(options) {
21437
21523
  }
21438
21524
  for (const sourceFile of sourceFiles) {
21439
21525
  scanSourceFile(sourceFile, checker, rootDir, diagnostics, options.strict ?? false);
21440
- diagnostics.push(...scanSourceSemantics(sourceFile, checker, normalizeRelative(rootDir, sourceFile.fileName), rootDir));
21526
+ diagnostics.push(...scanSourceSemantics(sourceFile, checker, normalizeRelative(rootDir, sourceFile.fileName), rootDir, options.allowAlternativeLibraries === undefined ? {} : { allowAlternativeLibraries: options.allowAlternativeLibraries }));
21441
21527
  diagnostics.push(...scanDrizzleSql(sourceFile, checker, normalizeRelative(rootDir, sourceFile.fileName), options.strict ?? false));
21442
21528
  const scanner = ts10.createScanner(ts10.ScriptTarget.Latest, false, sourceFile.languageVariant, sourceFile.text);
21443
21529
  for (let kind = scanner.scan();kind !== ts10.SyntaxKind.EndOfFileToken; kind = scanner.scan()) {
@@ -21699,6 +21785,17 @@ function validateEffectPolicies(graph, options = {}) {
21699
21785
  }
21700
21786
  return diagnostics;
21701
21787
  }
21788
+ function validateJobPolicies(graph) {
21789
+ const diagnostics = [];
21790
+ for (const module of graph.modules) {
21791
+ for (const job of module.jobs ?? []) {
21792
+ if (job.maxAttempts !== undefined && job.maxAttempts > 1 && job.idempotency !== "required") {
21793
+ diagnostics.push(diagnostic("job-retry-idempotency-required", `Job ${job.name} (${job.className}) enables ${job.maxAttempts} attempts without an idempotency contract.`, module.file, 'Use idempotency: "required" with a durable receipt, or set maxAttempts: 1.'));
21794
+ }
21795
+ }
21796
+ }
21797
+ return diagnostics;
21798
+ }
21702
21799
 
21703
21800
  // src/database-governance.ts
21704
21801
  import { execFile } from "node:child_process";
@@ -21748,13 +21845,19 @@ async function checkDatabaseGovernance(rootDir, value) {
21748
21845
 
21749
21846
  // src/compile.ts
21750
21847
  function withDefaultGovernance(options) {
21751
- return options.commandCapabilities === undefined ? { ...options, commandCapabilities: {
21752
- requirePersistentAdapters: true,
21753
- permission: true,
21754
- audit: true,
21755
- idempotency: true,
21756
- transaction: true
21757
- } } : options;
21848
+ return {
21849
+ ...options,
21850
+ requireRouteContracts: options.requireRouteContracts ?? true,
21851
+ disallowControllerDirectDb: options.disallowControllerDirectDb ?? true,
21852
+ detectOrphanModules: options.detectOrphanModules ?? true,
21853
+ ...options.commandCapabilities === undefined ? { commandCapabilities: {
21854
+ requirePersistentAdapters: true,
21855
+ permission: true,
21856
+ audit: true,
21857
+ idempotency: true,
21858
+ transaction: true
21859
+ } } : {}
21860
+ };
21758
21861
  }
21759
21862
  async function renderOptionalGraphql(options) {
21760
21863
  return options.graphql ? (await Promise.resolve().then(() => (init_graphql(), exports_graphql))).renderGraphql(options) : { diagnostics: [], files: {} };
@@ -21766,7 +21869,8 @@ async function compileProject(options) {
21766
21869
  ...graph.diagnostics ?? [],
21767
21870
  ...validateGraph(graph, options),
21768
21871
  ...validateClientSchemaBoundaries(graph, options),
21769
- ...validateEffectPolicies(graph, options.effect)
21872
+ ...validateEffectPolicies(graph, options.effect),
21873
+ ...validateJobPolicies(graph)
21770
21874
  ];
21771
21875
  const databaseDiagnostics = await checkDatabaseGovernance(options.rootDir, options.databaseGovernance);
21772
21876
  diagnostics.push(...databaseDiagnostics);
@@ -21836,7 +21940,8 @@ async function checkProject(options) {
21836
21940
  ...graph.diagnostics ?? [],
21837
21941
  ...validateGraph(graph, options),
21838
21942
  ...validateClientSchemaBoundaries(graph, options),
21839
- ...validateEffectPolicies(graph, options.effect)
21943
+ ...validateEffectPolicies(graph, options.effect),
21944
+ ...validateJobPolicies(graph)
21840
21945
  ];
21841
21946
  const databaseDiagnostics = await checkDatabaseGovernance(options.rootDir, options.databaseGovernance);
21842
21947
  diagnostics.push(...databaseDiagnostics);
@@ -21916,7 +22021,8 @@ function resolveTypeSafety(options) {
21916
22021
  return {
21917
22022
  noAnyInGenerated: true,
21918
22023
  scanProductionSource: true,
21919
- ...options.typeSafety?.exclude ? { exclude: options.typeSafety.exclude } : {}
22024
+ ...options.typeSafety?.exclude ? { exclude: options.typeSafety.exclude } : {},
22025
+ ...options.typeSafety?.allowAlternativeLibraries ? { allowAlternativeLibraries: options.typeSafety.allowAlternativeLibraries } : {}
21920
22026
  };
21921
22027
  }
21922
22028
 
@@ -22650,11 +22756,13 @@ var DEFAULT_SUPACLOUD_CONFIG = {
22650
22756
  outDir: "generated",
22651
22757
  include: ["**/*.module.ts", "**/*.ts"],
22652
22758
  strict: true,
22653
- requireRouteContracts: false,
22759
+ requireRouteContracts: true,
22654
22760
  generateClient: true,
22655
22761
  generateOpenApi: true,
22656
22762
  generatePermissions: true,
22657
22763
  treeShakeUnusedProviders: true,
22764
+ disallowControllerDirectDb: true,
22765
+ detectOrphanModules: true,
22658
22766
  commandCapabilities: {
22659
22767
  requirePersistentAdapters: true,
22660
22768
  permission: true,
@@ -22709,9 +22817,9 @@ function validateGovernanceConfig(config) {
22709
22817
  if (!isRecord(adapters))
22710
22818
  throw new Error("commandCapabilities.rpc must contain named adapters.");
22711
22819
  for (const [name, adapter] of Object.entries(adapters)) {
22712
- if (!name.trim() || !isRecord(adapter) || Object.keys(adapter).some((key) => !["audit", "idempotency", "transaction", "boundary"].includes(key)))
22820
+ if (!name.trim() || !isRecord(adapter) || Object.keys(adapter).some((key) => !["audit", "idempotency", "reconcile", "transaction", "boundary"].includes(key)))
22713
22821
  throw new Error("Invalid command adapter.");
22714
- for (const key of ["audit", "idempotency", "transaction"]) {
22822
+ for (const key of ["audit", "idempotency", "reconcile", "transaction"]) {
22715
22823
  if (adapter[key] !== undefined && typeof adapter[key] !== "boolean")
22716
22824
  throw new Error("Invalid command adapter capability.");
22717
22825
  }
@@ -22736,8 +22844,8 @@ function validateGovernanceConfig(config) {
22736
22844
  }
22737
22845
  if (config.typeSafety !== undefined) {
22738
22846
  const rules = config.typeSafety;
22739
- if (!isRecord(rules) || Object.keys(rules).some((key) => !["scanProductionSource", "noAnyInGenerated", "exclude"].includes(key)) || rules["scanProductionSource"] !== undefined && typeof rules["scanProductionSource"] !== "boolean" || rules["noAnyInGenerated"] !== undefined && typeof rules["noAnyInGenerated"] !== "boolean" || rules["exclude"] !== undefined && !isStrings(rules["exclude"])) {
22740
- throw new Error("typeSafety accepts boolean scanProductionSource/noAnyInGenerated and a string array exclude.");
22847
+ if (!isRecord(rules) || Object.keys(rules).some((key) => !["scanProductionSource", "noAnyInGenerated", "exclude", "allowAlternativeLibraries"].includes(key)) || rules["scanProductionSource"] !== undefined && typeof rules["scanProductionSource"] !== "boolean" || rules["noAnyInGenerated"] !== undefined && typeof rules["noAnyInGenerated"] !== "boolean" || rules["exclude"] !== undefined && !isStrings(rules["exclude"]) || rules["allowAlternativeLibraries"] !== undefined && !isStrings(rules["allowAlternativeLibraries"])) {
22848
+ throw new Error("typeSafety accepts boolean scanProductionSource/noAnyInGenerated and string arrays exclude/allowAlternativeLibraries.");
22741
22849
  }
22742
22850
  }
22743
22851
  for (const key of ["allowRouteCommandBindings", "disallowControllerDirectDb", "detectOrphanModules"]) {
@@ -22796,8 +22904,8 @@ function resolveSupacloudConfig(config = {}, cwd = process.cwd()) {
22796
22904
  }
22797
22905
  },
22798
22906
  ...resolved.allowRouteCommandBindings === undefined ? {} : { allowRouteCommandBindings: resolved.allowRouteCommandBindings },
22799
- ...resolved.disallowControllerDirectDb === undefined ? {} : { disallowControllerDirectDb: resolved.disallowControllerDirectDb },
22800
- ...resolved.detectOrphanModules === undefined ? {} : { detectOrphanModules: resolved.detectOrphanModules },
22907
+ disallowControllerDirectDb: resolved.disallowControllerDirectDb ?? DEFAULT_SUPACLOUD_CONFIG.disallowControllerDirectDb,
22908
+ detectOrphanModules: resolved.detectOrphanModules ?? DEFAULT_SUPACLOUD_CONFIG.detectOrphanModules,
22801
22909
  effect: resolved.effect === undefined ? { ...DEFAULT_SUPACLOUD_CONFIG.effect } : { ...DEFAULT_SUPACLOUD_CONFIG.effect, ...resolved.effect },
22802
22910
  treeShakeUnusedProviders: resolved.treeShakeUnusedProviders ?? DEFAULT_SUPACLOUD_CONFIG.treeShakeUnusedProviders,
22803
22911
  ...resolved.graphql === undefined || resolved.graphql === false ? {} : { graphql: {
@@ -22931,7 +23039,7 @@ function createDeliveryPlan(graph, input, validation) {
22931
23039
  if (target.name === "api" && target.kind !== "api" || target.name === "jobs" && target.kind !== "jobs") {
22932
23040
  fail("delivery-reserved-target", `"${target.name}" is reserved for its matching workload kind.`, "Rename the target or use the matching kind.");
22933
23041
  }
22934
- for (const name of sorted(target.modules)) {
23042
+ for (const name of sorted(target.modules ?? [])) {
22935
23043
  if (!modules.has(name)) {
22936
23044
  fail("delivery-unknown-module", `Target "${target.name}" refers to unknown module "${name}".`, "Select a module name from the compiler graph.");
22937
23045
  continue;
@@ -22945,6 +23053,23 @@ function createDeliveryPlan(graph, input, validation) {
22945
23053
  }
22946
23054
  }
22947
23055
  const targets = new Map;
23056
+ for (const declaration of declarations.values()) {
23057
+ if (declaration.kind !== "function" || declaration.function === undefined)
23058
+ continue;
23059
+ targets.set(declaration.name, {
23060
+ name: declaration.name,
23061
+ kind: "function",
23062
+ isolation: "shared",
23063
+ roots: [],
23064
+ modules: [],
23065
+ routes: [],
23066
+ jobs: [],
23067
+ externalTokens: [],
23068
+ function: declaration.function,
23069
+ requirements: { processIsolation: false, durableQueue: false, capabilities: sorted(declaration.capabilities ?? []) },
23070
+ runtimeStatus: options.runtime === undefined ? "unchecked" : "declared-compatible"
23071
+ });
23072
+ }
22948
23073
  function getTarget(name, fallback) {
22949
23074
  const existing = targets.get(name);
22950
23075
  if (existing)
@@ -23814,6 +23939,14 @@ async function buildDeliveryProject(options, delivery) {
23814
23939
  const planned = createDeliveryPlan({ ...checked.graph, diagnostics: checked.diagnostics }, settings, options);
23815
23940
  if (!planned.ok)
23816
23941
  return failed(planned.diagnostics);
23942
+ if (planned.plan.targets.some((target) => target.kind === "function")) {
23943
+ return failed([{
23944
+ severity: "error",
23945
+ code: "delivery-function-build-unsupported",
23946
+ message: "Function target rendering is not implemented yet; the delivery build only produces HTTP and Worker application objects.",
23947
+ suggestion: "Declare HTTP/Worker application targets for now; Function and schedule delivery is tracked separately."
23948
+ }]);
23949
+ }
23817
23950
  if (options.graphql && checked.mismatches.some((mismatch) => mismatch.startsWith("graphql"))) {
23818
23951
  throw new Error("Refresh GraphQL artifacts with compile before building independent targets.");
23819
23952
  }
package/dist/config.d.ts CHANGED
@@ -25,7 +25,7 @@ export interface SupaCloudConfig {
25
25
  commandCapabilities?: CommandExecutionCapabilities;
26
26
  treeShakeUnusedProviders?: boolean;
27
27
  }
28
- export declare const DEFAULT_SUPACLOUD_CONFIG: Required<Omit<SupaCloudConfig, "include" | "moduleBoundaryPreset" | "moduleBoundaries" | "typeSafety" | "delivery" | "allowRouteCommandBindings" | "disallowControllerDirectDb" | "detectOrphanModules" | "openApi" | "effect">> & {
28
+ export declare const DEFAULT_SUPACLOUD_CONFIG: Required<Omit<SupaCloudConfig, "include" | "moduleBoundaryPreset" | "moduleBoundaries" | "typeSafety" | "delivery" | "allowRouteCommandBindings" | "openApi" | "effect">> & {
29
29
  include: string[];
30
30
  moduleBoundaryPreset: ModuleBoundaryPresetName;
31
31
  effect: NonNullable<CompileOptions["effect"]>;
@@ -47,8 +47,8 @@ export declare function resolveSupacloudConfig(config?: SupaCloudConfig, cwd?: s
47
47
  typeSafety?: NonNullable<CompileOptions["typeSafety"]>;
48
48
  databaseGovernance?: NonNullable<CompileOptions["databaseGovernance"]>;
49
49
  allowRouteCommandBindings?: boolean;
50
- disallowControllerDirectDb?: boolean;
51
- detectOrphanModules?: boolean;
50
+ disallowControllerDirectDb: boolean;
51
+ detectOrphanModules: boolean;
52
52
  effect?: NonNullable<CompileOptions["effect"]>;
53
53
  treeShakeUnusedProviders: boolean;
54
54
  graphql?: GraphqlOptions;
@@ -43,14 +43,19 @@ declare const requestSchema: Type.TObject<{
43
43
  }>>;
44
44
  targets: Type.TOptional<Type.TArray<Type.TObject<{
45
45
  name: Type.TString;
46
- kind: Type.TUnion<[Type.TLiteral<"api">, Type.TLiteral<"jobs">, Type.TLiteral<"webhook">]>;
47
- modules: Type.TArray<Type.TString>;
46
+ kind: Type.TUnion<[Type.TLiteral<"api">, Type.TLiteral<"jobs">, Type.TLiteral<"webhook">, Type.TLiteral<"function">]>;
47
+ modules: Type.TOptional<Type.TArray<Type.TString>>;
48
48
  isolation: Type.TOptional<Type.TUnion<[Type.TLiteral<"shared">, Type.TLiteral<"process">]>>;
49
49
  capabilities: Type.TOptional<Type.TArray<Type.TString>>;
50
50
  compute: Type.TOptional<Type.TObject<{
51
51
  cpuLimit: Type.TNumber;
52
52
  memoryLimitMiB: Type.TInteger;
53
53
  }>>;
54
+ function: Type.TOptional<Type.TObject<{
55
+ slug: Type.TString;
56
+ entrypoint: Type.TString;
57
+ verifyJwt: Type.TBoolean;
58
+ }>>;
54
59
  }>>>;
55
60
  runtime: Type.TOptional<Type.TObject<{
56
61
  processIsolation: Type.TBoolean;
@@ -10064,6 +10064,11 @@ function parseDeliveryOptions(value) {
10064
10064
  if (value.targets?.some((target) => target.compute && target.isolation === "shared")) {
10065
10065
  throw new DeliveryConfigurationError;
10066
10066
  }
10067
+ for (const target of value.targets ?? []) {
10068
+ const invalidShape = target.kind === "function" ? target.function === undefined || target.modules !== undefined || target.compute !== undefined : target.modules === undefined || target.function !== undefined;
10069
+ if (invalidShape)
10070
+ throw new DeliveryConfigurationError;
10071
+ }
10067
10072
  const migrations = value.build?.migrations ?? [];
10068
10073
  try {
10069
10074
  validateWorkerExecution(value.execution?.groups ?? []);
@@ -10080,7 +10085,7 @@ function parseDeliveryPlanResult(value) {
10080
10085
  throw new Error("Invalid delivery plan result.");
10081
10086
  return value;
10082
10087
  }
10083
- var objectOptions, reference, references, kind, isolation, DeliveryFilePathSchema, DeliveryOptionsSchema, DeliveryDiagnosticSchema, routeSchema, jobSchema, DeliveryTargetSchema, DeliveryPlanSchema, DeliveryPlanResultSchema, DeliveryConfigurationError;
10088
+ var objectOptions, reference, references, kind, isolation, DeliveryFilePathSchema, functionTarget, DeliveryOptionsSchema, DeliveryDiagnosticSchema, routeSchema, jobSchema, DeliveryTargetSchema, DeliveryPlanSchema, DeliveryPlanResultSchema, DeliveryConfigurationError;
10084
10089
  var init_delivery_schema = __esm(() => {
10085
10090
  init_build2();
10086
10091
  init_value2();
@@ -10088,22 +10093,28 @@ var init_delivery_schema = __esm(() => {
10088
10093
  objectOptions = { additionalProperties: false };
10089
10094
  reference = String2({ minLength: 1, maxLength: 128, pattern: "^[A-Za-z][A-Za-z0-9_.:/-]*$" });
10090
10095
  references = _Array_(reference, { uniqueItems: true });
10091
- kind = Union([Literal("api"), Literal("jobs"), Literal("webhook")]);
10096
+ kind = Union([Literal("api"), Literal("jobs"), Literal("webhook"), Literal("function")]);
10092
10097
  isolation = Union([Literal("shared"), Literal("process")]);
10093
10098
  DeliveryFilePathSchema = String2({
10094
10099
  maxLength: 512,
10095
10100
  pattern: "^(?:[A-Za-z0-9_-][A-Za-z0-9_.-]*/)*[A-Za-z0-9_-][A-Za-z0-9_.-]*$"
10096
10101
  });
10102
+ functionTarget = _Object_({
10103
+ slug: String2({ pattern: "^[a-z][a-z0-9-]{0,62}$" }),
10104
+ entrypoint: DeliveryFilePathSchema,
10105
+ verifyJwt: Boolean2()
10106
+ }, objectOptions);
10097
10107
  DeliveryOptionsSchema = _Object_({
10098
10108
  version: Literal(1),
10099
10109
  execution: Optional(WorkerExecutionSchema),
10100
10110
  targets: Optional(_Array_(_Object_({
10101
10111
  name: String2({ pattern: "^[a-z][a-z0-9-]{0,62}$" }),
10102
10112
  kind,
10103
- modules: _Array_(reference, { minItems: 1, uniqueItems: true }),
10113
+ modules: Optional(_Array_(reference, { minItems: 1, uniqueItems: true })),
10104
10114
  isolation: Optional(isolation),
10105
10115
  capabilities: Optional(references),
10106
- compute: Optional(ComputeResourcesSchema)
10116
+ compute: Optional(ComputeResourcesSchema),
10117
+ function: Optional(functionTarget)
10107
10118
  }, objectOptions))),
10108
10119
  runtime: Optional(_Object_({
10109
10120
  processIsolation: Boolean2(),
@@ -10181,6 +10192,7 @@ var init_delivery_schema = __esm(() => {
10181
10192
  routes: _Array_(routeSchema),
10182
10193
  jobs: _Array_(jobSchema),
10183
10194
  externalTokens: _Array_(String2()),
10195
+ function: Optional(functionTarget),
10184
10196
  requirements: _Object_({
10185
10197
  processIsolation: Boolean2(),
10186
10198
  durableQueue: Boolean2(),
@@ -1,3 +1,5 @@
1
1
  import type { ApplicationGraph, Diagnostic, EffectCompilerOptions } from "./types";
2
2
  /** Validates the static Effect contract without inspecting Effect runtime internals. */
3
3
  export declare function validateEffectPolicies(graph: ApplicationGraph, options?: EffectCompilerOptions): Diagnostic[];
4
+ /** A retrying job must replay the same request without duplicating a write. */
5
+ export declare function validateJobPolicies(graph: ApplicationGraph): Diagnostic[];
package/dist/index.d.ts CHANGED
@@ -54,7 +54,7 @@ export { camelName } from "./util";
54
54
  export { ANGULAR_ENTERPRISE_RULES, CLEAN_ARCHITECTURE_RULES, MODULAR_MONOLITH_RULES, MODULE_BOUNDARY_PROFILES, getModuleBoundaryPreset, getModuleBoundaryProfile, resolveModuleBoundaries, } from "./profiles";
55
55
  export type { ApplicationGraph, AspectRefNode, CachedModuleEntry, CheckProjectResult, CommandExecutionCapabilities, CommandNode, CompileOptions, DatabaseGovernanceOptions, CompileResult, CompileStats, ControllerNode, DependencyGraphCache, DependencyGraphIndex, Diagnostic, DiagnosticFix, EffectCompilerOptions, EffectErrorMappingNode, EffectRetryPolicy, EffectRouteNode, ModuleBoundaryPresetName, ModuleBoundaryProfile, ModuleBoundaryRule, ModuleNode, JobNode, ProviderKind, ProviderNode, QueryNode, RouteNode, Scope, TokenKind, TypeSafetyOptions, ValidateOptions, WatchEvent, WatchHandle, WatchOptions, FeatureSpecNode, FeatureTransitionNode, GraphqlOptions, GraphqlContractSummary, OpenApiOptions, OpenApiSecurityScheme, OpenApiServer, } from "./types";
56
56
  export { inspectRouteContracts, validateRouteContracts } from "./route-contracts";
57
- export { validateEffectPolicies } from "./effect-governance";
57
+ export { validateEffectPolicies, validateJobPolicies } from "./effect-governance";
58
58
  export { pullGraphqlSchema } from "./graphql-schema";
59
59
  export type { PullGraphqlSchemaOptions } from "./graphql-schema";
60
60
  export { checkGraphqlCompatibility, createRoleSnapshotReport, diffGraphqlSchemas, graphqlFeatureMatrix, introspectionQuery, readGraphqlSchema, schemaFromSource, } from "./graphql-governance";
package/dist/index.js CHANGED
@@ -807,12 +807,15 @@ function validateGraph(graph, options = false) {
807
807
  if (typeof options === "object" && options.commandCapabilities) {
808
808
  const hostCaps = options.commandCapabilities;
809
809
  const rpcCaps = command.rpc && Object.hasOwn(hostCaps.rpc ?? {}, command.rpc) ? hostCaps.rpc?.[command.rpc] : undefined;
810
- if (hostCaps.requirePersistentAdapters && (command.rpc !== undefined && (!rpcCaps?.boundary || rpcCaps.audit !== true || rpcCaps.idempotency !== true) || hostCaps.permission !== true || !command.permission || !command.audit || command.idempotency !== "required" || command.rpc !== undefined && rpcCaps?.boundary === "database" && (rpcCaps.transaction !== true || command.transaction !== "required") || command.rpc === undefined && (hostCaps.audit !== true || hostCaps.idempotency !== true || hostCaps.transaction !== true || command.transaction !== "required"))) {
810
+ if (hostCaps.requirePersistentAdapters && (command.rpc !== undefined && (!rpcCaps?.boundary || rpcCaps.audit !== true || rpcCaps.idempotency !== true) || hostCaps.permission !== true || !command.permission || !command.audit || command.idempotency !== "required" || command.rpc !== undefined && rpcCaps?.boundary === "database" && (rpcCaps.transaction !== true || command.transaction !== "required") || command.rpc === undefined && (hostCaps.audit !== true || hostCaps.idempotency !== true || hostCaps.transaction !== true || command.transaction !== "required") || command.rpc !== undefined && rpcCaps?.boundary === "external" && rpcCaps.reconcile !== true)) {
811
811
  error("command-persistence-required", `Command ${command.name} requires an explicit persistent adapter, permission, audit and idempotency policy.`, module.file, module.line, "Register a named database/external adapter, enable permission checks and declare permission, audit and required idempotency on the command.");
812
812
  }
813
813
  if (rpcCaps?.boundary === "external" && (command.transaction === "required" || rpcCaps.transaction === true)) {
814
814
  error("command-external-transaction", `Command ${command.name} calls an external service; use durable intent and reconciliation, not transaction: 'required'.`, module.file, module.line, "Use transaction: 'none' with createExternalCommand; recover with read-only reconciliation. Do not claim a database transaction covers a remote request.");
815
815
  }
816
+ if (rpcCaps?.boundary === "external" && rpcCaps.reconcile !== true) {
817
+ error("command-reconcile-required", `Command ${command.name} uses an external adapter without a receipt reconciliation capability.`, module.file, module.line, "Declare reconcile: true only when the adapter can look up the same idempotency key and resolve unknown outcomes without redispatching.");
818
+ }
816
819
  const caps = command.rpc ? {
817
820
  permission: hostCaps.permission,
818
821
  audit: rpcCaps?.audit === true,
@@ -1229,6 +1232,7 @@ var init_validate = __esm(() => {
1229
1232
  "invalid-job-timeout": { code: "SC4015", docsUrl: "https://supacloud.dev/errors/SC4015" },
1230
1233
  "invalid-job-attempts": { code: "SC4016", docsUrl: "https://supacloud.dev/errors/SC4016" },
1231
1234
  "invalid-job-idempotency": { code: "SC4017", docsUrl: "https://supacloud.dev/errors/SC4017" },
1235
+ "job-retry-idempotency-required": { code: "SC4027", docsUrl: "https://supacloud.dev/errors/SC4027" },
1232
1236
  "invalid-job-mode": { code: "SC4018", docsUrl: "https://supacloud.dev/errors/SC4018" },
1233
1237
  "invalid-job-schema": { code: "SC4019", docsUrl: "https://supacloud.dev/errors/SC4019" },
1234
1238
  "command-persistence-required": { code: "SC4020", docsUrl: "https://supacloud.dev/errors/SC4020" },
@@ -1238,6 +1242,7 @@ var init_validate = __esm(() => {
1238
1242
  "command-idempotency-unsupported": { code: "SC4024", docsUrl: "https://supacloud.dev/errors/SC4024" },
1239
1243
  "command-transaction-rpc-only": { code: "SC4025", docsUrl: "https://supacloud.dev/errors/SC4025" },
1240
1244
  "command-transaction-unsupported": { code: "SC4026", docsUrl: "https://supacloud.dev/errors/SC4026" },
1245
+ "command-reconcile-required": { code: "SC4028", docsUrl: "https://supacloud.dev/errors/SC4028" },
1241
1246
  "invalid-route-contract": { code: "SC3020", docsUrl: "https://supacloud.dev/errors/SC3020" },
1242
1247
  "route-contract-required": { code: "SC3021", docsUrl: "https://supacloud.dev/errors/SC3021" },
1243
1248
  "route-contract-unverified": { code: "SC3022", docsUrl: "https://supacloud.dev/errors/SC3022" },
@@ -14557,6 +14562,11 @@ function parseDeliveryOptions(value) {
14557
14562
  if (value.targets?.some((target) => target.compute && target.isolation === "shared")) {
14558
14563
  throw new DeliveryConfigurationError;
14559
14564
  }
14565
+ for (const target of value.targets ?? []) {
14566
+ const invalidShape = target.kind === "function" ? target.function === undefined || target.modules !== undefined || target.compute !== undefined : target.modules === undefined || target.function !== undefined;
14567
+ if (invalidShape)
14568
+ throw new DeliveryConfigurationError;
14569
+ }
14560
14570
  const migrations = value.build?.migrations ?? [];
14561
14571
  try {
14562
14572
  validateWorkerExecution(value.execution?.groups ?? []);
@@ -14573,7 +14583,7 @@ function parseDeliveryPlanResult(value) {
14573
14583
  throw new Error("Invalid delivery plan result.");
14574
14584
  return value;
14575
14585
  }
14576
- var objectOptions, reference, references, kind, isolation, DeliveryFilePathSchema, DeliveryOptionsSchema, DeliveryDiagnosticSchema, routeSchema, jobSchema, DeliveryTargetSchema, DeliveryPlanSchema, DeliveryPlanResultSchema, DeliveryConfigurationError;
14586
+ var objectOptions, reference, references, kind, isolation, DeliveryFilePathSchema, functionTarget, DeliveryOptionsSchema, DeliveryDiagnosticSchema, routeSchema, jobSchema, DeliveryTargetSchema, DeliveryPlanSchema, DeliveryPlanResultSchema, DeliveryConfigurationError;
14577
14587
  var init_delivery_schema = __esm(() => {
14578
14588
  init_build();
14579
14589
  init_value2();
@@ -14581,22 +14591,28 @@ var init_delivery_schema = __esm(() => {
14581
14591
  objectOptions = { additionalProperties: false };
14582
14592
  reference = String2({ minLength: 1, maxLength: 128, pattern: "^[A-Za-z][A-Za-z0-9_.:/-]*$" });
14583
14593
  references = _Array_(reference, { uniqueItems: true });
14584
- kind = Union([Literal("api"), Literal("jobs"), Literal("webhook")]);
14594
+ kind = Union([Literal("api"), Literal("jobs"), Literal("webhook"), Literal("function")]);
14585
14595
  isolation = Union([Literal("shared"), Literal("process")]);
14586
14596
  DeliveryFilePathSchema = String2({
14587
14597
  maxLength: 512,
14588
14598
  pattern: "^(?:[A-Za-z0-9_-][A-Za-z0-9_.-]*/)*[A-Za-z0-9_-][A-Za-z0-9_.-]*$"
14589
14599
  });
14600
+ functionTarget = _Object_({
14601
+ slug: String2({ pattern: "^[a-z][a-z0-9-]{0,62}$" }),
14602
+ entrypoint: DeliveryFilePathSchema,
14603
+ verifyJwt: Boolean2()
14604
+ }, objectOptions);
14590
14605
  DeliveryOptionsSchema = _Object_({
14591
14606
  version: Literal(1),
14592
14607
  execution: Optional(WorkerExecutionSchema),
14593
14608
  targets: Optional(_Array_(_Object_({
14594
14609
  name: String2({ pattern: "^[a-z][a-z0-9-]{0,62}$" }),
14595
14610
  kind,
14596
- modules: _Array_(reference, { minItems: 1, uniqueItems: true }),
14611
+ modules: Optional(_Array_(reference, { minItems: 1, uniqueItems: true })),
14597
14612
  isolation: Optional(isolation),
14598
14613
  capabilities: Optional(references),
14599
- compute: Optional(ComputeResourcesSchema)
14614
+ compute: Optional(ComputeResourcesSchema),
14615
+ function: Optional(functionTarget)
14600
14616
  }, objectOptions))),
14601
14617
  runtime: Optional(_Object_({
14602
14618
  processIsolation: Boolean2(),
@@ -14674,6 +14690,7 @@ var init_delivery_schema = __esm(() => {
14674
14690
  routes: _Array_(routeSchema),
14675
14691
  jobs: _Array_(jobSchema),
14676
14692
  externalTokens: _Array_(String2()),
14693
+ function: Optional(functionTarget),
14677
14694
  requirements: _Object_({
14678
14695
  processIsolation: Boolean2(),
14679
14696
  durableQueue: Boolean2(),
@@ -15059,7 +15076,7 @@ function createContextPack(graph, subject) {
15059
15076
  }
15060
15077
  }
15061
15078
  }
15062
- const modules = graph.modules.filter((module) => selected.has(module.name));
15079
+ const modules = graph.modules.filter((module) => selected.has(module.name)).map(sanitizeContextModule);
15063
15080
  const queryDocuments = graph.graphql?.documents.filter((file) => modules.some((module) => {
15064
15081
  const path = relative6(dirname5(module.file), file);
15065
15082
  return path !== ".." && !path.startsWith(`..${sep6}`);
@@ -15101,7 +15118,12 @@ function createContextPack(graph, subject) {
15101
15118
  singleResult: "async-await",
15102
15119
  eventComposition: "rxjs",
15103
15120
  angularHost: "@supacloud/app/angular",
15104
- cancellation: "explicit-owner-signal"
15121
+ cancellation: "explicit-owner-signal",
15122
+ readTransport: "postgrest-supabase-js",
15123
+ writeTransport: "command-rpc",
15124
+ writeRetry: "disabled",
15125
+ externalOutcome: "receipt-reconcile",
15126
+ databaseSource: "drizzle-append-only"
15105
15127
  },
15106
15128
  relatedModules: {
15107
15129
  imports: subjectModule.imports.filter((name) => selected.has(name)),
@@ -15109,6 +15131,44 @@ function createContextPack(graph, subject) {
15109
15131
  }
15110
15132
  };
15111
15133
  }
15134
+ function sanitizeContextModule(module) {
15135
+ const sanitizeAspect = (aspect) => ({
15136
+ ...aspect,
15137
+ expression: "[omitted]"
15138
+ });
15139
+ return {
15140
+ ...module,
15141
+ providers: module.providers.map((provider) => ({
15142
+ ...provider,
15143
+ ...provider.useValueExpr === undefined ? {} : { useValueExpr: "[omitted]" },
15144
+ ...provider.useFactoryName === undefined ? {} : { useFactoryName: "[omitted]" },
15145
+ ...provider.functionalInjects === undefined ? {} : {
15146
+ functionalInjects: provider.functionalInjects.map((inject) => ({ ...inject, expression: "[omitted]" }))
15147
+ }
15148
+ })),
15149
+ ...module.aspects === undefined ? {} : { aspects: module.aspects.map(sanitizeAspect) },
15150
+ controllers: module.controllers.map((controller) => ({
15151
+ ...controller,
15152
+ ...controller.functionalInjects === undefined ? {} : {
15153
+ functionalInjects: controller.functionalInjects.map((inject) => ({ ...inject, expression: "[omitted]" }))
15154
+ },
15155
+ routes: controller.routes.map((route) => ({
15156
+ ...route,
15157
+ ...route.aspects === undefined ? {} : { aspects: route.aspects.map(sanitizeAspect) }
15158
+ }))
15159
+ })),
15160
+ commands: module.commands.map((command) => ({
15161
+ ...command,
15162
+ ...command.aspects === undefined ? {} : { aspects: command.aspects.map(sanitizeAspect) }
15163
+ })),
15164
+ ...module.jobs === undefined ? {} : {
15165
+ jobs: module.jobs.map((job) => ({
15166
+ ...job,
15167
+ ...job.aspects === undefined ? {} : { aspects: job.aspects.map(sanitizeAspect) }
15168
+ }))
15169
+ }
15170
+ };
15171
+ }
15112
15172
  function resolveContextModule(graph, subject) {
15113
15173
  const exact = graph.modules.find((module) => module.name === subject);
15114
15174
  if (exact)
@@ -21104,9 +21164,25 @@ import * as ts9 from "@typescript/typescript6";
21104
21164
  var SOURCE_SEMANTIC_DIAGNOSTIC_CODES = {
21105
21165
  "source-unsafe-any": { errorCode: "SC6009", docsUrl: "https://supacloud.dev/errors/SC6009" },
21106
21166
  "source-floating-promise": { errorCode: "SC6010", docsUrl: "https://supacloud.dev/errors/SC6010" },
21107
- "source-non-exhaustive-switch": { errorCode: "SC6011", docsUrl: "https://supacloud.dev/errors/SC6011" }
21167
+ "source-non-exhaustive-switch": { errorCode: "SC6011", docsUrl: "https://supacloud.dev/errors/SC6011" },
21168
+ "source-alternative-library": { errorCode: "SC6012", docsUrl: "https://supacloud.dev/errors/SC6012" }
21108
21169
  };
21109
- function scanSourceSemantics(source, checker, file, rootDir) {
21170
+ var DEFAULT_ALTERNATIVE_LIBRARIES = [
21171
+ "@apollo/client",
21172
+ "@urql/core",
21173
+ "graphql-request",
21174
+ "kefir",
21175
+ "most",
21176
+ "@pgflow",
21177
+ "pgflow",
21178
+ "pg_durable",
21179
+ "react-relay",
21180
+ "redux-observable",
21181
+ "urql",
21182
+ "wonka",
21183
+ "xstream"
21184
+ ];
21185
+ function scanSourceSemantics(source, checker, file, rootDir, options = {}) {
21110
21186
  const diagnostics = [];
21111
21187
  const report = (code, node, message) => {
21112
21188
  diagnostics.push({
@@ -21118,6 +21194,16 @@ function scanSourceSemantics(source, checker, file, rootDir) {
21118
21194
  line: source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1
21119
21195
  });
21120
21196
  };
21197
+ const allowedLibraries = new Set(options.allowAlternativeLibraries ?? []);
21198
+ for (const statement of source.statements) {
21199
+ if (!ts9.isImportDeclaration(statement) || !ts9.isStringLiteral(statement.moduleSpecifier))
21200
+ continue;
21201
+ const moduleName = statement.moduleSpecifier.text;
21202
+ const alternative = DEFAULT_ALTERNATIVE_LIBRARIES.find((name) => moduleName === name || moduleName.startsWith(`${name}/`));
21203
+ if (alternative && !allowedLibraries.has(alternative) && !allowedLibraries.has(moduleName)) {
21204
+ report("source-alternative-library", statement.moduleSpecifier, `Production source imports alternative library "${moduleName}". Use the governed Supabase client/PostgREST transport and RxJS event composition by default, or allow this package explicitly in typeSafety.allowAlternativeLibraries for a documented boundary.`);
21205
+ }
21206
+ }
21121
21207
  const unsafe = (value, target) => {
21122
21208
  if (isUnknownBoundary(target, checker) || containsAny(target, checker, new Set, rootDir))
21123
21209
  return;
@@ -21398,7 +21484,7 @@ function scanProductionSource(options) {
21398
21484
  }
21399
21485
  for (const sourceFile of sourceFiles) {
21400
21486
  scanSourceFile(sourceFile, checker, rootDir, diagnostics, options.strict ?? false);
21401
- diagnostics.push(...scanSourceSemantics(sourceFile, checker, normalizeRelative(rootDir, sourceFile.fileName), rootDir));
21487
+ diagnostics.push(...scanSourceSemantics(sourceFile, checker, normalizeRelative(rootDir, sourceFile.fileName), rootDir, options.allowAlternativeLibraries === undefined ? {} : { allowAlternativeLibraries: options.allowAlternativeLibraries }));
21402
21488
  diagnostics.push(...scanDrizzleSql(sourceFile, checker, normalizeRelative(rootDir, sourceFile.fileName), options.strict ?? false));
21403
21489
  const scanner = ts10.createScanner(ts10.ScriptTarget.Latest, false, sourceFile.languageVariant, sourceFile.text);
21404
21490
  for (let kind = scanner.scan();kind !== ts10.SyntaxKind.EndOfFileToken; kind = scanner.scan()) {
@@ -21660,6 +21746,17 @@ function validateEffectPolicies(graph, options = {}) {
21660
21746
  }
21661
21747
  return diagnostics;
21662
21748
  }
21749
+ function validateJobPolicies(graph) {
21750
+ const diagnostics = [];
21751
+ for (const module of graph.modules) {
21752
+ for (const job of module.jobs ?? []) {
21753
+ if (job.maxAttempts !== undefined && job.maxAttempts > 1 && job.idempotency !== "required") {
21754
+ diagnostics.push(diagnostic("job-retry-idempotency-required", `Job ${job.name} (${job.className}) enables ${job.maxAttempts} attempts without an idempotency contract.`, module.file, 'Use idempotency: "required" with a durable receipt, or set maxAttempts: 1.'));
21755
+ }
21756
+ }
21757
+ }
21758
+ return diagnostics;
21759
+ }
21663
21760
 
21664
21761
  // src/database-governance.ts
21665
21762
  import { execFile } from "node:child_process";
@@ -21709,13 +21806,19 @@ async function checkDatabaseGovernance(rootDir, value) {
21709
21806
 
21710
21807
  // src/compile.ts
21711
21808
  function withDefaultGovernance(options) {
21712
- return options.commandCapabilities === undefined ? { ...options, commandCapabilities: {
21713
- requirePersistentAdapters: true,
21714
- permission: true,
21715
- audit: true,
21716
- idempotency: true,
21717
- transaction: true
21718
- } } : options;
21809
+ return {
21810
+ ...options,
21811
+ requireRouteContracts: options.requireRouteContracts ?? true,
21812
+ disallowControllerDirectDb: options.disallowControllerDirectDb ?? true,
21813
+ detectOrphanModules: options.detectOrphanModules ?? true,
21814
+ ...options.commandCapabilities === undefined ? { commandCapabilities: {
21815
+ requirePersistentAdapters: true,
21816
+ permission: true,
21817
+ audit: true,
21818
+ idempotency: true,
21819
+ transaction: true
21820
+ } } : {}
21821
+ };
21719
21822
  }
21720
21823
  async function renderOptionalGraphql(options) {
21721
21824
  return options.graphql ? (await Promise.resolve().then(() => (init_graphql(), exports_graphql))).renderGraphql(options) : { diagnostics: [], files: {} };
@@ -21727,7 +21830,8 @@ async function compileProject(options) {
21727
21830
  ...graph.diagnostics ?? [],
21728
21831
  ...validateGraph(graph, options),
21729
21832
  ...validateClientSchemaBoundaries(graph, options),
21730
- ...validateEffectPolicies(graph, options.effect)
21833
+ ...validateEffectPolicies(graph, options.effect),
21834
+ ...validateJobPolicies(graph)
21731
21835
  ];
21732
21836
  const databaseDiagnostics = await checkDatabaseGovernance(options.rootDir, options.databaseGovernance);
21733
21837
  diagnostics.push(...databaseDiagnostics);
@@ -21797,7 +21901,8 @@ async function checkProject(options) {
21797
21901
  ...graph.diagnostics ?? [],
21798
21902
  ...validateGraph(graph, options),
21799
21903
  ...validateClientSchemaBoundaries(graph, options),
21800
- ...validateEffectPolicies(graph, options.effect)
21904
+ ...validateEffectPolicies(graph, options.effect),
21905
+ ...validateJobPolicies(graph)
21801
21906
  ];
21802
21907
  const databaseDiagnostics = await checkDatabaseGovernance(options.rootDir, options.databaseGovernance);
21803
21908
  diagnostics.push(...databaseDiagnostics);
@@ -21877,7 +21982,8 @@ function resolveTypeSafety(options) {
21877
21982
  return {
21878
21983
  noAnyInGenerated: true,
21879
21984
  scanProductionSource: true,
21880
- ...options.typeSafety?.exclude ? { exclude: options.typeSafety.exclude } : {}
21985
+ ...options.typeSafety?.exclude ? { exclude: options.typeSafety.exclude } : {},
21986
+ ...options.typeSafety?.allowAlternativeLibraries ? { allowAlternativeLibraries: options.typeSafety.allowAlternativeLibraries } : {}
21881
21987
  };
21882
21988
  }
21883
21989
 
@@ -21932,7 +22038,7 @@ function createDeliveryPlan(graph, input, validation) {
21932
22038
  if (target.name === "api" && target.kind !== "api" || target.name === "jobs" && target.kind !== "jobs") {
21933
22039
  fail("delivery-reserved-target", `"${target.name}" is reserved for its matching workload kind.`, "Rename the target or use the matching kind.");
21934
22040
  }
21935
- for (const name of sorted(target.modules)) {
22041
+ for (const name of sorted(target.modules ?? [])) {
21936
22042
  if (!modules.has(name)) {
21937
22043
  fail("delivery-unknown-module", `Target "${target.name}" refers to unknown module "${name}".`, "Select a module name from the compiler graph.");
21938
22044
  continue;
@@ -21946,6 +22052,23 @@ function createDeliveryPlan(graph, input, validation) {
21946
22052
  }
21947
22053
  }
21948
22054
  const targets = new Map;
22055
+ for (const declaration of declarations.values()) {
22056
+ if (declaration.kind !== "function" || declaration.function === undefined)
22057
+ continue;
22058
+ targets.set(declaration.name, {
22059
+ name: declaration.name,
22060
+ kind: "function",
22061
+ isolation: "shared",
22062
+ roots: [],
22063
+ modules: [],
22064
+ routes: [],
22065
+ jobs: [],
22066
+ externalTokens: [],
22067
+ function: declaration.function,
22068
+ requirements: { processIsolation: false, durableQueue: false, capabilities: sorted(declaration.capabilities ?? []) },
22069
+ runtimeStatus: options.runtime === undefined ? "unchecked" : "declared-compatible"
22070
+ });
22071
+ }
21949
22072
  function getTarget(name, fallback) {
21950
22073
  const existing = targets.get(name);
21951
22074
  if (existing)
@@ -22814,6 +22937,14 @@ async function buildDeliveryProject(options, delivery) {
22814
22937
  const planned = createDeliveryPlan({ ...checked.graph, diagnostics: checked.diagnostics }, settings, options);
22815
22938
  if (!planned.ok)
22816
22939
  return failed(planned.diagnostics);
22940
+ if (planned.plan.targets.some((target) => target.kind === "function")) {
22941
+ return failed([{
22942
+ severity: "error",
22943
+ code: "delivery-function-build-unsupported",
22944
+ message: "Function target rendering is not implemented yet; the delivery build only produces HTTP and Worker application objects.",
22945
+ suggestion: "Declare HTTP/Worker application targets for now; Function and schedule delivery is tracked separately."
22946
+ }]);
22947
+ }
22817
22948
  if (options.graphql && checked.mismatches.some((mismatch) => mismatch.startsWith("graphql"))) {
22818
22949
  throw new Error("Refresh GraphQL artifacts with compile before building independent targets.");
22819
22950
  }
@@ -24932,11 +25063,13 @@ var DEFAULT_SUPACLOUD_CONFIG = {
24932
25063
  outDir: "generated",
24933
25064
  include: ["**/*.module.ts", "**/*.ts"],
24934
25065
  strict: true,
24935
- requireRouteContracts: false,
25066
+ requireRouteContracts: true,
24936
25067
  generateClient: true,
24937
25068
  generateOpenApi: true,
24938
25069
  generatePermissions: true,
24939
25070
  treeShakeUnusedProviders: true,
25071
+ disallowControllerDirectDb: true,
25072
+ detectOrphanModules: true,
24940
25073
  commandCapabilities: {
24941
25074
  requirePersistentAdapters: true,
24942
25075
  permission: true,
@@ -24991,9 +25124,9 @@ function validateGovernanceConfig(config) {
24991
25124
  if (!isRecord(adapters))
24992
25125
  throw new Error("commandCapabilities.rpc must contain named adapters.");
24993
25126
  for (const [name, adapter] of Object.entries(adapters)) {
24994
- if (!name.trim() || !isRecord(adapter) || Object.keys(adapter).some((key) => !["audit", "idempotency", "transaction", "boundary"].includes(key)))
25127
+ if (!name.trim() || !isRecord(adapter) || Object.keys(adapter).some((key) => !["audit", "idempotency", "reconcile", "transaction", "boundary"].includes(key)))
24995
25128
  throw new Error("Invalid command adapter.");
24996
- for (const key of ["audit", "idempotency", "transaction"]) {
25129
+ for (const key of ["audit", "idempotency", "reconcile", "transaction"]) {
24997
25130
  if (adapter[key] !== undefined && typeof adapter[key] !== "boolean")
24998
25131
  throw new Error("Invalid command adapter capability.");
24999
25132
  }
@@ -25018,8 +25151,8 @@ function validateGovernanceConfig(config) {
25018
25151
  }
25019
25152
  if (config.typeSafety !== undefined) {
25020
25153
  const rules = config.typeSafety;
25021
- if (!isRecord(rules) || Object.keys(rules).some((key) => !["scanProductionSource", "noAnyInGenerated", "exclude"].includes(key)) || rules["scanProductionSource"] !== undefined && typeof rules["scanProductionSource"] !== "boolean" || rules["noAnyInGenerated"] !== undefined && typeof rules["noAnyInGenerated"] !== "boolean" || rules["exclude"] !== undefined && !isStrings(rules["exclude"])) {
25022
- throw new Error("typeSafety accepts boolean scanProductionSource/noAnyInGenerated and a string array exclude.");
25154
+ if (!isRecord(rules) || Object.keys(rules).some((key) => !["scanProductionSource", "noAnyInGenerated", "exclude", "allowAlternativeLibraries"].includes(key)) || rules["scanProductionSource"] !== undefined && typeof rules["scanProductionSource"] !== "boolean" || rules["noAnyInGenerated"] !== undefined && typeof rules["noAnyInGenerated"] !== "boolean" || rules["exclude"] !== undefined && !isStrings(rules["exclude"]) || rules["allowAlternativeLibraries"] !== undefined && !isStrings(rules["allowAlternativeLibraries"])) {
25155
+ throw new Error("typeSafety accepts boolean scanProductionSource/noAnyInGenerated and string arrays exclude/allowAlternativeLibraries.");
25023
25156
  }
25024
25157
  }
25025
25158
  for (const key of ["allowRouteCommandBindings", "disallowControllerDirectDb", "detectOrphanModules"]) {
@@ -25078,8 +25211,8 @@ function resolveSupacloudConfig(config = {}, cwd = process.cwd()) {
25078
25211
  }
25079
25212
  },
25080
25213
  ...resolved.allowRouteCommandBindings === undefined ? {} : { allowRouteCommandBindings: resolved.allowRouteCommandBindings },
25081
- ...resolved.disallowControllerDirectDb === undefined ? {} : { disallowControllerDirectDb: resolved.disallowControllerDirectDb },
25082
- ...resolved.detectOrphanModules === undefined ? {} : { detectOrphanModules: resolved.detectOrphanModules },
25214
+ disallowControllerDirectDb: resolved.disallowControllerDirectDb ?? DEFAULT_SUPACLOUD_CONFIG.disallowControllerDirectDb,
25215
+ detectOrphanModules: resolved.detectOrphanModules ?? DEFAULT_SUPACLOUD_CONFIG.detectOrphanModules,
25083
25216
  effect: resolved.effect === undefined ? { ...DEFAULT_SUPACLOUD_CONFIG.effect } : { ...DEFAULT_SUPACLOUD_CONFIG.effect, ...resolved.effect },
25084
25217
  treeShakeUnusedProviders: resolved.treeShakeUnusedProviders ?? DEFAULT_SUPACLOUD_CONFIG.treeShakeUnusedProviders,
25085
25218
  ...resolved.graphql === undefined || resolved.graphql === false ? {} : { graphql: {
@@ -25494,6 +25627,7 @@ export {
25494
25627
  validateEffectPolicies,
25495
25628
  validateFeatureSpec,
25496
25629
  validateGraph,
25630
+ validateJobPolicies,
25497
25631
  validateRouteContracts,
25498
25632
  watchProject,
25499
25633
  writeOpenApiJson
package/dist/inspect.d.ts CHANGED
@@ -22,6 +22,11 @@ export interface ContextPack {
22
22
  eventComposition: "rxjs";
23
23
  angularHost: "@supacloud/app/angular";
24
24
  cancellation: "explicit-owner-signal";
25
+ readTransport: "postgrest-supabase-js";
26
+ writeTransport: "command-rpc";
27
+ writeRetry: "disabled";
28
+ externalOutcome: "receipt-reconcile";
29
+ databaseSource: "drizzle-append-only";
25
30
  };
26
31
  relatedModules: {
27
32
  importedBy: string[];
@@ -13,6 +13,12 @@ export declare const SOURCE_SEMANTIC_DIAGNOSTIC_CODES: {
13
13
  readonly errorCode: "SC6011";
14
14
  readonly docsUrl: "https://supacloud.dev/errors/SC6011";
15
15
  };
16
+ readonly "source-alternative-library": {
17
+ readonly errorCode: "SC6012";
18
+ readonly docsUrl: "https://supacloud.dev/errors/SC6012";
19
+ };
16
20
  };
17
21
  /** Inspect values at use sites, including types resolved through imported declarations. */
18
- export declare function scanSourceSemantics(source: ts.SourceFile, checker: ts.TypeChecker, file: string, rootDir: string): Diagnostic[];
22
+ export declare function scanSourceSemantics(source: ts.SourceFile, checker: ts.TypeChecker, file: string, rootDir: string, options?: {
23
+ allowAlternativeLibraries?: readonly string[];
24
+ }): Diagnostic[];
@@ -36,6 +36,10 @@ export declare const TYPE_SAFETY_DIAGNOSTIC_CODES: {
36
36
  readonly errorCode: "SC6011";
37
37
  readonly docsUrl: "https://supacloud.dev/errors/SC6011";
38
38
  };
39
+ readonly "source-alternative-library": {
40
+ readonly errorCode: "SC6012";
41
+ readonly docsUrl: "https://supacloud.dev/errors/SC6012";
42
+ };
39
43
  readonly "sql-result-assertion": {
40
44
  readonly errorCode: "SC6007";
41
45
  readonly docsUrl: "https://supacloud.dev/errors/SC6007";
package/dist/types.d.ts CHANGED
@@ -536,6 +536,8 @@ export interface TypeSafetyOptions {
536
536
  scanProductionSource?: boolean;
537
537
  /** Additional relative glob patterns excluded from the production-source scan. */
538
538
  exclude?: string[];
539
+ /** Explicitly approved alternatives to the default client/stream libraries. */
540
+ allowAlternativeLibraries?: string[];
539
541
  }
540
542
  export interface DatabaseGovernanceOptions {
541
543
  /** Off for existing callers; check validates sources offline, enforce also checks SQL and Catalog. */
@@ -590,6 +592,7 @@ export interface CommandExecutionCapabilities {
590
592
  rpc?: Record<string, {
591
593
  audit?: boolean;
592
594
  idempotency?: boolean;
595
+ reconcile?: boolean;
593
596
  transaction?: boolean;
594
597
  boundary?: "database" | "external";
595
598
  }>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supacloud/compiler",
3
- "version": "0.45.1",
3
+ "version": "0.46.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",
@@ -57,8 +57,8 @@
57
57
  "directory": "packages/compiler"
58
58
  },
59
59
  "dependencies": {
60
- "@supacloud/db": "0.16.2",
61
- "@supacloud/delivery": "0.16.0",
60
+ "@supacloud/db": "0.17.0",
61
+ "@supacloud/delivery": "0.17.0",
62
62
  "typebox": "1.3.36",
63
63
  "@graphql-codegen/core": "^6.2.2",
64
64
  "@graphql-codegen/typed-document-node": "^7.1.1",