@palbase/backend 38.0.16 → 39.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 (58) hide show
  1. package/dist/bin/palbase-backend.cjs +46 -1
  2. package/dist/bin/palbase-backend.cjs.map +1 -1
  3. package/dist/bin/palbase-backend.js +5 -5
  4. package/dist/{chunk-LFUNRWUE.js → chunk-4J6I4KVQ.js} +67 -3
  5. package/dist/{chunk-LFUNRWUE.js.map → chunk-4J6I4KVQ.js.map} +1 -1
  6. package/dist/{chunk-7WAGQ3VR.js → chunk-AR2I4M7I.js} +497 -17
  7. package/dist/chunk-AR2I4M7I.js.map +1 -0
  8. package/dist/{chunk-YANGWAIM.js → chunk-H7EKL6HC.js} +2 -2
  9. package/dist/{chunk-EHKEMHB4.js → chunk-JJSR4BUX.js} +49 -5
  10. package/dist/chunk-JJSR4BUX.js.map +1 -0
  11. package/dist/{chunk-CS6NQ6PO.js → chunk-JPTFYHP3.js} +2 -2
  12. package/dist/{chunk-CS6NQ6PO.js.map → chunk-JPTFYHP3.js.map} +1 -1
  13. package/dist/{chunk-X7UR3VXA.js → chunk-XZWOMPD3.js} +3 -2
  14. package/dist/chunk-XZWOMPD3.js.map +1 -0
  15. package/dist/{chunk-PDD55QWN.js → chunk-ZNQDL466.js} +2 -2
  16. package/dist/db/index.cjs +378 -14
  17. package/dist/db/index.cjs.map +1 -1
  18. package/dist/db/index.d.cts +1 -1
  19. package/dist/db/index.d.ts +1 -1
  20. package/dist/db/index.js +3 -3
  21. package/dist/engine/index.cjs +46 -1
  22. package/dist/engine/index.cjs.map +1 -1
  23. package/dist/engine/index.d.cts +3 -3
  24. package/dist/engine/index.d.ts +3 -3
  25. package/dist/engine/index.js +5 -5
  26. package/dist/{index-CI7S0Wqv.d.cts → index-B0mjUnxy.d.cts} +25 -2
  27. package/dist/{index-CUy50OLU.d.ts → index-eN4KzGa5.d.ts} +555 -29
  28. package/dist/{index-DRx880KY.d.cts → index-pK2At5Yc.d.cts} +555 -29
  29. package/dist/{index-Ogi30dbt.d.ts → index-yDno9Ju9.d.ts} +25 -2
  30. package/dist/index.cjs +629 -17
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +216 -8
  33. package/dist/index.d.ts +216 -8
  34. package/dist/index.js +75 -6
  35. package/dist/index.js.map +1 -1
  36. package/dist/openapi/index.cjs.map +1 -1
  37. package/dist/openapi/index.d.cts +2 -2
  38. package/dist/openapi/index.d.ts +2 -2
  39. package/dist/openapi/index.js +2 -2
  40. package/dist/{registry-C7UCkQf0.d.cts → registry-9dNeVN5d.d.cts} +1 -1
  41. package/dist/{registry-DJcvbomD.d.ts → registry-B8ddZiMY.d.ts} +1 -1
  42. package/dist/test/index.cjs +19 -0
  43. package/dist/test/index.cjs.map +1 -1
  44. package/dist/test/index.d.cts +1 -1
  45. package/dist/test/index.d.ts +1 -1
  46. package/dist/test/index.js +20 -2
  47. package/dist/test/index.js.map +1 -1
  48. package/docs/README.md +1 -1
  49. package/docs/database.md +129 -0
  50. package/docs/llms-full.txt +512 -33
  51. package/docs/schema.md +382 -32
  52. package/package.json +1 -1
  53. package/template/package.json +1 -1
  54. package/dist/chunk-7WAGQ3VR.js.map +0 -1
  55. package/dist/chunk-EHKEMHB4.js.map +0 -1
  56. package/dist/chunk-X7UR3VXA.js.map +0 -1
  57. /package/dist/{chunk-YANGWAIM.js.map → chunk-H7EKL6HC.js.map} +0 -0
  58. /package/dist/{chunk-PDD55QWN.js.map → chunk-ZNQDL466.js.map} +0 -0
