@rebasepro/server-postgresql 0.5.0 → 0.6.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 (165) hide show
  1. package/dist/{server-postgresql/src/PostgresAdapter.d.ts → PostgresAdapter.d.ts} +1 -1
  2. package/dist/{server-postgresql/src/PostgresBackendDriver.d.ts → PostgresBackendDriver.d.ts} +2 -2
  3. package/dist/{server-postgresql/src/PostgresBootstrapper.d.ts → PostgresBootstrapper.d.ts} +11 -1
  4. package/dist/{server-postgresql/src/collections → collections}/PostgresCollectionRegistry.d.ts +4 -0
  5. package/dist/index.es.js +10168 -11145
  6. package/dist/index.es.js.map +1 -1
  7. package/dist/index.umd.js +10735 -11429
  8. package/dist/index.umd.js.map +1 -1
  9. package/dist/{server-postgresql/src/services → services}/EntityPersistService.d.ts +0 -14
  10. package/dist/utils/pg-error-utils.d.ts +55 -0
  11. package/package.json +24 -21
  12. package/src/PostgresAdapter.ts +9 -10
  13. package/src/PostgresBackendDriver.ts +134 -121
  14. package/src/PostgresBootstrapper.ts +86 -13
  15. package/src/auth/ensure-tables.ts +28 -5
  16. package/src/auth/services.ts +28 -18
  17. package/src/cli.ts +99 -96
  18. package/src/collections/PostgresCollectionRegistry.ts +7 -0
  19. package/src/connection.ts +11 -6
  20. package/src/data-transformer.ts +16 -14
  21. package/src/databasePoolManager.ts +3 -2
  22. package/src/history/HistoryService.ts +3 -2
  23. package/src/history/ensure-history-table.ts +5 -4
  24. package/src/schema/auth-schema.ts +1 -2
  25. package/src/schema/doctor-cli.ts +2 -1
  26. package/src/schema/doctor.ts +40 -37
  27. package/src/schema/generate-drizzle-schema-logic.ts +56 -18
  28. package/src/schema/generate-drizzle-schema.ts +11 -11
  29. package/src/schema/introspect-db-inference.ts +25 -25
  30. package/src/schema/introspect-db-logic.ts +38 -38
  31. package/src/schema/introspect-db.ts +28 -27
  32. package/src/services/BranchService.ts +14 -0
  33. package/src/services/EntityFetchService.ts +28 -25
  34. package/src/services/EntityPersistService.ts +11 -141
  35. package/src/services/RelationService.ts +57 -37
  36. package/src/services/entity-helpers.ts +6 -2
  37. package/src/services/realtimeService.ts +45 -32
  38. package/src/utils/drizzle-conditions.ts +31 -15
  39. package/src/utils/pg-error-utils.ts +211 -0
  40. package/src/websocket.ts +15 -12
  41. package/test/auth-services.test.ts +36 -19
  42. package/test/batch-many-to-many-regression.test.ts +119 -39
  43. package/test/data-transformer-hardening.test.ts +67 -33
  44. package/test/data-transformer.test.ts +4 -2
  45. package/test/doctor.test.ts +10 -5
  46. package/test/drizzle-conditions.test.ts +59 -6
  47. package/test/generate-drizzle-schema.test.ts +65 -40
  48. package/test/introspect-db-generation.test.ts +179 -81
  49. package/test/introspect-db-utils.test.ts +92 -37
  50. package/test/mocks/chalk.cjs +7 -0
  51. package/test/pg-error-utils.test.ts +221 -0
  52. package/test/postgresDataDriver.test.ts +14 -5
  53. package/test/property-ordering.test.ts +126 -79
  54. package/test/realtimeService.test.ts +6 -2
  55. package/test/relation-pipeline-gaps.test.ts +84 -36
  56. package/test/relations.test.ts +247 -0
  57. package/test/unmapped-tables-safety.test.ts +14 -6
  58. package/test/websocket.test.ts +1 -1
  59. package/tsconfig.json +5 -0
  60. package/tsconfig.prod.json +3 -0
  61. package/vite.config.ts +5 -5
  62. package/dist/common/src/collections/CollectionRegistry.d.ts +0 -56
  63. package/dist/common/src/collections/default-collections.d.ts +0 -9
  64. package/dist/common/src/collections/index.d.ts +0 -2
  65. package/dist/common/src/data/buildRebaseData.d.ts +0 -14
  66. package/dist/common/src/data/query_builder.d.ts +0 -55
  67. package/dist/common/src/index.d.ts +0 -4
  68. package/dist/common/src/util/builders.d.ts +0 -57
  69. package/dist/common/src/util/callbacks.d.ts +0 -6
  70. package/dist/common/src/util/collections.d.ts +0 -11
  71. package/dist/common/src/util/common.d.ts +0 -2
  72. package/dist/common/src/util/conditions.d.ts +0 -26
  73. package/dist/common/src/util/entities.d.ts +0 -58
  74. package/dist/common/src/util/enums.d.ts +0 -3
  75. package/dist/common/src/util/index.d.ts +0 -16
  76. package/dist/common/src/util/navigation_from_path.d.ts +0 -34
  77. package/dist/common/src/util/navigation_utils.d.ts +0 -20
  78. package/dist/common/src/util/parent_references_from_path.d.ts +0 -6
  79. package/dist/common/src/util/paths.d.ts +0 -14
  80. package/dist/common/src/util/permissions.d.ts +0 -14
  81. package/dist/common/src/util/references.d.ts +0 -2
  82. package/dist/common/src/util/relations.d.ts +0 -22
  83. package/dist/common/src/util/resolutions.d.ts +0 -72
  84. package/dist/common/src/util/storage.d.ts +0 -24
  85. package/dist/types/src/controllers/analytics_controller.d.ts +0 -7
  86. package/dist/types/src/controllers/auth.d.ts +0 -104
  87. package/dist/types/src/controllers/client.d.ts +0 -168
  88. package/dist/types/src/controllers/collection_registry.d.ts +0 -46
  89. package/dist/types/src/controllers/customization_controller.d.ts +0 -60
  90. package/dist/types/src/controllers/data.d.ts +0 -207
  91. package/dist/types/src/controllers/data_driver.d.ts +0 -218
  92. package/dist/types/src/controllers/database_admin.d.ts +0 -11
  93. package/dist/types/src/controllers/dialogs_controller.d.ts +0 -36
  94. package/dist/types/src/controllers/effective_role.d.ts +0 -4
  95. package/dist/types/src/controllers/email.d.ts +0 -36
  96. package/dist/types/src/controllers/index.d.ts +0 -18
  97. package/dist/types/src/controllers/local_config_persistence.d.ts +0 -20
  98. package/dist/types/src/controllers/navigation.d.ts +0 -225
  99. package/dist/types/src/controllers/registry.d.ts +0 -63
  100. package/dist/types/src/controllers/side_dialogs_controller.d.ts +0 -67
  101. package/dist/types/src/controllers/side_entity_controller.d.ts +0 -97
  102. package/dist/types/src/controllers/snackbar.d.ts +0 -24
  103. package/dist/types/src/controllers/storage.d.ts +0 -171
  104. package/dist/types/src/index.d.ts +0 -4
  105. package/dist/types/src/rebase_context.d.ts +0 -122
  106. package/dist/types/src/types/auth_adapter.d.ts +0 -301
  107. package/dist/types/src/types/backend.d.ts +0 -571
  108. package/dist/types/src/types/backend_hooks.d.ts +0 -172
  109. package/dist/types/src/types/builders.d.ts +0 -15
  110. package/dist/types/src/types/chips.d.ts +0 -5
  111. package/dist/types/src/types/collections.d.ts +0 -961
  112. package/dist/types/src/types/component_ref.d.ts +0 -47
  113. package/dist/types/src/types/cron.d.ts +0 -102
  114. package/dist/types/src/types/data_source.d.ts +0 -64
  115. package/dist/types/src/types/database_adapter.d.ts +0 -94
  116. package/dist/types/src/types/entities.d.ts +0 -145
  117. package/dist/types/src/types/entity_actions.d.ts +0 -104
  118. package/dist/types/src/types/entity_callbacks.d.ts +0 -173
  119. package/dist/types/src/types/entity_link_builder.d.ts +0 -7
  120. package/dist/types/src/types/entity_overrides.d.ts +0 -10
  121. package/dist/types/src/types/entity_views.d.ts +0 -87
  122. package/dist/types/src/types/export_import.d.ts +0 -21
  123. package/dist/types/src/types/formex.d.ts +0 -40
  124. package/dist/types/src/types/index.d.ts +0 -28
  125. package/dist/types/src/types/locales.d.ts +0 -4
  126. package/dist/types/src/types/modify_collections.d.ts +0 -5
  127. package/dist/types/src/types/plugins.d.ts +0 -282
  128. package/dist/types/src/types/properties.d.ts +0 -1173
  129. package/dist/types/src/types/property_config.d.ts +0 -74
  130. package/dist/types/src/types/relations.d.ts +0 -336
  131. package/dist/types/src/types/slots.d.ts +0 -262
  132. package/dist/types/src/types/translations.d.ts +0 -900
  133. package/dist/types/src/types/user_management_delegate.d.ts +0 -86
  134. package/dist/types/src/types/websockets.d.ts +0 -78
  135. package/dist/types/src/users/index.d.ts +0 -1
  136. package/dist/types/src/users/user.d.ts +0 -50
  137. /package/dist/{server-postgresql/src/auth → auth}/ensure-tables.d.ts +0 -0
  138. /package/dist/{server-postgresql/src/auth → auth}/services.d.ts +0 -0
  139. /package/dist/{server-postgresql/src/cli.d.ts → cli.d.ts} +0 -0
  140. /package/dist/{server-postgresql/src/connection.d.ts → connection.d.ts} +0 -0
  141. /package/dist/{server-postgresql/src/data-transformer.d.ts → data-transformer.d.ts} +0 -0
  142. /package/dist/{server-postgresql/src/databasePoolManager.d.ts → databasePoolManager.d.ts} +0 -0
  143. /package/dist/{server-postgresql/src/history → history}/HistoryService.d.ts +0 -0
  144. /package/dist/{server-postgresql/src/history → history}/ensure-history-table.d.ts +0 -0
  145. /package/dist/{server-postgresql/src/index.d.ts → index.d.ts} +0 -0
  146. /package/dist/{server-postgresql/src/interfaces.d.ts → interfaces.d.ts} +0 -0
  147. /package/dist/{server-postgresql/src/schema → schema}/auth-schema.d.ts +0 -0
  148. /package/dist/{server-postgresql/src/schema → schema}/doctor-cli.d.ts +0 -0
  149. /package/dist/{server-postgresql/src/schema → schema}/doctor.d.ts +0 -0
  150. /package/dist/{server-postgresql/src/schema → schema}/generate-drizzle-schema-logic.d.ts +0 -0
  151. /package/dist/{server-postgresql/src/schema → schema}/generate-drizzle-schema.d.ts +0 -0
  152. /package/dist/{server-postgresql/src/schema → schema}/introspect-db-inference.d.ts +0 -0
  153. /package/dist/{server-postgresql/src/schema → schema}/introspect-db-logic.d.ts +0 -0
  154. /package/dist/{server-postgresql/src/schema → schema}/introspect-db.d.ts +0 -0
  155. /package/dist/{server-postgresql/src/schema → schema}/test-schema.d.ts +0 -0
  156. /package/dist/{server-postgresql/src/services → services}/BranchService.d.ts +0 -0
  157. /package/dist/{server-postgresql/src/services → services}/EntityFetchService.d.ts +0 -0
  158. /package/dist/{server-postgresql/src/services → services}/RelationService.d.ts +0 -0
  159. /package/dist/{server-postgresql/src/services → services}/entity-helpers.d.ts +0 -0
  160. /package/dist/{server-postgresql/src/services → services}/entityService.d.ts +0 -0
  161. /package/dist/{server-postgresql/src/services → services}/index.d.ts +0 -0
  162. /package/dist/{server-postgresql/src/services → services}/realtimeService.d.ts +0 -0
  163. /package/dist/{server-postgresql/src/types.d.ts → types.d.ts} +0 -0
  164. /package/dist/{server-postgresql/src/utils → utils}/drizzle-conditions.d.ts +0 -0
  165. /package/dist/{server-postgresql/src/websocket.d.ts → websocket.d.ts} +0 -0
