@rebasepro/server-postgres 0.24.0 → 0.24.1-canary.g19596ef

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 (193) hide show
  1. package/README.md +16 -0
  2. package/dist/{BranchService-BRt78gfa.js → BranchService-UgKa416x.js} +14 -3
  3. package/dist/BranchService-UgKa416x.js.map +1 -0
  4. package/dist/PostgresBackendDriver.d.ts +104 -2
  5. package/dist/PostgresBootstrapper.d.ts +101 -2
  6. package/dist/auth/ensure-tables.d.ts +12 -0
  7. package/dist/auth/services.d.ts +6 -0
  8. package/dist/backup/backup-cli.d.ts +75 -2
  9. package/dist/backup/backup-service.d.ts +2 -2
  10. package/dist/backup/pg-binary.d.ts +5 -0
  11. package/dist/{backup-cli-DW5p9_zv.js → backup-cli-yiKd1GsC.js} +168 -75
  12. package/dist/backup-cli-yiKd1GsC.js.map +1 -0
  13. package/dist/{backup-service-EwcDVG-8.js → backup-service-AZWc2wvI.js} +99 -28
  14. package/dist/backup-service-AZWc2wvI.js.map +1 -0
  15. package/dist/boot-ddl-B9ZmBJ75.js +105 -0
  16. package/dist/boot-ddl-B9ZmBJ75.js.map +1 -0
  17. package/dist/cli-console-DGee3Zl7.js +225 -0
  18. package/dist/cli-console-DGee3Zl7.js.map +1 -0
  19. package/dist/cli-console.d.ts +39 -0
  20. package/dist/cli-errors-oVUuD8k0.js +2180 -0
  21. package/dist/cli-errors-oVUuD8k0.js.map +1 -0
  22. package/dist/cli-errors.d.ts +91 -9
  23. package/dist/{cli-flags-BglvpjHv.js → cli-flags-DnPHXjRH.js} +56 -6
  24. package/dist/cli-flags-DnPHXjRH.js.map +1 -0
  25. package/dist/cli-flags.d.ts +15 -0
  26. package/dist/cli-helpers.d.ts +76 -22
  27. package/dist/cli-output.d.ts +16 -28
  28. package/dist/{arg-Dni7MzLB.js → cli-prompt-egtNKkZl.js} +49 -3
  29. package/dist/cli-prompt-egtNKkZl.js.map +1 -0
  30. package/dist/cli-prompt.d.ts +19 -0
  31. package/dist/cli-push.d.ts +87 -0
  32. package/dist/cli-scratch-database.d.ts +33 -8
  33. package/dist/cli.js +764 -222
  34. package/dist/cli.js.map +1 -1
  35. package/dist/{column-plan-helpers-1-LQD0yI.js → column-plan-helpers-D_V4oaZh.js} +11 -5
  36. package/dist/{column-plan-helpers-1-LQD0yI.js.map → column-plan-helpers-D_V4oaZh.js.map} +1 -1
  37. package/dist/{databasePoolManager-Bj5FbeAs.js → connection-C938nR3E.js} +25 -113
  38. package/dist/connection-C938nR3E.js.map +1 -0
  39. package/dist/connection.d.ts +13 -2
  40. package/dist/databasePoolManager-DZgnBOgj.js +119 -0
  41. package/dist/databasePoolManager-DZgnBOgj.js.map +1 -0
  42. package/dist/{ensure-collection-tables-kkkHk8oo.js → deferred-index-build-BqmEHGaD.js} +485 -347
  43. package/dist/deferred-index-build-BqmEHGaD.js.map +1 -0
  44. package/dist/{doctor-C-sYWbmt.js → doctor-CUAAOFZm.js} +65 -54
  45. package/dist/doctor-CUAAOFZm.js.map +1 -0
  46. package/dist/ensure-collection-policies-Bb51V2OV.js +541 -0
  47. package/dist/ensure-collection-policies-Bb51V2OV.js.map +1 -0
  48. package/dist/ensure-collection-tables-egjGBjhv.js +532 -0
  49. package/dist/ensure-collection-tables-egjGBjhv.js.map +1 -0
  50. package/dist/{ensure-tables-BmI_tRxc.js → ensure-tables-DMPWhJ6R.js} +174 -78
  51. package/dist/ensure-tables-DMPWhJ6R.js.map +1 -0
  52. package/dist/enum-labels-kept-Msq5gMcb.js +56 -0
  53. package/dist/enum-labels-kept-Msq5gMcb.js.map +1 -0
  54. package/dist/generate-drizzle-schema-DrA-YgXv.js +57 -0
  55. package/dist/generate-drizzle-schema-DrA-YgXv.js.map +1 -0
  56. package/dist/{generate-drizzle-schema-logic-sSFDp6LR.js → generate-drizzle-schema-logic-BaT0H7Gq.js} +6 -6
  57. package/dist/generate-drizzle-schema-logic-BaT0H7Gq.js.map +1 -0
  58. package/dist/generate-files-OoRJLEdk.js +199 -0
  59. package/dist/generate-files-OoRJLEdk.js.map +1 -0
  60. package/dist/{generate-postgres-ddl-logic-BJsLaVNX.js → generate-postgres-ddl-logic-B4k9djAV.js} +4 -4
  61. package/dist/{generate-postgres-ddl-logic-BJsLaVNX.js.map → generate-postgres-ddl-logic-B4k9djAV.js.map} +1 -1
  62. package/dist/{generated-schema-staleness-Di9c4ZxN.js → generated-schema-staleness-BWN_PsMi.js} +2 -2
  63. package/dist/{generated-schema-staleness-Di9c4ZxN.js.map → generated-schema-staleness-BWN_PsMi.js.map} +1 -1
  64. package/dist/generated-sql.d.ts +8 -2
  65. package/dist/history/HistoryService.d.ts +64 -5
  66. package/dist/history/ensure-history-table.d.ts +63 -4
  67. package/dist/index.d.ts +1 -0
  68. package/dist/index.es.js +139 -19236
  69. package/dist/index.es.js.map +1 -1
  70. package/dist/{introspect-db-logic-kCETE8TY.js → introspect-db-logic-BCc1Jym2.js} +136 -9
  71. package/dist/introspect-db-logic-BCc1Jym2.js.map +1 -0
  72. package/dist/{introspect-db-queries-C_Q5VgQw.js → introspect-db-queries-BhhJg0b8.js} +48 -1
  73. package/dist/introspect-db-queries-BhhJg0b8.js.map +1 -0
  74. package/dist/open-rebase-CAjWpPjs.js +20895 -0
  75. package/dist/open-rebase-CAjWpPjs.js.map +1 -0
  76. package/dist/open-rebase.d.ts +94 -0
  77. package/dist/pg-error-utils-Bs_Ue4wn.js +395 -0
  78. package/dist/pg-error-utils-Bs_Ue4wn.js.map +1 -0
  79. package/dist/{plan-schema-DU9exq6C.js → plan-schema-CsNgK0qi.js} +152 -42
  80. package/dist/plan-schema-CsNgK0qi.js.map +1 -0
  81. package/dist/{policy-drift-B-J2hhm0.js → policy-drift-BKC50y2p.js} +32 -8
  82. package/dist/policy-drift-BKC50y2p.js.map +1 -0
  83. package/dist/push-index-prebuild-C_qhyoJa.js +45 -0
  84. package/dist/push-index-prebuild-C_qhyoJa.js.map +1 -0
  85. package/dist/push-legacy-renames-5g9h3HOa.js +34 -0
  86. package/dist/push-legacy-renames-5g9h3HOa.js.map +1 -0
  87. package/dist/{render-ddl-Ds2t_d9V.js → render-ddl-CrfEe__B.js} +15 -10
  88. package/dist/render-ddl-CrfEe__B.js.map +1 -0
  89. package/dist/{rls-bootstrap-sql-_KNnjanK.js → rls-bootstrap-sql-Bd-hJk08.js} +125 -1325
  90. package/dist/rls-bootstrap-sql-Bd-hJk08.js.map +1 -0
  91. package/dist/{rls-enforcement-CfXOJJaW.js → rls-enforcement-D7P4-ZYX.js} +130 -18
  92. package/dist/rls-enforcement-D7P4-ZYX.js.map +1 -0
  93. package/dist/{__vite-browser-external-BnuHet1e.js → rolldown-runtime-V7rFbMRe.js} +1 -8
  94. package/dist/schema/atlas-exclude.d.ts +42 -0
  95. package/dist/schema/auth-schema.d.ts +2 -2
  96. package/dist/schema/boot-ddl.d.ts +47 -0
  97. package/dist/schema/carved-out-migration.d.ts +5 -4
  98. package/dist/schema/collection-index.d.ts +45 -0
  99. package/dist/schema/column-plan-helpers.d.ts +5 -0
  100. package/dist/schema/deferred-index-build.d.ts +131 -0
  101. package/dist/schema/destructive-sql.d.ts +30 -1
  102. package/dist/schema/doctor-cli.js +110 -6
  103. package/dist/schema/doctor-cli.js.map +1 -1
  104. package/dist/schema/doctor-search-scale.d.ts +45 -0
  105. package/dist/schema/doctor.d.ts +20 -1
  106. package/dist/schema/ensure-collection-policies.d.ts +73 -35
  107. package/dist/schema/ensure-collection-tables.d.ts +56 -3
  108. package/dist/schema/enum-labels-kept.d.ts +26 -0
  109. package/dist/schema/generate-drizzle-schema.js +1 -1
  110. package/dist/schema/generate-files.d.ts +44 -0
  111. package/dist/schema/generate-next-step.d.ts +0 -13
  112. package/dist/schema/generate-postgres-ddl.js +10 -81
  113. package/dist/schema/generate-postgres-ddl.js.map +1 -1
  114. package/dist/schema/generated-schema-diff.d.ts +7 -0
  115. package/dist/schema/introspect-db-logic.d.ts +32 -0
  116. package/dist/schema/introspect-db-queries.d.ts +20 -1
  117. package/dist/schema/introspect-db-structure.d.ts +10 -0
  118. package/dist/schema/introspect-db.js +16 -20
  119. package/dist/schema/introspect-db.js.map +1 -1
  120. package/dist/schema/migrate-baseline.d.ts +33 -0
  121. package/dist/schema/migration-post-process.d.ts +34 -0
  122. package/dist/schema/plan/diff-plan.d.ts +126 -0
  123. package/dist/schema/plan/render-ddl.d.ts +2 -1
  124. package/dist/schema/plan/types.d.ts +8 -0
  125. package/dist/schema/push-index-prebuild.d.ts +25 -0
  126. package/dist/schema/push-legacy-renames.d.ts +20 -0
  127. package/dist/schema/rls-bootstrap-sql.d.ts +15 -0
  128. package/dist/schema/undeclarable-constraints.d.ts +59 -0
  129. package/dist/security/rls-enforcement.d.ts +32 -4
  130. package/dist/services/BranchService.d.ts +7 -0
  131. package/dist/services/FetchService.d.ts +58 -8
  132. package/dist/services/PersistService.d.ts +15 -0
  133. package/dist/services/RelationService.d.ts +88 -3
  134. package/dist/services/cdc/CdcListener.d.ts +17 -6
  135. package/dist/services/cdc/trigger-cdc.d.ts +151 -27
  136. package/dist/services/channel-bus/index.d.ts +1 -1
  137. package/dist/services/channel-history.d.ts +15 -4
  138. package/dist/services/collection-helpers.d.ts +15 -0
  139. package/dist/services/field-viewer.d.ts +15 -1
  140. package/dist/services/pg-notify-listener.d.ts +7 -0
  141. package/dist/services/realtimeService.d.ts +314 -14
  142. package/dist/services/row-pipeline.d.ts +16 -9
  143. package/dist/services/socket-liveness.d.ts +42 -2
  144. package/dist/services/sql-script.d.ts +207 -3
  145. package/dist/services/vector-iterative-scan.d.ts +40 -0
  146. package/dist/services/write-depth.d.ts +6 -0
  147. package/dist/services/write-transaction-scope.d.ts +9 -0
  148. package/dist/socket-opt-ins.d.ts +64 -0
  149. package/dist/source-DB6xJv3p.js +873 -0
  150. package/dist/source-DB6xJv3p.js.map +1 -0
  151. package/dist/tsx-loader.d.ts +14 -0
  152. package/dist/utils/connection-string.d.ts +31 -0
  153. package/dist/utils/drizzle-conditions.d.ts +21 -1
  154. package/dist/utils/error-cause.d.ts +7 -0
  155. package/dist/utils/pg-error-utils.d.ts +2 -4
  156. package/dist/utils/result-rows.d.ts +10 -0
  157. package/dist/{vector-index-CMJKb3C9.js → vector-index-BFhyEaiz.js} +2 -2
  158. package/dist/{vector-index-CMJKb3C9.js.map → vector-index-BFhyEaiz.js.map} +1 -1
  159. package/dist/websocket-DUiiwQd2.js +1632 -0
  160. package/dist/websocket-DUiiwQd2.js.map +1 -0
  161. package/dist/websocket.d.ts +13 -0
  162. package/package.json +7 -7
  163. package/dist/BranchService-BRt78gfa.js.map +0 -1
  164. package/dist/__vite-browser-external-BnuHet1e.js.map +0 -1
  165. package/dist/arg-Dni7MzLB.js.map +0 -1
  166. package/dist/backup-cli-DW5p9_zv.js.map +0 -1
  167. package/dist/backup-service-EwcDVG-8.js.map +0 -1
  168. package/dist/cli-errors-DsA-K9uP.js +0 -893
  169. package/dist/cli-errors-DsA-K9uP.js.map +0 -1
  170. package/dist/cli-flags-BglvpjHv.js.map +0 -1
  171. package/dist/cli-output-CNdMql-L.js +0 -47
  172. package/dist/cli-output-CNdMql-L.js.map +0 -1
  173. package/dist/collection-index-DdnsxiJ_.js +0 -286
  174. package/dist/collection-index-DdnsxiJ_.js.map +0 -1
  175. package/dist/databasePoolManager-Bj5FbeAs.js.map +0 -1
  176. package/dist/doctor-C-sYWbmt.js.map +0 -1
  177. package/dist/ensure-collection-policies-B1ureSIV.js +0 -160
  178. package/dist/ensure-collection-policies-B1ureSIV.js.map +0 -1
  179. package/dist/ensure-collection-tables-kkkHk8oo.js.map +0 -1
  180. package/dist/ensure-tables-BmI_tRxc.js.map +0 -1
  181. package/dist/generate-drizzle-schema-93M0lUxK.js +0 -123
  182. package/dist/generate-drizzle-schema-93M0lUxK.js.map +0 -1
  183. package/dist/generate-drizzle-schema-logic-sSFDp6LR.js.map +0 -1
  184. package/dist/introspect-db-logic-kCETE8TY.js.map +0 -1
  185. package/dist/introspect-db-queries-C_Q5VgQw.js.map +0 -1
  186. package/dist/plan-schema-DU9exq6C.js.map +0 -1
  187. package/dist/policy-drift-B-J2hhm0.js.map +0 -1
  188. package/dist/render-ddl-Ds2t_d9V.js.map +0 -1
  189. package/dist/rls-bootstrap-sql-_KNnjanK.js.map +0 -1
  190. package/dist/rls-enforcement-CfXOJJaW.js.map +0 -1
  191. package/dist/source-Br7L7GOI.js +0 -410
  192. package/dist/source-Br7L7GOI.js.map +0 -1
  193. package/dist/utils/sql-redaction.d.ts +0 -21
