@palbase/backend 32.0.0 → 33.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/bin/palbase-backend.cjs +79 -26
  2. package/dist/bin/palbase-backend.cjs.map +1 -1
  3. package/dist/bin/palbase-backend.js +5 -5
  4. package/dist/{chunk-AZJIKCOR.js → chunk-IKDONZ5D.js} +39 -23
  5. package/dist/{chunk-AZJIKCOR.js.map → chunk-IKDONZ5D.js.map} +1 -1
  6. package/dist/{chunk-IXAX5CON.js → chunk-SI4KGEM3.js} +1 -1
  7. package/dist/{chunk-IXAX5CON.js.map → chunk-SI4KGEM3.js.map} +1 -1
  8. package/dist/{chunk-XEGZ3S2Q.js → chunk-TVCCR6SO.js} +32 -7
  9. package/dist/chunk-TVCCR6SO.js.map +1 -0
  10. package/dist/{chunk-5C5UCILO.js → chunk-WWUG2QXF.js} +3 -3
  11. package/dist/{chunk-KGP6ALIU.js → chunk-XOX6RFPZ.js} +30 -8
  12. package/dist/chunk-XOX6RFPZ.js.map +1 -0
  13. package/dist/{chunk-Z4CZRMNF.js → chunk-YIQ4RS4F.js} +2 -2
  14. package/dist/db/index.cjs +14 -4
  15. package/dist/db/index.cjs.map +1 -1
  16. package/dist/db/index.d.cts +1 -1
  17. package/dist/db/index.d.ts +1 -1
  18. package/dist/db/index.js +3 -3
  19. package/dist/engine/index.cjs +79 -26
  20. package/dist/engine/index.cjs.map +1 -1
  21. package/dist/engine/index.d.cts +3 -3
  22. package/dist/engine/index.d.ts +3 -3
  23. package/dist/engine/index.js +5 -5
  24. package/dist/{index-BILC9WwS.d.ts → index-D-3duy8Y.d.ts} +2 -2
  25. package/dist/{index-DgYkdNT9.d.cts → index-DB_nW-AV.d.cts} +87 -30
  26. package/dist/{index-CWGiJ2Up.d.cts → index-DLveQoOf.d.cts} +2 -2
  27. package/dist/{index-BgWnP07a.d.ts → index-DMZG3kpo.d.ts} +87 -30
  28. package/dist/index.cjs +59 -12
  29. package/dist/index.cjs.map +1 -1
  30. package/dist/index.d.cts +6 -6
  31. package/dist/index.d.ts +6 -6
  32. package/dist/index.js +5 -5
  33. package/dist/openapi/index.d.cts +2 -2
  34. package/dist/openapi/index.d.ts +2 -2
  35. package/dist/{registry-BcRYIQ-R.d.cts → registry-DSTThhKf.d.cts} +1 -1
  36. package/dist/{registry-ClzjxIWy.d.ts → registry-JjF5lcj4.d.ts} +1 -1
  37. package/dist/test/index.cjs +1155 -12
  38. package/dist/test/index.cjs.map +1 -1
  39. package/dist/test/index.d.cts +1 -1
  40. package/dist/test/index.d.ts +1 -1
  41. package/dist/test/index.js +1126 -8
  42. package/dist/test/index.js.map +1 -1
  43. package/docs/README.md +124 -80
  44. package/docs/auth.md +112 -26
  45. package/docs/background.md +16 -7
  46. package/docs/database.md +22 -18
  47. package/docs/endpoints.md +26 -20
  48. package/docs/errors.md +2 -3
  49. package/docs/events.md +63 -29
  50. package/docs/getting-started.md +8 -7
  51. package/docs/llms-full.txt +519 -244
  52. package/docs/migrations.md +6 -5
  53. package/docs/routing.md +34 -11
  54. package/docs/schema.md +78 -27
  55. package/docs/services.md +30 -13
  56. package/package.json +2 -1
  57. package/stack-images.json +24 -0
  58. package/template/db/public.ts +23 -0
  59. package/template/package.json +1 -1
  60. package/dist/chunk-KGP6ALIU.js.map +0 -1
  61. package/dist/chunk-XEGZ3S2Q.js.map +0 -1
  62. /package/dist/{chunk-5C5UCILO.js.map → chunk-WWUG2QXF.js.map} +0 -0
  63. /package/dist/{chunk-Z4CZRMNF.js.map → chunk-YIQ4RS4F.js.map} +0 -0
