@palbase/backend 22.0.1 → 22.1.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 (42) hide show
  1. package/dist/bin/palbase-backend.cjs +340 -22
  2. package/dist/bin/palbase-backend.cjs.map +1 -1
  3. package/dist/bin/palbase-backend.js +2 -2
  4. package/dist/{chunk-QYOHMVUW.js → chunk-74XDEF5J.js} +338 -22
  5. package/dist/chunk-74XDEF5J.js.map +1 -0
  6. package/dist/{chunk-SSGAMC26.js → chunk-I3ON7MYF.js} +57 -5
  7. package/dist/chunk-I3ON7MYF.js.map +1 -0
  8. package/dist/{chunk-POYAFBLF.js → chunk-SQC5EIWY.js} +5 -3
  9. package/dist/chunk-SQC5EIWY.js.map +1 -0
  10. package/dist/db/index.cjs +60 -6
  11. package/dist/db/index.cjs.map +1 -1
  12. package/dist/db/index.d.cts +2 -2
  13. package/dist/db/index.d.ts +2 -2
  14. package/dist/db/index.js +7 -3
  15. package/dist/{endpoint-B0LpZixz.d.cts → endpoint-BVT6jcVW.d.cts} +39 -7
  16. package/dist/{endpoint-B0LpZixz.d.ts → endpoint-BVT6jcVW.d.ts} +39 -7
  17. package/dist/engine/index.cjs +340 -22
  18. package/dist/engine/index.cjs.map +1 -1
  19. package/dist/engine/index.d.cts +4 -4
  20. package/dist/engine/index.d.ts +4 -4
  21. package/dist/engine/index.js +2 -2
  22. package/dist/{index-B4W6d2VJ.d.cts → index-BS1gW4nV.d.cts} +24 -25
  23. package/dist/{index-BGSCWlUa.d.cts → index-BqCiHao8.d.cts} +97 -7
  24. package/dist/{index-BCNtlG1w.d.ts → index-CCZqzych.d.ts} +24 -25
  25. package/dist/{index-g-EzitI-.d.ts → index-vwHoS0l2.d.ts} +97 -7
  26. package/dist/index.cjs +244 -6
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.cts +124 -9
  29. package/dist/index.d.ts +124 -9
  30. package/dist/index.js +186 -3
  31. package/dist/index.js.map +1 -1
  32. package/dist/openapi/index.d.cts +2 -2
  33. package/dist/openapi/index.d.ts +2 -2
  34. package/dist/{registry-Cw0YEYCg.d.cts → registry-BWttGlaT.d.cts} +1 -1
  35. package/dist/{registry-3BLYv4si.d.ts → registry-Bsuf-orT.d.ts} +1 -1
  36. package/docs/endpoints.md +1 -1
  37. package/docs/errors.md +9 -0
  38. package/docs/llms-full.txt +10 -1
  39. package/package.json +2 -2
  40. package/dist/chunk-POYAFBLF.js.map +0 -1
  41. package/dist/chunk-QYOHMVUW.js.map +0 -1
  42. package/dist/chunk-SSGAMC26.js.map +0 -1
@@ -3,8 +3,8 @@ import {
3
3
  BootRefused,
4
4
  createApp,
5
5
  loadConfig
6
- } from "../chunk-QYOHMVUW.js";
7
- import "../chunk-POYAFBLF.js";
6
+ } from "../chunk-74XDEF5J.js";
7
+ import "../chunk-SQC5EIWY.js";
8
8
  import "../chunk-W5ODXPY3.js";
