@casekit/orm2-migrate 0.0.0-20250331181319 → 0.0.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 (33) hide show
  1. package/build/index.d.ts +3 -15
  2. package/build/pull/getColumns.d.ts +34 -0
  3. package/build/pull/{getTables.js → getColumns.js} +31 -4
  4. package/build/pull/{getTables.test.js → getColumns.test.js} +26 -4
  5. package/build/pull/getForeignKeys.d.ts +18 -0
  6. package/build/pull/getForeignKeys.js +72 -0
  7. package/build/pull/getForeignKeys.test.js +86 -0
  8. package/build/pull/getPrimaryKeys.d.ts +14 -0
  9. package/build/pull/getPrimaryKeys.js +30 -0
  10. package/build/pull/getPrimaryKeys.test.d.ts +1 -0
  11. package/build/pull/getPrimaryKeys.test.js +87 -0
  12. package/build/pull/getUniqueConstraints.d.ts +14 -0
  13. package/build/pull/getUniqueConstraints.js +27 -0
  14. package/build/pull/getUniqueConstraints.test.d.ts +1 -0
  15. package/build/pull/getUniqueConstraints.test.js +99 -0
  16. package/build/pull/index.d.ts +4 -0
  17. package/build/pull/index.js +1 -0
  18. package/build/pull/pullDefault.d.ts +6 -0
  19. package/build/pull/pullDefault.js +44 -0
  20. package/build/pull/pullDefault.test.d.ts +1 -0
  21. package/build/pull/pullDefault.test.js +82 -0
  22. package/build/pull.d.ts +12 -14
  23. package/build/pull.js +54 -2
  24. package/build/push/createTableSql.test.js +11 -0
  25. package/package.json +18 -18
  26. package/build/pull/getTables.d.ts +0 -60
  27. package/build/render/renderDefault.d.ts +0 -2
  28. package/build/render/renderDefault.js +0 -12
  29. package/build/render/renderDefault.test.js +0 -23
  30. package/build/render/renderModel.d.ts +0 -2
  31. package/build/render/renderModel.js +0 -35
  32. /package/build/pull/{getTables.test.d.ts → getColumns.test.d.ts} +0 -0
  33. /package/build/{render/renderDefault.test.d.ts → pull/getForeignKeys.test.d.ts} +0 -0
package/build/index.d.ts CHANGED
@@ -1,19 +1,7 @@
1
1
  export declare const migrate: {
2
2
  drop: (db: import("@casekit/orm2").Orm) => Promise<void>;
3
3
  push: (db: import("@casekit/orm2").Orm) => Promise<void>;
4
- pull: (db: import("@casekit/orm2").Orm, schemas: string[]) => Promise<{
5
- schema: string;
6
- table: string;
7
- column: string;
8
- ordinalPosition: number | null;
9
- type: string;
10
- default: string | null;
11
- nullable: boolean;
12
- udtSchema: string;
13
- udt: string;
14
- elementType: string | null;
15
- elementTypeSchema: string | null;
16
- cardinality: number;
17
- size: number | null;
18
- }[]>;
4
+ pull: (db: import("@casekit/orm2").Orm, schemas: string[]) => Promise<import("#pull.js").Table[]>;
19
5
  };