package/dist/index.cjs CHANGED
@@ -80,7 +80,7 @@ __export(src_exports, {
80
80
  Sse: () => Sse,
81
81
  SseOut: () => SseOut,
82
82
  Storage: () => Storage,
83
- TABLE_META: () => TABLE_META,
83
+ TABLE_META: () => TABLE_META2,
84
84
  TooManyRequests: () => TooManyRequests,
85
85
  TraceId: () => TraceId,
86
86
  TxPlanError: () => TxPlanError,
@@ -103,11 +103,13 @@ __export(src_exports, {
103
103
  __setRuntime: () => __setRuntime,
104
104
  assertNoOrphanEntryPoints: () => assertNoOrphanEntryPoints,
105
105
  assertZeroArgConstructor: () => assertZeroArgConstructor,
106
+ backfill: () => backfill,
106
107
  bigint: () => bigint,
107
108
  boolean: () => boolean,
108
109
  buildContainer: () => buildContainer,
109
110
  buildModuleClients: () => buildModuleClients,
110
111
  can: () => can,
112
+ check: () => check,
111
113
  col: () => col,
112
114
  controllersOf: () => controllersOf,
113
115
  dec: () => dec,
@@ -116,10 +118,13 @@ __export(src_exports, {
116
118
  defineDefaultAuth: () => defineDefaultAuth,
117
119
  defineError: () => defineError,
118
120
  defineMiddleware: () => defineMiddleware,
121
+ defineRepository: () => defineRepository,
119
122
  defineSchema: () => defineSchema,
120
123
  defineTable: () => defineTable,
121
124
  enumType: () => enumType,
122
125
  exprCtx: () => exprCtx,
126
+ foreignKey: () => foreignKey,
127
+ freeze: () => freeze,
123
128
  getErrorRegistry: () => getErrorRegistry,
124
129
  getHookConfig: () => getHookConfig,
125
130
  getHookManifest: () => getHookManifest,
@@ -129,6 +134,7 @@ __export(src_exports, {
129
134
  getRoutes: () => getRoutes,
130
135
  getWebhookConfig: () => getWebhookConfig,
131
136
  getWebhookManifest: () => getWebhookManifest,
137
+ guard: () => guard,
132
138
  hooksOf: () => hooksOf,
133
139
  inc: () => inc,
134
140
  increment: () => increment,
@@ -962,8 +968,7 @@ function tableToJSON(table, schemaName) {
962
968
  if (table.raw !== void 0 && table.raw.length > 0) {
963
969
  out.rawConstraints = table.raw.map((r) => ({
964
970
  name: r.name,
965
- up: r.up,
966
- down: r.down ?? null
971
+ up: r.up
967
972
  }));
968
973
  }
969
974
  if (table.checks !== void 0 && table.checks.length > 0) {
@@ -980,6 +985,7 @@ function tableToJSON(table, schemaName) {
980
985
  ...i.columns
981
986
  ] : []
982
987
  };
988
+ if (i.unique === true) ix.unique = true;
983
989
  if (i.where !== void 0) ix.where = i.where;
984
990
  if (i.expression !== void 0) ix.expression = i.expression;
985
991
  if (i.sort !== void 0) ix.sort = i.sort;
@@ -990,6 +996,70 @@ function tableToJSON(table, schemaName) {
990
996
  return ix;
991
997
  });
992
998
  }
999
+ if (table.dropConstraints !== void 0 && table.dropConstraints.length > 0) {
1000
+ out.dropConstraints = [
1001
+ ...table.dropConstraints
1002
+ ];
1003
+ }
1004
+ if (table.foreignKeys !== void 0 && table.foreignKeys.length > 0) {
1005
+ out.foreignKeys = table.foreignKeys.map((fk) => {
1006
+ const j = {
1007
+ name: fk.name,
1008
+ columns: [
1009
+ ...fk.columns
1010
+ ],
1011
+ refTable: fk.refTable,
1012
+ refColumns: [
1013
+ ...fk.refColumns
1014
+ ]
1015
+ };
1016
+ if (fk.onUpdate !== void 0) j.onUpdate = fk.onUpdate;
1017
+ if (fk.onDelete !== void 0) j.onDelete = fk.onDelete;
1018
+ if (fk.match !== void 0) j.match = fk.match;
1019
+ return j;
1020
+ });
1021
+ }
1022
+ if (table.freeze !== void 0 && table.freeze.length > 0) {
1023
+ out.freeze = table.freeze.map((f) => ({
1024
+ name: f.name,
1025
+ when: f.when,
1026
+ columns: [
1027
+ ...f.columns
1028
+ ],
1029
+ refTable: f.refTable,
1030
+ refColumns: [
1031
+ ...f.refColumns
1032
+ ]
1033
+ }));
1034
+ }
1035
+ if (table.guards !== void 0 && table.guards.length > 0) {
1036
+ out.guards = table.guards.map((g) => ({
1037
+ name: g.name,
1038
+ event: g.event,
1039
+ ...g.when !== void 0 ? {
1040
+ when: g.when
1041
+ } : {},
1042
+ ...g.exists !== void 0 ? {
1043
+ exists: g.exists
1044
+ } : {},
1045
+ message: g.message,
1046
+ ...g.detail !== void 0 ? {
1047
+ detail: g.detail
1048
+ } : {},
1049
+ ...g.hint !== void 0 ? {
1050
+ hint: g.hint
1051
+ } : {},
1052
+ ...g.column !== void 0 ? {
1053
+ column: g.column
1054
+ } : {}
1055
+ }));
1056
+ }
1057
+ if (table.backfills !== void 0 && table.backfills.length > 0) {
1058
+ out.backfills = table.backfills.map((b) => ({
1059
+ name: b.name,
1060
+ sql: b.sql
1061
+ }));
1062
+ }
993
1063
  if (table.appendOnly === true) out.appendOnly = true;
994
1064
  if (table.search !== void 0) {
995
1065
  const vectorColumn = Object.entries(columns).find(([, c]) => c.dimensions !== void 0)?.[0];
@@ -1244,6 +1314,7 @@ function makeTypedSurface(raw2) {
1244
1314
  $facets: /* @__PURE__ */ __name((table, params) => reco.facets(table, params), "$facets"),
1245
1315
  $claim: /* @__PURE__ */ __name((table, unique, extra) => reco.claim(table, unique, extra), "$claim"),
1246
1316
  $lockRows: /* @__PURE__ */ __name((table, ids) => reco.lockRows(table, ids), "$lockRows"),
1317
+ $lockRowsWhere: /* @__PURE__ */ __name((table, where, opts) => reco.lockRowsWhere(table, where, opts), "$lockRowsWhere"),
1247
1318
  $advisoryXactLock: /* @__PURE__ */ __name((key) => reco.advisoryXactLock(key), "$advisoryXactLock"),
1248
1319
  $aggregate: /* @__PURE__ */ __name((table, q) => raw2.aggregate(table, q), "$aggregate"),
1249
1320
  $insertMany: /* @__PURE__ */ __name((table, rows, opts) => raw2.insertMany(table, rows, opts), "$insertMany"),
@@ -2576,6 +2647,192 @@ __name(buildModuleClients, "buildModuleClients");
2576
2647
 
2577
2648
  // src/db/policy.ts
2578
2649
  var import_zod = require("zod");
2650
+
2651
+ // src/db/foreign-key.ts
2652
+ var TABLE_META = /* @__PURE__ */ Symbol.for("palbase.table.meta");
2653
+ var REF_TARGET = /* @__PURE__ */ Symbol.for("palbase.ref.target");
2654
+ var ForeignKeyBuilder = class {
2655
+ static {
2656
+ __name(this, "ForeignKeyBuilder");
2657
+ }
2658
+ _def;
2659
+ constructor(name) {
2660
+ if (name.trim() === "") {
2661
+ throw new Error("foreignKey(ad): foreign key'in ad\u0131 bo\u015F olamaz \u2014 Palbase k\u0131s\u0131tlar\u0131 ADA g\xF6re uzla\u015Ft\u0131r\u0131r.");
2662
+ }
2663
+ this._def = {
2664
+ name,
2665
+ columns: [],
2666
+ refTable: "",
2667
+ refColumns: []
2668
+ };
2669
+ }
2670
+ /** Yerel sütunlar. SIRA KORUNUR — hedef sütunlarla birebir eşleşir. */
2671
+ on(...cols) {
2672
+ this._def.columns = cols.map((c) => {
2673
+ const e = c.expr;
2674
+ if (e.kind !== "col") {
2675
+ throw new Error(`foreignKey(${this._def.name}).on(): yaln\u0131z kolon referans\u0131 al\u0131r \u2014 ifade bir FK'n\u0131n par\xE7as\u0131 olamaz.`);
2676
+ }
2677
+ return e.name;
2678
+ });
2679
+ return this;
2680
+ }
2681
+ /**
2682
+ * Hedef tablo ve sütunları. Sütun sayısı `.on()`'unkiyle eşit OLMALI ve bu
2683
+ * burada, bildirim yazılırken kontrol edilir — bozuk bir FK wire'a hiç çıkmaz.
2684
+ */
2685
+ references(table, ...cols) {
2686
+ if (cols.length === 0) {
2687
+ throw new Error(`foreignKey(${this._def.name}).references(): en az bir hedef s\xFCtun gerekir.`);
2688
+ }
2689
+ this._def.refTable = table[TABLE_META].name;
2690
+ this._def.refColumns = [
2691
+ ...cols
2692
+ ];
2693
+ Object.defineProperty(this._def, REF_TARGET, {
2694
+ value: table[TABLE_META],
2695
+ enumerable: false,
2696
+ configurable: true
2697
+ });
2698
+ return this;
2699
+ }
2700
+ onUpdate(action) {
2701
+ this._def.onUpdate = action;
2702
+ return this;
2703
+ }
2704
+ onDelete(action) {
2705
+ this._def.onDelete = action;
2706
+ return this;
2707
+ }
2708
+ /**
2709
+ * `MATCH FULL`: referans veren sütunların HEPSİ NULL olmadıkça kısıt
2710
+ * değerlendirilir. Varsayılan `MATCH SIMPLE`'da tek bir NULL kısıtı atlatır —
2711
+ * `freeze` bunu bilerek kullanır, sıradan bir FK'da genelde istenmez.
2712
+ */
2713
+ match(m) {
2714
+ this._def.match = m;
2715
+ return this;
2716
+ }
2717
+ };
2718
+ function foreignKey(name) {
2719
+ return new ForeignKeyBuilder(name);
2720
+ }
2721
+ __name(foreignKey, "foreignKey");
2722
+ function toForeignKeyDef(b) {
2723
+ const def = b instanceof ForeignKeyBuilder ? b._def : b;
2724
+ if (def.columns.length === 0) {
2725
+ throw new Error(`foreignKey(${def.name}): en az bir yerel s\xFCtun gerekir \u2014 .on(...) \xE7a\u011F\u0131r\u0131n.`);
2726
+ }
2727
+ if (def.refColumns.length === 0 || def.refTable === "") {
2728
+ throw new Error(`foreignKey(${def.name}): hedef tablo ve s\xFCtunlar gerekir \u2014 .references(...) \xE7a\u011F\u0131r\u0131n.`);
2729
+ }
2730
+ if (def.columns.length !== def.refColumns.length) {
2731
+ throw new Error(`foreignKey(${def.name}): ${def.columns.length} yerel s\xFCtun (${def.columns.join(", ")}) ama ${def.refColumns.length} hedef s\xFCtun (${def.refColumns.join(", ")}) \u2014 iki liste e\u015Fit uzunlukta olmal\u0131.`);
2732
+ }
2733
+ return def;
2734
+ }
2735
+ __name(toForeignKeyDef, "toForeignKeyDef");
2736
+ var FreezeBuilder = class {
2737
+ static {
2738
+ __name(this, "FreezeBuilder");
2739
+ }
2740
+ _def;
2741
+ constructor(name) {
2742
+ if (name.trim() === "") {
2743
+ throw new Error("freeze(ad): freeze k\u0131s\u0131t\u0131n\u0131n ad\u0131 bo\u015F olamaz \u2014 Palbase k\u0131s\u0131tlar\u0131 ADA g\xF6re uzla\u015Ft\u0131r\u0131r.");
2744
+ }
2745
+ this._def = {
2746
+ name,
2747
+ columns: [],
2748
+ refTable: "",
2749
+ refColumns: []
2750
+ };
2751
+ }
2752
+ /**
2753
+ * DONMA KOŞULU — çocuk satırının kendi kolonları üzerinde bir yüklem.
2754
+ *
2755
+ * Generated sütunlara `CASE WHEN <koşul> THEN <sütun> END` olarak iner; yani
2756
+ * koşul yanlışken sütunlar NULL olur ve FK o satır için hiç değerlendirilmez.
2757
+ */
2758
+ when(pred) {
2759
+ this._def.when = pred.expr;
2760
+ return this;
2761
+ }
2762
+ /** Çocuğun DONAN sütunları. SIRA KORUNUR — hedef sütunlarla birebir eşleşir. */
2763
+ columns(...cols) {
2764
+ if (cols.length === 0) {
2765
+ throw new Error(`freeze(${this._def.name}).columns(): en az bir donan s\xFCtun gerekir \u2014 hi\xE7birini vermeyen bir freeze hi\xE7bir \u015Feyi dondurmaz.`);
2766
+ }
2767
+ this._def.columns = [
2768
+ ...cols
2769
+ ];
2770
+ return this;
2771
+ }
2772
+ /**
2773
+ * Ebeveyn tablo ve donan demetin ORADAKİ karşılığı. Zincirin SON halkası:
2774
+ * bildirim burada tamamlanır ve tutarlılığı burada doğrulanır, yani
2775
+ * `.references()`'ı unutmuş bir freeze `FreezeDef` üretmediği için tabloya
2776
+ * hiç giremez.
2777
+ */
2778
+ references(table, ...cols) {
2779
+ if (cols.length === 0) {
2780
+ throw new Error(`freeze(${this._def.name}).references(): en az bir hedef s\xFCtun gerekir.`);
2781
+ }
2782
+ this._def.refTable = table[TABLE_META].name;
2783
+ this._def.refColumns = [
2784
+ ...cols
2785
+ ];
2786
+ Object.defineProperty(this._def, REF_TARGET, {
2787
+ value: table[TABLE_META],
2788
+ enumerable: false,
2789
+ configurable: true
2790
+ });
2791
+ return toFreezeDef(this);
2792
+ }
2793
+ };
2794
+ function freeze(name) {
2795
+ return new FreezeBuilder(name);
2796
+ }
2797
+ __name(freeze, "freeze");
2798
+ function toFreezeDef(b) {
2799
+ const def = b instanceof FreezeBuilder ? b._def : b;
2800
+ if (def.columns.length === 0) {
2801
+ throw new Error(`freeze(${def.name}): en az bir donan s\xFCtun gerekir \u2014 .columns(...) \xE7a\u011F\u0131r\u0131n.`);
2802
+ }
2803
+ if (def.refColumns.length === 0 || def.refTable === "") {
2804
+ throw new Error(`freeze(${def.name}): hedef tablo ve s\xFCtunlar gerekir \u2014 .references(...) \xE7a\u011F\u0131r\u0131n.`);
2805
+ }
2806
+ if (def.columns.length !== def.refColumns.length) {
2807
+ throw new Error(`freeze(${def.name}): ${def.columns.length} donan s\xFCtun (${def.columns.join(", ")}) ama ${def.refColumns.length} hedef s\xFCtun (${def.refColumns.join(", ")}) \u2014 iki liste e\u015Fit uzunlukta olmal\u0131.`);
2808
+ }
2809
+ if (def.when === void 0) {
2810
+ throw new Error(`freeze(${def.name}): donma ko\u015Fulu gerekir \u2014 .when(...) \xE7a\u011F\u0131r\u0131n. Ko\u015Fulsuz bir freeze s\u0131radan bir FK'd\u0131r; onun i\xE7in foreignKey(...) var.`);
2811
+ }
2812
+ const out = {
2813
+ name: def.name,
2814
+ when: def.when,
2815
+ columns: [
2816
+ ...def.columns
2817
+ ],
2818
+ refTable: def.refTable,
2819
+ refColumns: [
2820
+ ...def.refColumns
2821
+ ]
2822
+ };
2823
+ const target = def[REF_TARGET];
2824
+ if (target !== void 0) {
2825
+ Object.defineProperty(out, REF_TARGET, {
2826
+ value: target,
2827
+ enumerable: false,
2828
+ configurable: true
2829
+ });
2830
+ }
2831
+ return out;
2832
+ }
2833
+ __name(toFreezeDef, "toFreezeDef");
2834
+
2835
+ // src/db/policy.ts
2579
2836
  var PolicyExprRef = class _PolicyExprRef {
2580
2837
  static {
2581
2838
  __name(this, "PolicyExprRef");
@@ -2687,15 +2944,21 @@ function exprCtx(_columns, depth = 0, ownTable = "") {
2687
2944
  expr: part.expr
2688
2945
  }), "not"),
2689
2946
  existsIn: /* @__PURE__ */ __name((table, predicate) => {
2690
- const meta = table[TABLE_META];
2947
+ const meta = table[TABLE_META2];
2691
2948
  const inner = exprCtx(Object.keys(meta.columns), depth + 1, ownTable);
2692
- return new PolicyExprRef({
2949
+ const node = {
2693
2950
  kind: "exists",
2694
2951
  table: meta.name,
2695
2952
  alias: `pb_e${depth + 1}`,
2696
2953
  outer: ownTable,
2697
2954
  predicate: predicate(inner).expr
2955
+ };
2956
+ Object.defineProperty(node, REF_TARGET, {
2957
+ value: meta,
2958
+ enumerable: false,
2959
+ configurable: true
2698
2960
  });
2961
+ return new PolicyExprRef(node);
2699
2962
  }, "existsIn")
2700
2963
  };
2701
2964
  }
@@ -2783,13 +3046,13 @@ var PolicyBuilder = class {
2783
3046
  * // → id IN (SELECT "channel_id" FROM "channel_members"
2784
3047
  * // WHERE "user_id" = (select auth.uid()))
2785
3048
  */
2786
- memberOf(membershipTable, foreignKey, options = {}) {
3049
+ memberOf(membershipTable, foreignKey2, options = {}) {
2787
3050
  if (this._def.using !== null) {
2788
3051
  throw new Error(`policy(${this._def.name}).memberOf(): this policy already has a using() expression. Write one or the other \u2014 memberOf IS the using expression.`);
2789
3052
  }
2790
3053
  const column = options.column ?? "id";
2791
3054
  const userColumn = options.userColumn ?? "user_id";
2792
- this._def.using = `${quote(column)} IN (SELECT ${quote(foreignKey)} FROM ${quote(membershipTable)} WHERE ${quote(userColumn)} = (select auth.uid()))`;
3055
+ this._def.using = `${quote(column)} IN (SELECT ${quote(foreignKey2)} FROM ${quote(membershipTable)} WHERE ${quote(userColumn)} = (select auth.uid()))`;
2793
3056
  return this;
2794
3057
  }
2795
3058
  /** `WITH CHECK (...)` yazma doğrulaması — `using` ile aynı iki biçim. */
@@ -2808,6 +3071,92 @@ function policy(name) {
2808
3071
  return new PolicyBuilder(name);
2809
3072
  }
2810
3073
  __name(policy, "policy");
3074
+ function assertCheckNode(node, name) {
3075
+ switch (node.kind) {
3076
+ case "exists":
3077
+ throw new Error(`check(${name}): \`existsIn(...)\` is a subquery and PostgreSQL forbids subqueries in a CHECK constraint \u2014 express a cross-table rule as a composite FOREIGN KEY (foreignKeys), or as a guard.`);
3078
+ case "authUid":
3079
+ throw new Error(`check(${name}): \`auth.uid()\` renders as a subquery \`(SELECT auth.uid())\` and PostgreSQL forbids subqueries in a CHECK constraint \u2014 a caller-dependent rule belongs in an RLS policy, not a CHECK.`);
3080
+ case "binop":
3081
+ assertCheckNode(node.left, name);
3082
+ assertCheckNode(node.right, name);
3083
+ return;
3084
+ case "and":
3085
+ case "or":
3086
+ for (const part of node.parts) assertCheckNode(part, name);
3087
+ return;
3088
+ case "not":
3089
+ case "isNull":
3090
+ assertCheckNode(node.expr, name);
3091
+ return;
3092
+ default:
3093
+ return;
3094
+ }
3095
+ }
3096
+ __name(assertCheckNode, "assertCheckNode");
3097
+ var CHECK_BIN_OPS = {
3098
+ eq: "=",
3099
+ neq: "<>",
3100
+ gt: ">",
3101
+ gte: ">=",
3102
+ lt: "<",
3103
+ lte: "<="
3104
+ };
3105
+ function literalSQL(v) {
3106
+ if (v === null) return "NULL";
3107
+ if (typeof v === "boolean") return v ? "true" : "false";
3108
+ if (typeof v === "number") {
3109
+ if (!Number.isFinite(v)) {
3110
+ throw new Error(`check(): ${v} bir SQL say\u0131 literali de\u011Fil.`);
3111
+ }
3112
+ return String(v);
3113
+ }
3114
+ return `'${v.replace(/'/g, "''")}'`;
3115
+ }
3116
+ __name(literalSQL, "literalSQL");
3117
+ function checkNodeSQL(node, name) {
3118
+ switch (node.kind) {
3119
+ case "col":
3120
+ return quote(node.name);
3121
+ case "lit":
3122
+ return literalSQL(node.value);
3123
+ case "binop": {
3124
+ const op = CHECK_BIN_OPS[node.op];
3125
+ if (op === void 0) {
3126
+ throw new Error(`check(${name}): \`${node.op}\` bir CHECK ifadesinde desteklenmiyor.`);
3127
+ }
3128
+ return `(${checkNodeSQL(node.left, name)} ${op} ${checkNodeSQL(node.right, name)})`;
3129
+ }
3130
+ case "and":
3131
+ case "or": {
3132
+ if (node.parts.length === 0) {
3133
+ return node.kind === "and" ? "(true)" : "(false)";
3134
+ }
3135
+ const joiner = node.kind === "and" ? " AND " : " OR ";
3136
+ return `(${node.parts.map((p) => checkNodeSQL(p, name)).join(joiner)})`;
3137
+ }
3138
+ case "not":
3139
+ return `(NOT ${checkNodeSQL(node.expr, name)})`;
3140
+ case "isNull":
3141
+ return `(${checkNodeSQL(node.expr, name)} IS ${node.negated ? "NOT NULL" : "NULL"})`;
3142
+ default: {
3143
+ const kind = node.kind;
3144
+ throw new Error(`check(${name}): \`${kind}\` d\xFC\u011F\xFCm\xFC bir CHECK ifadesinde tan\u0131nm\u0131yor.`);
3145
+ }
3146
+ }
3147
+ }
3148
+ __name(checkNodeSQL, "checkNodeSQL");
3149
+ function check(name, expr) {
3150
+ if (name.trim() === "") {
3151
+ throw new Error("check(): k\u0131s\u0131t\u0131n ad\u0131 bo\u015F olamaz \u2014 CHECK'ler ADA g\xF6re uzla\u015Ft\u0131r\u0131l\u0131yor.");
3152
+ }
3153
+ assertCheckNode(expr.expr, name);
3154
+ return {
3155
+ name,
3156
+ expr: checkNodeSQL(expr.expr, name)
3157
+ };
3158
+ }
3159
+ __name(check, "check");
2811
3160
  function can(table) {
2812
3161
  if (table === void 0 || table === null) {
2813
3162
  throw new Error("can(<table>): received " + String(table) + " \u2014 the schema declares no such table (check the name against db/*.ts; can(publicSchema.tables.<name>))");
@@ -2851,6 +3200,117 @@ function reservedCanRelation(tableName, columns) {
2851
3200
  }
2852
3201
  __name(reservedCanRelation, "reservedCanRelation");
2853
3202
 
3203
+ // src/db/guard.ts
3204
+ var GuardBuilder = class {
3205
+ static {
3206
+ __name(this, "GuardBuilder");
3207
+ }
3208
+ _def;
3209
+ constructor(name) {
3210
+ if (name.trim() === "") {
3211
+ throw new Error("guard(ad): guard'\u0131n ad\u0131 bo\u015F olamaz \u2014 Postgres ayn\u0131 olaydaki trigger'lar\u0131 ADA G\xD6RE alfabetik ko\u015Fuyor ve ba\u015Fka s\u0131ra tutamac\u0131 yok.");
3212
+ }
3213
+ this._def = {
3214
+ name
3215
+ };
3216
+ }
3217
+ /** Hangi yazma fiilinde bakılacak. */
3218
+ on(event) {
3219
+ this._def.event = event;
3220
+ return this;
3221
+ }
3222
+ /**
3223
+ * Satırın KENDİ kolonları üzerinde koşul — trigger'ın `WHEN` yan tümcesi.
3224
+ *
3225
+ * Emitter karşılaştırmaları `IS DISTINCT FROM` ile yazar (`<>` NULL'da yanlış
3226
+ * cevap verir) ve geçen sütunlardan bir `UPDATE OF (…)` listesi türetir
3227
+ * (FR-018).
3228
+ */
3229
+ when(pred) {
3230
+ this._def.when = pred.expr;
3231
+ return this;
3232
+ }
3233
+ /**
3234
+ * ÇAPRAZ SATIR koşulu — gövdedeki `EXISTS` bloğuna iner, `WHEN`'e DEĞİL:
3235
+ * Postgres trigger `WHEN`'inde alt sorgu kabul etmiyor (FR-019).
3236
+ *
3237
+ * Yüklem içinde takma adsız kalan kolon guard'ın KENDİ satırıdır (`NEW`/`OLD`);
3238
+ * `r.col(...)` hedef tablonun kolonlarını verir ve ayrı nitelenir. Bu yüzden
3239
+ * düğümün `outer` alanı BOŞ kalır: bir guard'ın "dış satır"ı bir tablo takma
3240
+ * adı değildir.
3241
+ */
3242
+ exists(table, predicate) {
3243
+ this._def.exists = exprCtx().existsIn(table, predicate).expr;
3244
+ return this;
3245
+ }
3246
+ /**
3247
+ * REDDİN KENDİSİ ve zincirin son halkası: mesaj olmadan bir guard tamamlanmaz.
3248
+ *
3249
+ * `detail`/`hint` Postgres'in aynı adlı alanlarına, `column` ise `COLUMN`
3250
+ * alanına gider — istemci hangi alanın reddedildiğini mesajı parse etmeden
3251
+ * okur.
3252
+ */
3253
+ refuse(message, opts) {
3254
+ this._def.message = message;
3255
+ if (opts?.detail !== void 0) this._def.detail = opts.detail;
3256
+ if (opts?.hint !== void 0) this._def.hint = opts.hint;
3257
+ if (opts?.column !== void 0) this._def.column = opts.column;
3258
+ return toGuardDef(this);
3259
+ }
3260
+ };
3261
+ function guard(name) {
3262
+ return new GuardBuilder(name);
3263
+ }
3264
+ __name(guard, "guard");
3265
+ function toGuardDef(b) {
3266
+ const def = b instanceof GuardBuilder ? b._def : b;
3267
+ if (def.event === void 0) {
3268
+ throw new Error(`guard(${def.name}): hangi olayda bakaca\u011F\u0131 gerekir \u2014 .on("insert"|"update"|"delete") \xE7a\u011F\u0131r\u0131n.`);
3269
+ }
3270
+ if (def.when === void 0 && def.exists === void 0) {
3271
+ throw new Error(`guard(${def.name}): bir ko\u015Ful gerekir \u2014 .when(...) ya da .exists(...) \xE7a\u011F\u0131r\u0131n. Ko\u015Fulsuz bir guard o olaydaki HER yazmay\u0131 reddeder.`);
3272
+ }
3273
+ if (def.message === void 0 || def.message.trim() === "") {
3274
+ throw new Error(`guard(${def.name}): bo\u015F mesaj kabul edilmez \u2014 reddin mesaj\u0131 guard'\u0131n kullan\u0131c\u0131ya ula\u015Fan TEK \xE7\u0131kt\u0131s\u0131.`);
3275
+ }
3276
+ return {
3277
+ name: def.name,
3278
+ event: def.event,
3279
+ ...def.when !== void 0 ? {
3280
+ when: def.when
3281
+ } : {},
3282
+ ...def.exists !== void 0 ? {
3283
+ exists: def.exists
3284
+ } : {},
3285
+ message: def.message,
3286
+ ...def.detail !== void 0 ? {
3287
+ detail: def.detail
3288
+ } : {},
3289
+ ...def.hint !== void 0 ? {
3290
+ hint: def.hint
3291
+ } : {},
3292
+ ...def.column !== void 0 ? {
3293
+ column: def.column
3294
+ } : {}
3295
+ };
3296
+ }
3297
+ __name(toGuardDef, "toGuardDef");
3298
+
3299
+ // src/db/backfill.ts
3300
+ function backfill(name, sql) {
3301
+ if (name.trim() === "") {
3302
+ throw new Error("backfill(ad, sql): backfill'in ad\u0131 bo\u015F olamaz \u2014 'bir kez ko\u015Ftu' kayd\u0131 ADA g\xF6re tutuluyor.");
3303
+ }
3304
+ if (sql.trim() === "") {
3305
+ throw new Error(`backfill(${name}): SQL bo\u015F olamaz \u2014 bo\u015F bir g\xF6vde ad\u0131 kayda ge\xE7irip KALICI olarak yakar; sonradan yaz\u0131lan ger\xE7ek SQL "zaten ko\u015Ftu" diye hi\xE7 \xE7al\u0131\u015Fmaz.`);
3306
+ }
3307
+ return {
3308
+ name,
3309
+ sql
3310
+ };
3311
+ }
3312
+ __name(backfill, "backfill");
3313
+
2854
3314
  // src/db/schema.ts
2855
3315
  var IndexBuilder = class {
2856
3316
  static {
@@ -2865,6 +3325,15 @@ var IndexBuilder = class {
2865
3325
  name
2866
3326
  };
2867
3327
  }
3328
+ /**
3329
+ * `CREATE UNIQUE INDEX` (FR-007). `.where()` ile birlikte PARTIAL UNIQUE
3330
+ * index verir (FR-007b) — "NULL'lar serbest, non-NULL'lar tekil" kalıbının
3331
+ * tek bildirimsel yolu.
3332
+ */
3333
+ unique() {
3334
+ this._def.unique = true;
3335
+ return this;
3336
+ }
2868
3337
  /** Index'in kolonları. SIRA KORUNUR — çok kolonlu bir index'in anlamı sırasıdır. */
2869
3338
  on(...cols) {
2870
3339
  this._def.columns = cols.map((c) => {
@@ -2922,7 +3391,7 @@ function toPolicyDef(p) {
2922
3391
  return p instanceof PolicyBuilder ? p._def : p;
2923
3392
  }
2924
3393
  __name(toPolicyDef, "toPolicyDef");
2925
- var TABLE_META = /* @__PURE__ */ Symbol.for("palbase.table.meta");
3394
+ var TABLE_META2 = /* @__PURE__ */ Symbol.for("palbase.table.meta");
2926
3395
  function defineTable(name, input) {
2927
3396
  if (name.trim() === "") {
2928
3397
  throw new Error("defineTable(name, \u2026): name must not be empty");
@@ -2955,9 +3424,46 @@ function defineTable(name, input) {
2955
3424
  if (input.primaryKey !== void 0) def.primaryKey = input.primaryKey;
2956
3425
  if (input.unique !== void 0) def.unique = input.unique;
2957
3426
  if (input.raw !== void 0 && input.raw.length > 0) def.raw = input.raw.slice();
2958
- if (input.checks !== void 0 && input.checks.length > 0) def.checks = input.checks.slice();
3427
+ const declaredChecks = typeof input.checks === "function" ? input.checks(exprCtx(Object.keys(input.columns), 0, name)) : input.checks?.slice() ?? [];
3428
+ if (declaredChecks.length > 0) def.checks = declaredChecks;
2959
3429
  const declaredIndexes = (input.indexes?.(exprCtx(Object.keys(input.columns), 0, name)) ?? []).map((i) => i instanceof IndexBuilder ? i._def : i);
2960
3430
  if (declaredIndexes.length > 0) def.indexes = declaredIndexes;
3431
+ const declaredFKs = (input.foreignKeys?.(exprCtx(Object.keys(input.columns), 0, name)) ?? []).map(toForeignKeyDef);
3432
+ if (declaredFKs.length > 0) def.foreignKeys = declaredFKs;
3433
+ const declaredFreezes = (input.freeze?.(exprCtx(Object.keys(input.columns), 0, name)) ?? []).map(toFreezeDef);
3434
+ if (declaredFreezes.length > 0) def.freeze = declaredFreezes;
3435
+ for (const fz of declaredFreezes) {
3436
+ for (const c of fz.columns) {
3437
+ if (!(c in input.columns)) {
3438
+ throw new Error(`table "${name}": freeze("${fz.name}").columns("${c}") \u2014 no such column on this table. Declared columns: ${Object.keys(input.columns).join(", ")}`);
3439
+ }
3440
+ }
3441
+ }
3442
+ const declaredGuards = (input.guards?.(exprCtx(Object.keys(input.columns), 0, name)) ?? []).map(toGuardDef);
3443
+ if (declaredGuards.length > 0) def.guards = declaredGuards;
3444
+ for (const g of declaredGuards) {
3445
+ if (g.column !== void 0 && !(g.column in input.columns)) {
3446
+ throw new Error(`table "${name}": guard("${g.name}").refuse(\u2026, { column: "${g.column}" }) \u2014 no such column on this table. Declared columns: ${Object.keys(input.columns).join(", ")}`);
3447
+ }
3448
+ }
3449
+ if (input.backfills !== void 0 && input.backfills.length > 0) {
3450
+ const seen = /* @__PURE__ */ new Set();
3451
+ for (const b of input.backfills) {
3452
+ if (seen.has(b.name)) {
3453
+ throw new Error(`table ${name}: backfill ad\u0131 iki kez bildirildi: ${b.name} \u2014 kay\u0131t ADA g\xF6re tutuluyor, ikincisi hi\xE7 ko\u015Fmazd\u0131.`);
3454
+ }
3455
+ seen.add(b.name);
3456
+ }
3457
+ def.backfills = input.backfills.map((b) => ({
3458
+ name: b.name,
3459
+ sql: b.sql
3460
+ }));
3461
+ }
3462
+ if (input.dropConstraints !== void 0 && input.dropConstraints.length > 0) {
3463
+ def.dropConstraints = [
3464
+ ...input.dropConstraints
3465
+ ];
3466
+ }
2961
3467
  if (input.appendOnly === true) def.appendOnly = true;
2962
3468
  if (input.ignored === true) def.ignored = true;
2963
3469
  if (input.search !== void 0) {
@@ -3011,7 +3517,7 @@ function defineTable(name, input) {
3011
3517
  const handle = {
3012
3518
  ...owned2
3013
3519
  };
3014
- Object.defineProperty(handle, TABLE_META, {
3520
+ Object.defineProperty(handle, TABLE_META2, {
3015
3521
  value: def,
3016
3522
  enumerable: false
3017
3523
  });
@@ -3020,6 +3526,9 @@ function defineTable(name, input) {
3020
3526
  __name(defineTable, "defineTable");
3021
3527
  var declaringSchemaOf = /* @__PURE__ */ new WeakMap();
3022
3528
  function resolveReferences(tables, schemaName) {
3529
+ for (const table of Object.values(tables)) {
3530
+ qualifyRefTargets(table, `table "${table.name}"`);
3531
+ }
3023
3532
  for (const table of Object.values(tables)) {
3024
3533
  for (const [colName, builder] of Object.entries(table.columns)) {
3025
3534
  const def = builder._def;
@@ -3064,7 +3573,7 @@ function defineSchema(name, input) {
3064
3573
  }
3065
3574
  const tables = {};
3066
3575
  for (const handle of input.tables) {
3067
- const table = handle[TABLE_META];
3576
+ const table = handle[TABLE_META2];
3068
3577
  if (tables[table.name] !== void 0) {
3069
3578
  throw new Error(`defineSchema("${name}"): two tables declare the name "${table.name}" \u2014 table names must be unique within a schema`);
3070
3579
  }
@@ -3108,6 +3617,48 @@ function defineSchema(name, input) {
3108
3617
  };
3109
3618
  }
3110
3619
  __name(defineSchema, "defineSchema");
3620
+ function qualifyRefTargets(table, where) {
3621
+ const qualify = /* @__PURE__ */ __name((holder, what) => {
3622
+ const target = holder[REF_TARGET];
3623
+ if (target === void 0) return;
3624
+ const targetSchema = declaringSchemaOf.get(target);
3625
+ if (targetSchema === void 0) {
3626
+ throw new Error(`${where}: ${what} points at table "${target.name}", which no defineSchema(...) has claimed yet \u2014 a cross-table reference cannot be written without its target's schema. Declare that table's schema first (one file per schema: db/public.ts, db/billing.ts \u2026, imported by this one).`);
3627
+ }
3628
+ const qualified = qualifiedTableKey(targetSchema, target.name);
3629
+ if (holder.refTable !== void 0) holder.refTable = qualified;
3630
+ if (holder.table !== void 0) holder.table = qualified;
3631
+ }, "qualify");
3632
+ for (const fk of table.foreignKeys ?? []) qualify(fk, `foreignKey("${fk.name}")`);
3633
+ for (const fz of table.freeze ?? []) qualify(fz, `freeze("${fz.name}")`);
3634
+ for (const g of table.guards ?? []) {
3635
+ if (g.exists !== void 0) {
3636
+ qualifyExistsNodes(g.exists, `guard("${g.name}").exists(...)`, where);
3637
+ }
3638
+ }
3639
+ }
3640
+ __name(qualifyRefTargets, "qualifyRefTargets");
3641
+ function qualifyExistsNodes(node, what, where) {
3642
+ if (node === null || typeof node !== "object") return;
3643
+ if (node.kind === "exists") {
3644
+ const target = node[REF_TARGET];
3645
+ if (target !== void 0) {
3646
+ const targetSchema = declaringSchemaOf.get(target);
3647
+ if (targetSchema === void 0) {
3648
+ throw new Error(`${where}: ${what} points at table "${target.name}", which no defineSchema(...) has claimed yet \u2014 a guard body is parsed at RUN TIME under a pinned search_path, so an unqualified name never resolves.`);
3649
+ }
3650
+ node.table = qualifiedTableKey(targetSchema, target.name);
3651
+ }
3652
+ }
3653
+ for (const v of Object.values(node)) {
3654
+ if (Array.isArray(v)) {
3655
+ for (const e of v) qualifyExistsNodes(e, what, where);
3656
+ } else if (v !== null && typeof v === "object") {
3657
+ qualifyExistsNodes(v, what, where);
3658
+ }
3659
+ }
3660
+ }
3661
+ __name(qualifyExistsNodes, "qualifyExistsNodes");
3111
3662
 
3112
3663
  // src/channels.ts
3113
3664
  var CHANNELS = /* @__PURE__ */ Symbol.for("palbase.backend.channels");
@@ -3584,13 +4135,10 @@ function installationRef(opts) {
3584
4135
  __name(installationRef, "installationRef");
3585
4136
 
3586
4137
  // src/db/raw.ts
3587
- function raw(name, up, opts) {
4138
+ function raw(name, up) {
3588
4139
  return {
3589
4140
  name,
3590
- up,
3591
- ...opts?.down != null ? {
3592
- down: opts.down
3593
- } : {}
4141
+ up
3594
4142
  };
3595
4143
  }
3596
4144
  __name(raw, "raw");
@@ -3791,7 +4339,7 @@ var DeadlockDetected = class extends Conflict {
3791
4339
  return typeof e === "object" && e !== null && e.name === "DeadlockDetected";
3792
4340
  }
3793
4341
  constructor(sqlstate = SQLSTATE.deadlockDetected, message, data) {
3794
- super(message ?? "deadlock: iki i\u015Flem birbirinin kilidini bekledi ve bu i\u015Flem iptal edildi \u2014 kilit s\u0131ras\u0131n\u0131 sabitlemek i\xE7in Database.$lockRows(tablo, id'ler) kullan\u0131n", "deadlock_detected", data);
4342
+ super(message ?? "deadlock: iki i\u015Flem birbirinin kilidini bekledi ve bu i\u015Flem iptal edildi \u2014 kilit s\u0131ras\u0131n\u0131 sabitleyin: id'ler elinizdeyse Database.$lockRows(tablo, id'ler), elinizde bir filtre varsa Database.$lockRowsWhere(tablo, filtre) kullan\u0131n", "deadlock_detected", data);
3795
4343
  this.name = "DeadlockDetected";
3796
4344
  this.sqlstate = sqlstate;
3797
4345
  }
@@ -3905,6 +4453,64 @@ function sqlFragment(strings, ...values) {
3905
4453
  }
3906
4454
  __name(sqlFragment, "sqlFragment");
3907
4455
 
4456
+ // src/db/repository.ts
4457
+ function defineRepository(table, opts) {
4458
+ const tenantColumn = opts.tenant;
4459
+ const rowKey = opts.key ?? "id";
4460
+ const scope = /* @__PURE__ */ __name((tenant) => ({
4461
+ [tenantColumn]: tenant
4462
+ }), "scope");
4463
+ const scopeRow = /* @__PURE__ */ __name((tenant, id) => ({
4464
+ [tenantColumn]: tenant,
4465
+ [rowKey]: id
4466
+ }), "scopeRow");
4467
+ const scopedPayload = /* @__PURE__ */ __name((values, tenant) => ({
4468
+ ...values,
4469
+ [tenantColumn]: tenant
4470
+ }), "scopedPayload");
4471
+ let Repository = class Repository {
4472
+ static {
4473
+ __name(this, "Repository");
4474
+ }
4475
+ list(tenant) {
4476
+ return table.findMany({
4477
+ where: scope(tenant)
4478
+ });
4479
+ }
4480
+ async find(tenant, id) {
4481
+ const rows = await table.findMany({
4482
+ where: scopeRow(tenant, id),
4483
+ limit: 1
4484
+ });
4485
+ return rows[0] ?? null;
4486
+ }
4487
+ insert(tenant, values) {
4488
+ return table.insert(scopedPayload(values, tenant));
4489
+ }
4490
+ async update(tenant, id, patch) {
4491
+ const row = await this.updateScoped(tenant, id, patch);
4492
+ if (row === null) {
4493
+ throw new NotFound(`${String(tenantColumn)}=${String(tenant)} kapsam\u0131nda ${rowKey}=${String(id)} bulunamad\u0131`);
4494
+ }
4495
+ return row;
4496
+ }
4497
+ async updateScoped(tenant, id, patch) {
4498
+ const rows = await table.updateMany({
4499
+ where: scopeRow(tenant, id),
4500
+ set: patch
4501
+ });
4502
+ return rows[0] ?? null;
4503
+ }
4504
+ async delete(tenant, id) {
4505
+ await table.deleteMany({
4506
+ where: scopeRow(tenant, id)
4507
+ });
4508
+ }
4509
+ };
4510
+ return Repository;
4511
+ }
4512
+ __name(defineRepository, "defineRepository");
4513
+
3908
4514
  // src/refusals.ts
3909
4515
  var DECLARATION_REFUSAL = /* @__PURE__ */ Symbol.for("palbase.backend.declarationRefusal");
3910
4516
  var DeclarationRefused = class extends Error {
@@ -5761,11 +6367,13 @@ var import_zod3 = require("zod");
5761
6367
  __setRuntime,
5762
6368
  assertNoOrphanEntryPoints,
5763
6369
  assertZeroArgConstructor,
6370
+ backfill,
5764
6371
  bigint,
5765
6372
  boolean,
5766
6373
  buildContainer,
5767
6374
  buildModuleClients,
5768
6375
  can,
6376
+ check,
5769
6377
  col,
5770
6378
  controllersOf,
5771
6379
  dec,
@@ -5774,10 +6382,13 @@ var import_zod3 = require("zod");
5774
6382
  defineDefaultAuth,
5775
6383
  defineError,
5776
6384
  defineMiddleware,
6385
+ defineRepository,
5777
6386
  defineSchema,
5778
6387
  defineTable,
5779
6388
  enumType,
5780
6389
  exprCtx,
6390
+ foreignKey,
6391
+ freeze,
5781
6392
  getErrorRegistry,
5782
6393
  getHookConfig,
5783
6394
  getHookManifest,
@@ -5787,6 +6398,7 @@ var import_zod3 = require("zod");
5787
6398
  getRoutes,
5788
6399
  getWebhookConfig,
5789
6400
  getWebhookManifest,
6401
+ guard,
5790
6402
  hooksOf,
5791
6403
  inc,
5792
6404
  increment,