@@ -1,22 +1,33 @@
1
1
  import {
2
2
  generateCollectionFile, buildTablesMap, buildEnumMap,
3
3
  identifyJoinTables, TableRow, TableColumn, PrimaryKeyRow,
4
- ForeignKeyRow, EnumValue, TableMeta,
4
+ ForeignKeyRow, EnumValue, TableMeta
5
5
  } from "../src/schema/introspect-db-logic";
6
6
 
7
7
  // ── Helpers ───────────────────────────────────────────────────────────
8
8
 
9
9
  const mkCol = (table: string, col: string, opts: Partial<TableColumn> = {}): TableColumn => ({
10
- table_name: table, column_name: col, data_type: "character varying",
11
- udt_name: "varchar", is_nullable: "YES", column_default: null, ...opts,
10
+ table_name: table,
11
+ column_name: col,
12
+ data_type: "character varying",
13
+ udt_name: "varchar",
14
+ is_nullable: "YES",
15
+ column_default: null,
16
+ ...opts
12
17
  });
13
18
 
14
19
  const mkFk = (table: string, col: string, fTable: string, fCol = "id"): ForeignKeyRow => ({
15
- table_name: table, column_name: col, foreign_table_name: fTable, foreign_column_name: fCol,
20
+ table_name: table,
21
+ column_name: col,
22
+ foreign_table_name: fTable,
23
+ foreign_column_name: fCol
16
24
  });