@@ -1,4 +1,4 @@
1
- export { h as AnyColumn, n as ColumnBuilder, o as ColumnDef, p as ColumnMap, q as ColumnType, v as EXTENSION_DEPENDENCIES, w as EmbeddingModelRef, E as EnvServiceDatabase, z as EnvTypedDatabase, V as InsertShape, $ as Materialized, a6 as OnDeleteAction, a8 as PALBASE_EXTENSIONS, aI as PalbaseExtension, bF as PolicyBuilder, bG as PolicyCommand, bH as PolicyDef, bK as PolicyMode, bN as RawConstraintDef, bP as Ref, bR as RowShape, S as SchemaDef, b_ as TABLE_META, b$ as TableDef, c0 as TableHandle, c1 as TableInput, c3 as TxColumnExpr, c4 as TxInsertShape, c6 as TxNow, c7 as TxPlan, T as TxPlanBody, c8 as TxPlanError, c9 as TxPlanHandle, ca as TxPlanOpResult, cb as TxPlanRejection, c as TxPlanResponse, cc as TxRefError, cd as TxRow, ce as TxRows, cf as TxSelectOptions, cg as TxSetShape, ci as TxTable, cj as TxTables, ck as TxWhere, cq as TypedDB, cr as TypedTable, cs as TypedTx, cD as bigint, cE as boolean, cG as dec, cJ as defineSchema, cK as defineTable, cL as enumType, cN as inc, cQ as installationRef, cR as integer, cS as isPalbaseExtension, cU as jsonb, cV as makeTypedDB, cW as now, c_ as openai, c$ as ownedByUser, d0 as policy, d1 as raw, d3 as text, d4 as timestamp, d5 as userRef, d6 as uuid, d7 as vector } from '../index-DgYkdNT9.cjs';
1
+ export { h as AnyColumn, n as ColumnBuilder, o as ColumnDef, p as ColumnMap, q as ColumnType, v as EXTENSION_DEPENDENCIES, w as EmbeddingModelRef, E as EnvServiceDatabase, z as EnvTypedDatabase, V as InsertShape, $ as Materialized, a6 as OnDeleteAction, a8 as PALBASE_EXTENSIONS, aI as PalbaseExtension, bF as PolicyBuilder, bG as PolicyCommand, bH as PolicyDef, bK as PolicyMode, bN as RawConstraintDef, bP as Ref, bR as RowShape, S as SchemaDef, b_ as TABLE_META, b$ as TableDef, c0 as TableHandle, c1 as TableInput, c3 as TxColumnExpr, c4 as TxInsertShape, c6 as TxNow, c7 as TxPlan, T as TxPlanBody, c8 as TxPlanError, c9 as TxPlanHandle, ca as TxPlanOpResult, cb as TxPlanRejection, c as TxPlanResponse, cc as TxRefError, cd as TxRow, ce as TxRows, cf as TxSelectOptions, cg as TxSetShape, ci as TxTable, cj as TxTables, ck as TxWhere, cq as TypedDB, cr as TypedTable, cs as TypedTx, cD as bigint, cE as boolean, cG as dec, cJ as defineSchema, cK as defineTable, cL as enumType, cN as inc, cQ as installationRef, cR as integer, cS as isPalbaseExtension, cU as jsonb, cV as makeTypedDB, cW as now, c_ as openai, c$ as ownedByUser, d0 as policy, d1 as raw, d3 as text, d4 as timestamp, d5 as userRef, d6 as uuid, d7 as vector } from '../index-DB_nW-AV.cjs';
2
2
  import './env.cjs';
