@palbase/backend 30.0.1 → 33.0.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.
Files changed (69) hide show
  1. package/dist/bin/palbase-backend.cjs +210 -24
  2. package/dist/bin/palbase-backend.cjs.map +1 -1
  3. package/dist/bin/palbase-backend.js +7 -7
  4. package/dist/{chunk-TWX6JTGJ.js → chunk-26GH54D2.js} +3 -5
  5. package/dist/{chunk-TWX6JTGJ.js.map → chunk-26GH54D2.js.map} +1 -1
  6. package/dist/{chunk-VVMJEVQP.js → chunk-BRLJOXWS.js} +2 -2
  7. package/dist/{chunk-3TUJWHC2.js → chunk-C4ZA5AT2.js} +378 -19
  8. package/dist/chunk-C4ZA5AT2.js.map +1 -0
  9. package/dist/{chunk-RADVOY7Y.js → chunk-DCDHAKF3.js} +147 -33
  10. package/dist/chunk-DCDHAKF3.js.map +1 -0
  11. package/dist/{chunk-ENZ2RFFJ.js → chunk-GOPZPM2A.js} +3 -310
  12. package/dist/chunk-GOPZPM2A.js.map +1 -0
  13. package/dist/{chunk-CGNN2PUH.js → chunk-KATPXCJ5.js} +37 -4
  14. package/dist/{chunk-CGNN2PUH.js.map → chunk-KATPXCJ5.js.map} +1 -1
  15. package/dist/{chunk-YRVIWHJC.js → chunk-N54QZER3.js} +23 -323
  16. package/dist/chunk-N54QZER3.js.map +1 -0
  17. package/dist/chunk-QMFOL2K6.js +371 -0
  18. package/dist/chunk-QMFOL2K6.js.map +1 -0
  19. package/dist/chunk-XOX6RFPZ.js +383 -0
  20. package/dist/chunk-XOX6RFPZ.js.map +1 -0
  21. package/dist/db/index.cjs +41 -2
  22. package/dist/db/index.cjs.map +1 -1
  23. package/dist/db/index.d.cts +1 -1
  24. package/dist/db/index.d.ts +1 -1
  25. package/dist/db/index.js +12 -13
  26. package/dist/engine/index.cjs +210 -24
  27. package/dist/engine/index.cjs.map +1 -1
  28. package/dist/engine/index.d.cts +3 -3
  29. package/dist/engine/index.d.ts +3 -3
  30. package/dist/engine/index.js +7 -7
  31. package/dist/{index-DtCgaZAg.d.cts → index-BWgnGj68.d.cts} +185 -35
  32. package/dist/{index-b-Q3l7W5.d.ts → index-Bi74dcOu.d.ts} +9 -2
  33. package/dist/{index-8qy3kIuA.d.ts → index-Dg10RnZO.d.ts} +185 -35
  34. package/dist/{index-Zi7MptvP.d.cts → index-g6iQyYci.d.cts} +9 -2
  35. package/dist/index.cjs +135 -8
  36. package/dist/index.cjs.map +1 -1
  37. package/dist/index.d.cts +6 -23
  38. package/dist/index.d.ts +6 -23
  39. package/dist/index.js +48 -48
  40. package/dist/index.js.map +1 -1
  41. package/dist/openapi/index.d.cts +2 -2
  42. package/dist/openapi/index.d.ts +2 -2
  43. package/dist/openapi/index.js +3 -4
  44. package/dist/openapi/index.js.map +1 -1
  45. package/dist/{registry-CEYLH-Iz.d.cts → registry-6VT5RPeO.d.cts} +1 -1
  46. package/dist/{registry-BGJ-Al6F.d.ts → registry-B-sxJJN0.d.ts} +1 -1
  47. package/dist/test/index.cjs +733 -17
  48. package/dist/test/index.cjs.map +1 -1
  49. package/dist/test/index.d.cts +59 -4
  50. package/dist/test/index.d.ts +59 -4
  51. package/dist/test/index.js +56 -16
  52. package/dist/test/index.js.map +1 -1
  53. package/docs/README.md +1 -1
  54. package/docs/auth.md +102 -20
  55. package/docs/database.md +54 -0
  56. package/docs/llms-full.txt +215 -21
  57. package/docs/schema.md +58 -0
  58. package/package.json +1 -1
  59. package/template/db/public.ts +23 -0
  60. package/template/package.json +1 -1
  61. package/dist/chunk-3TUJWHC2.js.map +0 -1
  62. package/dist/chunk-ENZ2RFFJ.js.map +0 -1
  63. package/dist/chunk-RADVOY7Y.js.map +0 -1
  64. package/dist/chunk-SG4UTNOP.js +0 -374
  65. package/dist/chunk-SG4UTNOP.js.map +0 -1
  66. package/dist/chunk-VXPNPVAG.js +0 -40
  67. package/dist/chunk-VXPNPVAG.js.map +0 -1
  68. package/dist/chunk-YRVIWHJC.js.map +0 -1
  69. /package/dist/{chunk-VVMJEVQP.js.map → chunk-BRLJOXWS.js.map} +0 -0