17
25
 
18
26
  function makeSimpleTable(name: string, columns: TableColumn[], pks: string[] = ["id"], fks: ForeignKeyRow[] = []): TableMeta {
19
- return { name, columns, pks, fks };
27
+ return { name,
28
+ columns,
29
+ pks,
30
+ fks };
20
31
  }
21
32
 
22
33
  // ═══════════════════════════════════════════════════════════════════════
@@ -26,8 +37,10 @@ describe("generateCollectionFile", () => {
26
37
  describe("basic property generation", () => {
27
38
  it("generates a simple collection with slug, name, singularName and table", () => {
28
39
  const meta = makeSimpleTable("products", [
29
- mkCol("products", "id", { data_type: "uuid", udt_name: "uuid", is_nullable: "NO" }),
30
- mkCol("products", "name", { is_nullable: "NO" }),
40
+ mkCol("products", "id", { data_type: "uuid",
41
+ udt_name: "uuid",
42
+ is_nullable: "NO" }),
43
+ mkCol("products", "name", { is_nullable: "NO" })
31
44
  ]);
32
45
  const result = generateCollectionFile("products", meta, [], new Set(), new Map([["products", meta]]), new Map());
33
46
  expect(result).toContain('slug: "products"');
@@ -38,14 +51,20 @@ describe("generateCollectionFile", () => {
38
51
 
39
52
  it("generates correct property types from columns", () => {
40
53
  const meta = makeSimpleTable("items", [
41
- mkCol("items", "id", { data_type: "uuid", udt_name: "uuid", is_nullable: "NO" }),
42
- mkCol("items", "count", { data_type: "integer", udt_name: "int4" }),
43
- mkCol("items", "active", { data_type: "boolean", udt_name: "bool" }),
44
- mkCol("items", "created_at", { data_type: "timestamp", udt_name: "timestamp" }),
45
- mkCol("items", "metadata", { data_type: "jsonb", udt_name: "jsonb" }),
54
+ mkCol("items", "id", { data_type: "uuid",
55
+ udt_name: "uuid",
56
+ is_nullable: "NO" }),
57
+ mkCol("items", "count", { data_type: "integer",
58
+ udt_name: "int4" }),
59
+ mkCol("items", "active", { data_type: "boolean",
60
+ udt_name: "bool" }),
61
+ mkCol("items", "created_at", { data_type: "timestamp",
62
+ udt_name: "timestamp" }),
63
+ mkCol("items", "metadata", { data_type: "jsonb",
64
+ udt_name: "jsonb" })
46
65
  ]);
47
66
  const result = generateCollectionFile("items", meta, [], new Set(), new Map([["items", meta]]), new Map());
48
- expect(result).toContain('type: "string"'); // id
67
+ expect(result).toContain('type: "string"'); // id
49
68
  expect(result).toContain('type: "number"');
50
69
  expect(result).toContain('type: "boolean"');
51
70
  expect(result).toContain('type: "date"');
@@ -55,9 +74,11 @@ describe("generateCollectionFile", () => {
55
74
  it("skips FK columns from properties (they become relations)", () => {
56
75
  const fks = [mkFk("posts", "author_id", "users")];
57
76
  const meta = makeSimpleTable("posts", [
58
- mkCol("posts", "id", { data_type: "uuid", udt_name: "uuid", is_nullable: "NO" }),
77
+ mkCol("posts", "id", { data_type: "uuid",
78
+ udt_name: "uuid",
79
+ is_nullable: "NO" }),
59
80
  mkCol("posts", "title", { is_nullable: "NO" }),
60
- mkCol("posts", "author_id", { is_nullable: "NO" }),
81
+ mkCol("posts", "author_id", { is_nullable: "NO" })
61
82
  ], ["id"], fks);
62
83
  const result = generateCollectionFile("posts", meta, fks, new Set(), new Map([["posts", meta]]), new Map());
63
84
  // author_id should NOT appear as a regular property
@@ -71,9 +92,11 @@ describe("generateCollectionFile", () => {
71
92
  it("includes relation property key, not FK column name", () => {
72
93
  const fks = [mkFk("posts", "author_id", "users")];
73
94
  const meta = makeSimpleTable("posts", [
74
- mkCol("posts", "id", { data_type: "uuid", udt_name: "uuid", is_nullable: "NO" }),
95
+ mkCol("posts", "id", { data_type: "uuid",
96
+ udt_name: "uuid",
97
+ is_nullable: "NO" }),
75
98
  mkCol("posts", "title"),
76
- mkCol("posts", "author_id"),
99
+ mkCol("posts", "author_id")
77
100
  ], ["id"], fks);
78
101
  const result = generateCollectionFile("posts", meta, fks, new Set(), new Map([["posts", meta]]), new Map());
79
102
  // propertiesOrder should contain "author" (the relation key), not "author_id" (the FK column)
@@ -89,7 +112,9 @@ describe("generateCollectionFile", () => {
89
112
  describe("ID detection", () => {
90
113
  it("marks uuid PK as isId uuid", () => {
91
114
  const meta = makeSimpleTable("users", [
92
- mkCol("users", "id", { data_type: "uuid", udt_name: "uuid", is_nullable: "NO" }),
115
+ mkCol("users", "id", { data_type: "uuid",
116
+ udt_name: "uuid",
117
+ is_nullable: "NO" })
93
118
  ]);
94
119
  const result = generateCollectionFile("users", meta, [], new Set(), new Map([["users", meta]]), new Map());
95
120
  expect(result).toContain('isId: "uuid"');
@@ -97,7 +122,10 @@ describe("generateCollectionFile", () => {
97
122
 
98
123
  it("marks integer PK as isId increment", () => {
99
124
  const meta = makeSimpleTable("counters", [
100
- mkCol("counters", "id", { data_type: "integer", udt_name: "int4", is_nullable: "NO", column_default: "nextval" }),
125
+ mkCol("counters", "id", { data_type: "integer",
126
+ udt_name: "int4",
127
+ is_nullable: "NO",
128
+ column_default: "nextval" })
101
129
  ]);
102
130
  const result = generateCollectionFile("counters", meta, [], new Set(), new Map([["counters", meta]]), new Map());
103
131
  expect(result).toContain('isId: "increment"');
@@ -105,9 +133,14 @@ describe("generateCollectionFile", () => {
105
133
 
106
134
  it("flags composite primary keys with a comment", () => {
107
135
  const meta = makeSimpleTable("scores", [
108
- mkCol("scores", "user_id", { data_type: "uuid", udt_name: "uuid", is_nullable: "NO" }),
109
- mkCol("scores", "game_id", { data_type: "uuid", udt_name: "uuid", is_nullable: "NO" }),
110
- mkCol("scores", "score", { data_type: "integer", udt_name: "int4" }),
136
+ mkCol("scores", "user_id", { data_type: "uuid",
137
+ udt_name: "uuid",
138
+ is_nullable: "NO" }),
139
+ mkCol("scores", "game_id", { data_type: "uuid",
140
+ udt_name: "uuid",
141
+ is_nullable: "NO" }),
142
+ mkCol("scores", "score", { data_type: "integer",
143
+ udt_name: "int4" })
111
144
  ], ["user_id", "game_id"]);
112
145
  const result = generateCollectionFile("scores", meta, [], new Set(), new Map([["scores", meta]]), new Map());
113
146
  expect(result).toContain("composite primary key");
@@ -118,8 +151,10 @@ describe("generateCollectionFile", () => {
118
151
  describe("validation.required", () => {
119
152
  it("adds required when is_nullable is NO, not a PK, and no default", () => {
120
153
  const meta = makeSimpleTable("items", [
121
- mkCol("items", "id", { data_type: "uuid", udt_name: "uuid", is_nullable: "NO" }),
122
- mkCol("items", "name", { is_nullable: "NO" }),
154
+ mkCol("items", "id", { data_type: "uuid",
155
+ udt_name: "uuid",
156
+ is_nullable: "NO" }),
157
+ mkCol("items", "name", { is_nullable: "NO" })
123
158
  ]);
124
159
  const result = generateCollectionFile("items", meta, [], new Set(), new Map([["items", meta]]), new Map());
125
160
  // name should have required
@@ -128,8 +163,10 @@ describe("generateCollectionFile", () => {
128
163
 
129
164
  it("does NOT add required for nullable columns", () => {
130
165
  const meta = makeSimpleTable("items", [
131
- mkCol("items", "id", { data_type: "uuid", udt_name: "uuid", is_nullable: "NO" }),
132
- mkCol("items", "bio", { is_nullable: "YES" }),
166
+ mkCol("items", "id", { data_type: "uuid",
167
+ udt_name: "uuid",
168
+ is_nullable: "NO" }),
169
+ mkCol("items", "bio", { is_nullable: "YES" })
133
170
  ]);
134
171
  const result = generateCollectionFile("items", meta, [], new Set(), new Map([["items", meta]]), new Map());
135
172
  const bioSection = result.split("bio:")[1].split("},")[0];
@@ -138,8 +175,11 @@ describe("generateCollectionFile", () => {
138
175
 
139
176
  it("does NOT add required for columns with defaults", () => {
140
177
  const meta = makeSimpleTable("items", [
141
- mkCol("items", "id", { data_type: "uuid", udt_name: "uuid", is_nullable: "NO" }),
142
- mkCol("items", "role", { is_nullable: "NO", column_default: "'user'" }),
178
+ mkCol("items", "id", { data_type: "uuid",
179
+ udt_name: "uuid",
180
+ is_nullable: "NO" }),
181
+ mkCol("items", "role", { is_nullable: "NO",
182
+ column_default: "'user'" })
143
183
  ]);
144
184
  const result = generateCollectionFile("items", meta, [], new Set(), new Map([["items", meta]]), new Map());
145
185
  const roleSection = result.split("role:")[1].split("},")[0];
@@ -151,11 +191,14 @@ describe("generateCollectionFile", () => {
151
191
  it("generates enum for USER-DEFINED columns with matching enum", () => {
152
192
  const enumMap = new Map([["order_status", ["pending", "shipped", "delivered"]]]);
153
193
  const meta = makeSimpleTable("orders", [
154
- mkCol("orders", "id", { data_type: "uuid", udt_name: "uuid", is_nullable: "NO" }),
155
- mkCol("orders", "status", { data_type: "USER-DEFINED", udt_name: "order_status" }),
194
+ mkCol("orders", "id", { data_type: "uuid",
195
+ udt_name: "uuid",
196
+ is_nullable: "NO" }),
197
+ mkCol("orders", "status", { data_type: "USER-DEFINED",
198
+ udt_name: "order_status" })
156
199
  ]);
157
200
  const result = generateCollectionFile("orders", meta, [], new Set(), new Map([["orders", meta]]), enumMap);
158
- expect(result).toContain('enum:');
201
+ expect(result).toContain("enum:");
159
202
  expect(result).toContain('{ id: "pending", label: "Pending" }');
160
203
  expect(result).toContain('{ id: "shipped", label: "Shipped" }');
161
204
  expect(result).toContain('{ id: "delivered", label: "Delivered" }');
@@ -164,8 +207,11 @@ describe("generateCollectionFile", () => {
164
207
 
165
208
  it("does NOT add enum for USER-DEFINED without matching enum", () => {
166
209
  const meta = makeSimpleTable("things", [
167
- mkCol("things", "id", { data_type: "uuid", udt_name: "uuid", is_nullable: "NO" }),
168
- mkCol("things", "geom", { data_type: "USER-DEFINED", udt_name: "geometry" }),
210
+ mkCol("things", "id", { data_type: "uuid",
211
+ udt_name: "uuid",
212
+ is_nullable: "NO" }),
213
+ mkCol("things", "geom", { data_type: "USER-DEFINED",
214
+ udt_name: "geometry" })
169
215
  ]);
170
216
  const result = generateCollectionFile("things", meta, [], new Set(), new Map([["things", meta]]), new Map());
171
217
  expect(result).not.toContain("enum");
@@ -174,8 +220,11 @@ describe("generateCollectionFile", () => {
174
220
  it("humanizes enum value labels with underscores", () => {
175
221
  const enumMap = new Map([["my_enum", ["in_progress", "on_hold"]]]);
176
222
  const meta = makeSimpleTable("tasks", [
177
- mkCol("tasks", "id", { data_type: "uuid", udt_name: "uuid", is_nullable: "NO" }),
178
- mkCol("tasks", "state", { data_type: "USER-DEFINED", udt_name: "my_enum" }),
223
+ mkCol("tasks", "id", { data_type: "uuid",
224
+ udt_name: "uuid",
225
+ is_nullable: "NO" }),
226
+ mkCol("tasks", "state", { data_type: "USER-DEFINED",
227
+ udt_name: "my_enum" })
179
228
  ]);
180
229
  const result = generateCollectionFile("tasks", meta, [], new Set(), new Map([["tasks", meta]]), enumMap);
181
230
  expect(result).toContain('label: "In Progress"');
@@ -186,8 +235,11 @@ describe("generateCollectionFile", () => {
186
235
  describe("date auto-value heuristics", () => {
187
236
  it("sets autoValue on_create for created_at", () => {
188
237
  const meta = makeSimpleTable("items", [
189
- mkCol("items", "id", { data_type: "uuid", udt_name: "uuid", is_nullable: "NO" }),
190
- mkCol("items", "created_at", { data_type: "timestamp", udt_name: "timestamp" }),
238
+ mkCol("items", "id", { data_type: "uuid",
239
+ udt_name: "uuid",
240
+ is_nullable: "NO" }),
241
+ mkCol("items", "created_at", { data_type: "timestamp",
242
+ udt_name: "timestamp" })
191
243
  ]);
192
244
  const result = generateCollectionFile("items", meta, [], new Set(), new Map([["items", meta]]), new Map());
193
245
  expect(result).toContain('autoValue: "on_create"');
@@ -196,8 +248,11 @@ describe("generateCollectionFile", () => {
196
248
 
197
249
  it("sets autoValue on_update for updated_at", () => {
198
250
  const meta = makeSimpleTable("items", [
199
- mkCol("items", "id", { data_type: "uuid", udt_name: "uuid", is_nullable: "NO" }),
200
- mkCol("items", "updated_at", { data_type: "timestamp", udt_name: "timestamp" }),
251
+ mkCol("items", "id", { data_type: "uuid",
252
+ udt_name: "uuid",
253
+ is_nullable: "NO" }),
254
+ mkCol("items", "updated_at", { data_type: "timestamp",
255
+ udt_name: "timestamp" })
201
256
  ]);
202
257
  const result = generateCollectionFile("items", meta, [], new Set(), new Map([["items", meta]]), new Map());
203
258
  expect(result).toContain('autoValue: "on_update"');
@@ -205,8 +260,12 @@ describe("generateCollectionFile", () => {
205
260
 
206
261
  it("sets autoValue on_create for columns with now() default", () => {
207
262
  const meta = makeSimpleTable("items", [
208
- mkCol("items", "id", { data_type: "uuid", udt_name: "uuid", is_nullable: "NO" }),
209
- mkCol("items", "published_at", { data_type: "timestamp", udt_name: "timestamp", column_default: "now()" }),
263
+ mkCol("items", "id", { data_type: "uuid",
264
+ udt_name: "uuid",
265
+ is_nullable: "NO" }),
266
+ mkCol("items", "published_at", { data_type: "timestamp",
267
+ udt_name: "timestamp",
268
+ column_default: "now()" })
210
269
  ]);
211
270
  const result = generateCollectionFile("items", meta, [], new Set(), new Map([["items", meta]]), new Map());
212
271
  expect(result).toContain('autoValue: "on_create"');
@@ -217,8 +276,10 @@ describe("generateCollectionFile", () => {
217
276
  it("adds storage config for image-like column names", () => {
218
277
  for (const name of ["profile_image", "avatar", "photo_url", "logo", "cover_image"]) {
219
278
  const meta = makeSimpleTable("t", [
220
- mkCol("t", "id", { data_type: "uuid", udt_name: "uuid", is_nullable: "NO" }),
221
- mkCol("t", name),
279
+ mkCol("t", "id", { data_type: "uuid",
280
+ udt_name: "uuid",
281
+ is_nullable: "NO" }),
282
+ mkCol("t", name)
222
283
  ]);
223
284
  const result = generateCollectionFile("t", meta, [], new Set(), new Map([["t", meta]]), new Map());
224
285
  expect(result).toContain("storagePath:");
@@ -228,8 +289,10 @@ describe("generateCollectionFile", () => {
228
289
  it("adds multiline for description/summary/excerpt", () => {
229
290
  for (const name of ["description", "summary", "excerpt"]) {
230
291
  const meta = makeSimpleTable("t", [
231
- mkCol("t", "id", { data_type: "uuid", udt_name: "uuid", is_nullable: "NO" }),
232
- mkCol("t", name),
292
+ mkCol("t", "id", { data_type: "uuid",
293
+ udt_name: "uuid",
294
+ is_nullable: "NO" }),
295
+ mkCol("t", name)
233
296
  ]);
234
297
  const result = generateCollectionFile("t", meta, [], new Set(), new Map([["t", meta]]), new Map());
235
298
  expect(result).toContain("multiline: true");
@@ -239,8 +302,10 @@ describe("generateCollectionFile", () => {
239
302
  it("adds markdown for content/body", () => {
240
303
  for (const name of ["content", "body"]) {
241
304
  const meta = makeSimpleTable("t", [
242
- mkCol("t", "id", { data_type: "uuid", udt_name: "uuid", is_nullable: "NO" }),
243
- mkCol("t", name),
305
+ mkCol("t", "id", { data_type: "uuid",
306
+ udt_name: "uuid",
307
+ is_nullable: "NO" }),
308
+ mkCol("t", name)
244
309
  ]);
245
310
  const result = generateCollectionFile("t", meta, [], new Set(), new Map([["t", meta]]), new Map());
246
311
  expect(result).toContain("markdown: true");
@@ -249,8 +314,11 @@ describe("generateCollectionFile", () => {
249
314
 
250
315
  it("adds multiline for text data_type columns", () => {
251
316
  const meta = makeSimpleTable("t", [
252
- mkCol("t", "id", { data_type: "uuid", udt_name: "uuid", is_nullable: "NO" }),
253
- mkCol("t", "notes", { data_type: "text", udt_name: "text" }),
317
+ mkCol("t", "id", { data_type: "uuid",
318
+ udt_name: "uuid",
319
+ is_nullable: "NO" }),
320
+ mkCol("t", "notes", { data_type: "text",
321
+ udt_name: "text" })
254
322
  ]);
255
323
  const result = generateCollectionFile("t", meta, [], new Set(), new Map([["t", meta]]), new Map());
256
324
  expect(result).toContain("multiline: true");
@@ -259,8 +327,11 @@ describe("generateCollectionFile", () => {
259
327
  it("does NOT apply string heuristics to enum columns", () => {
260
328
  const enumMap = new Map([["img_type", ["png", "jpg"]]]);
261
329
  const meta = makeSimpleTable("t", [
262
- mkCol("t", "id", { data_type: "uuid", udt_name: "uuid", is_nullable: "NO" }),
263
- mkCol("t", "image_type", { data_type: "USER-DEFINED", udt_name: "img_type" }),
330
+ mkCol("t", "id", { data_type: "uuid",
331
+ udt_name: "uuid",
332
+ is_nullable: "NO" }),
333
+ mkCol("t", "image_type", { data_type: "USER-DEFINED",
334
+ udt_name: "img_type" })
264
335
  ]);
265
336
  const result = generateCollectionFile("t", meta, [], new Set(), new Map([["t", meta]]), enumMap);
266
337
  expect(result).not.toContain("storagePath");
@@ -272,12 +343,14 @@ describe("generateCollectionFile", () => {
272
343
  it("generates a one-to-one owning relation with correct import", () => {
273
344
  const fks = [mkFk("posts", "author_id", "users")];
274
345
  const meta = makeSimpleTable("posts", [
275
- mkCol("posts", "id", { data_type: "uuid", udt_name: "uuid", is_nullable: "NO" }),
276
- mkCol("posts", "author_id"),
346
+ mkCol("posts", "id", { data_type: "uuid",
347
+ udt_name: "uuid",
348
+ is_nullable: "NO" }),
349
+ mkCol("posts", "author_id")
277
350
  ], ["id"], fks);
278
351
  const result = generateCollectionFile("posts", meta, fks, new Set(), new Map([["posts", meta]]), new Map());
279
352
  expect(result).toContain('import usersCollection from "./users"');
280
- expect(result).toContain('author: {');
353
+ expect(result).toContain("author: {");
281
354
  expect(result).toContain('cardinality: "one"');
282
355
  expect(result).toContain('direction: "owning"');
283
356
  expect(result).toContain('localKey: "author_id"');
@@ -288,26 +361,30 @@ describe("generateCollectionFile", () => {
288
361
  it("generates a one-to-many inverse relation in the relations array", () => {
289
362
  const allFks: ForeignKeyRow[] = [mkFk("comments", "post_id", "posts")];
290
363
  const meta = makeSimpleTable("posts", [
291
- mkCol("posts", "id", { data_type: "uuid", udt_name: "uuid", is_nullable: "NO" }),
364
+ mkCol("posts", "id", { data_type: "uuid",
365
+ udt_name: "uuid",
366
+ is_nullable: "NO" })
292
367
  ]);
293
368
  const result = generateCollectionFile("posts", meta, allFks, new Set(), new Map([["posts", meta]]), new Map());
294
369
  expect(result).toContain('import commentsCollection from "./comments"');
295
370
  // Should be in the relations array, not as an inline property
296
- expect(result).toContain('relations: [');
371
+ expect(result).toContain("relations: [");
297
372
  expect(result).toContain('relationName: "comments"');
298
373
  expect(result).toContain('cardinality: "many"');
299
374
  expect(result).toContain('direction: "inverse"');
300
375
  expect(result).toContain('inverseRelationName: "post"');
301
376
  expect(result).toContain('foreignKeyOnTarget: "post_id"');
302
377
  // Should NOT appear as an inline property with type: "relation"
303
- const propsSection = result.split('properties:')[1].split('relations:')[0];
378
+ const propsSection = result.split("properties:")[1].split("relations:")[0];
304
379
  expect(propsSection).not.toContain('"relation"');
305
380
  });
306
381
 
307
382
  it("does NOT include inverse relations in propertiesOrder", () => {
308
383
  const allFks: ForeignKeyRow[] = [mkFk("comments", "post_id", "posts")];
309
384
  const meta = makeSimpleTable("posts", [
310
- mkCol("posts", "id", { data_type: "uuid", udt_name: "uuid", is_nullable: "NO" }),
385
+ mkCol("posts", "id", { data_type: "uuid",
386
+ udt_name: "uuid",
387
+ is_nullable: "NO" })
311
388
  ]);
312
389
  const result = generateCollectionFile("posts", meta, allFks, new Set(), new Map([["posts", meta]]), new Map());
313
390
  const orderMatch = result.match(/propertiesOrder:\s*(\[[\s\S]*?\])/);
@@ -321,21 +398,24 @@ describe("generateCollectionFile", () => {
321
398
  it("generates owning M2M with through config in relations array", () => {
322
399
  const jtFks: ForeignKeyRow[] = [
323
400
  mkFk("articles_tags", "article_id", "articles"),
324
- mkFk("articles_tags", "tag_id", "tags"),
401
+ mkFk("articles_tags", "tag_id", "tags")
325
402
  ];
326
403
  const jtMeta: TableMeta = {
327
- name: "articles_tags", pks: [],
404
+ name: "articles_tags",
405
+ pks: [],
328
406
  columns: [mkCol("articles_tags", "article_id"), mkCol("articles_tags", "tag_id")],
329
- fks: jtFks,
407
+ fks: jtFks
330
408
  };
331
409
  const articlesMeta = makeSimpleTable("articles", [
332
- mkCol("articles", "id", { data_type: "uuid", udt_name: "uuid", is_nullable: "NO" }),
410
+ mkCol("articles", "id", { data_type: "uuid",
411
+ udt_name: "uuid",
412
+ is_nullable: "NO" })
333
413
  ]);
334
414
  const tablesMap = new Map([["articles", articlesMeta], ["articles_tags", jtMeta]]);
335
415
  const joinTables = new Set(["articles_tags"]);
336
416
 
337
417
  const result = generateCollectionFile("articles", articlesMeta, [], joinTables, tablesMap, new Map());
338
- expect(result).toContain('relations: [');
418
+ expect(result).toContain("relations: [");
339
419
  expect(result).toContain('relationName: "tags"');
340
420
  expect(result).toContain('direction: "owning"');
341
421
  expect(result).toContain('table: "articles_tags"');
@@ -346,21 +426,24 @@ describe("generateCollectionFile", () => {
346
426
  it("generates inverse M2M in relations array", () => {
347
427
  const jtFks: ForeignKeyRow[] = [
348
428
  mkFk("articles_tags", "article_id", "articles"),
349
- mkFk("articles_tags", "tag_id", "tags"),
429
+ mkFk("articles_tags", "tag_id", "tags")
350
430
  ];
351
431
  const jtMeta: TableMeta = {
352
- name: "articles_tags", pks: [],
432
+ name: "articles_tags",
433
+ pks: [],
353
434
  columns: [mkCol("articles_tags", "article_id"), mkCol("articles_tags", "tag_id")],
354
- fks: jtFks,
435
+ fks: jtFks
355
436
  };
356
437
  const tagsMeta = makeSimpleTable("tags", [
357
- mkCol("tags", "id", { data_type: "uuid", udt_name: "uuid", is_nullable: "NO" }),
438
+ mkCol("tags", "id", { data_type: "uuid",
439
+ udt_name: "uuid",
440
+ is_nullable: "NO" })
358
441
  ]);
359
442
  const tablesMap = new Map([["tags", tagsMeta], ["articles_tags", jtMeta]]);
360
443
  const joinTables = new Set(["articles_tags"]);
361
444
 
362
445
  const result = generateCollectionFile("tags", tagsMeta, [], joinTables, tablesMap, new Map());
363
- expect(result).toContain('relations: [');
446
+ expect(result).toContain("relations: [");
364
447
  expect(result).toContain('direction: "inverse"');
365
448
  });
366
449
  });
@@ -369,21 +452,24 @@ describe("generateCollectionFile", () => {
369
452
  it("generates self-ref M2M with _via_ relation name in relations array", () => {
370
453
  const jtFks: ForeignKeyRow[] = [
371
454
  mkFk("user_friends", "user_id", "users"),
372
- mkFk("user_friends", "friend_id", "users"),
455
+ mkFk("user_friends", "friend_id", "users")
373
456
  ];
374
457
  const jtMeta: TableMeta = {
375
- name: "user_friends", pks: [],
458
+ name: "user_friends",
459
+ pks: [],
376
460
  columns: [mkCol("user_friends", "user_id"), mkCol("user_friends", "friend_id")],
377
- fks: jtFks,
461
+ fks: jtFks
378
462
  };
379
463
  const usersMeta = makeSimpleTable("users", [
380
- mkCol("users", "id", { data_type: "uuid", udt_name: "uuid", is_nullable: "NO" }),
464
+ mkCol("users", "id", { data_type: "uuid",
465
+ udt_name: "uuid",
466
+ is_nullable: "NO" })
381
467
  ]);
382
468
  const tablesMap = new Map([["users", usersMeta], ["user_friends", jtMeta]]);
383
469
  const joinTables = new Set(["user_friends"]);
384
470
 
385
471
  const result = generateCollectionFile("users", usersMeta, [], joinTables, tablesMap, new Map());
386
- expect(result).toContain('relations: [');
472
+ expect(result).toContain("relations: [");
387
473
  expect(result).toContain('relationName: "users_via_friend"');
388
474
  expect(result).toContain('table: "user_friends"');
389
475
  expect(result).toContain('sourceColumn: "user_id"');
@@ -396,7 +482,9 @@ describe("generateCollectionFile", () => {
396
482
  describe("icon mapping", () => {
397
483
  it("assigns correct icons based on table name", () => {
398
484
  const make = (name: string) => {
399
- const meta = makeSimpleTable(name, [mkCol(name, "id", { data_type: "uuid", udt_name: "uuid", is_nullable: "NO" })]);
485
+ const meta = makeSimpleTable(name, [mkCol(name, "id", { data_type: "uuid",
486
+ udt_name: "uuid",
487
+ is_nullable: "NO" })]);
400
488
  return generateCollectionFile(name, meta, [], new Set(), new Map([[name, meta]]), new Map());
401
489
  };
402
490
  expect(make("users")).toContain('icon: "Users"');
@@ -411,7 +499,9 @@ describe("generateCollectionFile", () => {
411
499
  describe("humanized names", () => {
412
500
  it("uses Title Case for collection name from snake_case table", () => {
413
501
  const meta = makeSimpleTable("user_profiles", [
414
- mkCol("user_profiles", "id", { data_type: "uuid", udt_name: "uuid", is_nullable: "NO" }),
502
+ mkCol("user_profiles", "id", { data_type: "uuid",
503
+ udt_name: "uuid",
504
+ is_nullable: "NO" })
415
505
  ]);
416
506
  const result = generateCollectionFile("user_profiles", meta, [], new Set(), new Map([["user_profiles", meta]]), new Map());
417
507
  expect(result).toContain('name: "User Profiles"');
@@ -420,8 +510,10 @@ describe("generateCollectionFile", () => {
420
510
 
421
511
  it("humanizes property names", () => {
422
512
  const meta = makeSimpleTable("t", [
423
- mkCol("t", "id", { data_type: "uuid", udt_name: "uuid", is_nullable: "NO" }),
424
- mkCol("t", "first_name"),
513
+ mkCol("t", "id", { data_type: "uuid",
514
+ udt_name: "uuid",
515
+ is_nullable: "NO" }),
516
+ mkCol("t", "first_name")
425
517
  ]);
426
518
  const result = generateCollectionFile("t", meta, [], new Set(), new Map([["t", meta]]), new Map());
427
519
  expect(result).toContain('name: "First Name"');
@@ -430,7 +522,9 @@ describe("generateCollectionFile", () => {
430
522
 
431
523
  describe("import generation", () => {
432
524
  it("always imports PostgresCollection", () => {
433
- const meta = makeSimpleTable("t", [mkCol("t", "id", { data_type: "uuid", udt_name: "uuid", is_nullable: "NO" })]);
525
+ const meta = makeSimpleTable("t", [mkCol("t", "id", { data_type: "uuid",
526
+ udt_name: "uuid",
527
+ is_nullable: "NO" })]);
434
528
  const result = generateCollectionFile("t", meta, [], new Set(), new Map([["t", meta]]), new Map());
435
529
  expect(result).toContain('import { PostgresCollection } from "@rebasepro/types"');
436
530
  });
@@ -438,9 +532,11 @@ describe("generateCollectionFile", () => {
438
532
  it("does not duplicate imports for multiple relations to same table", () => {
439
533
  const fks = [mkFk("posts", "author_id", "users"), mkFk("posts", "reviewer_id", "users")];
440
534
  const meta = makeSimpleTable("posts", [
441
- mkCol("posts", "id", { data_type: "uuid", udt_name: "uuid", is_nullable: "NO" }),
535
+ mkCol("posts", "id", { data_type: "uuid",
536
+ udt_name: "uuid",
537
+ is_nullable: "NO" }),
442
538
  mkCol("posts", "author_id"),
443
- mkCol("posts", "reviewer_id"),
539
+ mkCol("posts", "reviewer_id")
444
540
  ], ["id"], fks);
445
541
  const result = generateCollectionFile("posts", meta, fks, new Set(), new Map([["posts", meta]]), new Map());
446
542
  const importMatches = result.match(/import usersCollection/g);
@@ -450,7 +546,9 @@ describe("generateCollectionFile", () => {
450
546
 
451
547
  describe("export default", () => {
452
548
  it("exports the collection variable as default", () => {
453
- const meta = makeSimpleTable("orders", [mkCol("orders", "id", { data_type: "uuid", udt_name: "uuid", is_nullable: "NO" })]);
549
+ const meta = makeSimpleTable("orders", [mkCol("orders", "id", { data_type: "uuid",
550
+ udt_name: "uuid",
551
+ is_nullable: "NO" })]);
454
552
  const result = generateCollectionFile("orders", meta, [], new Set(), new Map([["orders", meta]]), new Map());
455
553
  expect(result).toContain("export default ordersCollection;");
456
554
  });