@rebasepro/server-postgresql 0.0.1-canary.629af03 → 0.0.1-canary.94dff14

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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rebasepro/server-postgresql",
3
3
  "type": "module",
4
- "version": "0.0.1-canary.629af03",
4
+ "version": "0.0.1-canary.94dff14",
5
5
  "description": "PostgreSQL data source backend implementation for Rebase with Drizzle ORM",
6
6
  "funding": {
7
7
  "url": "https://github.com/sponsors/rebaseco"
@@ -64,10 +64,10 @@
64
64
  "drizzle-orm": "^0.44.4",
65
65
  "execa": "^4.1.0",
66
66
  "pg": "^8.11.3",
67
- "@rebasepro/server-core": "0.0.1-canary.629af03",
68
- "@rebasepro/types": "0.0.1-canary.629af03",
69
- "@rebasepro/utils": "0.0.1-canary.629af03",
70
- "@rebasepro/common": "0.0.1-canary.629af03"
67
+ "@rebasepro/common": "0.0.1-canary.94dff14",
68
+ "@rebasepro/utils": "0.0.1-canary.94dff14",
69
+ "@rebasepro/server-core": "0.0.1-canary.94dff14",
70
+ "@rebasepro/types": "0.0.1-canary.94dff14"
71
71
  },