@@ -1,7 +1,9 @@
1
1
  import { createRequire as __createRequire } from "module";
2
2
  __createRequire(import.meta.url);
3
3
  import { a as buildSearchColumnSpec, u as searchColumnStamps } from "./search-column-BM-GV6vH.js";
4
+ import { p as foreignKeyIndexSpec } from "./source-DB6xJv3p.js";
4
5
  import { DEFAULT_SEARCH_COLUMN, DEFAULT_SEARCH_LANGUAGE } from "@rebasepro/types";
6
+ import { getGeneratedPolicyNames, policyToSecurityRule } from "@rebasepro/common";
5
7
  import { firstFreeKey, toPostgresIdentifier, toWireKey } from "@rebasepro/utils";
6
8
  //#region src/schema/introspect-db-naming.ts
7
9
  /**
@@ -276,8 +278,8 @@ function stringStorage(col) {
276
278
  case "character varying":
277
279
  if (typeof col.character_maximum_length === "number" && col.character_maximum_length > 0) return { keys: [`columnType: ${literal("varchar")}`] };
278
280
  return {
279
- keys: [`columnType: ${literal("varchar")}`],
280
- note: `"${col.column_name}" is a varchar with no length; the planner always writes one (255 unless \`validation.max\` says otherwise), so a push would plan a narrowing type change.`
281
+ keys: [],
282
+ note: `"${col.column_name}" is a varchar with no length, declared as text: a push would plan converting it to text, which keeps every value.`
281
283
  };
282
284
  case "character": return { keys: [`columnType: ${literal("char")}`] };
283
285
  case "uuid": return { keys: [`columnType: ${literal("uuid")}`] };
@@ -708,6 +710,41 @@ function classifyJunction(table, context) {
708
710
  };
709
711
  }
710
712
  /**
713
+ * A policy expression that reads who is asking: the `app.uid` setting every
714
+ * request runs with, `rebase.uid()` (or the retired `auth.uid()`), the JWT,
715
+ * or the session's own role.
716
+ */
717
+ var CALLER_IDENTITY = /current_setting\(\s*'app\.(uid|user_id|jwt)'|\b(rebase|auth)\.(uid|jwt)\s*\(|\bcurrent_user\b|\bsession_user\b/i;
718
+ /**
719
+ * The tables whose row-level security hands each caller rows of their own,
720
+ * with the policies that do it.
721
+ *
722
+ * Structure in the sense this module means: a policy is a rule the database
723
+ * enforces, and one that compares a row to the caller's identity says the rows
724
+ * belong to users. That is the app's data. A young database holds three
725
+ * customers and one order; by row count the customers are "a 3-row code list"
726
+ * and the orders a child reached only through them, and both go missing from
727
+ * the navigation of the app that owns them.
728
+ */
729
+ function callerScopedTables(metadata) {
730
+ const scoped = /* @__PURE__ */ new Map();
731
+ for (const policy of metadata.policies ?? []) {
732
+ if (!CALLER_IDENTITY.test(policy.qual ?? "") && !CALLER_IDENTITY.test(policy.with_check ?? "")) continue;
733
+ scoped.set(policy.table_name, [...scoped.get(policy.table_name) ?? [], policy.policy_name]);
734
+ }
735
+ return scoped;
736
+ }
737
+ /** An entity because its policies give each user their own rows — see {@link callerScopedTables}. */
738
+ function classifyCallerScoped(table, perCaller) {
739
+ const policies = perCaller.get(table);
740
+ if (!policies) return null;
741
+ return {
742
+ table,
743
+ role: "entity",
744
+ reason: `its row-level security gives each user their own rows (${policies.join(", ")}), so they are the app's data`
745
+ };
746
+ }
747
+ /**
711
748
  * A small, referenced, self-contained code list.
712
749
  *
713
750
  * Requires: something references it, it references nothing, it carries few and
@@ -833,8 +870,9 @@ function pickOwner(meta, constraints) {
833
870
  function classifyTables(metadata, tables) {
834
871
  const context = buildContext(metadata, tables);
835
872
  const result = /* @__PURE__ */ new Map();
873
+ const perCaller = callerScopedTables(metadata);
836
874
  for (const table of tables.keys()) {
837
- const classification = classifyJunction(table, context) ?? classifyLookup(table, context) ?? classifyOwnedChild(table, context) ?? {
875
+ const classification = classifyJunction(table, context) ?? classifyCallerScoped(table, perCaller) ?? classifyLookup(table, context) ?? classifyOwnedChild(table, context) ?? {
838
876
  table,
839
877
  role: "entity",
840
878
  reason: "no structural evidence that it is a join table, a code list, or owned by another table"
@@ -913,6 +951,32 @@ function deriveKanbanProperty(facts) {
913
951
  return fact.column.column_name;
914
952
  }
915
953
  }
954
+ /** Names a string column goes by when it holds a board's card order (a fractional-indexing key). */
955
+ var ORDER_KEY_NAMES = /* @__PURE__ */ new Set([
956
+ "__order",
957
+ "order_key",
958
+ "sort_key",
959
+ "rank",
960
+ "lexorank",
961
+ "position_key"
962
+ ]);
963
+ /**
964
+ * The string column that can hold a board's card order, or nothing.
965
+ *
966
+ * A board without `orderProperty` cannot keep a card's position within a
967
+ * column, and the config validator says so on every generate — so a board is
968
+ * emitted only when the table already has somewhere to store the order. A
969
+ * legacy table almost never does; asking its owner to add a hidden column
970
+ * just to satisfy generated output was the wrong way round.
971
+ */
972
+ function deriveKanbanOrderProperty(facts) {
973
+ for (const fact of facts.values()) {
974
+ if (fact.isPk || fact.isFk || fact.isGenerated || fact.enumValues) continue;
975
+ if (fact.propType !== "string") continue;
976
+ if (!/^(text|character varying)$/.test(fact.column.data_type)) continue;
977
+ if (ORDER_KEY_NAMES.has(fact.column.column_name.toLowerCase())) return fact.column.column_name;
978
+ }
979
+ }
916
980
  /**
917
981
  * The column a list should be sorted by, newest first.
918
982
  *
@@ -1302,6 +1366,57 @@ function sortPropertiesOrder(entries) {
1302
1366
  var ADMIN_TYPES_PACKAGE = "@rebasepro/cms-types";
1303
1367
  var COMMON_PACKAGE = "@rebasepro/common";
1304
1368
  var TYPES_PACKAGE = "@rebasepro/types";
1369
+ /**
1370
+ * The table's row-level security policies, as the collection's `securityRules`.
1371
+ *
1372
+ * A table's policies are its access model, and a collection that describes
1373
+ * none leaves every one of them an "orphan": `doctor` fails on it, every push
1374
+ * warns about it, and a push drops one whose name has the shape an unnamed
1375
+ * rule compiles to (`<table>_<op>_<hash>`, which is every rule on a database
1376
+ * Rebase built). Described here, each is recreated from its rule as it was.
1377
+ *
1378
+ * Read back with `policyToSecurityRule` — what Studio's "Import from table"
1379
+ * and "Import to codebase" use — so the `TO` list, the mode and both clauses
1380
+ * come back as they are. The framework's own defaults
1381
+ * (`<table>_default_admin_read` and friends) are left out: every collection is
1382
+ * given them anyway, and described twice they would be written twice.
1383
+ */
1384
+ function renderSecurityRules(tableName, policies) {
1385
+ const injected = getGeneratedPolicyNames({
1386
+ name: tableName,
1387
+ slug: tableName,
1388
+ table: tableName,
1389
+ properties: {}
1390
+ });
1391
+ const own = policies.filter((policy) => policy.table_name === tableName && !injected.has(policy.policy_name));
1392
+ if (own.length === 0) return "";
1393
+ return `
1394
+ // The table's row-level security policies, read from the database. \`rebase db push\`
1395
+ // recreates each from its rule, so a policy is changed here from now on.\n securityRules: [${own.map((policy) => {
1396
+ const rule = policyToSecurityRule({
1397
+ policyname: policy.policy_name,
1398
+ cmd: toPolicyCommand(policy.cmd),
1399
+ permissive: policy.permissive.toUpperCase() === "RESTRICTIVE" ? "RESTRICTIVE" : "PERMISSIVE",
1400
+ roles: policy.roles,
1401
+ qual: policy.qual,
1402
+ with_check: policy.with_check
1403
+ });
1404
+ return `\n {\n${Object.entries(rule).map(([key, value]) => {
1405
+ return ` ${key}: ${Array.isArray(value) ? `[${value.map((item) => quote(String(item))).join(", ")}]` : quote(String(value))}`;
1406
+ }).join(",\n")}\n },`;
1407
+ }).join("")}\n ],`;
1408
+ }
1409
+ /** `pg_policies.cmd`, narrowed to the commands a policy can have. */
1410
+ function toPolicyCommand(cmd) {
1411
+ switch (cmd.toUpperCase()) {
1412
+ case "SELECT": return "SELECT";
1413
+ case "INSERT": return "INSERT";
1414
+ case "UPDATE": return "UPDATE";
1415
+ case "DELETE": return "DELETE";
1416
+ case "ALL": return "ALL";
1417
+ default: return;
1418
+ }
1419
+ }
1305
1420
  /** Adds entries to a property's `validation` block, creating it if absent. */
1306
1421
  function withValidation(extra, entries) {
1307
1422
  if (entries.length === 0) return extra;
@@ -1565,7 +1680,11 @@ function generateCollectionFile(tableName, meta, allFks, joinTables, tablesMap,
1565
1680
  if (!hasGeneratedKey(extra, "autoValue") && !hasGeneratedKey(extra, "defaultValue")) {
1566
1681
  const fallback = literalDefaultOf(col, finalPropType);
1567
1682
  if (fallback && "source" in fallback) extra += `\n defaultValue: ${fallback.source},`;
1568
- else if (fallback) fileNotes.push(fallback.note);
1683
+ else if (fallback) {
1684
+ const expression = col.column_default?.trim();
1685
+ if (col.is_nullable === "NO" && expression) extra += `\n databaseDefault: ${JSON.stringify(`sql\`${expression}\``)},`;
1686
+ else fileNotes.push(fallback.note);
1687
+ }
1569
1688
  }
1570
1689
  }
1571
1690
  const declaredValidation = [];
@@ -1666,6 +1785,9 @@ function generateCollectionFile(tableName, meta, allFks, joinTables, tablesMap,
1666
1785
  const onDelete = onDeleteOf(fk, required);
1667
1786
  const requiredLine = required ? "\n validation: { required: true }," : "";
1668
1787
  const onDeleteLine = onDelete ? `,\n onDelete: ${quote(onDelete)}` : "";
1788
+ const derivedIndex = foreignKeyIndexSpec(context.metadata?.schema ?? "public", tableName, fk.column_name);
1789
+ const indexLine = context.metadata?.indexes !== void 0 && meta.pks[0] !== fk.column_name && !context.metadata.indexes.some((i) => i.table_name === tableName && i.index_name === derivedIndex.indexName) ? `,\n // The database has no index of Rebase's on ${commentText(fk.column_name)}. Delete this line to have one built.
1790
+ index: false` : "";
1669
1791
  propertyBlocks.set(relName, `
1670
1792
  ${propKey(relName)}: {
1671
1793
  name: ${quote(relHumanName)},
@@ -1674,7 +1796,7 @@ function generateCollectionFile(tableName, meta, allFks, joinTables, tablesMap,
1674
1796
  relation: {
1675
1797
  kind: "belongsTo",
1676
1798
  target: ${relationTarget(targetCollectionCamel)},
1677
- localKey: ${quote(fk.column_name)}${onDeleteLine}
1799
+ localKey: ${quote(fk.column_name)}${onDeleteLine}${indexLine}
1678
1800
  }
1679
1801
  },`);
1680
1802
  }
@@ -1769,6 +1891,7 @@ function generateCollectionFile(tableName, meta, allFks, joinTables, tablesMap,
1769
1891
  if (search.note) fileNotes.push(search.note);
1770
1892
  const searchBlock = search.block ?? "";
1771
1893
  const relationsBlock = relationsOutput ? `\n relations: [${relationsOutput}\n ],` : "";
1894
+ const securityRulesBlock = renderSecurityRules(tableName, context.metadata?.policies ?? []);
1772
1895
  const sortedPropertiesOrder = sortPropertiesOrder(orderEntries);
1773
1896
  for (const key of sortedPropertiesOrder) propsOutput += propertyBlocks.get(key) || "";
1774
1897
  const adminEntries = [`icon: ${quote(icon)}`];
@@ -1781,7 +1904,11 @@ function generateCollectionFile(tableName, meta, allFks, joinTables, tablesMap,
1781
1904
  const titleProperty = deriveTitleProperty(derivedFacts);
1782
1905
  if (titleProperty) adminEntries.push(`display: { title: ${quote(asProperty(titleProperty))} }`);
1783
1906
  const kanbanProperty = deriveKanbanProperty(derivedFacts);
1784
- if (kanbanProperty) adminEntries.push(`kanban: {\n columnProperty: ${quote(asProperty(kanbanProperty))}\n }`);
1907
+ const orderProperty = kanbanProperty ? deriveKanbanOrderProperty(derivedFacts) : void 0;
1908
+ if (kanbanProperty && orderProperty) {
1909
+ adminEntries.push(`kanban: {\n columnProperty: ${quote(asProperty(kanbanProperty))}\n }`);
1910
+ adminEntries.push(`orderProperty: ${quote(asProperty(orderProperty))}`);
1911
+ } else if (kanbanProperty) adminEntries.push(`// A board over ${quote(asProperty(kanbanProperty))} fits this table, but a board needs a string column to keep each card's order in. Add one, then declare kanban: { columnProperty: ${quote(asProperty(kanbanProperty))} } and orderProperty: "<that property>".`);
1785
1912
  const sort = deriveSort(derivedFacts);
1786
1913
  if (sort) adminEntries.push(`sort: [${quote(asProperty(sort[0]))}, "desc"]`);
1787
1914
  }
@@ -1793,7 +1920,7 @@ function generateCollectionFile(tableName, meta, allFks, joinTables, tablesMap,
1793
1920
  const descriptionBlock = tableComment ? `\n description: ${quote(tableComment)},` : "";
1794
1921
  const pgSchema = context.metadata?.schema;
1795
1922
  const schemaBlock = pgSchema && pgSchema !== "public" ? `\n schema: ${quote(pgSchema)},` : "";
1796
- const classificationNote = classification && classification.role !== "entity" ? `\n// Introspected as a ${commentText(classification.role)}: ${commentText(classification.reason)}.\n` : "";
1923
+ const classificationNote = classification && classification.role !== "entity" ? `\n// Introspected as ${/^[aeiou]/.test(classification.role) ? "an" : "a"} ${commentText(classification.role)}: ${commentText(classification.reason)}.\n` : "";
1797
1924
  for (const note of fileNotes) context.notes?.push(`${tableName}: ${note}`);
1798
1925
  const storageNote = fileNotes.length > 0 ? `\n// ⚠ Not reproduced exactly by \`rebase db push\` — read before pushing:\n${fileNotes.map((n) => `// - ${commentText(n)}`).join("\n")}\n` : "";
1799
1926
  const collectionVarName = toCollectionVarName(tableName);
@@ -1807,7 +1934,7 @@ ${open}
1807
1934
  slug: ${quote(tableName)},
1808
1935
  table: ${quote(tableName)},${schemaBlock}${descriptionBlock}${searchBlock}
1809
1936
  properties: {${propsOutput}
1810
- },${relationsBlock}${adminBlock}
1937
+ },${relationsBlock}${securityRulesBlock}${adminBlock}
1811
1938
  ${close}
1812
1939
 
1813
1940
  export default ${collectionVarName};
@@ -1868,4 +1995,4 @@ function safeHostFromUrl(url) {
1868
1995
  //#endregion
1869
1996
  export { compositeKeySkipReason as a, getIconForTable as c, safeHostFromUrl as d, singularize as f, humanize as g, mapPgType as h, buildTablesMap as i, identifyJoinTables as l, lookupCandidates as m, COMMON_PACKAGE as n, generateCollectionFile as o, classifyTables as p, buildEnumMap as r, generateIndexContent as s, ADMIN_TYPES_PACKAGE as t, mergeIndexContent as u };
1870
1997
 
1871
- //# sourceMappingURL=introspect-db-logic-kCETE8TY.js.map
1998
+ //# sourceMappingURL=introspect-db-logic-BCc1Jym2.js.map