@@ -1,15 +1,17 @@
1
1
  import {
2
2
  DeclarationRefused,
3
- __requestALS,
4
- __runStartHooks,
5
- __runWithRuntime,
6
3
  assertNoOrphanEntryPoints,
7
4
  buildContainer,
8
5
  buildRelations
9
- } from "./chunk-YRVIWHJC.js";
6
+ } from "./chunk-N54QZER3.js";
10
7
  import {
11
- qualifiedTableKey
12
- } from "./chunk-SG4UTNOP.js";
8
+ __requestALS,
9
+ __runStartHooks,
10
+ __runWithRuntime
11
+ } from "./chunk-QMFOL2K6.js";
12
+ import {
13
+ CODECS
14
+ } from "./chunk-XOX6RFPZ.js";
13
15
  import {
14
16
  assertNoExpressionHandles,
15
17
  assertUsableFilter,
@@ -21,23 +23,22 @@ import {
21
23
  isPlanRef,
22
24
  isSqlFragment,
23
25
  looksLikeUnbrandedPlanRef,
24
- looksLikeUnbrandedRef
25
- } from "./chunk-3TUJWHC2.js";
26
+ looksLikeUnbrandedRef,
27
+ qualifiedTableKey
28
+ } from "./chunk-C4ZA5AT2.js";
26
29
  import {
27
30
  getRoutes,
28
31
  resolveEffectiveAuth
29
- } from "./chunk-VVMJEVQP.js";
32
+ } from "./chunk-BRLJOXWS.js";
30
33
  import {
31
34
  DeadlockDetected,
32
35
  SerializationFailure,
33
36
  UniqueViolation,
37
+ __name,
34
38
  isEngineRaised,
35
39
  isHttpError,
36
40
  markEngineRaised
37
- } from "./chunk-CGNN2PUH.js";
38
- import {
39
- __name
40
- } from "./chunk-VXPNPVAG.js";
41
+ } from "./chunk-KATPXCJ5.js";
41
42
 
42
43
  // src/engine/config.ts