6
+ export type { Table } from "#pull.js";
7
+ export type { Column, ForeignKey, PrimaryKey, UniqueConstraint, } from "#pull/index.js";
@@ -0,0 +1,34 @@
1
+ import { z } from "zod";
2
+ export declare const ColumnSchema: z.ZodObject<{
3
+ schema: z.ZodString;
4
+ table: z.ZodString;
5
+ column: z.ZodString;
6
+ ordinalPosition: z.ZodNullable<z.ZodNumber>;
7
+ type: z.ZodString;
8
+ default: z.ZodNullable<z.ZodString>;
9
+ nullable: z.ZodBoolean;
10
+ udtSchema: z.ZodString;
11
+ udt: z.ZodString;
12
+ elementType: z.ZodNullable<z.ZodString>;
13
+ elementTypeSchema: z.ZodNullable<z.ZodString>;
14
+ cardinality: z.ZodNumber;
15
+ size: z.ZodNullable<z.ZodNumber>;
16
+ isSerial: z.ZodBoolean;
17
+ }, z.core.$strip>;
18
+ export type Column = z.infer<typeof ColumnSchema>;
19
+ export declare const getColumns: (schemas: string[]) => import("@casekit/orm2").SQLStatement<{
20
+ schema: string;
21
+ table: string;
22
+ column: string;
23
+ ordinalPosition: number | null;
24
+ type: string;
25
+ default: string | null;
26
+ nullable: boolean;
27
+ udtSchema: string;
28
+ udt: string;
29
+ elementType: string | null;
30
+ elementTypeSchema: string | null;
31
+ cardinality: number;
32
+ size: number | null;
33
+ isSerial: boolean;
34
+ }>;
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { sql } from "@casekit/orm2";
3
- export const TableColumnSchema = z.object({
3
+ export const ColumnSchema = z.object({
4
4
  schema: z.string(),
5
5
  table: z.string(),
6
6
  column: z.string(),
@@ -14,14 +14,20 @@ export const TableColumnSchema = z.object({
14
14
  elementTypeSchema: z.string().nullable(),
15
15
  cardinality: z.number(),
16
16
  size: z.number().nullable(),
17
+ isSerial: z.boolean(),
17
18
  });
18
- export const getTables = (schemas) => sql(TableColumnSchema) `
19
+ export const getColumns = (schemas) => sql(ColumnSchema) `
19
20
  SELECT
20
21
  c.table_schema AS "schema",
21
22
  c.table_name AS "table",
22
23
  c.column_name AS "column",
23
24
  c.ordinal_position AS "ordinalPosition",
24
- c.data_type AS "type",
25
+ CASE
26
+ WHEN c.udt_name = 'int2vector' THEN 'int2vector'
27
+ WHEN c.udt_name = 'oidvector' THEN 'oidvector'
28
+ WHEN c.data_type = 'ARRAY' AND c.udt_name LIKE '\_int2vector' THEN 'int2vector[]'
29
+ ELSE c.data_type
30
+ END AS "type",
25
31
  c.column_default AS "default",
26
32
  c.is_nullable = 'YES' AS "nullable",
27
33
  c.udt_schema AS "udtSchema",
@@ -29,7 +35,8 @@ export const getTables = (schemas) => sql(TableColumnSchema) `
29
35
  e.data_type AS "elementType",
30
36
  e.udt_schema AS "elementTypeSchema",
31
37
  pa.attndims AS cardinality,
32
- c.character_maximum_length AS "size"
38
+ c.character_maximum_length AS "size",
39
+ COALESCE(seq_owned.is_serial, false) AS "isSerial"
33
40
  FROM
34
41
  information_schema.tables t
35
42
  JOIN information_schema.columns c ON t.table_name = c.table_name
@@ -46,6 +53,26 @@ export const getTables = (schemas) => sql(TableColumnSchema) `
46
53
  AND e.object_type = 'TABLE'
47
54
  AND e.collection_type_identifier = c.dtd_identifier
48
55
  AND c.data_type = 'ARRAY'
56
+ LEFT JOIN (
57
+ SELECT
58
+ pn.nspname AS schema_name,
59
+ pc.relname AS table_name,
60
+ pa.attname AS column_name,
61
+ true AS is_serial
62
+ FROM
63
+ pg_depend pd
64
+ JOIN pg_class pc ON pd.refobjid = pc.oid AND pc.relkind = 'r'
65
+ JOIN pg_attribute pa ON pd.refobjid = pa.attrelid AND pd.refobjsubid = pa.attnum
66
+ JOIN pg_namespace pn ON pc.relnamespace = pn.oid
67
+ JOIN pg_class seq ON pd.objid = seq.oid AND seq.relkind = 'S'
68
+ WHERE
69
+ pd.deptype = 'a'
70
+ AND pd.classid = 'pg_class'::regclass
71
+ AND pd.refclassid = 'pg_class'::regclass
72
+ AND pn.nspname IN (${schemas})
73
+ ) seq_owned ON seq_owned.schema_name = c.table_schema
74
+ AND seq_owned.table_name = c.table_name
75
+ AND seq_owned.column_name = c.column_name
49
76
  WHERE
50
77
  t.table_schema IN (${schemas})
51
78
  AND t.table_type = 'BASE TABLE'
@@ -1,7 +1,7 @@
1
1
  import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test, } from "vitest";
2
2
  import { orm, sql } from "@casekit/orm2";
3
- import { getTables } from "./getTables.js";
4
- describe("getTables", () => {
3
+ import { getColumns } from "./getColumns.js";
4
+ describe("getColumns", () => {
5
5
  const db = orm({
6
6
  schema: "migrate-get-tables-test",
7
7
  models: {},
@@ -20,7 +20,7 @@ describe("getTables", () => {
20
20
  await db.query `DROP SCHEMA IF EXISTS "migrate-get-tables-test" CASCADE;`;
21
21
  });
22
22
  test("with an empty database it returns no tables or columns", async () => {
23
- const statement = getTables(["migrate-get-tables-test"]);
23
+ const statement = getColumns(["migrate-get-tables-test"]);
24
24
  const result = await db.query(statement);
25
25
  expect(result).toEqual([]);
26
26
  });
@@ -59,16 +59,19 @@ describe("getTables", () => {
59
59
  type: "smallint",
60
60
  default: expect.stringContaining("nextval"),
61
61
  nullable: false,
62
+ isSerial: true,
62
63
  }),
63
64
  expect.objectContaining({
64
65
  type: "integer",
65
66
  default: expect.stringContaining("nextval"),
66
67
  nullable: false,
68
+ isSerial: true,
67
69
  }),
68
70
  expect.objectContaining({
69
71
  type: "bigint",
70
72
  default: expect.stringContaining("nextval"),
71
73
  nullable: false,
74
+ isSerial: true,
72
75
  }),
73
76
  ],
74
77
  ],
@@ -288,10 +291,29 @@ describe("getTables", () => {
288
291
  expect.objectContaining({ type: "macaddr" }),
289
292
  ],
290
293
  ],
294
+ [
295
+ "handles sql now() default values correctly",
296
+ sql `
297
+ CREATE TABLE "migrate-get-tables-test"."timestamp_defaults_test" (
298
+ "created_at" TIMESTAMP DEFAULT now(),
299
+ "updated_at" TIMESTAMP WITH TIME ZONE DEFAULT now()
300
+ );
301
+ `,
302
+ [
303
+ expect.objectContaining({
304
+ type: "timestamp without time zone",
305
+ default: "now()",
306
+ }),
307
+ expect.objectContaining({
308
+ type: "timestamp with time zone",
309
+ default: "now()",
310
+ }),
311
+ ],
312
+ ],
291
313
  ])("%s", async ([_, query, expected]) => {
292
314
  await db.transact(async (db) => {
293
315
  await db.query(query);
294
- const result = await db.query(getTables(["migrate-get-tables-test"]));
316
+ const result = await db.query(getColumns(["migrate-get-tables-test"]));
295
317
  expect(result).toEqual(expected);
296
318
  }, { rollback: true });
297
319
  });
@@ -0,0 +1,18 @@
1
+ import { z } from "zod";
2
+ export declare const ForeignKeySchema: z.ZodObject<{
3
+ schema: z.ZodString;
4
+ constraintName: z.ZodString;
5
+ tableFrom: z.ZodString;
6
+ columnsFrom: z.ZodArray<z.ZodString>;
7
+ tableTo: z.ZodString;
8
+ columnsTo: z.ZodArray<z.ZodString>;
9
+ }, z.core.$strip>;
10
+ export type ForeignKey = z.infer<typeof ForeignKeySchema>;
11
+ export declare const getForeignKeys: (schemas: string[]) => import("@casekit/orm2").SQLStatement<{
12
+ schema: string;
13
+ constraintName: string;
14
+ tableFrom: string;
15
+ columnsFrom: string[];
16
+ tableTo: string;
17
+ columnsTo: string[];
18
+ }>;
@@ -0,0 +1,72 @@
1
+ import { z } from "zod";
2
+ import { sql } from "@casekit/orm2";
3
+ export const ForeignKeySchema = z.object({
4
+ schema: z.string(),
5
+ constraintName: z.string(),
6
+ tableFrom: z.string(),
7
+ columnsFrom: z.array(z.string()),
8
+ tableTo: z.string(),
9
+ columnsTo: z.array(z.string()),
10
+ });
11
+ export const getForeignKeys = (schemas) => sql(ForeignKeySchema) `
12
+ SELECT
13
+ nspname AS "schema",
14
+ conname AS "constraintName",
15
+ table_from AS "tableFrom",
16
+ array_agg(columns_from::text ORDER BY ordinality) AS "columnsFrom",
17
+ table_to AS "tableTo",
18
+ array_agg(columns_to::text ORDER BY ordinality) AS "columnsTo"
19
+ FROM (
20
+ SELECT
21
+ conname,
22
+ c.relname AS table_from,
23
+ a.attname AS columns_from,
24
+ cf.relname AS table_to,
25
+ af.attname AS columns_to,
26
+ n.nspname,
27
+ ss2.ordinality
28
+ FROM
29
+ pg_attribute AS af,
30
+ pg_attribute AS a,
31
+ pg_class c,
32
+ pg_class cf,
33
+ pg_namespace n,
34
+ (
35
+ SELECT
36
+ conname,
37
+ conrelid,
38
+ confrelid,
39
+ conkey[i] AS conkey,
40
+ confkey[i] AS confkey,
41
+ i AS ordinality
42
+ FROM (
43
+ SELECT
44
+ conname,
45
+ conrelid,
46
+ confrelid,
47
+ conkey,
48
+ confkey,
49
+ generate_series(1, array_upper(conkey, 1)) AS i
50
+ FROM
51
+ pg_constraint
52
+ WHERE
53
+ contype = 'f') AS ss) AS ss2
54
+ WHERE
55
+ af.attnum = confkey
56
+ AND af.attrelid = confrelid
57
+ AND a.attnum = conkey
58
+ AND a.attrelid = conrelid
59
+ AND a.attrelid = c.oid
60
+ AND af.attrelid = cf.oid
61
+ AND c.relnamespace = n.oid
62
+ AND n.nspname IN (${schemas})) AS ss3
63
+ GROUP BY
64
+ nspname,
65
+ conname,
66
+ table_to,
67
+ table_from
68
+ ORDER BY
69
+ nspname,
70
+ table_from,
71
+ conname
72
+ `;
@@ -0,0 +1,86 @@
1
+ import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test, } from "vitest";
2
+ import { orm, sql } from "@casekit/orm2";
3
+ import { getForeignKeys } from "./getForeignKeys.js";
4
+ describe("getForeignKeys", () => {
5
+ const db = orm({
6
+ schema: "migrate-get-foreign-keys-test",
7
+ models: {},
8
+ });
9
+ beforeAll(async () => {
10
+ await db.connect();
11
+ });
12
+ afterAll(async () => {
13
+ await db.close();
14
+ });
15
+ beforeEach(async () => {
16
+ await db.query `DROP SCHEMA IF EXISTS "migrate-get-foreign-keys-test" CASCADE;`;
17
+ await db.query `CREATE SCHEMA IF NOT EXISTS "migrate-get-foreign-keys-test";`;
18
+ });
19
+ afterEach(async () => {
20
+ await db.query `DROP SCHEMA IF EXISTS "migrate-get-foreign-keys-test" CASCADE;`;
21
+ });
22
+ test("with an empty database it returns no foreign keys", async () => {
23
+ const statement = getForeignKeys(["migrate-get-foreign-keys-test"]);
24
+ const result = await db.query(statement);
25
+ expect(result).toEqual([]);
26
+ });
27
+ test("returns foreign key relationships", async () => {
28
+ await db.transact(async (db) => {
29
+ await db.query(sql `
30
+ CREATE TABLE "migrate-get-foreign-keys-test"."users" (
31
+ "id" SERIAL PRIMARY KEY,
32
+ "name" TEXT NOT NULL
33
+ );
34
+ `);
35
+ await db.query(sql `
36
+ CREATE TABLE "migrate-get-foreign-keys-test"."posts" (
37
+ "id" SERIAL PRIMARY KEY,
38
+ "title" TEXT NOT NULL,
39
+ "user_id" INTEGER NOT NULL,
40
+ CONSTRAINT "fk_posts_user_id" FOREIGN KEY ("user_id") REFERENCES "migrate-get-foreign-keys-test"."users" ("id")
41
+ );
42
+ `);
43
+ const result = await db.query(getForeignKeys(["migrate-get-foreign-keys-test"]));
44
+ expect(result).toEqual([
45
+ expect.objectContaining({
46
+ schema: "migrate-get-foreign-keys-test",
47
+ constraintName: "fk_posts_user_id",
48
+ tableFrom: "posts",
49
+ columnsFrom: ["user_id"],
50
+ tableTo: "users",
51
+ columnsTo: ["id"],
52
+ }),
53
+ ]);
54
+ }, { rollback: true });
55
+ });
56
+ test("handles composite foreign keys", async () => {
57
+ await db.transact(async (db) => {
58
+ await db.query(sql `
59
+ CREATE TABLE "migrate-get-foreign-keys-test"."companies" (
60
+ "id" INTEGER NOT NULL,
61
+ "code" TEXT NOT NULL,
62
+ PRIMARY KEY ("id", "code")
63
+ );
64
+ `);
65
+ await db.query(sql `
66
+ CREATE TABLE "migrate-get-foreign-keys-test"."employees" (
67
+ "id" SERIAL PRIMARY KEY,
68
+ "company_id" INTEGER NOT NULL,
69
+ "company_code" TEXT NOT NULL,
70
+ CONSTRAINT "fk_employees_company" FOREIGN KEY ("company_id", "company_code") REFERENCES "migrate-get-foreign-keys-test"."companies" ("id", "code")
71
+ );
72
+ `);
73
+ const result = await db.query(getForeignKeys(["migrate-get-foreign-keys-test"]));
74
+ expect(result).toEqual([
75
+ expect.objectContaining({
76
+ schema: "migrate-get-foreign-keys-test",
77
+ constraintName: "fk_employees_company",
78
+ tableFrom: "employees",
79
+ columnsFrom: ["company_id", "company_code"],
80
+ tableTo: "companies",
81
+ columnsTo: ["id", "code"],
82
+ }),
83
+ ]);
84
+ }, { rollback: true });
85
+ });
86
+ });
@@ -0,0 +1,14 @@
1
+ import { z } from "zod";
2
+ export declare const PrimaryKeySchema: z.ZodObject<{
3
+ schema: z.ZodString;
4
+ table: z.ZodString;
5
+ constraintName: z.ZodString;
6
+ columns: z.ZodArray<z.ZodString>;
7
+ }, z.core.$strip>;
8
+ export type PrimaryKey = z.infer<typeof PrimaryKeySchema>;
9
+ export declare const getPrimaryKeys: (schemas: string[]) => import("@casekit/orm2").SQLStatement<{
10
+ schema: string;
11
+ table: string;
12
+ constraintName: string;
13
+ columns: string[];
14
+ }>;
@@ -0,0 +1,30 @@
1
+ import { z } from "zod";
2
+ import { sql } from "@casekit/orm2";
3
+ export const PrimaryKeySchema = z.object({
4
+ schema: z.string(),
5
+ table: z.string(),
6
+ constraintName: z.string(),
7
+ columns: z.array(z.string()),
8
+ });
9
+ export const getPrimaryKeys = (schemas) => sql(PrimaryKeySchema) `
10
+ SELECT
11
+ c.constraint_schema AS "schema",
12
+ c.table_name AS "table",
13
+ c.constraint_name AS "constraintName",
14
+ array_agg(cc.column_name::text ORDER BY cc.ordinal_position) AS "columns"
15
+ FROM
16
+ information_schema.table_constraints c
17
+ JOIN information_schema.key_column_usage cc ON c.constraint_name = cc.constraint_name
18
+ AND c.constraint_schema = cc.constraint_schema
19
+ AND c.table_name = cc.table_name
20
+ WHERE
21
+ c.constraint_type = 'PRIMARY KEY'
22
+ AND c.constraint_schema IN (${schemas})
23
+ GROUP BY
24
+ c.constraint_schema,
25
+ c.table_name,
26
+ c.constraint_name
27
+ ORDER BY
28
+ c.constraint_schema,
29
+ c.table_name
30
+ `;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,87 @@
1
+ import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test, } from "vitest";
2
+ import { orm, sql } from "@casekit/orm2";
3
+ import { getPrimaryKeys } from "./getPrimaryKeys.js";
4
+ describe("getPrimaryKeys", () => {
5
+ const db = orm({
6
+ schema: "migrate-get-primary-keys-test",
7
+ models: {},
8
+ });
9
+ beforeAll(async () => {
10
+ await db.connect();
11
+ });
12
+ afterAll(async () => {
13
+ await db.close();
14
+ });
15
+ beforeEach(async () => {
16
+ await db.query `DROP SCHEMA IF EXISTS "migrate-get-primary-keys-test" CASCADE;`;
17
+ await db.query `CREATE SCHEMA IF NOT EXISTS "migrate-get-primary-keys-test";`;
18
+ });
19
+ afterEach(async () => {
20
+ await db.query `DROP SCHEMA IF EXISTS "migrate-get-primary-keys-test" CASCADE;`;
21
+ });
22
+ test("with an empty database it returns no primary keys", async () => {
23
+ const statement = getPrimaryKeys(["migrate-get-primary-keys-test"]);
24
+ const result = await db.query(statement);
25
+ expect(result).toEqual([]);
26
+ });
27
+ test("returns single column primary key", async () => {
28
+ await db.transact(async (db) => {
29
+ await db.query(sql `
30
+ CREATE TABLE "migrate-get-primary-keys-test"."users" (
31
+ "id" SERIAL PRIMARY KEY,
32
+ "name" TEXT NOT NULL
33
+ );
34
+ `);
35
+ const result = await db.query(getPrimaryKeys(["migrate-get-primary-keys-test"]));
36
+ expect(result).toEqual([
37
+ expect.objectContaining({
38
+ schema: "migrate-get-primary-keys-test",
39
+ table: "users",
40
+ constraintName: "users_pkey",
41
+ columns: ["id"],
42
+ }),
43
+ ]);
44
+ }, { rollback: true });
45
+ });
46
+ test("returns composite primary key with correct order", async () => {
47
+ await db.transact(async (db) => {
48
+ await db.query(sql `
49
+ CREATE TABLE "migrate-get-primary-keys-test"."user_roles" (
50
+ "user_id" INTEGER NOT NULL,
51
+ "role_id" INTEGER NOT NULL,
52
+ "granted_at" TIMESTAMP DEFAULT NOW(),
53
+ PRIMARY KEY ("user_id", "role_id")
54
+ );
55
+ `);
56
+ const result = await db.query(getPrimaryKeys(["migrate-get-primary-keys-test"]));
57
+ expect(result).toEqual([
58
+ expect.objectContaining({
59
+ schema: "migrate-get-primary-keys-test",
60
+ table: "user_roles",
61
+ constraintName: "user_roles_pkey",
62
+ columns: ["user_id", "role_id"],
63
+ }),
64
+ ]);
65
+ }, { rollback: true });
66
+ });
67
+ test("returns named primary key constraint", async () => {
68
+ await db.transact(async (db) => {
69
+ await db.query(sql `
70
+ CREATE TABLE "migrate-get-primary-keys-test"."products" (
71
+ "id" UUID NOT NULL,
72
+ "name" TEXT NOT NULL,
73
+ CONSTRAINT "pk_products_id" PRIMARY KEY ("id")
74
+ );
75
+ `);
76
+ const result = await db.query(getPrimaryKeys(["migrate-get-primary-keys-test"]));
77
+ expect(result).toEqual([
78
+ expect.objectContaining({
79
+ schema: "migrate-get-primary-keys-test",
80
+ table: "products",
81
+ constraintName: "pk_products_id",
82
+ columns: ["id"],
83
+ }),
84
+ ]);
85
+ }, { rollback: true });
86
+ });
87
+ });
@@ -0,0 +1,14 @@
1
+ import { z } from "zod";
2
+ export declare const UniqueConstraintSchema: z.ZodObject<{
3
+ schema: z.ZodString;
4
+ table: z.ZodString;
5
+ name: z.ZodString;
6
+ definition: z.ZodString;
7
+ }, z.core.$strip>;
8
+ export type UniqueConstraint = z.infer<typeof UniqueConstraintSchema>;
9
+ export declare const getUniqueConstraints: (schemas: string[]) => import("@casekit/orm2").SQLStatement<{
10
+ schema: string;
11
+ table: string;
12
+ name: string;
13
+ definition: string;
14
+ }>;
@@ -0,0 +1,27 @@
1
+ import { z } from "zod";
2
+ import { sql } from "@casekit/orm2";
3
+ export const UniqueConstraintSchema = z.object({
4
+ schema: z.string(),
5
+ table: z.string(),
6
+ name: z.string(),
7
+ definition: z.string(),
8
+ });
9
+ export const getUniqueConstraints = (schemas) => sql(UniqueConstraintSchema) `
10
+ SELECT
11
+ i.schemaname AS "schema",
12
+ i.tablename AS "table",
13
+ i.indexname AS "name",
14
+ i.indexdef AS "definition"
15
+ FROM
16
+ pg_indexes i
17
+ LEFT JOIN pg_constraint c ON i.indexname = c.conname
18
+ AND c.contype IN ('p', 'x')
19
+ WHERE
20
+ i.schemaname IN (${schemas})
21
+ AND c.conname IS NULL
22
+ AND i.indexdef LIKE 'CREATE UNIQUE INDEX%'
23
+ ORDER BY
24
+ i.schemaname,
25
+ i.tablename,
26
+ i.indexname
27
+ `;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,99 @@
1
+ import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test, } from "vitest";
2
+ import { orm, sql } from "@casekit/orm2";
3
+ import { getUniqueConstraints } from "./getUniqueConstraints.js";
4
+ describe("getUniqueConstraints", () => {
5
+ const db = orm({
6
+ schema: "migrate-get-unique-constraints-test",
7
+ models: {},
8
+ });
9
+ beforeAll(async () => {
10
+ await db.connect();
11
+ });
12
+ afterAll(async () => {
13
+ await db.close();
14
+ });
15
+ beforeEach(async () => {
16
+ await db.query `DROP SCHEMA IF EXISTS "migrate-get-unique-constraints-test" CASCADE;`;
17
+ await db.query `CREATE SCHEMA IF NOT EXISTS "migrate-get-unique-constraints-test";`;
18
+ });
19
+ afterEach(async () => {
20
+ await db.query `DROP SCHEMA IF EXISTS "migrate-get-unique-constraints-test" CASCADE;`;
21
+ });
22
+ test("with an empty database it returns no unique constraints", async () => {
23
+ const statement = getUniqueConstraints([
24
+ "migrate-get-unique-constraints-test",
25
+ ]);
26
+ const result = await db.query(statement);
27
+ expect(result).toEqual([]);
28
+ });
29
+ test("returns unique index constraints", async () => {
30
+ await db.transact(async (db) => {
31
+ await db.query(sql `
32
+ CREATE TABLE "migrate-get-unique-constraints-test"."users" (
33
+ "id" SERIAL PRIMARY KEY,
34
+ "email" TEXT NOT NULL
35
+ );
36
+ `);
37
+ await db.query(sql `
38
+ CREATE UNIQUE INDEX "idx_users_email" ON "migrate-get-unique-constraints-test"."users" ("email");
39
+ `);
40
+ const result = await db.query(getUniqueConstraints([
41
+ "migrate-get-unique-constraints-test",
42
+ ]));
43
+ expect(result).toEqual([
44
+ expect.objectContaining({
45
+ schema: "migrate-get-unique-constraints-test",
46
+ table: "users",
47
+ name: "idx_users_email",
48
+ definition: expect.stringContaining("CREATE UNIQUE INDEX idx_users_email"),
49
+ }),
50
+ ]);
51
+ }, { rollback: true });
52
+ });
53
+ test("returns composite unique index constraints", async () => {
54
+ await db.transact(async (db) => {
55
+ await db.query(sql `
56
+ CREATE TABLE "migrate-get-unique-constraints-test"."products" (
57
+ "id" SERIAL PRIMARY KEY,
58
+ "name" TEXT NOT NULL,
59
+ "category" TEXT NOT NULL
60
+ );
61
+ `);
62
+ await db.query(sql `
63
+ CREATE UNIQUE INDEX "idx_products_name_category" ON "migrate-get-unique-constraints-test"."products" ("name", "category");
64
+ `);
65
+ const result = await db.query(getUniqueConstraints([
66
+ "migrate-get-unique-constraints-test",
67
+ ]));
68
+ expect(result).toEqual([
69
+ expect.objectContaining({
70
+ schema: "migrate-get-unique-constraints-test",
71
+ table: "products",
72
+ name: "idx_products_name_category",
73
+ definition: expect.stringContaining("CREATE UNIQUE INDEX idx_products_name_category"),
74
+ }),
75
+ ]);
76
+ }, { rollback: true });
77
+ });
78
+ test("excludes primary key constraints", async () => {
79
+ await db.transact(async (db) => {
80
+ await db.query(sql `
81
+ CREATE TABLE "migrate-get-unique-constraints-test"."users" (
82
+ "id" SERIAL PRIMARY KEY,
83
+ "email" TEXT UNIQUE
84
+ );
85
+ `);
86
+ const result = await db.query(getUniqueConstraints([
87
+ "migrate-get-unique-constraints-test",
88
+ ]));
89
+ // Should only return the unique constraint on email, not the primary key
90
+ expect(result).toHaveLength(1);
91
+ expect(result[0]).toEqual(expect.objectContaining({
92
+ schema: "migrate-get-unique-constraints-test",
93
+ table: "users",
94
+ definition: expect.stringContaining("email"),
95
+ }));
96
+ expect(result[0].definition).not.toContain("id");
97
+ }, { rollback: true });
98
+ });
99
+ });
@@ -0,0 +1,4 @@
1
+ export type { Column } from "./getColumns.js";
2
+ export type { ForeignKey } from "./getForeignKeys.js";
3
+ export type { PrimaryKey } from "./getPrimaryKeys.js";
4
+ export type { UniqueConstraint } from "./getUniqueConstraints.js";
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Normalizes PostgreSQL default values to their canonical forms
3
+ * This is the inverse of renderDefault - it takes raw PostgreSQL defaults
4
+ * and converts them back to standard representations.
5
+ */
6
+ export declare const pullDefault: (defaultValue: string | null) => string | null;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Normalizes PostgreSQL default values to their canonical forms
3
+ * This is the inverse of renderDefault - it takes raw PostgreSQL defaults
4
+ * and converts them back to standard representations.
5
+ */
6
+ export const pullDefault = (defaultValue) => {
7
+ if (defaultValue === null) {
8
+ return null;
9
+ }
10
+ // Only convert when the default is literally "now()" - not timestamp literals
11
+ if (defaultValue === "now()") {
12
+ return "now()";
13
+ }
14
+ // Handle other common SQL functions that should remain as function calls
15
+ if (/^(gen_random_uuid|uuid_generate_v\d+|current_timestamp|current_date|current_time)\(\)$/.test(defaultValue)) {
16
+ return defaultValue;
17
+ }
18
+ // Handle nextval sequences (for serial types) - keep as-is
19
+ if (/^nextval\('.*'::regclass\)$/.test(defaultValue)) {
20
+ return defaultValue;
21
+ }
22
+ // Handle simple literals without type casting
23
+ if (/^'(.*)'::(text|varchar|char|bpchar)$/.test(defaultValue)) {
24
+ return defaultValue.replace(/^'(.*)'::(text|varchar|char|bpchar)$/, "'$1'");
25
+ }
26
+ // Handle numeric literals with type casting
27
+ if (/^'(.*)'::(numeric|decimal|real|double precision|float\d*|int\d*|smallint|bigint|integer)$/.test(defaultValue)) {
28
+ return defaultValue.replace(/^'(.*)'::(numeric|decimal|real|double precision|float\d*|int\d*|smallint|bigint|integer)$/, "$1");
29
+ }
30
+ // Handle boolean literals
31
+ if (defaultValue === "true" || defaultValue === "false") {
32
+ return defaultValue;
33
+ }
34
+ // Handle NULL with type casting
35
+ if (/^NULL::.+$/.test(defaultValue)) {
36
+ return null;
37
+ }
38
+ // Handle array literals - keep as-is since they're complex
39
+ if (/^(ARRAY\[.*\]|\{.*\})/.test(defaultValue)) {
40
+ return defaultValue;
41
+ }
42
+ // For everything else, return as-is
43
+ return defaultValue;
44
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,82 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { pullDefault } from "./pullDefault.js";
3
+ describe("pullDefault", () => {
4
+ test("should return null for null values", () => {
5
+ expect(pullDefault(null)).toBe(null);
6
+ });
7
+ test("should preserve timestamp literals as-is", () => {
8
+ expect(pullDefault("'2025-06-28 15:02:55.251517'::timestamp without time zone")).toBe("'2025-06-28 15:02:55.251517'::timestamp without time zone");
9
+ expect(pullDefault("'2025-06-28 15:02:55.251517+00'::timestamptz")).toBe("'2025-06-28 15:02:55.251517+00'::timestamptz");
10
+ expect(pullDefault("'2023-01-01 12:00:00'::timestamp")).toBe("'2023-01-01 12:00:00'::timestamp");
11
+ expect(pullDefault("'2025-06-28 15:02:55'::timestamp with time zone")).toBe("'2025-06-28 15:02:55'::timestamp with time zone");
12
+ });
13
+ test("should preserve SQL function calls", () => {
14
+ expect(pullDefault("now()")).toBe("now()");
15
+ expect(pullDefault("gen_random_uuid()")).toBe("gen_random_uuid()");
16
+ expect(pullDefault("uuid_generate_v4()")).toBe("uuid_generate_v4()");
17
+ expect(pullDefault("current_timestamp()")).toBe("current_timestamp()");
18
+ expect(pullDefault("current_date()")).toBe("current_date()");
19
+ expect(pullDefault("current_time()")).toBe("current_time()");
20
+ });
21
+ test("should preserve nextval sequences for serial types", () => {
22
+ expect(pullDefault("nextval('user_id_seq'::regclass)")).toBe("nextval('user_id_seq'::regclass)");
23
+ expect(pullDefault("nextval('public.post_id_seq'::regclass)")).toBe("nextval('public.post_id_seq'::regclass)");
24
+ });
25
+ test("should handle text literals with type casting", () => {
26
+ expect(pullDefault("'hello'::text")).toBe("'hello'");
27
+ expect(pullDefault("'world'::varchar")).toBe("'world'");
28
+ expect(pullDefault("'test'::char")).toBe("'test'");
29
+ expect(pullDefault("'example'::bpchar")).toBe("'example'");
30
+ });
31
+ test("should handle numeric literals with type casting", () => {
32
+ expect(pullDefault("'123'::numeric")).toBe("123");
33
+ expect(pullDefault("'456'::integer")).toBe("456");
34
+ expect(pullDefault("'789'::bigint")).toBe("789");
35
+ expect(pullDefault("'12.34'::decimal")).toBe("12.34");
36
+ expect(pullDefault("'3.14'::real")).toBe("3.14");
37
+ expect(pullDefault("'2.718'::double precision")).toBe("2.718");
38
+ expect(pullDefault("'42'::smallint")).toBe("42");
39
+ expect(pullDefault("'99.99'::float4")).toBe("99.99");
40
+ expect(pullDefault("'88.88'::int4")).toBe("88.88");
41
+ });
42
+ test("should handle boolean literals", () => {
43
+ expect(pullDefault("true")).toBe("true");
44
+ expect(pullDefault("false")).toBe("false");
45
+ });
46
+ test("should handle NULL with type casting", () => {
47
+ expect(pullDefault("NULL::text")).toBe(null);
48
+ expect(pullDefault("NULL::integer")).toBe(null);
49
+ expect(pullDefault("NULL::timestamp")).toBe(null);
50
+ });
51
+ test("should preserve array literals", () => {
52
+ expect(pullDefault("ARRAY[1, 2, 3]")).toBe("ARRAY[1, 2, 3]");
53
+ expect(pullDefault("ARRAY['foo', 'bar']")).toBe("ARRAY['foo', 'bar']");
54
+ expect(pullDefault("ARRAY[ARRAY[ARRAY['a']]]")).toBe("ARRAY[ARRAY[ARRAY['a']]]");
55
+ expect(pullDefault("{1, 2, 3}")).toBe("{1, 2, 3}");
56
+ expect(pullDefault("{{foo, bar}, {baz, qux}}")).toBe("{{foo, bar}, {baz, qux}}");
57
+ });
58
+ test("should handle complex expressions as-is", () => {
59
+ expect(pullDefault('\'{"key": "value"}\'::jsonb')).toBe('\'{"key": "value"}\'::jsonb');
60
+ expect(pullDefault("'[]'::json")).toBe("'[]'::json");
61
+ expect(pullDefault("'happy'::mood")).toBe("'happy'::mood");
62
+ expect(pullDefault("'admin'::user_role")).toBe("'admin'::user_role");
63
+ expect(pullDefault("'(0,0)'::point")).toBe("'(0,0)'::point");
64
+ expect(pullDefault("'1 day'::interval")).toBe("'1 day'::interval");
65
+ });
66
+ test("should handle money defaults", () => {
67
+ expect(pullDefault("42.42")).toBe("42.42");
68
+ expect(pullDefault("$100.00")).toBe("$100.00");
69
+ });
70
+ test("should handle edge cases", () => {
71
+ expect(pullDefault("")).toBe("");
72
+ expect(pullDefault("0")).toBe("0");
73
+ expect(pullDefault("-1")).toBe("-1");
74
+ expect(pullDefault("'0'")).toBe("'0'");
75
+ });
76
+ test("should handle complex SQL expressions", () => {
77
+ // These should be preserved as-is since they're complex
78
+ expect(pullDefault("CASE WHEN foo = 'bar' THEN 1 ELSE 0 END")).toBe("CASE WHEN foo = 'bar' THEN 1 ELSE 0 END");
79
+ expect(pullDefault("COALESCE(column1, column2, 'default')")).toBe("COALESCE(column1, column2, 'default')");
80
+ expect(pullDefault("LENGTH('hello')")).toBe("LENGTH('hello')");
81
+ });
82
+ });
package/build/pull.d.ts CHANGED
@@ -1,16 +1,14 @@
1
1
  import { Orm } from "@casekit/orm2";
2
- export declare const pull: (db: Orm, schemas: string[]) => Promise<{
2
+ import { type Column } from "./pull/getColumns.js";
3
+ import { type ForeignKey } from "./pull/getForeignKeys.js";
4
+ import { type PrimaryKey } from "./pull/getPrimaryKeys.js";
5
+ import { type UniqueConstraint } from "./pull/getUniqueConstraints.js";
6
+ export type Table = {
3
7
  schema: string;
4
- table: string;
5
- column: string;
6
- ordinalPosition: number | null;
7
- type: string;
8
- default: string | null;
9
- nullable: boolean;
10
- udtSchema: string;
11
- udt: string;
12
- elementType: string | null;
13
- elementTypeSchema: string | null;
14
- cardinality: number;
15
- size: number | null;
16
- }[]>;
8
+ name: string;
9
+ columns: Column[];
10
+ foreignKeys: ForeignKey[];
11
+ primaryKey: PrimaryKey | null;
12
+ uniqueConstraints: UniqueConstraint[];
13
+ };
14
+ export declare const pull: (db: Orm, schemas: string[]) => Promise<Table[]>;
package/build/pull.js CHANGED
@@ -1,4 +1,56 @@
1
- import { getTables } from "./pull/getTables.js";
1
+ import { orderBy } from "es-toolkit";
2
+ import { getColumns } from "./pull/getColumns.js";
3
+ import { getForeignKeys } from "./pull/getForeignKeys.js";
4
+ import { getPrimaryKeys } from "./pull/getPrimaryKeys.js";
5
+ import { getUniqueConstraints, } from "./pull/getUniqueConstraints.js";
6
+ import { pullDefault } from "./pull/pullDefault.js";
2
7
  export const pull = async (db, schemas) => {
3
- return await db.query(getTables(schemas));
8
+ const [columns, foreignKeys, primaryKeys, uniqueConstraints] = await Promise.all([
9
+ db.query(getColumns(schemas)),
10
+ db.query(getForeignKeys(schemas)),
11
+ db.query(getPrimaryKeys(schemas)),
12
+ db.query(getUniqueConstraints(schemas)),
13
+ ]);
14
+ const tablesMap = new Map();
15
+ for (const column of orderBy(columns, ["ordinalPosition"], ["asc"])) {
16
+ const key = `${column.schema}.${column.table}`;
17
+ if (!tablesMap.has(key)) {
18
+ tablesMap.set(key, {
19
+ schema: column.schema,
20
+ name: column.table,
21
+ columns: [],
22
+ foreignKeys: [],
23
+ primaryKey: null,
24
+ uniqueConstraints: [],
25
+ });
26
+ }
27
+ // Normalize the default value using pullDefault
28
+ const normalizedColumn = {
29
+ ...column,
30
+ default: pullDefault(column.default),
31
+ };
32
+ tablesMap.get(key).columns.push(normalizedColumn);
33
+ }
34
+ for (const fk of foreignKeys) {
35
+ const key = `${fk.schema}.${fk.tableFrom}`;
36
+ const table = tablesMap.get(key);
37
+ if (table) {
38
+ table.foreignKeys.push(fk);
39
+ }
40
+ }
41
+ for (const pk of primaryKeys) {
42
+ const key = `${pk.schema}.${pk.table}`;
43
+ const table = tablesMap.get(key);
44
+ if (table) {
45
+ table.primaryKey = pk;
46
+ }
47
+ }
48
+ for (const uc of uniqueConstraints) {
49
+ const key = `${uc.schema}.${uc.table}`;
50
+ const table = tablesMap.get(key);
51
+ if (table) {
52
+ table.uniqueConstraints.push(uc);
53
+ }
54
+ }
55
+ return orderBy(Array.from(tablesMap.values()), ["schema", "name"], ["asc", "asc"]);
4
56
  };
@@ -79,6 +79,17 @@ describe("createTableSql", () => {
79
79
  );
80
80
  `,
81
81
  ],
82
+ [
83
+ "timestamp without time zone",
84
+ { default: sql `now()` },
85
+ unindent `
86
+ CREATE TABLE IF NOT EXISTS "public"."foo" (
87
+ "id" serial NOT NULL,
88
+ "value" timestamp without time zone NOT NULL DEFAULT now(),
89
+ PRIMARY KEY ("id")
90
+ );
91
+ `,
92
+ ],
82
93
  ])("%s %s", async ([type, options, expected]) => {
83
94
  const config = normalizeConfig({
84
95
  models: {
package/package.json CHANGED
@@ -1,33 +1,34 @@
1
1
  {
2
2
  "name": "@casekit/orm2-migrate",
3
3
  "description": "",
4
- "version": "0.0.0-20250331181319",
4
+ "version": "0.0.0",
5
5
  "author": "",
6
6
  "dependencies": {
7
7
  "camelcase": "^8.0.0",
8
- "@casekit/orm2": "0.0.0-20250331181319",
9
- "@casekit/orm2-config": "0.0.0-20250331181319",
10
- "@casekit/sql": "0.0.0-20250331181319",
11
- "@casekit/orm2-schema": "0.0.0-20250331181319",
12
- "@casekit/toolbox": "0.0.0-20250331181319"
8
+ "es-toolkit": "^1.39.3",
9
+ "@casekit/orm2": "0.0.0",
10
+ "@casekit/orm2-config": "0.0.0",
11
+ "@casekit/sql": "0.0.0",
12
+ "@casekit/toolbox": "0.0.0",
13
+ "@casekit/orm2-schema": "0.0.0"
13
14
  },
14
15
  "devDependencies": {
15
16
  "@casekit/unindent": "^1.0.5",
16
17
  "@trivago/prettier-plugin-sort-imports": "^5.2.2",
17
- "@types/node": "^22.13.13",
18
- "@types/pg": "^8.11.11",
18
+ "@types/node": "^24.0.3",
19
+ "@types/pg": "^8.15.4",
19
20
  "@types/uuid": "^10.0.0",
20
- "@vitest/coverage-v8": "^3.0.9",
21
- "dotenv": "^16.4.7",
21
+ "@vitest/coverage-v8": "^3.2.4",
22
+ "dotenv": "^16.5.0",
22
23
  "prettier": "^3.5.3",
23
- "prettier-plugin-svelte": "^3.3.3",
24
- "typescript": "^5.8.2",
24
+ "prettier-plugin-svelte": "^3.4.0",
25
+ "typescript": "^5.8.3",
25
26
  "uuid": "^11.1.0",
26
27
  "vite-tsconfig-paths": "^5.1.4",
27
- "vitest": "^3.0.9",
28
- "@casekit/orm2-fixtures": "0.0.0-20250331181319",
29
- "@casekit/prettier-config": "0.0.0-20250331181319",
30
- "@casekit/tsconfig": "0.0.0-20250331181319"
28
+ "vitest": "^3.2.4",
29
+ "@casekit/orm2-fixtures": "0.0.0",
30
+ "@casekit/tsconfig": "0.0.0",
31
+ "@casekit/prettier-config": "0.0.0"
31
32
  },
32
33
  "exports": {
33
34
  ".": "./build/index.js"
@@ -40,10 +41,9 @@
40
41
  },
41
42
  "keywords": [],
42
43
  "license": "ISC",
43
- "main": "index.js",
44
44
  "peerDependencies": {
45
45
  "pg": "^8.13.1",
46
- "zod": "^3.24.2"
46
+ "zod": "^4.0.17"
47
47
  },
48
48
  "prettier": "@casekit/prettier-config",
49
49
  "type": "module",
@@ -1,60 +0,0 @@
1
- import { z } from "zod";
2
- export declare const TableColumnSchema: z.ZodObject<{
3
- schema: z.ZodString;
4
- table: z.ZodString;
5
- column: z.ZodString;
6
- ordinalPosition: z.ZodNullable<z.ZodNumber>;
7
- type: z.ZodString;
8
- default: z.ZodNullable<z.ZodString>;
9
- nullable: z.ZodBoolean;
10
- udtSchema: z.ZodString;
11
- udt: z.ZodString;
12
- elementType: z.ZodNullable<z.ZodString>;
13
- elementTypeSchema: z.ZodNullable<z.ZodString>;
14
- cardinality: z.ZodNumber;
15
- size: z.ZodNullable<z.ZodNumber>;
16
- }, "strip", z.ZodTypeAny, {
17
- schema: string;
18
- table: string;
19
- column: string;
20
- ordinalPosition: number | null;
21
- type: string;
22
- default: string | null;
23
- nullable: boolean;
24
- udtSchema: string;
25
- udt: string;
26
- elementType: string | null;
27
- elementTypeSchema: string | null;
28
- cardinality: number;
29
- size: number | null;
30
- }, {
31
- schema: string;
32
- table: string;
33
- column: string;
34
- ordinalPosition: number | null;
35
- type: string;
36
- default: string | null;
37
- nullable: boolean;
38
- udtSchema: string;
39
- udt: string;
40
- elementType: string | null;
41
- elementTypeSchema: string | null;
42
- cardinality: number;
43
- size: number | null;
44
- }>;
45
- export type TableColumn = z.infer<typeof TableColumnSchema>;
46
- export declare const getTables: (schemas: string[]) => import("@casekit/orm2").SQLStatement<{
47
- schema: string;
48
- table: string;
49
- column: string;
50
- ordinalPosition: number | null;
51
- type: string;
52
- default: string | null;
53
- nullable: boolean;
54
- udtSchema: string;
55
- udt: string;
56
- elementType: string | null;
57
- elementTypeSchema: string | null;
58
- cardinality: number;
59
- size: number | null;
60
- }>;
@@ -1,2 +0,0 @@
1
- export declare const isNumeric: (value: string) => boolean;
2
- export declare const renderDefault: (d: string) => string;
@@ -1,12 +0,0 @@
1
- export const isNumeric = (value) => {
2
- return /^-?\d+(\.\d+)?$/.test(value);
3
- };
4
- export const renderDefault = (d) => {
5
- return /^-?\d+$/.exec(d)
6
- ? d
7
- : /::text$/.exec(d)
8
- ? d.replace(/::text$/, "")
9
- : ["true", "false"].includes(d)
10
- ? d
11
- : `sql\`${d}\``;
12
- };
@@ -1,23 +0,0 @@
1
- import { describe, expect, test } from "vitest";
2
- import { renderDefault } from "./renderDefault.js";
3
- describe("renderDefault", () => {
4
- test("should return numeric values as-is", () => {
5
- expect(renderDefault("42")).toBe("42");
6
- expect(renderDefault("0")).toBe("0");
7
- expect(renderDefault("-123")).toBe("-123");
8
- });
9
- test("should remove ::text suffix from text values", () => {
10
- expect(renderDefault("hello::text")).toBe("hello");
11
- expect(renderDefault("some string::text")).toBe("some string");
12
- });
13
- test("should return boolean literals as-is", () => {
14
- expect(renderDefault("true")).toBe("true");
15
- expect(renderDefault("false")).toBe("false");
16
- });
17
- test("should wrap other values in sql tag", () => {
18
- expect(renderDefault("CURRENT_TIMESTAMP")).toBe("sql`CURRENT_TIMESTAMP`");
19
- expect(renderDefault("uuid_generate_v4()")).toBe("sql`uuid_generate_v4()`");
20
- expect(renderDefault("now()")).toBe("sql`now()`");
21
- expect(renderDefault("hello")).toBe("sql`hello`");
22
- });
23
- });
@@ -1,2 +0,0 @@
1
- import { TableColumn } from "#pull/getTables.js";
2
- export declare const generateModel: (columns: TableColumn[]) => string;
@@ -1,35 +0,0 @@
1
- import camelcase from "camelcase";
2
- import { unindent } from "@casekit/unindent";
3
- // const renderType = (column: TableColumn) => {
4
- // if (column.type === "ARRAY") {
5
- // return (
6
- // column.elementType +
7
- // times(column.)
8
- // .map(() => "[]")
9
- // .join("")
10
- // );
11
- // } else {
12
- // return column.type;
13
- // }
14
- // };
15
- export const generateModel = (columns) => {
16
- return unindent `
17
- import { ModelDefinition } from "@casekit/orm2";
18
-
19
- export const ${camelcase(columns[0].table)} = {
20
- table: "${columns[0].table}",
21
- fields: {
22
- ${columns
23
- .map((column) => {
24
- return `${camelcase(column.column)}: {
25
- name: "${column.column}",
26
- type: "${column.type}",
27
- ${column.nullable ? "nullable: true," : ""}
28
- ${column.default ? `default: "${column.default}",` : ""}
29
- }`;
30
- })
31
- .join(",\n")}
32
- },
33
- } as const satisfies ModelDefinition;
34
- `;
35
- };