3
3
  import '../stack.cjs';
4
4
  import 'zod';
@@ -1,4 +1,4 @@
1
- export { h as AnyColumn, n as ColumnBuilder, o as ColumnDef, p as ColumnMap, q as ColumnType, v as EXTENSION_DEPENDENCIES, w as EmbeddingModelRef, E as EnvServiceDatabase, z as EnvTypedDatabase, V as InsertShape, $ as Materialized, a6 as OnDeleteAction, a8 as PALBASE_EXTENSIONS, aI as PalbaseExtension, bF as PolicyBuilder, bG as PolicyCommand, bH as PolicyDef, bK as PolicyMode, bN as RawConstraintDef, bP as Ref, bR as RowShape, S as SchemaDef, b_ as TABLE_META, b$ as TableDef, c0 as TableHandle, c1 as TableInput, c3 as TxColumnExpr, c4 as TxInsertShape, c6 as TxNow, c7 as TxPlan, T as TxPlanBody, c8 as TxPlanError, c9 as TxPlanHandle, ca as TxPlanOpResult, cb as TxPlanRejection, c as TxPlanResponse, cc as TxRefError, cd as TxRow, ce as TxRows, cf as TxSelectOptions, cg as TxSetShape, ci as TxTable, cj as TxTables, ck as TxWhere, cq as TypedDB, cr as TypedTable, cs as TypedTx, cD as bigint, cE as boolean, cG as dec, cJ as defineSchema, cK as defineTable, cL as enumType, cN as inc, cQ as installationRef, cR as integer, cS as isPalbaseExtension, cU as jsonb, cV as makeTypedDB, cW as now, c_ as openai, c$ as ownedByUser, d0 as policy, d1 as raw, d3 as text, d4 as timestamp, d5 as userRef, d6 as uuid, d7 as vector } from '../index-BgWnP07a.js';
1
+ export { h as AnyColumn, n as ColumnBuilder, o as ColumnDef, p as ColumnMap, q as ColumnType, v as EXTENSION_DEPENDENCIES, w as EmbeddingModelRef, E as EnvServiceDatabase, z as EnvTypedDatabase, V as InsertShape, $ as Materialized, a6 as OnDeleteAction, a8 as PALBASE_EXTENSIONS, aI as PalbaseExtension, bF as PolicyBuilder, bG as PolicyCommand, bH as PolicyDef, bK as PolicyMode, bN as RawConstraintDef, bP as Ref, bR as RowShape, S as SchemaDef, b_ as TABLE_META, b$ as TableDef, c0 as TableHandle, c1 as TableInput, c3 as TxColumnExpr, c4 as TxInsertShape, c6 as TxNow, c7 as TxPlan, T as TxPlanBody, c8 as TxPlanError, c9 as TxPlanHandle, ca as TxPlanOpResult, cb as TxPlanRejection, c as TxPlanResponse, cc as TxRefError, cd as TxRow, ce as TxRows, cf as TxSelectOptions, cg as TxSetShape, ci as TxTable, cj as TxTables, ck as TxWhere, cq as TypedDB, cr as TypedTable, cs as TypedTx, cD as bigint, cE as boolean, cG as dec, cJ as defineSchema, cK as defineTable, cL as enumType, cN as inc, cQ as installationRef, cR as integer, cS as isPalbaseExtension, cU as jsonb, cV as makeTypedDB, cW as now, c_ as openai, c$ as ownedByUser, d0 as policy, d1 as raw, d3 as text, d4 as timestamp, d5 as userRef, d6 as uuid, d7 as vector } from '../index-DMZG3kpo.js';
2
2
  import './env.js';
3
3
  import '../stack.js';
4
4
  import 'zod';
package/dist/db/index.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  openai,
10
10
  policy,
11
11
  raw
