@rebasepro/server-postgresql 0.0.1-canary.eae7889 → 0.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 (70) hide show
  1. package/dist/index.es.js +458 -201
  2. package/dist/index.es.js.map +1 -1
  3. package/dist/index.umd.js +458 -201
  4. package/dist/index.umd.js.map +1 -1
  5. package/dist/server-postgresql/src/PostgresBackendDriver.d.ts +8 -1
  6. package/dist/server-postgresql/src/schema/introspect-db-inference.d.ts +5 -0
  7. package/dist/server-postgresql/src/schema/introspect-db-logic.d.ts +117 -0
  8. package/dist/server-postgresql/src/schema/introspect-db.d.ts +1 -0
  9. package/dist/server-postgresql/src/services/EntityPersistService.d.ts +9 -0
  10. package/dist/types/src/controllers/auth.d.ts +8 -2
  11. package/dist/types/src/controllers/client.d.ts +13 -0
  12. package/dist/types/src/controllers/collection_registry.d.ts +2 -1
  13. package/dist/types/src/controllers/data_driver.d.ts +36 -1
  14. package/dist/types/src/controllers/navigation.d.ts +18 -6
  15. package/dist/types/src/controllers/registry.d.ts +9 -1
  16. package/dist/types/src/controllers/side_entity_controller.d.ts +7 -0
  17. package/dist/types/src/rebase_context.d.ts +17 -0
  18. package/dist/types/src/types/backend_hooks.d.ts +187 -0
  19. package/dist/types/src/types/collections.d.ts +31 -11
  20. package/dist/types/src/types/component_ref.d.ts +47 -0
  21. package/dist/types/src/types/cron.d.ts +1 -1
  22. package/dist/types/src/types/entity_views.d.ts +6 -7
  23. package/dist/types/src/types/formex.d.ts +40 -0
  24. package/dist/types/src/types/index.d.ts +3 -0
  25. package/dist/types/src/types/plugins.d.ts +6 -3
  26. package/dist/types/src/types/properties.d.ts +72 -88
  27. package/dist/types/src/types/slots.d.ts +20 -10
  28. package/dist/types/src/types/translations.d.ts +6 -0
  29. package/examples/sdk-demo/node_modules/esbuild/LICENSE.md +21 -0
  30. package/examples/sdk-demo/node_modules/esbuild/README.md +3 -0
  31. package/examples/sdk-demo/node_modules/esbuild/bin/esbuild +223 -0
  32. package/examples/sdk-demo/node_modules/esbuild/install.js +289 -0
  33. package/examples/sdk-demo/node_modules/esbuild/lib/main.d.ts +716 -0
  34. package/examples/sdk-demo/node_modules/esbuild/lib/main.js +2242 -0
  35. package/examples/sdk-demo/node_modules/esbuild/package.json +49 -0
  36. package/package.json +6 -5
  37. package/src/PostgresBackendDriver.ts +32 -6
  38. package/src/cli.ts +68 -2
  39. package/src/data-transformer.ts +84 -1
  40. package/src/schema/doctor.ts +14 -2
  41. package/src/schema/generate-drizzle-schema-logic.ts +59 -30
  42. package/src/schema/introspect-db-inference.ts +238 -0
  43. package/src/schema/introspect-db-logic.ts +896 -0
  44. package/src/schema/introspect-db.ts +254 -0
  45. package/src/services/EntityFetchService.ts +16 -0
  46. package/src/services/EntityPersistService.ts +95 -13
  47. package/test/generate-drizzle-schema.test.ts +342 -0
  48. package/test/introspect-db-generation.test.ts +458 -0
  49. package/test/introspect-db-utils.test.ts +392 -0
  50. package/test/property-ordering.test.ts +395 -0
  51. package/test/relations.test.ts +4 -4
  52. package/test/unmapped-tables-safety.test.ts +345 -0
  53. package/jest-all.log +0 -3128
  54. package/jest.log +0 -49
  55. package/scratch.ts +0 -41
  56. package/test-drizzle-bug.ts +0 -18
  57. package/test-drizzle-out/0000_cultured_freak.sql +0 -7
  58. package/test-drizzle-out/0001_tiresome_professor_monster.sql +0 -1
  59. package/test-drizzle-out/meta/0000_snapshot.json +0 -55
  60. package/test-drizzle-out/meta/0001_snapshot.json +0 -63
  61. package/test-drizzle-out/meta/_journal.json +0 -20
  62. package/test-drizzle-prompt.sh +0 -2
  63. package/test-policy-prompt.sh +0 -3
  64. package/test-programmatic.ts +0 -30
  65. package/test-programmatic2.ts +0 -59
  66. package/test-schema-no-policies.ts +0 -12
  67. package/test_drizzle_mock.js +0 -3
  68. package/test_find_changed.mjs +0 -32
  69. package/test_hash.js +0 -14
  70. package/test_output.txt +0 -3145