43
44
  var BootRefused = class extends Error {
@@ -515,6 +516,15 @@ function transformsOf(schema, table) {
515
516
  if (def) {
516
517
  for (const [col, c] of Object.entries(def.columns ?? {})) {
517
518
  const d = c !== null && typeof c === "object" && "_def" in c ? c._def : c;
519
+ const codec = d?.codec;
520
+ if (typeof codec === "string" && codec in CODECS) {
521
+ const c2 = CODECS[codec];
522
+ out.set(col, {
523
+ fromDb: c2.fromDb,
524
+ toDb: c2.toDb
525
+ });
526
+ continue;
527
+ }
518
528
  const t = d?.transform;
519
529
  if (t !== null && typeof t === "object") {
520
530
  out.set(col, t);
@@ -850,6 +860,29 @@ function selectClause(table, known, select) {
850
860
  }).join(", ");
851
861
  }
852
862
  __name(selectClause, "selectClause");
863
+ function quoteLiteral(v) {
864
+ return `'${v.replace(/'/g, "''")}'`;
865
+ }
866
+ __name(quoteLiteral, "quoteLiteral");
867
+ function orderClauseFor(table, known, orderBy, alias) {
868
+ if (orderBy === void 0 || orderBy === null) return "";
869
+ const items = Array.isArray(orderBy) ? orderBy : [
870
+ orderBy
871
+ ];
872
+ if (items.length === 0) return "";
873
+ const rendered = items.map((o) => {
874
+ const col = o.column;
875
+ const shapeOK = /^[A-Za-z_][A-Za-z0-9_]*$/.test(col);
876
+ if (known !== null ? !known.has(col) : !shapeOK) {
877
+ throw new Error(`with(${table}): orderBy kolonu "${col}" tabloda yok`);
878
+ }
879
+ const dir = o.direction === "desc" ? "DESC" : "ASC";
880
+ const nulls = o.nulls === "first" ? " NULLS FIRST" : o.nulls === "last" ? " NULLS LAST" : "";
881
+ return `${alias}.${quoteIdent(col)} ${dir}${nulls}`;
882
+ });
883
+ return ` ORDER BY ${rendered.join(", ")}`;
884
+ }
885
+ __name(orderClauseFor, "orderClauseFor");
853
886
  function orderClause(table, known, orderBy) {
854
887
  if (!orderBy) return "";
855
888
  const items = Array.isArray(orderBy) ? orderBy : [
@@ -1153,6 +1186,65 @@ function aggregateExpr(table, fn, col, alias) {
1153
1186
  return t !== null && NUMERIC_PG_TYPES.has(t) && t !== "integer" ? `${fn}(${q})::text AS ${quoteIdent(alias)}` : `${fn}(${q}) AS ${quoteIdent(alias)}`;
1154
1187
  }
1155
1188
  __name(aggregateExpr, "aggregateExpr");
1189
+ function jsonObjectFor(table, cols, alias) {
1190
+ const pairs = cols.map((c) => {
1191
+ const t = columnTypeOf(table, c);
1192
+ const expr = t !== null && (t === "numeric" || t === "bigint") ? `${alias}.${quoteIdent(c)}::text` : `${alias}.${quoteIdent(c)}`;
1193
+ return `${quoteLiteral(c)}, ${expr}`;
1194
+ });
1195
+ return `json_build_object(${pairs.join(", ")})`;
1196
+ }
1197
+ __name(jsonObjectFor, "jsonObjectFor");
1198
+ function withClauses(table, spec, add, caller) {
1199
+ const rels = currentRelations.get(table) ?? [];
1200
+ const select = [];
1201
+ const joins = [];
1202
+ let i = 0;
1203
+ for (const [name, opt] of Object.entries(spec)) {
1204
+ if (opt === false || opt === void 0) continue;
1205
+ const rel = rels.find((r) => r.name === name);
1206
+ if (rel === void 0) {
1207
+ const known = rels.map((r) => r.name).sort();
1208
+ throw new Error(`${caller}(${table}): with.${name} diye bir ili\u015Fki yok` + (known.length > 0 ? ` (bilinenler: ${known.join(", ")})` : `. Bu tablo hi\xE7 yabanc\u0131 anahtar bildirmiyor ve ili\u015Fkiler onlardan T\xDCRET\u0130L\u0130R`) + `. \u0130li\u015Fkiler references(() => <tablo>.<kolon>) ile bildirilir.`);
1209
+ }
1210
+ const ourCol = rel.kind === "many" ? primaryKeyOf(table) : rel.via;
1211
+ const theirCol = rel.kind === "many" ? rel.via : primaryKeyOf(rel.to);
1212
+ if (ourCol === null || theirCol === null) {
1213
+ throw new Error(`${caller}(${table}): with.${name} birincil anahtar bilmeden kurulamaz \u2014 ili\u015Fkinin bir ucu tablonun PK's\u0131 ve bu tablo tek kolonlu bir PK bildirmiyor.`);
1214
+ }
1215
+ const o = typeof opt === "object" && opt !== null ? opt : {};
1216
+ const childAlias = `w${++i}`;
1217
+ const childKnown = schemaColumns(rel.to);
1218
+ const childCols = Array.isArray(o["select"]) && o["select"].length > 0 ? o["select"] : childKnown !== null ? [
1219
+ ...childKnown
1220
+ ] : [];
1221
+ if (childCols.length === 0) {
1222
+ throw new Error(`${caller}(${table}): with.${name} i\xE7in "${rel.to}" tablosunun kolonlar\u0131 bilinmiyor \u2014 select ile hangi kolonlar\u0131 istedi\u011Finizi yaz\u0131n.`);
1223
+ }
1224
+ for (const c of childCols) {
1225
+ if (childKnown !== null && !childKnown.has(c)) {
1226
+ throw new Error(`${caller}(${table}): with.${name}.select kolonu "${c}" "${rel.to}" tablosunda yok`);
1227
+ }
1228
+ }
1229
+ const obj = jsonObjectFor(rel.to, childCols, childAlias);
1230
+ const innerWhere = o["where"] !== void 0 && o["where"] !== null ? compileWhereBare(rel.to, childKnown, o["where"], add, caller, childAlias, i) : "";
1231
+ const link = `${childAlias}.${quoteIdent(theirCol)} = t.${quoteIdent(ourCol)}`;
1232
+ const cond = innerWhere === "" ? link : `${link} AND ${innerWhere}`;
1233
+ const inner = orderClauseFor(rel.to, childKnown, o["orderBy"], childAlias) + limitClause(o["limit"]);
1234
+ const outAlias = `wj${i}`;
1235
+ if (rel.kind === "many") {
1236
+ joins.push(`LEFT JOIN LATERAL (SELECT coalesce(json_agg(x.o), '[]'::json) AS d FROM (SELECT ${obj} AS o FROM ${quoteTable(rel.to)} ${childAlias} WHERE ${cond}${inner}) x) ${quoteIdent(outAlias)} ON true`);
1237
+ } else {
1238
+ joins.push(`LEFT JOIN LATERAL (SELECT ${obj} AS d FROM ${quoteTable(rel.to)} ${childAlias} WHERE ${cond}${inner} LIMIT 1) ${quoteIdent(outAlias)} ON true`);
1239
+ }
1240
+ select.push(`${quoteIdent(outAlias)}."d" AS ${quoteIdent(name)}`);
1241
+ }
1242
+ return {
1243
+ select,
1244
+ joins
1245
+ };
1246
+ }
1247
+ __name(withClauses, "withClauses");
1156
1248
  function createOps(tx) {
1157
1249
  const at = /* @__PURE__ */ __name(() => resolveTx(tx), "at");
1158
1250
  const ops = {
@@ -1596,7 +1688,13 @@ function createOps(tx) {
1596
1688
  const limit = limitClause(opts.limit);
1597
1689
  const offset = offsetClause(opts.offset, opts.limit);
1598
1690
  const cols = selectClause(table, known, opts.select);
1599
- const sql = `SELECT ${cols} FROM ${quoteTable(table)} t WHERE true${whereSql}${order}${limit}${offset}`;
1691
+ const w = opts.with !== void 0 && Object.keys(opts.with).length > 0 ? withClauses(table, opts.with, add, "findMany") : {
1692
+ select: [],
1693
+ joins: []
1694
+ };
1695
+ const selectList = w.select.length > 0 ? `${cols === "*" ? "t.*" : cols}, ${w.select.join(", ")}` : cols;
1696
+ const joinSql = w.joins.length > 0 ? ` ${w.joins.join(" ")}` : "";
1697
+ const sql = `SELECT ${selectList} FROM ${quoteTable(table)} t${joinSql} WHERE true${whereSql}${order}${limit}${offset}`;
1600
1698
  return asTableRows(table, await (await at()).unsafe(sql, params));
1601
1699
  },
1602
1700
  /**
@@ -3089,23 +3187,21 @@ async function createApp(opts) {
3089
3187
  if (spec.required && !callerClaims) {
3090
3188
  return envelope("unauthorized", "A valid access token is required", 401, requestId);
3091
3189
  }
3092
- if (callerClaims) {
3093
- const uploadDb = createRequestDatabase(sql, {
3094
- role: config.dbRole,
3095
- serviceRole: config.dbServiceRole,
3096
- claimsJson: JSON.stringify(callerClaims)
3097
- });
3098
- try {
3099
- const refusal = await refuseUnlessAuthorized(uploadDb.client, spec, requestId, makeRoleResolver(uploadDb.client));
3100
- if (refusal) {
3101
- await uploadDb.commit();
3102
- return refusal;
3103
- }
3190
+ const uploadDb = createRequestDatabase(sql, {
3191
+ role: config.dbRole,
3192
+ serviceRole: config.dbServiceRole,
3193
+ claimsJson: JSON.stringify(callerClaims ?? {})
3194
+ });
3195
+ try {
3196
+ const refusal = await refuseUnlessAuthorized(uploadDb.client, spec, requestId, makeRoleResolver(uploadDb.client));
3197
+ if (refusal) {
3104
3198
  await uploadDb.commit();
3105
- } catch (err) {
3106
- await uploadDb.rollback(err);
3107
- throw err;
3199
+ return refusal;
3108
3200
  }
3201
+ await uploadDb.commit();
3202
+ } catch (err) {
3203
+ await uploadDb.rollback(err);
3204
+ throw err;
3109
3205
  }
3110
3206
  const grant = grantFor(target?.entry, {
3111
3207
  userId: typeof callerClaims?.sub === "string" ? callerClaims.sub : null,
@@ -3144,11 +3240,18 @@ async function createApp(opts) {
3144
3240
  claimsJson: JSON.stringify(claims ?? {})
3145
3241
  });
3146
3242
  const rolesOfCaller = makeRoleResolver(db.client);
3147
- if (claims) {
3148
- const refusal = await refuseUnlessAuthorized(db.client, spec, requestId, rolesOfCaller);
3149
- if (refusal) return refusal;
3243
+ let dispatched = false;
3244
+ const settleBeforeRefusal = /* @__PURE__ */ __name(async () => {
3245
+ if (dispatched) return;
3246
+ await db.rollback(new Error("request refused before dispatch"));
3247
+ }, "settleBeforeRefusal");
3248
+ const refusal = await refuseUnlessAuthorized(db.client, spec, requestId, rolesOfCaller);
3249
+ if (refusal) {
3250
+ await settleBeforeRefusal();
3251
+ return refusal;
3150
3252
  }
3151
3253
  if (claims && spec.verifiedEmail && claims.email_verified !== true) {
3254
+ await settleBeforeRefusal();
3152
3255
  return envelope("email_not_verified", "A verified email address is required", 403, requestId);
3153
3256
  }
3154
3257
  let parsedBody;
@@ -3157,6 +3260,7 @@ async function createApp(opts) {
3157
3260
  if (req.method !== "GET" && req.method !== "HEAD") {
3158
3261
  const declared = Number(req.headers.get("content-length") ?? "");
3159
3262
  if (Number.isFinite(declared) && declared > MAX_BODY_BYTES) {
3263
+ await settleBeforeRefusal();
3160
3264
  return new Response(JSON.stringify({
3161
3265
  error: "payload_too_large",
3162
3266
  error_description: `Request body exceeds ${MAX_BODY_BYTES} bytes`,
@@ -3169,6 +3273,7 @@ async function createApp(opts) {
3169
3273
  }
3170
3274
  const buf = await req.arrayBuffer().catch(() => null);
3171
3275
  if (buf && buf.byteLength > MAX_BODY_BYTES) {
3276
+ await settleBeforeRefusal();
3172
3277
  return new Response(JSON.stringify({
3173
3278
  error: "payload_too_large",
3174
3279
  error_description: `Request body exceeds ${MAX_BODY_BYTES} bytes`,
@@ -3204,6 +3309,7 @@ async function createApp(opts) {
3204
3309
  ...JSON_HEADERS
3205
3310
  };
3206
3311
  if (attest.nextChallenge) headers["x-palbase-attest-challenge"] = attest.nextChallenge;
3312
+ await settleBeforeRefusal();
3207
3313
  return new Response(JSON.stringify({
3208
3314
  error: attest.reason,
3209
3315
  error_description: "This endpoint requires a verified app instance",
@@ -3218,6 +3324,7 @@ async function createApp(opts) {
3218
3324
  attestChallengeOut = attest.nextChallenge;
3219
3325
  const retryAfter = limiter.check(meta.options?.rateLimit, RateLimiter.key(hit.entry.id, userId, req.headers), Date.now());
3220
3326
  if (retryAfter !== null) {
3327
+ await settleBeforeRefusal();
3221
3328
  return new Response(JSON.stringify({
3222
3329
  error: "too_many_requests",
3223
3330
  error_description: "Rate limit exceeded for this endpoint",
@@ -3249,6 +3356,7 @@ async function createApp(opts) {
3249
3356
  readParsedBody();
3250
3357
  const r = p.schema.safeParse(parsedBody);
3251
3358
  if (!r.success) {
3359
+ await settleBeforeRefusal();
3252
3360
  return envelope("bad_request", "Request body failed validation", 400, requestId, {
3253
3361
  data: {
3254
3362
  fields: fieldErrors(r.error)
@@ -3261,6 +3369,7 @@ async function createApp(opts) {
3261
3369
  case "query": {
3262
3370
  const r = p.schema.safeParse(Object.fromEntries(url.searchParams));
3263
3371
  if (!r.success) {
3372
+ await settleBeforeRefusal();
3264
3373
  return envelope("bad_request", "Query parameters failed validation", 400, requestId, {
3265
3374
  data: {
3266
3375
  fields: fieldErrors(r.error)
@@ -3281,6 +3390,7 @@ async function createApp(opts) {
3281
3390
  }
3282
3391
  const r = p.schema.safeParse(headersFor(raw, p.schema));
3283
3392
  if (!r.success) {
3393
+ await settleBeforeRefusal();
3284
3394
  return envelope("bad_request", "Request headers failed validation", 400, requestId, {
3285
3395
  data: {
3286
3396
  fields: fieldErrors(r.error)
@@ -3313,17 +3423,20 @@ async function createApp(opts) {
3313
3423
  break;
3314
3424
  case "uploadedObject": {
3315
3425
  if (uploadSecret === "" || !verifySignature(req.headers.get(SIGNATURE_HEADER) ?? "", uploadSecret)) {
3426
+ await settleBeforeRefusal();
3316
3427
  return envelope("unauthorized", "This endpoint accepts uploads through storage, not directly", 401, requestId);
3317
3428
  }
3318
3429
  readParsedBody();
3319
3430
  const envelopeIn = parsedBody;
3320
3431
  if (!envelopeIn?.uploadedObject) {
3432
+ await settleBeforeRefusal();
3321
3433
  return envelope("bad_request", "the completion call carried no uploaded object", 400, requestId);
3322
3434
  }
3323
3435
  const uploadId = envelopeIn.uploadedObject.uploadId;
3324
3436
  if (typeof uploadId === "string" && uploadId !== "") {
3325
3437
  const already = completions.recall(uploadId);
3326
3438
  if (already) {
3439
+ await settleBeforeRefusal();
3327
3440
  return new Response(already.body, {
3328
3441
  status: already.status,
3329
3442
  headers: already.contentType ? {
@@ -3373,6 +3486,7 @@ async function createApp(opts) {
3373
3486
  args[p.index] = void 0;
3374
3487
  }
3375
3488
  }
3489
+ dispatched = true;
3376
3490
  try {
3377
3491
  const services = buildServices(db);
3378
3492
  if (meta.options?.sseConfig !== void 0) {
@@ -3565,4 +3679,4 @@ export {
3565
3679
  installEgressFence,
3566
3680
  createApp
3567
3681
  };
3568
- //# sourceMappingURL=chunk-RADVOY7Y.js.map
3682
+ //# sourceMappingURL=chunk-DCDHAKF3.js.map