9
9
  import {
10
10
  getRegisteredControllers
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  __requestALS,
3
3
  __runWithRuntime
4
- } from "./chunk-POYAFBLF.js";
4
+ } from "./chunk-SQC5EIWY.js";
5
5
  import {
6
6
  getRoutes,
7
7
  isHttpError
@@ -382,6 +382,194 @@ function asWireRow(row) {
382
382
  function asWireRows(rows) {
383
383
  return rows.map((row) => asWireRow(row));
384
384
  }
385
+ function toVectorLiteral(v) {
386
+ return `[${v.join(",")}]`;
387
+ }
388
+ function vectorColumnsOf(schema, table) {
389
+ const out = /* @__PURE__ */ new Set();
390
+ for (const [key, def] of Object.entries(schema.tables ?? {})) {
391
+ if ((def.name ?? key) !== table) continue;
392
+ for (const [col, c] of Object.entries(def.columns ?? {})) {
393
+ const d = c !== null && typeof c === "object" && "_def" in c ? c._def : c;
394
+ if (d !== null && typeof d === "object" && d.type === "vector") out.add(col);
395
+ }
396
+ }
397
+ return out;
398
+ }
399
+ function reviveVectors(row, vectorCols) {
400
+ if (row === null || typeof row !== "object" || vectorCols.size === 0) return row;
401
+ const out = row;
402
+ for (const col of vectorCols) {
403
+ const v = out[col];
404
+ if (typeof v === "string") out[col] = JSON.parse(v);
405
+ }
406
+ return row;
407
+ }
408
+ function asTableRow(table, row) {
409
+ return reviveVectors(asWireRow(row), vectorColumnsOf(currentSchema, table));
410
+ }
411
+ function asTableRows(table, rows) {
412
+ const vectorCols = vectorColumnsOf(currentSchema, table);
413
+ return rows.map((row) => reviveVectors(asWireRow(row), vectorCols));
414
+ }
415
+ function asBindParams(table, cols, data) {
416
+ const vectorCols = vectorColumnsOf(currentSchema, table);
417
+ return cols.map((c) => {
418
+ const v = data[c];
419
+ return Array.isArray(v) && vectorCols.has(c) ? toVectorLiteral(v) : v;
420
+ });
421
+ }
422
+ var SELECTIVITY_EXACT_THRESHOLD = 1e4;
423
+ var METRIC_OPERATOR = {
424
+ cosine: "<=>",
425
+ euclidean: "<->",
426
+ inner_product: "<#>"
427
+ };
428
+ function searchConfigFor(table) {
429
+ const t = currentSchema.tables?.[table];
430
+ if (!t) return null;
431
+ const columns = t.columns ?? {};
432
+ const defOf = (c) => c !== null && typeof c === "object" && "_def" in c ? c._def : c ?? {};
433
+ const cols = Object.keys(columns);
434
+ const vectorCols = cols.filter((c) => defOf(columns[c]).type === "vector");
435
+ const defOfFull = (c) => c !== null && typeof c === "object" && "_def" in c ? c._def : c ?? {};
436
+ const pkCols = cols.filter((c) => defOfFull(columns[c]).primaryKey === true);
437
+ const pk = pkCols.length === 1 ? pkCols[0] : cols.includes("id") ? "id" : null;
438
+ if (pk === null) {
439
+ throw new Error(
440
+ `search(${table}): tek-kolon primary key bulunamad\u0131 \u2014 arama s\u0131ralamas\u0131 ve sat\u0131r birle\u015Fimi PK ister (FR-020)`
441
+ );
442
+ }
443
+ const search = t.search;
444
+ const ftsCols = search?.text ?? [];
445
+ const rawLegs = search?.vector === void 0 ? [] : Array.isArray(search.vector) ? search.vector : [search.vector];
446
+ let legs;
447
+ if (rawLegs.length > 0) {
448
+ legs = rawLegs.map((leg) => {
449
+ const l = leg;
450
+ const column = l.column ?? (vectorCols.length === 1 ? vectorCols[0] : void 0);
451
+ if (column === void 0) {
452
+ throw new Error(`search(${table}): birden \xE7ok vector kolonu var \u2014 beyanda 'column' zorunlu (FR-010)`);
453
+ }
454
+ const model = l.model;
455
+ return {
456
+ column,
457
+ metric: l.metric ?? "cosine",
458
+ ...model !== void 0 ? { embed: {
459
+ model: model.model,
460
+ apiKeyName: model.apiKeyName ?? "OPENAI_API_KEY",
461
+ ...model.baseURL !== void 0 ? { baseURL: model.baseURL } : {},
462
+ ...model.dimensions !== void 0 ? { dimensions: model.dimensions } : {}
463
+ } } : {}
464
+ };
465
+ });
466
+ } else {
467
+ legs = vectorCols.map((column) => ({ column, metric: "cosine" }));
468
+ }
469
+ if (ftsCols.length === 0 && legs.length === 0) return null;
470
+ return { pk, cols, colSet: new Set(cols), ftsCols, legs };
471
+ }
472
+ function pickLeg(table, legs, using) {
473
+ if (legs.length === 0) return null;
474
+ if (using !== void 0) {
475
+ const hit = legs.find((l) => l.column === using);
476
+ if (!hit) {
477
+ throw new Error(
478
+ `search(${table}): using "${using}" bir vekt\xF6r kolunu adlam\u0131yor \u2014 mevcut: ${legs.map((l) => l.column).join(", ")}`
479
+ );
480
+ }
481
+ return hit;
482
+ }
483
+ if (legs.length === 1) return legs[0];
484
+ throw new Error(`search(${table}): birden \xE7ok vekt\xF6r kolu var \u2014 'using' ile se\xE7in (FR-013) \u2014 salt metin ar\u0131yorsan mode:"text" kullan`);
485
+ }
486
+ var WHERE_OPS = { gt: ">", gte: ">=", lt: "<", lte: "<=", neq: "<>" };
487
+ function compileWhere(table, colSet, where, add) {
488
+ const parts = [];
489
+ for (const [col, cond] of Object.entries(where)) {
490
+ if (!colSet.has(col)) {
491
+ throw new Error(`search(${table}): where kolonu "${col}" tabloda yok (FR-016)`);
492
+ }
493
+ const q = `t.${quoteIdent(col)}`;
494
+ if (cond !== null && typeof cond === "object" && !Array.isArray(cond)) {
495
+ for (const [op, v] of Object.entries(cond)) {
496
+ if (op === "in") {
497
+ if (!Array.isArray(v)) throw new Error(`search(${table}): where.${col}.in bir dizi olmal\u0131`);
498
+ if (v.length === 0) {
499
+ parts.push("false");
500
+ continue;
501
+ }
502
+ parts.push(`${q} IN (${v.map((x) => add(x)).join(", ")})`);
503
+ } else if (op in WHERE_OPS) {
504
+ parts.push(`${q} ${WHERE_OPS[op]} ${add(v)}`);
505
+ } else {
506
+ throw new Error(`search(${table}): where.${col} bilinmeyen operat\xF6r "${op}" (gt/gte/lt/lte/neq/in)`);
507
+ }
508
+ }
509
+ } else {
510
+ parts.push(`${q} = ${add(cond)}`);
511
+ }
512
+ }
513
+ return parts.length === 0 ? "" : ` AND ${parts.join(" AND ")}`;
514
+ }
515
+ var cachedVectorSchema = null;
516
+ async function vectorSchemaWithGuc(runner) {
517
+ if (cachedVectorSchema !== null) {
518
+ await runner.unsafe(
519
+ "select set_config('hnsw.iterative_scan','relaxed_order',true), set_config('hnsw.max_scan_tuples','200000',true), set_config('hnsw.ef_search','200',true)"
520
+ );
521
+ return cachedVectorSchema;
522
+ }
523
+ const rows = await runner.unsafe(
524
+ "select set_config('hnsw.iterative_scan','relaxed_order',true), set_config('hnsw.max_scan_tuples','200000',true), set_config('hnsw.ef_search','200',true), (select n.nspname from pg_extension e join pg_namespace n on n.oid = e.extnamespace where e.extname = 'vector') as nspname"
525
+ );
526
+ const name = rows?.[0]?.nspname;
527
+ if (typeof name !== "string" || name === "") {
528
+ throw new Error("pgvector extension kurulu de\u011Fil \u2014 vector aramas\u0131 \xE7al\u0131\u015Famaz (extensions beyan\u0131 deploy'dan ge\xE7ti mi?)");
529
+ }
530
+ cachedVectorSchema = name;
531
+ return name;
532
+ }
533
+ var secretReader = null;
534
+ function setSecretReader(fn) {
535
+ secretReader = fn;
536
+ }
537
+ var embedFetch = (url, init) => fetch(url, init);
538
+ async function embedQuery(embed, text) {
539
+ if (secretReader === null) {
540
+ throw new Error(`query embed: secret reader ba\u011Flanmam\u0131\u015F \u2014 ${embed.apiKeyName} okunam\u0131yor`);
541
+ }
542
+ const key = await secretReader(embed.apiKeyName);
543
+ if (key === null || key === "") {
544
+ throw new Error(`query embed: vault'ta ${embed.apiKeyName} yok (FR-021/FR-025)`);
545
+ }
546
+ const url = (embed.baseURL ?? "https://api.openai.com/v1").replace(/\/$/, "") + "/embeddings";
547
+ const controller = new AbortController();
548
+ const timer = setTimeout(() => controller.abort(), 1e4);
549
+ try {
550
+ const res = await embedFetch(url, {
551
+ method: "POST",
552
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${key}` },
553
+ body: JSON.stringify({
554
+ model: embed.model,
555
+ input: [text],
556
+ ...embed.dimensions !== void 0 ? { dimensions: embed.dimensions } : {}
557
+ }),
558
+ signal: controller.signal
559
+ });
560
+ if (!res.ok) {
561
+ throw new Error(`query embed: sa\u011Flay\u0131c\u0131 ${res.status} d\xF6nd\xFC (${embed.apiKeyName} ile) \u2014 anahtar/model do\u011Fru mu?`);
562
+ }
563
+ const data = await res.json();
564
+ const vec = data.data?.[0]?.embedding;
565
+ if (!Array.isArray(vec)) {
566
+ throw new Error("query embed: sa\u011Flay\u0131c\u0131 yan\u0131t\u0131nda data[0].embedding yok (CLAIM-N1 \u015Fekli)");
567
+ }
568
+ return vec;
569
+ } finally {
570
+ clearTimeout(timer);
571
+ }
572
+ }
385
573
  function createOps(tx) {
386
574
  const at = () => resolveTx(tx);
387
575
  const ops = {
@@ -393,22 +581,22 @@ function createOps(tx) {
393
581
  if (cols.length === 0) throw new Error(`insert into ${table}: no columns given`);
394
582
  const placeholders = cols.map((_, i) => `$${i + 1}`).join(", ");
395
583
  const sql = `INSERT INTO ${quoteIdent(table)} (${cols.map(quoteIdent).join(", ")}) VALUES (${placeholders}) RETURNING *`;
396
- const rows = await (await at()).unsafe(sql, cols.map((c) => data[c]));
584
+ const rows = await (await at()).unsafe(sql, asBindParams(table, cols, data));
397
585
  const inserted = rows[0];
398
586
  if (!inserted) {
399
587
  throw new Error(
400
588
  `insert into ${table} returned no row \u2014 the write was rejected (an RLS policy, most likely).`
401
589
  );
402
590
  }
403
- return asWireRow(inserted);
591
+ return asTableRow(table, inserted);
404
592
  },
405
593
  async update(table, id, data) {
406
594
  const cols = Object.keys(data);
407
595
  if (cols.length === 0) return ops.findById(table, id);
408
596
  const assignments = cols.map((c, i) => `${quoteIdent(c)} = $${i + 1}`).join(", ");
409
597
  const sql = `UPDATE ${quoteIdent(table)} SET ${assignments} WHERE id = $${cols.length + 1} RETURNING *`;
410
- const rows = await (await at()).unsafe(sql, [...cols.map((c) => data[c]), id]);
411
- return rows[0] ? asWireRow(rows[0]) : null;
598
+ const rows = await (await at()).unsafe(sql, [...asBindParams(table, cols, data), id]);
599
+ return rows[0] ? asTableRow(table, rows[0]) : null;
412
600
  },
413
601
  async delete(table, id) {
414
602
  await (await at()).unsafe(`DELETE FROM ${quoteIdent(table)} WHERE id = $1`, [id]);
@@ -418,16 +606,96 @@ function createOps(tx) {
418
606
  `SELECT * FROM ${quoteIdent(table)} WHERE id = $1`,
419
607
  [id]
420
608
  );
421
- return rows[0] ? asWireRow(rows[0]) : null;
609
+ return rows[0] ? asTableRow(table, rows[0]) : null;
422
610
  },
423
611
  async findMany(table, query = {}) {
424
612
  const cols = Object.keys(query);
425
613
  const where = cols.length ? ` WHERE ${cols.map((c, i) => `${quoteIdent(c)} = $${i + 1}`).join(" AND ")}` : "";
426
- return asWireRows(await (await at()).unsafe(
614
+ return asTableRows(table, await (await at()).unsafe(
427
615
  `SELECT * FROM ${quoteIdent(table)}${where}`,
428
616
  cols.map((c) => query[c])
429
617
  ));
430
618
  },
619
+ /**
620
+ * Tek-SQL hibrit arama (FR-014): iki kol CTE + FULL OUTER JOIN + RRF
621
+ * (1/(50+rank), CLAIM-N4). Operatör şema-nitelikli (C-10, M-1); GUC
622
+ * hnsw.iterative_scan=relaxed_order aynı tx'te set_config ile (CLAIM-N3 —
623
+ * RLS/filtre altında LIMIT-altı dönüş açığını kapatır). Sorgu-anı embed
624
+ * T024'te gelir; o zamana dek vector kolu yalnız params.vector ile koşar.
625
+ */
626
+ async search(table, params = {}) {
627
+ const cfg = searchConfigFor(table);
628
+ if (!cfg) {
629
+ throw new Error(`search(${table}): tablo aranabilir de\u011Fil \u2014 ne vector kolonu ne search beyan\u0131 var (FR-013)`);
630
+ }
631
+ const rawLimit = params.limit ?? 20;
632
+ if (typeof rawLimit !== "number" || !Number.isFinite(rawLimit)) {
633
+ throw new Error(`search(${table}): limit sonlu bir say\u0131 olmal\u0131, ${String(rawLimit)} verildi (FR-013)`);
634
+ }
635
+ const limit = Math.min(Math.max(1, Math.trunc(rawLimit)), 100);
636
+ const pool = Math.max(limit * 3, 30);
637
+ const wantText = params.mode !== "vector" && cfg.ftsCols.length > 0 && typeof params.query === "string" && params.query !== "";
638
+ const anyEmbed = cfg.legs.some((l) => l.embed !== void 0);
639
+ const vectorAsked = params.mode !== "text" && (Array.isArray(params.vector) || params.using !== void 0 || params.mode === "vector" || anyEmbed && typeof params.query === "string" && params.query !== "");
640
+ const leg = vectorAsked ? pickLeg(table, cfg.legs, params.using) : null;
641
+ let qv = Array.isArray(params.vector) ? params.vector : null;
642
+ if (qv === null && leg?.embed !== void 0 && typeof params.query === "string" && params.query !== "") {
643
+ try {
644
+ qv = await embedQuery(leg.embed, params.query);
645
+ } catch (e) {
646
+ if (!wantText) throw e;
647
+ qv = null;
648
+ }
649
+ }
650
+ const wantVector = leg !== null && qv !== null;
651
+ if (!wantText && !wantVector) {
652
+ throw new Error(
653
+ `search(${table}): ko\u015Fulabilir kol yok \u2014 metin i\xE7in 'query' (FTS beyan\u0131 gerekir), semantik i\xE7in 'vector' verin (FR-015)`
654
+ );
655
+ }
656
+ const bind = [];
657
+ const add = (v) => {
658
+ bind.push(v);
659
+ return `$${bind.length}`;
660
+ };
661
+ const whereSql = compileWhere(table, cfg.colSet, params.where ?? {}, add);
662
+ const live = await at();
663
+ const K = 50;
664
+ let semSql = "";
665
+ let kwSql = "";
666
+ if (wantVector && leg) {
667
+ const sch = await vectorSchemaWithGuc(live);
668
+ const op = METRIC_OPERATOR[leg.metric] ?? METRIC_OPERATOR.cosine;
669
+ let exactOrder = "";
670
+ if (whereSql !== "") {
671
+ const probeRows = await live.unsafe(
672
+ `SELECT count(*)::int AS n FROM (SELECT 1 FROM ${quoteIdent(table)} t WHERE t.${quoteIdent(leg.column)} IS NOT NULL${whereSql} LIMIT ${SELECTIVITY_EXACT_THRESHOLD + 1}) s`,
673
+ bind.slice()
674
+ );
675
+ const n = probeRows?.[0]?.n;
676
+ if (typeof n === "number" && n <= SELECTIVITY_EXACT_THRESHOLD) {
677
+ exactOrder = " + 0.0";
678
+ }
679
+ }
680
+ const vp = add(toVectorLiteral(qv));
681
+ semSql = `SELECT t.${quoteIdent(cfg.pk)} AS id, ROW_NUMBER() OVER (ORDER BY (t.${quoteIdent(leg.column)} OPERATOR(${quoteIdent(sch)}.${op}) ${vp}::${quoteIdent(sch)}.vector)${exactOrder}) AS r FROM ${quoteIdent(table)} t WHERE t.${quoteIdent(leg.column)} IS NOT NULL${whereSql} ORDER BY r LIMIT ${pool}`;
682
+ }
683
+ if (wantText) {
684
+ const qp = add(params.query);
685
+ kwSql = `SELECT t.${quoteIdent(cfg.pk)} AS id, ROW_NUMBER() OVER (ORDER BY ts_rank_cd(t.palbase_fts, websearch_to_tsquery('simple', ${qp})) DESC) AS r FROM ${quoteIdent(table)} t WHERE t.palbase_fts @@ websearch_to_tsquery('simple', ${qp})${whereSql} ORDER BY r LIMIT ${pool}`;
686
+ }
687
+ const colList = cfg.cols.map((c) => `t.${quoteIdent(c)}`).join(", ");
688
+ let sql;
689
+ if (semSql !== "" && kwSql !== "") {
690
+ sql = `WITH sem AS (${semSql}), kw AS (${kwSql}), fused AS (SELECT COALESCE(sem.id, kw.id) AS id, (COALESCE(1.0/(${K} + sem.r), 0) + COALESCE(1.0/(${K} + kw.r), 0))::float8 AS _score FROM sem FULL OUTER JOIN kw ON sem.id = kw.id) SELECT ${colList}, fused._score AS _score FROM fused JOIN ${quoteIdent(table)} t ON t.${quoteIdent(cfg.pk)} = fused.id ORDER BY fused._score DESC, t.${quoteIdent(cfg.pk)} LIMIT ${limit}`;
691
+ } else {
692
+ const single = semSql !== "" ? `sem AS (${semSql})` : `kw AS (${kwSql})`;
693
+ const alias = semSql !== "" ? "sem" : "kw";
694
+ sql = `WITH ${single} SELECT ${colList}, (1.0/(${K} + ${alias}.r))::float8 AS _score FROM ${alias} JOIN ${quoteIdent(table)} t ON t.${quoteIdent(cfg.pk)} = ${alias}.id ORDER BY _score DESC, t.${quoteIdent(cfg.pk)} LIMIT ${limit}`;
695
+ }
696
+ const rows = await live.unsafe(sql, bind);
697
+ return asTableRows(table, rows);
698
+ },
431
699
  /** A real SAVEPOINT inside the request's transaction. */
432
700
  async transaction(cb) {
433
701
  const live = await at();
@@ -456,7 +724,7 @@ function createOps(tx) {
456
724
  return live.savepoint(async (sp) => {
457
725
  const results = [];
458
726
  for (const op of plan.ops) {
459
- const rows = await runPlanOp(sp, op, results);
727
+ const rows = asTableRows(op.table, await runPlanOp(sp, op, results));
460
728
  const result = { rows, rows_affected: rows.length };
461
729
  results.push(result);
462
730
  assertGuard(op, result);
@@ -469,6 +737,7 @@ function createOps(tx) {
469
737
  }
470
738
  var currentSchema = {};
471
739
  function setSchema(schema) {
740
+ cachedVectorSchema = null;
472
741
  const s = schema;
473
742
  currentSchema = (s && "default" in s ? s.default : s) ?? {};
474
743
  }
@@ -555,7 +824,7 @@ function isRef(v) {
555
824
  function isExpr(v) {
556
825
  return typeof v === "object" && v !== null && "$expr" in v;
557
826
  }
558
- function renderValue(value, column, args, results) {
827
+ function renderValue(value, column, args, results, vectorCols) {
559
828
  if (isRef(value)) {
560
829
  const source = results[value.$ref.op];
561
830
  const row = source?.rows[0];
@@ -564,15 +833,15 @@ function renderValue(value, column, args, results) {
564
833
  error_code: "tx_ref_unresolved"
565
834
  });
566
835
  }
567
- return args.bind(row[value.$ref.field]);
836
+ return bindMaybeVector(args, column, vectorCols, row[value.$ref.field]);
568
837
  }
569
838
  if (isExpr(value)) {
570
839
  const fn = value.$expr;
571
840
  if (fn.fn === "now") return "now()";
572
841
  const operator = fn.fn === "inc" ? "+" : "-";
573
- return `${quoteIdent(column)} ${operator} ${args.bind(fn.by)}`;
842
+ return `${quoteIdent(column)} ${operator} ${bindMaybeVector(args, column, vectorCols, fn.by)}`;
574
843
  }
575
- return args.bind(value);
844
+ return bindMaybeVector(args, column, vectorCols, value);
576
845
  }
577
846
  function renderWhere(where, args, results) {
578
847
  const cols = Object.keys(where ?? {});
@@ -584,14 +853,21 @@ function renderWhere(where, args, results) {
584
853
  });
585
854
  return ` WHERE ${terms.join(" AND ")}`;
586
855
  }
856
+ function bindMaybeVector(args, column, vectorCols, value) {
857
+ if (column !== void 0 && vectorCols?.has(column) && Array.isArray(value)) {
858
+ return args.bind(toVectorLiteral(value));
859
+ }
860
+ return args.bind(value);
861
+ }
587
862
  async function runPlanOp(sp, op, results) {
863
+ const opVectorCols = vectorColumnsOf(currentSchema, op.table);
588
864
  const args = new Args();
589
865
  const table = quoteIdent(op.table);
590
866
  let sql;
591
867
  switch (op.op) {
592
868
  case "insert": {
593
869
  const cols = Object.keys(op.values ?? {});
594
- const rendered = cols.map((c) => renderValue(op.values[c], c, args, results));
870
+ const rendered = cols.map((c) => renderValue(op.values[c], c, args, results, opVectorCols));
595
871
  sql = cols.length ? `INSERT INTO ${table} (${cols.map(quoteIdent).join(", ")}) VALUES (${rendered.join(", ")}) RETURNING *` : `INSERT INTO ${table} DEFAULT VALUES RETURNING *`;
596
872
  break;
597
873
  }
@@ -600,7 +876,7 @@ async function runPlanOp(sp, op, results) {
600
876
  if (rows.length === 0 || !rows[0]) return [];
601
877
  const cols = Object.keys(rows[0]);
602
878
  const tuples = rows.map(
603
- (r) => `(${cols.map((c) => renderValue(r[c], c, args, results)).join(", ")})`
879
+ (r) => `(${cols.map((c) => renderValue(r[c], c, args, results, opVectorCols)).join(", ")})`
604
880
  );
605
881
  sql = `INSERT INTO ${table} (${cols.map(quoteIdent).join(", ")}) VALUES ${tuples.join(", ")} RETURNING *`;
606
882
  break;
@@ -609,7 +885,7 @@ async function runPlanOp(sp, op, results) {
609
885
  const cols = Object.keys(op.set ?? {});
610
886
  if (cols.length === 0) throw new Error(`update ${op.table}: nothing to set`);
611
887
  const assignments = cols.map(
612
- (c) => `${quoteIdent(c)} = ${renderValue(op.set[c], c, args, results)}`
888
+ (c) => `${quoteIdent(c)} = ${renderValue(op.set[c], c, args, results, opVectorCols)}`
613
889
  );
614
890
  sql = `UPDATE ${table} SET ${assignments.join(", ")}${renderWhere(op.where, args, results)} RETURNING *`;
615
891
  break;
@@ -720,7 +996,8 @@ function grantFor(entry, ctx, bucketLimits) {
720
996
  filename: ctx.filename
721
997
  }),
722
998
  maxBytes: bucketLimits?.maxBytes ?? null,
723
- mimeTypes: bucketLimits?.mimeTypes ?? null
999
+ mimeTypes: bucketLimits?.mimeTypes ?? null,
1000
+ ownerUid: ctx.userId ?? null
724
1001
  };
725
1002
  }
726
1003
  var CompletionLedger = class {
@@ -817,6 +1094,23 @@ function installEgressFence(policy) {
817
1094
 
818
1095
  // src/engine/index.ts
819
1096
  var JSON_HEADERS = { "content-type": "application/json" };
1097
+ function fieldErrors(err) {
1098
+ return err.issues.map((i) => ({ field: i.path.join("."), message: i.message }));
1099
+ }
1100
+ function headersFor(raw, schema) {
1101
+ const shape = schema.shape;
1102
+ if (typeof shape !== "object" || shape === null) return raw;
1103
+ let aliased = null;
1104
+ for (const declared of Object.keys(shape)) {
1105
+ const lower = declared.toLowerCase();
1106
+ if (lower === declared) continue;
1107
+ const value = raw[lower];
1108
+ if (value === void 0) continue;
1109
+ aliased ??= { ...raw };
1110
+ aliased[declared] = value;
1111
+ }
1112
+ return aliased ?? raw;
1113
+ }
820
1114
  function envelope(error, description, status, requestId, extra) {
821
1115
  return new Response(
822
1116
  JSON.stringify({ error, error_description: description, status, request_id: requestId, ...extra }),
@@ -850,6 +1144,7 @@ async function defaultSqlDriver(config) {
850
1144
  async function createApp(opts) {
851
1145
  const { config, controllers } = opts;
852
1146
  setSchema(opts.schema ?? {});
1147
+ setSecretReader(opts.secretReader ?? null);
853
1148
  const routes = buildRouteTable(controllers);
854
1149
  if (routes.length === 0) {
855
1150
  throw new BootRefused([], "boot refused: zero endpoints collected \u2014 nothing would answer.");
@@ -962,7 +1257,8 @@ async function createApp(opts) {
962
1257
  error: "too_many_requests",
963
1258
  error_description: "Rate limit exceeded for this endpoint",
964
1259
  status: 429,
965
- request_id: requestId
1260
+ request_id: requestId,
1261
+ data: { retryAfter }
966
1262
  }),
967
1263
  { status: 429, headers: { ...JSON_HEADERS, "retry-after": String(retryAfter) } }
968
1264
  );
@@ -981,7 +1277,7 @@ async function createApp(opts) {
981
1277
  const r = p.schema.safeParse(parsedBody);
982
1278
  if (!r.success) {
983
1279
  return envelope("bad_request", "Request body failed validation", 400, requestId, {
984
- fields: r.error.issues.map((i) => ({ field: i.path.join("."), message: i.message }))
1280
+ data: { fields: fieldErrors(r.error) }
985
1281
  });
986
1282
  }
987
1283
  args[p.index] = r.data;
@@ -991,7 +1287,7 @@ async function createApp(opts) {
991
1287
  const r = p.schema.safeParse(Object.fromEntries(url.searchParams));
992
1288
  if (!r.success) {
993
1289
  return envelope("bad_request", "Query parameters failed validation", 400, requestId, {
994
- fields: r.error.issues.map((i) => ({ field: i.path.join("."), message: i.message }))
1290
+ data: { fields: fieldErrors(r.error) }
995
1291
  });
996
1292
  }
997
1293
  args[p.index] = r.data;
@@ -1000,9 +1296,21 @@ async function createApp(opts) {
1000
1296
  case "param":
1001
1297
  args[p.index] = hit.params[p.name];
1002
1298
  break;
1003
- case "headers":
1004
- args[p.index] = Object.fromEntries(req.headers);
1299
+ case "headers": {
1300
+ const raw = Object.fromEntries(req.headers);
1301
+ if (!p.schema) {
1302
+ args[p.index] = raw;
1303
+ break;
1304
+ }
1305
+ const r = p.schema.safeParse(headersFor(raw, p.schema));
1306
+ if (!r.success) {
1307
+ return envelope("bad_request", "Request headers failed validation", 400, requestId, {
1308
+ data: { fields: fieldErrors(r.error) }
1309
+ });
1310
+ }
1311
+ args[p.index] = r.data;
1005
1312
  break;
1313
+ }
1006
1314
  case "user":
1007
1315
  case "optionalUser":
1008
1316
  args[p.index] = claims ? {
@@ -1051,6 +1359,14 @@ async function createApp(opts) {
1051
1359
  case "traceId":
1052
1360
  args[p.index] = requestId;
1053
1361
  break;
1362
+ case "client":
1363
+ args[p.index] = {
1364
+ sdkVersion: req.headers.get("x-palbase-sdk-version"),
1365
+ appVersion: req.headers.get("x-palbase-client-version"),
1366
+ platform: req.headers.get("x-platform"),
1367
+ osVersion: req.headers.get("x-os-version")
1368
+ };
1369
+ break;
1054
1370
  case "req":
1055
1371
  args[p.index] = req;
1056
1372
  break;
@@ -1155,4 +1471,4 @@ export {
1155
1471
  installEgressFence,
1156
1472
  createApp
1157
1473
  };
1158
- //# sourceMappingURL=chunk-QYOHMVUW.js.map
1474
+ //# sourceMappingURL=chunk-74XDEF5J.js.map