@@ -0,0 +1,395 @@
1
+ import {
2
+ computePropertyPriority, sortPropertiesOrder,
3
+ PropertyOrderingContext, PropertyOrderEntry,
4
+ } from "../src/schema/introspect-db-logic";
5
+
6
+ // ── Helpers ───────────────────────────────────────────────────────────
7
+
8
+ function mkCtx(overrides: Partial<PropertyOrderingContext> = {}): PropertyOrderingContext {
9
+ return {
10
+ propType: "string",
11
+ isPk: false,
12
+ isEnum: false,
13
+ isStorage: false,
14
+ pgDataType: "character varying",
15
+ originalIndex: 0,
16
+ ...overrides,
17
+ };
18
+ }
19
+
20
+ function mkEntry(key: string, overrides: Partial<PropertyOrderingContext> = {}): PropertyOrderEntry {
21
+ return { key, ctx: mkCtx(overrides) };
22
+ }
23
+
24
+ // ═══════════════════════════════════════════════════════════════════════
25
+ // computePropertyPriority() — tier assignment
26
+ // ═══════════════════════════════════════════════════════════════════════
27
+ describe("computePropertyPriority", () => {
28
+ describe("Tier 0: Identity / Primary keys", () => {
29
+ it("ranks 'id' PK as tier 0 (score 0)", () => {
30
+ const score = computePropertyPriority("id", mkCtx({ isPk: true }));
31
+ expect(score).toBeGreaterThanOrEqual(0);
32
+ expect(score).toBeLessThan(10);
33
+ });
34
+
35
+ it("ranks any PK column in tier 0", () => {
36
+ const score = computePropertyPriority("uuid", mkCtx({ isPk: true }));
37
+ expect(score).toBeGreaterThanOrEqual(0);
38
+ expect(score).toBeLessThan(10);
39
+ });
40
+
41
+ it("ranks unknown PK name with fallback score 5", () => {
42
+ const score = computePropertyPriority("pk_col", mkCtx({ isPk: true }));
43
+ expect(score).toBeGreaterThanOrEqual(5);
44
+ expect(score).toBeLessThan(10);
45
+ });
46
+ });
47
+
48
+ describe("Tier 1: Title / Name fields", () => {
49
+ it.each(["name", "title", "label", "display_name", "headline", "subject", "heading"])(
50
+ "ranks '%s' in tier 1 (10-19)",
51
+ (col) => {
52
+ const score = computePropertyPriority(col, mkCtx());
53
+ expect(score).toBeGreaterThanOrEqual(10);
54
+ expect(score).toBeLessThan(20);
55
+ },
56
+ );
57
+
58
+ it("ranks 'product_name' as partial match in tier 1b (17-19)", () => {
59
+ const score = computePropertyPriority("product_name", mkCtx());
60
+ expect(score).toBeGreaterThanOrEqual(17);
61
+ expect(score).toBeLessThan(20);
62
+ });
63
+
64
+ it("ranks 'page_title' as partial match in tier 1b", () => {
65
+ const score = computePropertyPriority("page_title", mkCtx());
66
+ expect(score).toBeGreaterThanOrEqual(17);
67
+ expect(score).toBeLessThan(20);
68
+ });
69
+ });
70
+
71
+ describe("Tier 2: Human identity fields", () => {
72
+ it.each(["first_name", "last_name", "full_name", "username", "email", "phone", "phone_number"])(
73
+ "ranks '%s' in tier 2 (20-29)",
74
+ (col) => {
75
+ const score = computePropertyPriority(col, mkCtx());
76
+ expect(score).toBeGreaterThanOrEqual(20);
77
+ expect(score).toBeLessThan(30);
78
+ },
79
+ );
80
+
81
+ it("ranks first_name before last_name", () => {
82
+ const first = computePropertyPriority("first_name", mkCtx());
83
+ const last = computePropertyPriority("last_name", mkCtx());
84
+ expect(first).toBeLessThan(last);
85
+ });
86
+ });
87
+
88
+ describe("Tier 3: Core descriptors", () => {
89
+ it.each(["slug", "code", "sku", "type", "status", "role", "category"])(
90
+ "ranks '%s' in tier 3 (30-39)",
91
+ (col) => {
92
+ const score = computePropertyPriority(col, mkCtx());
93
+ expect(score).toBeGreaterThanOrEqual(30);
94
+ expect(score).toBeLessThan(40);
95
+ },
96
+ );
97
+ });
98
+
99
+ describe("Tier 4: Short text, enums, booleans", () => {
100
+ it("ranks enum fields in tier 4", () => {
101
+ const score = computePropertyPriority("payment_method", mkCtx({ isEnum: true }));
102
+ expect(score).toBeGreaterThanOrEqual(40);
103
+ expect(score).toBeLessThan(50);
104
+ });
105
+
106
+ it("ranks boolean fields in tier 4", () => {
107
+ const score = computePropertyPriority("active", mkCtx({ propType: "boolean" }));
108
+ expect(score).toBeGreaterThanOrEqual(40);
109
+ expect(score).toBeLessThan(50);
110
+ });
111
+
112
+ it("ranks short string (varchar) in tier 4", () => {
113
+ const score = computePropertyPriority("color", mkCtx({ propType: "string", pgDataType: "character varying" }));
114
+ expect(score).toBeGreaterThanOrEqual(40);
115
+ expect(score).toBeLessThan(50);
116
+ });
117
+ });
118
+
119
+ describe("Tier 5: Numbers & user-facing dates", () => {
120
+ it("ranks number fields in tier 5", () => {
121
+ const score = computePropertyPriority("price", mkCtx({ propType: "number" }));
122
+ expect(score).toBeGreaterThanOrEqual(50);
123
+ expect(score).toBeLessThan(60);
124
+ });
125
+
126
+ it("ranks non-system date fields in tier 5", () => {
127
+ const score = computePropertyPriority("published_at", mkCtx({ propType: "date" }));
128
+ expect(score).toBeGreaterThanOrEqual(50);
129
+ expect(score).toBeLessThan(60);
130
+ });
131
+ });
132
+
133
+ describe("Tier 6: Relations", () => {
134
+ it("ranks relation fields in tier 6", () => {
135
+ const score = computePropertyPriority("author", mkCtx({ propType: "relation" }));
136
+ expect(score).toBeGreaterThanOrEqual(60);
137
+ expect(score).toBeLessThan(70);
138
+ });
139
+ });
140
+
141
+ describe("Tier 7: Long text fields", () => {
142
+ it.each(["description", "summary", "excerpt", "bio", "about"])(
143
+ "ranks '%s' in tier 7 (70-79)",
144
+ (col) => {
145
+ const score = computePropertyPriority(col, mkCtx());
146
+ expect(score).toBeGreaterThanOrEqual(70);
147
+ expect(score).toBeLessThan(80);
148
+ },
149
+ );
150
+ });
151
+
152
+ describe("Tier 8: Rich content fields", () => {
153
+ it.each(["content", "body", "html"])(
154
+ "ranks '%s' in tier 8 (80-89)",
155
+ (col) => {
156
+ const score = computePropertyPriority(col, mkCtx());
157
+ expect(score).toBeGreaterThanOrEqual(80);
158
+ expect(score).toBeLessThan(90);
159
+ },
160
+ );
161
+ });
162
+
163
+ describe("Tier 9: Media / storage fields", () => {
164
+ it("ranks storage fields in tier 9", () => {
165
+ const score = computePropertyPriority("profile_image", mkCtx({ isStorage: true }));
166
+ expect(score).toBeGreaterThanOrEqual(90);
167
+ expect(score).toBeLessThan(100);
168
+ });
169
+
170
+ it.each(["avatar", "photo_url", "logo", "cover_image", "thumbnail"])(
171
+ "ranks '%s' (media pattern) in tier 9",
172
+ (col) => {
173
+ const score = computePropertyPriority(col, mkCtx());
174
+ expect(score).toBeGreaterThanOrEqual(90);
175
+ expect(score).toBeLessThan(100);
176
+ },
177
+ );
178
+
179
+ it("ranks URL-suffix fields in tier 9", () => {
180
+ const score = computePropertyPriority("website_url", mkCtx());
181
+ expect(score).toBeGreaterThanOrEqual(90);
182
+ expect(score).toBeLessThan(100);
183
+ });
184
+ });
185
+
186
+ describe("Tier 10: JSON / Map types", () => {
187
+ it("ranks map types in tier 10", () => {
188
+ const score = computePropertyPriority("metadata", mkCtx({ propType: "map" }));
189
+ expect(score).toBeGreaterThanOrEqual(100);
190
+ expect(score).toBeLessThan(110);
191
+ });
192
+
193
+ it("ranks unknown map field names slightly higher", () => {
194
+ const known = computePropertyPriority("metadata", mkCtx({ propType: "map" }));
195
+ const unknown = computePropertyPriority("weird_json", mkCtx({ propType: "map" }));
196
+ expect(unknown).toBeGreaterThan(known);
197
+ });
198
+ });
199
+
200
+ describe("Tier 11: Array types", () => {
201
+ it("ranks array types in tier 11", () => {
202
+ const score = computePropertyPriority("tags", mkCtx({ propType: "array" }));
203
+ expect(score).toBeGreaterThanOrEqual(110);
204
+ expect(score).toBeLessThan(120);
205
+ });
206
+ });
207
+
208
+ describe("Tier 12: System timestamps", () => {
209
+ it.each(["created_at", "updated_at", "deleted_at", "archived_at"])(
210
+ "ranks '%s' in tier 12 (120-129)",
211
+ (col) => {
212
+ const score = computePropertyPriority(col, mkCtx({ propType: "date" }));
213
+ expect(score).toBeGreaterThanOrEqual(120);
214
+ expect(score).toBeLessThan(130);
215
+ },
216
+ );
217
+
218
+ it("ranks created_at before updated_at", () => {
219
+ const created = computePropertyPriority("created_at", mkCtx({ propType: "date" }));
220
+ const updated = computePropertyPriority("updated_at", mkCtx({ propType: "date" }));
221
+ expect(created).toBeLessThan(updated);
222
+ });
223
+
224
+ it("ranks updated_at before deleted_at", () => {
225
+ const updated = computePropertyPriority("updated_at", mkCtx({ propType: "date" }));
226
+ const deleted = computePropertyPriority("deleted_at", mkCtx({ propType: "date" }));
227
+ expect(updated).toBeLessThan(deleted);
228
+ });
229
+ });
230
+
231
+ describe("tiebreaking with originalIndex", () => {
232
+ it("preserves original column order within the same tier", () => {
233
+ const a = computePropertyPriority("color", mkCtx({ originalIndex: 0 }));
234
+ const b = computePropertyPriority("size", mkCtx({ originalIndex: 1 }));
235
+ expect(a).toBeLessThan(b);
236
+ });
237
+
238
+ it("tiebreaker is fractional (doesn't cross tier boundaries)", () => {
239
+ const lastInTier = computePropertyPriority("color", mkCtx({ originalIndex: 9999 }));
240
+ // Should still be in tier 4 (42.x) not tier 5 (50+)
241
+ expect(lastInTier).toBeLessThan(50);
242
+ });
243
+ });
244
+ });
245
+
246
+ // ═══════════════════════════════════════════════════════════════════════
247
+ // sortPropertiesOrder() — full sorting integration
248
+ // ═══════════════════════════════════════════════════════════════════════
249
+ describe("sortPropertiesOrder", () => {
250
+ it("places 'id' before 'name' before generic fields", () => {
251
+ const entries: PropertyOrderEntry[] = [
252
+ mkEntry("created_at", { propType: "date", originalIndex: 3 }),
253
+ mkEntry("name", { originalIndex: 1 }),
254
+ mkEntry("id", { isPk: true, originalIndex: 0 }),
255
+ mkEntry("status", { originalIndex: 2 }),
256
+ ];
257
+ const result = sortPropertiesOrder(entries);
258
+ expect(result).toEqual(["id", "name", "status", "created_at"]);
259
+ });
260
+
261
+ it("sorts a realistic user table correctly", () => {
262
+ const entries: PropertyOrderEntry[] = [
263
+ mkEntry("id", { isPk: true, propType: "string", pgDataType: "uuid", originalIndex: 0 }),
264
+ mkEntry("created_at", { propType: "date", originalIndex: 1 }),
265
+ mkEntry("updated_at", { propType: "date", originalIndex: 2 }),
266
+ mkEntry("email", { originalIndex: 3 }),
267
+ mkEntry("first_name", { originalIndex: 4 }),
268
+ mkEntry("last_name", { originalIndex: 5 }),
269
+ mkEntry("avatar", { isStorage: true, originalIndex: 6 }),
270
+ mkEntry("role", { isEnum: true, originalIndex: 7 }),
271
+ mkEntry("active", { propType: "boolean", originalIndex: 8 }),
272
+ mkEntry("bio", { pgDataType: "text", originalIndex: 9 }),
273
+ mkEntry("metadata", { propType: "map", pgDataType: "jsonb", originalIndex: 10 }),
274
+ ];
275
+ const result = sortPropertiesOrder(entries);
276
+ expect(result).toEqual([
277
+ "id", // tier 0: PK
278
+ "first_name", // tier 2: human identity
279
+ "last_name", // tier 2: human identity
280
+ "email", // tier 2: human identity
281
+ "role", // tier 4: enum
282
+ "active", // tier 4: boolean
283
+ "bio", // tier 7: long text
284
+ "avatar", // tier 9: storage
285
+ "metadata", // tier 10: map
286
+ "created_at", // tier 12: system timestamp
287
+ "updated_at", // tier 12: system timestamp
288
+ ]);
289
+ });
290
+
291
+ it("sorts a realistic product table correctly", () => {
292
+ const entries: PropertyOrderEntry[] = [
293
+ mkEntry("id", { isPk: true, propType: "number", pgDataType: "integer", originalIndex: 0 }),
294
+ mkEntry("created_at", { propType: "date", originalIndex: 1 }),
295
+ mkEntry("sku", { originalIndex: 2 }),
296
+ mkEntry("name", { originalIndex: 3 }),
297
+ mkEntry("description", { pgDataType: "text", originalIndex: 4 }),
298
+ mkEntry("price", { propType: "number", originalIndex: 5 }),
299
+ mkEntry("category", { propType: "relation", originalIndex: 6 }),
300
+ mkEntry("cover_image", { isStorage: true, originalIndex: 7 }),
301
+ mkEntry("active", { propType: "boolean", originalIndex: 8 }),
302
+ ];
303
+ const result = sortPropertiesOrder(entries);
304
+ expect(result).toEqual([
305
+ "id", // tier 0: PK
306
+ "name", // tier 1: title/name
307
+ "sku", // tier 3: descriptor
308
+ "category", // tier 3: descriptor (name match overrides relation tier)
309
+ "active", // tier 4: boolean
310
+ "price", // tier 5: number
311
+ "description", // tier 7: long text
312
+ "cover_image", // tier 9: storage
313
+ "created_at", // tier 12: system timestamp
314
+ ]);
315
+ });
316
+
317
+ it("sorts a blog post table correctly", () => {
318
+ const entries: PropertyOrderEntry[] = [
319
+ mkEntry("id", { isPk: true, propType: "string", pgDataType: "uuid", originalIndex: 0 }),
320
+ mkEntry("updated_at", { propType: "date", originalIndex: 1 }),
321
+ mkEntry("created_at", { propType: "date", originalIndex: 2 }),
322
+ mkEntry("content", { pgDataType: "text", originalIndex: 3 }),
323
+ mkEntry("title", { originalIndex: 4 }),
324
+ mkEntry("slug", { originalIndex: 5 }),
325
+ mkEntry("status", { isEnum: true, originalIndex: 6 }),
326
+ mkEntry("author", { propType: "relation", originalIndex: 7 }),
327
+ mkEntry("published_at", { propType: "date", originalIndex: 8 }),
328
+ mkEntry("cover_image", { isStorage: true, originalIndex: 9 }),
329
+ mkEntry("excerpt", { pgDataType: "text", originalIndex: 10 }),
330
+ ];
331
+ const result = sortPropertiesOrder(entries);
332
+ expect(result).toEqual([
333
+ "id", // tier 0: PK
334
+ "title", // tier 1: title
335
+ "slug", // tier 3: descriptor
336
+ "status", // tier 4: enum
337
+ "published_at", // tier 5: user-facing date
338
+ "author", // tier 6: relation
339
+ "excerpt", // tier 7: long text
340
+ "content", // tier 8: rich content
341
+ "cover_image", // tier 9: storage
342
+ "created_at", // tier 12: system timestamp
343
+ "updated_at", // tier 12: system timestamp
344
+ ]);
345
+ });
346
+
347
+ it("preserves original order for properties in the same tier", () => {
348
+ const entries: PropertyOrderEntry[] = [
349
+ mkEntry("color", { propType: "string", pgDataType: "character varying", originalIndex: 0 }),
350
+ mkEntry("size", { propType: "string", pgDataType: "character varying", originalIndex: 1 }),
351
+ mkEntry("weight", { propType: "string", pgDataType: "character varying", originalIndex: 2 }),
352
+ ];
353
+ const result = sortPropertiesOrder(entries);
354
+ // All three are tier 4 (short strings), should preserve original order
355
+ expect(result).toEqual(["color", "size", "weight"]);
356
+ });
357
+
358
+ it("handles tables with only system columns", () => {
359
+ const entries: PropertyOrderEntry[] = [
360
+ mkEntry("id", { isPk: true, originalIndex: 0 }),
361
+ mkEntry("created_at", { propType: "date", originalIndex: 1 }),
362
+ mkEntry("updated_at", { propType: "date", originalIndex: 2 }),
363
+ ];
364
+ const result = sortPropertiesOrder(entries);
365
+ expect(result).toEqual(["id", "created_at", "updated_at"]);
366
+ });
367
+
368
+ it("handles partial name matches (product_name, page_title)", () => {
369
+ const entries: PropertyOrderEntry[] = [
370
+ mkEntry("id", { isPk: true, originalIndex: 0 }),
371
+ mkEntry("product_name", { originalIndex: 1 }),
372
+ mkEntry("category", { originalIndex: 2 }),
373
+ mkEntry("price", { propType: "number", originalIndex: 3 }),
374
+ ];
375
+ const result = sortPropertiesOrder(entries);
376
+ // product_name should come after id but before category and price
377
+ expect(result[0]).toBe("id");
378
+ expect(result[1]).toBe("product_name");
379
+ });
380
+
381
+ it("handles mixed URL and media fields", () => {
382
+ const entries: PropertyOrderEntry[] = [
383
+ mkEntry("name", { originalIndex: 0 }),
384
+ mkEntry("website_url", { originalIndex: 1 }),
385
+ mkEntry("avatar", { isStorage: true, originalIndex: 2 }),
386
+ mkEntry("thumbnail", { originalIndex: 3 }),
387
+ ];
388
+ const result = sortPropertiesOrder(entries);
389
+ expect(result[0]).toBe("name");
390
+ // All media/url fields should cluster together after name
391
+ expect(result.indexOf("website_url")).toBeGreaterThan(result.indexOf("name"));
392
+ expect(result.indexOf("avatar")).toBeGreaterThan(result.indexOf("name"));
393
+ expect(result.indexOf("thumbnail")).toBeGreaterThan(result.indexOf("name"));
394
+ });
395
+ });
@@ -382,8 +382,8 @@ relationName: "author" }
382
382
  // Should create owning relation on profiles