12
- } from "../chunk-Z4CZRMNF.js";
12
+ } from "../chunk-YIQ4RS4F.js";
13
13
  import {
14
14
  bigint,
15
15
  boolean,
@@ -23,7 +23,7 @@ import {
23
23
  userRef,
24
24
  uuid,
25
25
  vector
26
- } from "../chunk-KGP6ALIU.js";
26
+ } from "../chunk-XOX6RFPZ.js";
27
27
  import {
28
28
  TxPlanError,
29
29
  TxRefError,
@@ -31,7 +31,7 @@ import {
31
31
  inc,
32
32
  makeTypedDB,
33
33
  now
34
- } from "../chunk-IXAX5CON.js";
34
+ } from "../chunk-SI4KGEM3.js";
35
35
  import "../chunk-KATPXCJ5.js";
36
36
  export {
37
37
  EXTENSION_DEPENDENCIES,
@@ -875,9 +875,29 @@ function __runWithRuntime(services, fn) {
875
875
  }, fn);
876
876
  }
877
877
  __name(__runWithRuntime, "__runWithRuntime");
878
+ var REQUIRED_SERVICES = [
879
+ "Database",
880
+ "Auth",
881
+ "Secrets",
882
+ "Documents",
883
+ "Storage",
884
+ "Cache",
885
+ "Log",
886
+ "Notifications",
887
+ "Flags",
888
+ "Realtime"
889
+ ];
890
+ function refuseIncompleteBox(services) {
891
+ const missing = REQUIRED_SERVICES.filter((k) => services[k] === void 0);
892
+ if (missing.length > 0) {
893
+ throw new Error(`the request scope is missing ${missing.join(", ")} \u2014 this box was most likely written by a DIFFERENT @palbase/backend major sharing the same process (the request store is a process-wide well-known Symbol, deliberately, so two bundles of ONE version can share it). Align the versions, or pass every service when building the scope yourself.`);
894
+ }
895
+ return services;
896
+ }
897
+ __name(refuseIncompleteBox, "refuseIncompleteBox");
878
898
  function __getRuntime() {
879
899
  const scoped = __requestALS.getStore();
880
- if (scoped) return scoped.runtime;
900
+ if (scoped) return refuseIncompleteBox(scoped.runtime);
881
901
  if (runtime === null) {
882
902
  throw new Error("Palbase services accessed outside a request scope. The Database/Documents/\u2026 singletons are only available inside an endpoint handler (or after the runtime has called __runWithRuntime / __setRuntime).");
883
903
  }
@@ -1000,7 +1020,12 @@ function makeTypedSurface(raw) {
1000
1020
  $findById: /* @__PURE__ */ __name((table, id) => raw.findById(table, id), "$findById"),
1001
1021
  $findMany: /* @__PURE__ */ __name((table, query, opts) => raw.findMany(table, query, opts), "$findMany"),
1002
1022
  $put: /* @__PURE__ */ __name((table, data, opts) => raw.put(table, data, opts), "$put"),
1003
- $updateMany: /* @__PURE__ */ __name((table, where, set) => raw.updateMany(table, where, set), "$updateMany"),
1023
+ // `opts` İLETİLİR. Düşürüldüğü sürece `Database.$updateMany(t, w, s,
1024
+ // { returning: false })` sessizce SATIRLARI döndürüyordu, sayıyı değil —
1025
+ // ve `if (n === 0) throw new Conflict(...)` guard'ı HİÇ çalışmıyordu,
1026
+ // çünkü `[] === 0` yanlıştır. Komşu forwarder'lar (`$findMany`, `$put`)
1027
+ // kendi opsiyonlarını zaten iletiyordu; bu biri unutulmuştu.
1028
+ $updateMany: /* @__PURE__ */ __name((table, where, set, opts) => raw.updateMany(table, where, set, opts), "$updateMany"),
1004
1029
  $deleteMany: /* @__PURE__ */ __name((table, where) => raw.deleteMany(table, where), "$deleteMany"),
1005
1030
  $count: /* @__PURE__ */ __name((table, where) => raw.count(table, where), "$count"),
1006
1031
  $search: /* @__PURE__ */ __name((table, params) => raw.search(table, params), "$search"),
@@ -1131,13 +1156,13 @@ var Flags = Object.assign({
1131
1156
  * Lazily resolve the runtime's cross-user sibling on each call. We do NOT
1132
1157
  * cache it: `rawFlags.asService()` reads the CURRENT request scope through
1133
1158
  * the runtime proxy, so caching would leak one request's sibling into
1134
- * another concurrent request. Mirrors `Database.asService()`.
1159
+ * another concurrent request. Mirrors `Database.$asService()`.
1135
1160
  */
1136
1161
  $asService() {
1137
1162
  return rawFlags.asService();
1138
1163
  },
1139
1164
  /**
1140
- * The name this member carried before 31.0.0.
1165
+ * The name this member carried before 32.0.0.
1141
1166
  *
1142
1167
  * It does NOT work silently. A retired member that quietly keeps returning
1143
1168
  * is how a rename becomes a mystery: the old call site goes on compiling,
@@ -1147,7 +1172,7 @@ var Flags = Object.assign({
1147
1172
  * Database.$asService()" while the promise went unkept.
1148
1173
  */
1149
1174
  asService() {
1150
- throw new Error("Flags.asService() was renamed to Flags.$asService() in 31.0.0 \u2014 system members carry the `$` prefix, like Database.$asService().");
1175
+ throw new Error("Flags.asService() was renamed to Flags.$asService() in 32.0.0 \u2014 system members carry the `$` prefix, like Database.$asService().");
1151
1176
  }
1152
1177
  });
1153
1178
  var Realtime = makeServiceProxy("Realtime");
@@ -1526,15 +1551,27 @@ var CODECS = {
1526
1551
  // not representable and the author wants `asDecimal()` instead.
1527
1552
  number: {
1528
1553
  fromDb: /* @__PURE__ */ __name((v) => Number(v), "fromDb"),
1529
- toDb: /* @__PURE__ */ __name((v) => String(v), "toDb"),
1554
+ // YAZMA YOLUNDA SESSİZ BOZULMA YOK. `toDb(9007199254740993)` bir zamanlar
1555
+ // "9007199254740992" yazıyordu: uygulamanın tuttuğundan BAŞKA bir sayı
1556
+ // kolona gidiyordu ve hiçbir şey söylemiyordu. `asNumber()`'ın JSDoc'u
1557
+ // "2^53'ün altında güvenli" diyordu ama bunu uygulayan bir şey yoktu.
1558
+ toDb: /* @__PURE__ */ __name((v) => {
1559
+ if (typeof v === "number" && !Number.isSafeInteger(v)) {
1560
+ throw new Error(`asNumber() cannot write ${v}: it is outside the safe integer range and would be stored as a different number. Declare .asDecimal() and hand this column a string.`);
1561
+ }
1562
+ return String(v);
1563
+ }, "toDb"),
1530
1564
  tsType: "number"
1531
1565
  },
1532
1566
  // Exact-precision column → the string Postgres sent. It exists so the
1533
1567
  // DECLARATION is explicit: "this column is a decimal I handle as text",
1534
1568
  // rather than the ABSENCE of a declaration meaning the same thing by default.
1569
+ // NULL güvencesi ÇİFTİN KENDİSİNDE. Motor NULL'ı zaten atlıyor (üç ayrı
1570
+ // yerde), ama bir değişmez, onu koruyan üç `if`'in DIŞINDA yazılmalı: biri
1571
+ // düşerse `String(null)` kolonu "null" dizesine çevirirdi.
1535
1572
  decimal: {
1536
- fromDb: /* @__PURE__ */ __name((v) => String(v), "fromDb"),
1537
- toDb: /* @__PURE__ */ __name((v) => String(v), "toDb"),
1573
+ fromDb: /* @__PURE__ */ __name((v) => v === null || v === void 0 ? v : String(v), "fromDb"),
1574
+ toDb: /* @__PURE__ */ __name((v) => v === null || v === void 0 ? v : String(v), "toDb"),
1538
1575
  tsType: "string"
1539
1576
  }
1540
1577
  };
@@ -4860,23 +4897,21 @@ async function createApp(opts) {
4860
4897
  if (spec.required && !callerClaims) {
4861
4898
  return envelope("unauthorized", "A valid access token is required", 401, requestId);
4862
4899
  }
4863
- if (callerClaims) {
4864
- const uploadDb = createRequestDatabase(sql, {
4865
- role: config.dbRole,
4866
- serviceRole: config.dbServiceRole,
4867
- claimsJson: JSON.stringify(callerClaims)
4868
- });
4869
- try {
4870
- const refusal = await refuseUnlessAuthorized(uploadDb.client, spec, requestId, makeRoleResolver(uploadDb.client));
4871
- if (refusal) {
4872
- await uploadDb.commit();
4873
- return refusal;
4874
- }
4900
+ const uploadDb = createRequestDatabase(sql, {
4901
+ role: config.dbRole,
4902
+ serviceRole: config.dbServiceRole,
4903
+ claimsJson: JSON.stringify(callerClaims ?? {})
4904
+ });
4905
+ try {
4906
+ const refusal = await refuseUnlessAuthorized(uploadDb.client, spec, requestId, makeRoleResolver(uploadDb.client));
4907
+ if (refusal) {
4875
4908
  await uploadDb.commit();
4876
- } catch (err) {
4877
- await uploadDb.rollback(err);
4878
- throw err;
4909
+ return refusal;
4879
4910
  }
4911
+ await uploadDb.commit();
4912
+ } catch (err) {
4913
+ await uploadDb.rollback(err);
4914
+ throw err;
4880
4915
  }
4881
4916
  const grant = grantFor(target?.entry, {
4882
4917
  userId: typeof callerClaims?.sub === "string" ? callerClaims.sub : null,
@@ -4915,11 +4950,18 @@ async function createApp(opts) {
4915
4950
  claimsJson: JSON.stringify(claims ?? {})
4916
4951
  });
4917
4952
  const rolesOfCaller = makeRoleResolver(db.client);
4918
- if (claims) {
4919
- const refusal = await refuseUnlessAuthorized(db.client, spec, requestId, rolesOfCaller);
4920
- if (refusal) return refusal;
4953
+ let dispatched = false;
4954
+ const settleBeforeRefusal = /* @__PURE__ */ __name(async () => {
4955
+ if (dispatched) return;
4956
+ await db.rollback(new Error("request refused before dispatch"));
4957
+ }, "settleBeforeRefusal");
4958
+ const refusal = await refuseUnlessAuthorized(db.client, spec, requestId, rolesOfCaller);
4959
+ if (refusal) {
4960
+ await settleBeforeRefusal();
4961
+ return refusal;
4921
4962
  }
4922
4963
  if (claims && spec.verifiedEmail && claims.email_verified !== true) {
4964
+ await settleBeforeRefusal();
4923
4965
  return envelope("email_not_verified", "A verified email address is required", 403, requestId);
4924
4966
  }
4925
4967
  let parsedBody;
@@ -4928,6 +4970,7 @@ async function createApp(opts) {
4928
4970
  if (req.method !== "GET" && req.method !== "HEAD") {
4929
4971
  const declared = Number(req.headers.get("content-length") ?? "");
4930
4972
  if (Number.isFinite(declared) && declared > MAX_BODY_BYTES) {
4973
+ await settleBeforeRefusal();
4931
4974
  return new Response(JSON.stringify({
4932
4975
  error: "payload_too_large",
4933
4976
  error_description: `Request body exceeds ${MAX_BODY_BYTES} bytes`,
@@ -4940,6 +4983,7 @@ async function createApp(opts) {
4940
4983
  }
4941
4984
  const buf = await req.arrayBuffer().catch(() => null);
4942
4985
  if (buf && buf.byteLength > MAX_BODY_BYTES) {
4986
+ await settleBeforeRefusal();
4943
4987
  return new Response(JSON.stringify({
4944
4988
  error: "payload_too_large",
4945
4989
  error_description: `Request body exceeds ${MAX_BODY_BYTES} bytes`,
@@ -4975,6 +5019,7 @@ async function createApp(opts) {
4975
5019
  ...JSON_HEADERS
4976
5020
  };
4977
5021
  if (attest.nextChallenge) headers["x-palbase-attest-challenge"] = attest.nextChallenge;
5022
+ await settleBeforeRefusal();
4978
5023
  return new Response(JSON.stringify({
4979
5024
  error: attest.reason,
4980
5025
  error_description: "This endpoint requires a verified app instance",
@@ -4989,6 +5034,7 @@ async function createApp(opts) {
4989
5034
  attestChallengeOut = attest.nextChallenge;
4990
5035
  const retryAfter = limiter.check(meta.options?.rateLimit, RateLimiter.key(hit.entry.id, userId, req.headers), Date.now());
4991
5036
  if (retryAfter !== null) {
5037
+ await settleBeforeRefusal();
4992
5038
  return new Response(JSON.stringify({
4993
5039
  error: "too_many_requests",
4994
5040
  error_description: "Rate limit exceeded for this endpoint",
@@ -5020,6 +5066,7 @@ async function createApp(opts) {
5020
5066
  readParsedBody();
5021
5067
  const r = p.schema.safeParse(parsedBody);
5022
5068
  if (!r.success) {
5069
+ await settleBeforeRefusal();
5023
5070
  return envelope("bad_request", "Request body failed validation", 400, requestId, {
5024
5071
  data: {
5025
5072
  fields: fieldErrors(r.error)
@@ -5032,6 +5079,7 @@ async function createApp(opts) {
5032
5079
  case "query": {
5033
5080
  const r = p.schema.safeParse(Object.fromEntries(url.searchParams));
5034
5081
  if (!r.success) {
5082
+ await settleBeforeRefusal();
5035
5083
  return envelope("bad_request", "Query parameters failed validation", 400, requestId, {
5036
5084
  data: {
5037
5085
  fields: fieldErrors(r.error)
@@ -5052,6 +5100,7 @@ async function createApp(opts) {
5052
5100
  }
5053
5101
  const r = p.schema.safeParse(headersFor(raw, p.schema));
5054
5102
  if (!r.success) {
5103
+ await settleBeforeRefusal();
5055
5104
  return envelope("bad_request", "Request headers failed validation", 400, requestId, {
5056
5105
  data: {
5057
5106
  fields: fieldErrors(r.error)
@@ -5084,17 +5133,20 @@ async function createApp(opts) {
5084
5133
  break;
5085
5134
  case "uploadedObject": {
5086
5135
  if (uploadSecret === "" || !verifySignature(req.headers.get(SIGNATURE_HEADER) ?? "", uploadSecret)) {
5136
+ await settleBeforeRefusal();
5087
5137
  return envelope("unauthorized", "This endpoint accepts uploads through storage, not directly", 401, requestId);
5088
5138
  }
5089
5139
  readParsedBody();
5090
5140
  const envelopeIn = parsedBody;
5091
5141
  if (!envelopeIn?.uploadedObject) {
5142
+ await settleBeforeRefusal();
5092
5143
  return envelope("bad_request", "the completion call carried no uploaded object", 400, requestId);
5093
5144
  }
5094
5145
  const uploadId = envelopeIn.uploadedObject.uploadId;
5095
5146
  if (typeof uploadId === "string" && uploadId !== "") {
5096
5147
  const already = completions.recall(uploadId);
5097
5148
  if (already) {
5149
+ await settleBeforeRefusal();
5098
5150
  return new Response(already.body, {
5099
5151
  status: already.status,
5100
5152
  headers: already.contentType ? {
@@ -5144,6 +5196,7 @@ async function createApp(opts) {
5144
5196
  args[p.index] = void 0;
5145
5197
  }
5146
5198
  }
5199
+ dispatched = true;
5147
5200
  try {
5148
5201
  const services = buildServices(db);
5149
5202
  if (meta.options?.sseConfig !== void 0) {