72
72
  "devDependencies": {
73
73
  "@types/jest": "^29.5.14",
@@ -159,7 +159,7 @@ export function mapPgType(dataType: string): string {
159
159
  if (dt === "interval") return "string";
160
160
 
161
161
  // Array types MUST be checked before numeric ("_int4" contains "int")
162
- if (dt === "array" || dt.startsWith("_")) return "json";
162
+ if (dt === "array" || dt.startsWith("_")) return "array";
163
163
 
164
164
  // Numeric types
165
165
  if (
@@ -183,7 +183,7 @@ export function mapPgType(dataType: string): string {
183
183
  if (dt.includes("time") || dt.includes("date")) return "date";
184
184
 
185
185
  // JSON
186
- if (dt === "json" || dt === "jsonb") return "json";
186
+ if (dt === "json" || dt === "jsonb") return "map";
187
187
 
188
188
  // Binary
189
189
  if (dt === "bytea") return "string";
@@ -321,6 +321,20 @@ export function generateCollectionFile(
321
321
  }
322
322
  }
323
323
 
324
+ // Array/Map heuristics
325
+ if (propType === "array") {
326
+ let innerType = "string";
327
+ if (col.udt_name.startsWith("_")) {
328
+ const baseType = col.udt_name.substring(1);
329
+ // Simple recursive check or hardcoded for inner type:
330
+ // We'll just call mapPgType on the baseType
331
+ innerType = mapPgType(baseType);
332
+ }
333
+ extra = `\n of: { type: "${innerType}" },`;
334
+ } else if (propType === "map") {
335
+ extra = `\n keyValue: true,`;
336
+ }
337
+
324
338
  // String sub-type heuristics (skip if already handled as enum)
325
339
  if (propType === "string" && !isEnumColumn) {
326
340
  if (colNameLower.includes("image") || colNameLower.includes("avatar") || colNameLower.includes("photo") || colNameLower.includes("logo") || colNameLower.includes("cover")) {
@@ -509,11 +523,14 @@ export default ${tableName}Collection;
509
523
  */
510
524
  export function generateIndexContent(fileNames: string[]): string {
511
525
  const sorted = [...fileNames].sort();
512
- let content = "";
526
+ let imports = "";
527
+ let arrayElements = "";
513
528
  for (const f of sorted) {
514
- content += `export { default as ${f} } from "./${f}";\n`;
529
+ const varName = toCollectionVarName(f);
530
+ imports += `import ${varName} from "./${f}";\n`;
531
+ arrayElements += ` ${varName},\n`;
515
532
  }
516
- return content;
533
+ return `${imports}\nexport const collections = [\n${arrayElements}];\n`;
517
534
  }
518
535
 
519
536
  /**
@@ -521,17 +538,50 @@ export function generateIndexContent(fileNames: string[]): string {
521
538
  * Returns the merged content string.
522
539
  */
523
540
  export function mergeIndexContent(existingContent: string, newFileNames: string[]): string {
524
- const existingExports = new Set(
525
- [...existingContent.matchAll(/export\s+\{[^}]+\}\s+from\s+"\.\/([^"]+)"/g)].map((m) => m[1])
541
+ const existingImports = new Set(
542
+ [...existingContent.matchAll(/import\s+([a-zA-Z0-9_]+)\s+from\s+"\.\/([^"]+)"/g)].map((m) => m[2])
526
543
  );
527
544
  const sorted = [...newFileNames].sort();
528
- let merged = existingContent.trimEnd() + "\n";
545
+
546
+ let newImports = "";
547
+ let newElements = "";
548
+
529
549
  for (const f of sorted) {
530
- if (!existingExports.has(f)) {
531
- merged += `export { default as ${f} } from "./${f}";\n`;
550
+ if (!existingImports.has(f)) {
551
+ const varName = toCollectionVarName(f);
552
+ newImports += `import ${varName} from "./${f}";\n`;
553
+ newElements += ` ${varName},\n`;
532
554
  }
533
555
  }
534
- return merged;
556
+
557
+ if (!newImports) return existingContent;
558
+
559
+ // Simple injection logic:
560
+ // Add new imports below the last import or at the top
561
+ const importRegex = /import\s+.*?;/g;
562
+ let lastImportMatch;
563
+ let match;
564
+ while ((match = importRegex.exec(existingContent)) !== null) {
565
+ lastImportMatch = match;
566
+ }
567
+
568
+ let contentWithImports = existingContent;
569
+ if (lastImportMatch) {
570
+ const pos = lastImportMatch.index + lastImportMatch[0].length;
571
+ contentWithImports = existingContent.slice(0, pos) + "\n" + newImports.trimEnd() + existingContent.slice(pos);
572
+ } else {
573
+ contentWithImports = newImports + "\n" + existingContent;
574
+ }
575
+
576
+ // Inject into the `collections = [...]` array
577
+ const arrayRegex = /export\s+const\s+collections\s*=\s*\[([\s\S]*?)\];/;
578
+ return contentWithImports.replace(arrayRegex, (fullMatch, arrayContent) => {
579
+ let mergedArray = arrayContent.trimEnd();
580
+ if (mergedArray && !mergedArray.endsWith(",")) mergedArray += ",";
581
+ if (mergedArray) mergedArray += "\n";
582
+ mergedArray += newElements.trimEnd();
583
+ return `export const collections = [\n ${mergedArray.trim()}\n];`;
584
+ });
535
585
  }
536
586
 
537
587
  /**
@@ -49,7 +49,7 @@ describe("generateCollectionFile", () => {
49
49
  expect(result).toContain('type: "number"');
50
50
  expect(result).toContain('type: "boolean"');
51
51
  expect(result).toContain('type: "date"');
52
- expect(result).toContain('type: "json"');
52
+ expect(result).toContain('type: "map"');
53
53
  });
54
54
 
55
55
  it("skips FK columns from properties (they become relations)", () => {
@@ -190,14 +190,14 @@ describe("mapPgType", () => {
190
190
  expect(mapPgType(t)).toBe("date");
191
191
  }
192
192
  });
193
- it("maps JSON types to json", () => {
194
- expect(mapPgType("json")).toBe("json");
195
- expect(mapPgType("jsonb")).toBe("json");
193
+ it("maps JSON types to map", () => {
194
+ expect(mapPgType("json")).toBe("map");
195
+ expect(mapPgType("jsonb")).toBe("map");
196
196
  });
197
- it("maps ARRAY and underscore-prefixed types to json", () => {
198
- expect(mapPgType("ARRAY")).toBe("json");
199
- expect(mapPgType("_int4")).toBe("json");
200
- expect(mapPgType("_text")).toBe("json");
197
+ it("maps ARRAY and underscore-prefixed types to array", () => {
198
+ expect(mapPgType("ARRAY")).toBe("array");
199
+ expect(mapPgType("_int4")).toBe("array");
200
+ expect(mapPgType("_text")).toBe("array");
201
201
  });
202
202
  it("maps string-like types to string", () => {
203
203
  for (const t of ["text", "varchar", "character varying", "char", "character", "uuid", "bytea", "inet", "cidr", "macaddr", "macaddr8", "interval"]) {
@@ -350,9 +350,12 @@ describe("generateIndexContent", () => {
350
350
  expect(lines[2]).toContain("zebra");
351
351
  });
352
352
 
353
- it("each line is a default re-export", () => {
353
+ it("generates import statements and collections array", () => {
354
354
  const result = generateIndexContent(["users"]);
355
- expect(result).toBe('export { default as users } from "./users";\n');
355
+ expect(result).toContain('import usersCollection from "./users";');
356
+ expect(result).toContain('export const collections = [');
357
+ expect(result).toContain(' usersCollection,');
358
+ expect(result).toContain('];');
356
359
  });
357
360
  });
358
361
 
@@ -361,16 +364,16 @@ describe("generateIndexContent", () => {
361
364
  // ═══════════════════════════════════════════════════════════════════════
362
365
  describe("mergeIndexContent", () => {
363
366
  it("adds new exports without duplicating existing ones", () => {
364
- const existing = 'export { default as users } from "./users";\n';
367
+ const existing = 'import usersCollection from "./users";\n\nexport const collections = [\n usersCollection,\n];\n';
365
368
  const result = mergeIndexContent(existing, ["users", "posts"]);
366
- expect(result.match(/users/g)!.length).toBe(2); // one in existing, one mention in "users" still just 1 export line
367
- expect(result).toContain('export { default as posts } from "./posts";');
368
- // users should appear exactly once as an export statement
369
- expect(result.match(/export.*users.*from/g)!.length).toBe(1);
369
+ expect(result.match(/import usersCollection from ".\/users";/g)!.length).toBe(1);
370
+ expect(result).toContain('import postsCollection from "./posts";');
371
+ expect(result).toContain('usersCollection,');
372
+ expect(result).toContain('postsCollection,');
370
373
  });
371
374
 
372
375
  it("returns existing content trimmed + newline when no new files", () => {
373
- const existing = 'export { default as a } from "./a";\n';
376
+ const existing = 'import aCollection from "./a";\n\nexport const collections = [\n aCollection,\n];\n';
374
377
  const result = mergeIndexContent(existing, ["a"]);
375
378
  expect(result.trim()).toBe(existing.trim());
376
379
  });