383
383
  expect(cleanResult).toContain("export const profilesRelations = drizzleRelations(profiles, ({ one, many }) => ({ \"author\": one(authors, { fields: [profiles.author_id], references: [authors.id], relationName: \"profiles_author_id\" }) }));");
384
384
 
385
- // Should create inverse relation on authors (this was previously missing)
386
- expect(cleanResult).toContain("export const authorsRelations = drizzleRelations(authors, ({ one, many }) => ({ \"profile\": one(profiles, { fields: [authors.id], references: [profiles.author_id], relationName: \"profiles_author_id\" }) }));");
385
+ // Should create inverse relation on authors inverse side has NO fields/references
386
+ expect(cleanResult).toContain("export const authorsRelations = drizzleRelations(authors, ({ one, many }) => ({ \"profile\": one(profiles, { relationName: \"profiles_author_id\" }) }));");
387
387
  });
388
388
 
389
389
  it("should generate owning one-to-many relations", async () => {
@@ -818,9 +818,9 @@ relationName: "user" }
818
818
  `"user": one(users, { fields: [profiles.user_id], references: [users.id], relationName: \"${expectedSharedName}\" })`
819
819
  );
820
820
 
821
- // Inverse side (users → profiles)
821
+ // Inverse side (users → profiles) — no fields/references, paired by relationName only
822
822
  expect(cleanResult).toContain(
823
- `"profile": one(profiles, { fields: [users.id], references: [profiles.user_id], relationName: \"${expectedSharedName}\" })`
823
+ `"profile": one(profiles, { relationName: \"${expectedSharedName}\" })`
824
824
  );
825
825
 
826
826
  // Both must match