@casekit/orm2-migrate 0.0.0-20250322230249

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 (43) hide show
  1. package/build/drop.d.ts +2 -0
  2. package/build/drop.js +12 -0
  3. package/build/index.d.ts +19 -0
  4. package/build/index.js +8 -0
  5. package/build/pull/getTables.d.ts +60 -0
  6. package/build/pull/getTables.js +56 -0
  7. package/build/pull/getTables.test.d.ts +1 -0
  8. package/build/pull/getTables.test.js +298 -0
  9. package/build/pull.d.ts +16 -0
  10. package/build/pull.js +4 -0
  11. package/build/push/arrayToSqlArray.d.ts +1 -0
  12. package/build/push/arrayToSqlArray.js +6 -0
  13. package/build/push/arrayToSqlArray.test.d.ts +1 -0
  14. package/build/push/arrayToSqlArray.test.js +29 -0
  15. package/build/push/createExtensionSql.d.ts +2 -0
  16. package/build/push/createExtensionSql.js +4 -0
  17. package/build/push/createExtensionSql.test.d.ts +1 -0
  18. package/build/push/createExtensionSql.test.js +20 -0
  19. package/build/push/createForeignKeyConstraintSql.d.ts +3 -0
  20. package/build/push/createForeignKeyConstraintSql.js +17 -0
  21. package/build/push/createForeignKeyConstraintSql.test.d.ts +1 -0
  22. package/build/push/createForeignKeyConstraintSql.test.js +142 -0
  23. package/build/push/createSchemaSql.d.ts +1 -0
  24. package/build/push/createSchemaSql.js +7 -0
  25. package/build/push/createSchemaSql.test.d.ts +1 -0
  26. package/build/push/createSchemaSql.test.js +26 -0
  27. package/build/push/createTableSql.d.ts +3 -0
  28. package/build/push/createTableSql.js +37 -0
  29. package/build/push/createTableSql.test.d.ts +1 -0
  30. package/build/push/createTableSql.test.js +99 -0
  31. package/build/push/createUniqueConstraintSql.d.ts +2 -0
  32. package/build/push/createUniqueConstraintSql.js +17 -0
  33. package/build/push/createUniqueConstraintSql.test.d.ts +1 -0
  34. package/build/push/createUniqueConstraintSql.test.js +105 -0
  35. package/build/push.d.ts +2 -0
  36. package/build/push.js +36 -0
  37. package/build/render/renderDefault.d.ts +2 -0
  38. package/build/render/renderDefault.js +12 -0
  39. package/build/render/renderDefault.test.d.ts +1 -0
  40. package/build/render/renderDefault.test.js +23 -0
  41. package/build/render/renderModel.d.ts +2 -0
  42. package/build/render/renderModel.js +35 -0
  43. package/package.json +59 -0
@@ -0,0 +1,2 @@
1
+ import { Orm } from "@casekit/orm2";
2
+ export declare const drop: (db: Orm) => Promise<void>;
package/build/drop.js ADDED
@@ -0,0 +1,12 @@
1
+ import { sql } from "@casekit/orm2";
2
+ export const drop = async (db) => {
3
+ const schemas = new Set(Object.values(db.config.models).map((model) => model.schema));
4
+ await db.transact(async (db) => {
5
+ for (const schema of schemas.values()) {
6
+ console.log(` - Dropping schema ${schema}`);
7
+ await db.query `
8
+ DROP SCHEMA IF EXISTS ${sql.ident(schema)} CASCADE;
9
+ `;
10
+ }
11
+ });
12
+ };
@@ -0,0 +1,19 @@
1
+ export declare const migrate: {
2
+ drop: (db: import("@casekit/orm2").Orm) => Promise<void>;
3
+ push: (db: import("@casekit/orm2").Orm) => Promise<void>;
4
+ pull: (db: import("@casekit/orm2").Orm, schemas: string[]) => Promise<{
5
+ table: string;
6
+ schema: string;
7
+ column: string;
8
+ type: string;
9
+ nullable: boolean;
10
+ default: string | null;
11
+ ordinalPosition: number | null;
12
+ udtSchema: string;
13
+ udt: string;
14
+ elementType: string | null;
15
+ elementTypeSchema: string | null;
16
+ cardinality: number;
17
+ size: number | null;
18
+ }[]>;
19
+ };
package/build/index.js ADDED
@@ -0,0 +1,8 @@
1
+ import { drop } from "#drop.js";
2
+ import { pull } from "#pull.js";
3
+ import { push } from "#push.js";
4
+ export const migrate = {
5
+ drop,
6
+ push,
7
+ pull,
8
+ };
@@ -0,0 +1,60 @@
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
+ table: string;
18
+ schema: string;
19
+ column: string;
20
+ type: string;
21
+ nullable: boolean;
22
+ default: string | null;
23
+ ordinalPosition: number | null;
24
+ udtSchema: string;
25
+ udt: string;
26
+ elementType: string | null;
27
+ elementTypeSchema: string | null;
28
+ cardinality: number;
29
+ size: number | null;
30
+ }, {
31
+ table: string;
32
+ schema: string;
33
+ column: string;
34
+ type: string;
35
+ nullable: boolean;
36
+ default: string | null;
37
+ ordinalPosition: number | null;
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
+ table: string;
48
+ schema: string;
49
+ column: string;
50
+ type: string;
51
+ nullable: boolean;
52
+ default: string | null;
53
+ ordinalPosition: number | null;
54
+ udtSchema: string;
55
+ udt: string;
56
+ elementType: string | null;
57
+ elementTypeSchema: string | null;
58
+ cardinality: number;
59
+ size: number | null;
60
+ }>;
@@ -0,0 +1,56 @@
1
+ import { z } from "zod";
2
+ import { sql } from "@casekit/orm2";
3
+ export const TableColumnSchema = z.object({
4
+ schema: z.string(),
5
+ table: z.string(),
6
+ column: z.string(),
7
+ ordinalPosition: z.number().nullable(),
8
+ type: z.string(),
9
+ default: z.string().nullable(),
10
+ nullable: z.boolean(),
11
+ udtSchema: z.string(),
12
+ udt: z.string(),
13
+ elementType: z.string().nullable(),
14
+ elementTypeSchema: z.string().nullable(),
15
+ cardinality: z.number(),
16
+ size: z.number().nullable(),
17
+ });
18
+ export const getTables = (schemas) => sql(TableColumnSchema) `
19
+ SELECT
20
+ c.table_schema AS "schema",
21
+ c.table_name AS "table",
22
+ c.column_name AS "column",
23
+ c.ordinal_position AS "ordinalPosition",
24
+ c.data_type AS "type",
25
+ c.column_default AS "default",
26
+ c.is_nullable = 'YES' AS "nullable",
27
+ c.udt_schema AS "udtSchema",
28
+ c.udt_name AS "udt",
29
+ e.data_type AS "elementType",
30
+ e.udt_schema AS "elementTypeSchema",
31
+ pa.attndims AS cardinality,
32
+ c.character_maximum_length AS "size"
33
+ FROM
34
+ information_schema.tables t
35
+ JOIN information_schema.columns c ON t.table_name = c.table_name
36
+ AND t.table_schema = c.table_schema
37
+ AND t.table_catalog = c.table_catalog
38
+ JOIN pg_catalog.pg_class pc ON pc.relname = c.table_name
39
+ JOIN pg_catalog.pg_namespace pn ON pn.oid = pc.relnamespace
40
+ AND pn.nspname = c.table_schema
41
+ JOIN pg_catalog.pg_attribute pa ON pa.attrelid = pc.oid
42
+ AND pa.attname = c.column_name
43
+ LEFT JOIN information_schema.element_types e ON c.table_catalog = e.object_catalog
44
+ AND c.table_schema = e.object_schema
45
+ AND c.table_name = e.object_name
46
+ AND e.object_type = 'TABLE'
47
+ AND e.collection_type_identifier = c.dtd_identifier
48
+ AND c.data_type = 'ARRAY'
49
+ WHERE
50
+ t.table_schema IN (${schemas})
51
+ AND t.table_type = 'BASE TABLE'
52
+ AND pa.attnum > 0
53
+ ORDER BY
54
+ t.table_name,
55
+ c.ordinal_position
56
+ `;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,298 @@
1
+ import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test, } from "vitest";
2
+ import { orm, sql } from "@casekit/orm2";
3
+ import { getTables } from "./getTables.js";
4
+ describe("getTables", () => {
5
+ const db = orm({
6
+ schema: "migrate-get-tables-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-tables-test" CASCADE;`;
17
+ await db.query `CREATE SCHEMA IF NOT EXISTS "migrate-get-tables-test";`;
18
+ });
19
+ afterEach(async () => {
20
+ await db.query `DROP SCHEMA IF EXISTS "migrate-get-tables-test" CASCADE;`;
21
+ });
22
+ test("with an empty database it returns no tables or columns", async () => {
23
+ const statement = getTables(["migrate-get-tables-test"]);
24
+ const result = await db.query(statement);
25
+ expect(result).toEqual([]);
26
+ });
27
+ test.for([
28
+ [
29
+ "handles uuid columns",
30
+ sql `
31
+ CREATE TABLE "migrate-get-tables-test"."uuid_test" (
32
+ "uuid_col" UUID DEFAULT gen_random_uuid(),
33
+ "uuid_col_notnull" UUID NOT NULL
34
+ );
35
+ `,
36
+ [
37
+ expect.objectContaining({
38
+ type: "uuid",
39
+ default: "gen_random_uuid()",
40
+ nullable: true,
41
+ }),
42
+ expect.objectContaining({
43
+ type: "uuid",
44
+ nullable: false,
45
+ }),
46
+ ],
47
+ ],
48
+ [
49
+ "handles serial columns",
50
+ sql `
51
+ CREATE TABLE "migrate-get-tables-test"."serial_test" (
52
+ "smallserial_col" SMALLSERIAL,
53
+ "serial_col" SERIAL,
54
+ "bigserial_col" BIGSERIAL
55
+ );
56
+ `,
57
+ [
58
+ expect.objectContaining({
59
+ type: "smallint",
60
+ default: expect.stringContaining("nextval"),
61
+ nullable: false,
62
+ }),
63
+ expect.objectContaining({
64
+ type: "integer",
65
+ default: expect.stringContaining("nextval"),
66
+ nullable: false,
67
+ }),
68
+ expect.objectContaining({
69
+ type: "bigint",
70
+ default: expect.stringContaining("nextval"),
71
+ nullable: false,
72
+ }),
73
+ ],
74
+ ],
75
+ [
76
+ "handles boolean columns",
77
+ sql `
78
+ CREATE TABLE "migrate-get-tables-test"."boolean_test" (
79
+ "bool_col" BOOLEAN DEFAULT true
80
+ );
81
+ `,
82
+ [
83
+ expect.objectContaining({
84
+ type: "boolean",
85
+ default: "true",
86
+ }),
87
+ ],
88
+ ],
89
+ [
90
+ "handles numeric columns",
91
+ sql `
92
+ CREATE TABLE "migrate-get-tables-test"."numeric_test" (
93
+ "numeric_col" NUMERIC(10,2)
94
+ );
95
+ `,
96
+ [
97
+ expect.objectContaining({
98
+ type: "numeric",
99
+ udt: "numeric",
100
+ }),
101
+ ],
102
+ ],
103
+ [
104
+ "handles various numeric and decimal types",
105
+ sql `
106
+ CREATE TABLE "migrate-get-tables-test"."number_test" (
107
+ "smallint_col" SMALLINT,
108
+ "integer_col" INTEGER,
109
+ "bigint_col" BIGINT,
110
+ "decimal_col" DECIMAL(10,2),
111
+ "real_col" REAL,
112
+ "double_col" DOUBLE PRECISION
113
+ );
114
+ `,
115
+ [
116
+ expect.objectContaining({ type: "smallint", udt: "int2" }),
117
+ expect.objectContaining({ type: "integer", udt: "int4" }),
118
+ expect.objectContaining({ type: "bigint", udt: "int8" }),
119
+ expect.objectContaining({ type: "numeric", udt: "numeric" }),
120
+ expect.objectContaining({ type: "real", udt: "float4" }),
121
+ expect.objectContaining({
122
+ type: "double precision",
123
+ udt: "float8",
124
+ }),
125
+ ],
126
+ ],
127
+ [
128
+ "handles character types",
129
+ sql `
130
+ CREATE TABLE "migrate-get-tables-test"."char_test" (
131
+ "char_col" CHAR(10),
132
+ "varchar_col" VARCHAR(50),
133
+ "text_col" TEXT,
134
+ "name_col" NAME
135
+ );
136
+ `,
137
+ [
138
+ expect.objectContaining({ type: "character", size: 10 }),
139
+ expect.objectContaining({
140
+ type: "character varying",
141
+ size: 50,
142
+ }),
143
+ expect.objectContaining({ type: "text" }),
144
+ expect.objectContaining({ type: "name" }),
145
+ ],
146
+ ],
147
+ [
148
+ "handles date and time types",
149
+ sql `
150
+ CREATE TABLE "migrate-get-tables-test"."datetime_test" (
151
+ "date_col" DATE,
152
+ "time_col" TIME,
153
+ "timetz_col" TIME WITH TIME ZONE,
154
+ "timestamp_col" TIMESTAMP,
155
+ "timestamptz_col" TIMESTAMP WITH TIME ZONE,
156
+ "interval_col" INTERVAL
157
+ );
158
+ `,
159
+ [
160
+ expect.objectContaining({ type: "date" }),
161
+ expect.objectContaining({ type: "time without time zone" }),
162
+ expect.objectContaining({ type: "time with time zone" }),
163
+ expect.objectContaining({
164
+ type: "timestamp without time zone",
165
+ }),
166
+ expect.objectContaining({ type: "timestamp with time zone" }),
167
+ expect.objectContaining({ type: "interval" }),
168
+ ],
169
+ ],
170
+ [
171
+ "handles array columns",
172
+ sql `
173
+ CREATE TABLE "migrate-get-tables-test"."array_test" (
174
+ "array_col" INTEGER[]
175
+ );
176
+ `,
177
+ [
178
+ expect.objectContaining({
179
+ type: "ARRAY",
180
+ elementType: "integer",
181
+ cardinality: 1,
182
+ }),
183
+ ],
184
+ ],
185
+ [
186
+ "handles multi-dimensional array columns",
187
+ sql `
188
+ CREATE TABLE "migrate-get-tables-test"."array_test" (
189
+ "multi_dim_array_col" TEXT[][][] NOT NULL DEFAULT ARRAY[ARRAY[ARRAY['a']]]
190
+ );
191
+ `,
192
+ [
193
+ expect.objectContaining({
194
+ type: "ARRAY",
195
+ elementType: "text",
196
+ cardinality: 3,
197
+ nullable: false,
198
+ default: "ARRAY[ARRAY[ARRAY['a'::text]]]",
199
+ }),
200
+ ],
201
+ ],
202
+ [
203
+ "handles json columns",
204
+ sql `
205
+ CREATE TABLE "migrate-get-tables-test"."json_test" (
206
+ "json_col" JSON
207
+ );
208
+ `,
209
+ [
210
+ expect.objectContaining({
211
+ type: "json",
212
+ }),
213
+ ],
214
+ ],
215
+ [
216
+ "handles enum columns",
217
+ sql `
218
+ CREATE TYPE "migrate-get-tables-test"."mood" AS ENUM ('happy', 'sad');
219
+ CREATE TABLE "migrate-get-tables-test"."enum_test" (
220
+ "enum_col" "migrate-get-tables-test"."mood"
221
+ );
222
+ `,
223
+ [
224
+ expect.objectContaining({
225
+ type: "USER-DEFINED",
226
+ udtSchema: "migrate-get-tables-test",
227
+ udt: "mood",
228
+ }),
229
+ ],
230
+ ],
231
+ [
232
+ "handles composite type columns",
233
+ sql `
234
+ CREATE TYPE "migrate-get-tables-test"."complex" AS (x double precision, y double precision);
235
+ CREATE TABLE "migrate-get-tables-test"."composite_test" (
236
+ "complex_col" "migrate-get-tables-test"."complex"
237
+ );
238
+ `,
239
+ [
240
+ expect.objectContaining({
241
+ type: "USER-DEFINED",
242
+ udtSchema: "migrate-get-tables-test",
243
+ udt: "complex",
244
+ }),
245
+ ],
246
+ ],
247
+ [
248
+ "handles currency columns",
249
+ sql `
250
+ CREATE TABLE "migrate-get-tables-test"."currency_test" (
251
+ "money_col" MONEY DEFAULT 42.42
252
+ );
253
+ `,
254
+ [
255
+ expect.objectContaining({
256
+ type: "money",
257
+ default: "42.42",
258
+ }),
259
+ ],
260
+ ],
261
+ [
262
+ "handles geometric columns",
263
+ sql `
264
+ CREATE TABLE "migrate-get-tables-test"."geometry_test" (
265
+ "point_col" POINT,
266
+ "line_col" LINE,
267
+ "polygon_col" POLYGON
268
+ );
269
+ `,
270
+ [
271
+ expect.objectContaining({ type: "point" }),
272
+ expect.objectContaining({ type: "line" }),
273
+ expect.objectContaining({ type: "polygon" }),
274
+ ],
275
+ ],
276
+ [
277
+ "handles network address columns",
278
+ sql `
279
+ CREATE TABLE "migrate-get-tables-test"."network_test" (
280
+ "inet_col" INET,
281
+ "cidr_col" CIDR,
282
+ "macaddr_col" MACADDR
283
+ );
284
+ `,
285
+ [
286
+ expect.objectContaining({ type: "inet" }),
287
+ expect.objectContaining({ type: "cidr" }),
288
+ expect.objectContaining({ type: "macaddr" }),
289
+ ],
290
+ ],
291
+ ])("%s", async ([_, query, expected]) => {
292
+ await db.transact(async (db) => {
293
+ await db.query(query);
294
+ const result = await db.query(getTables(["migrate-get-tables-test"]));
295
+ expect(result).toEqual(expected);
296
+ }, { rollback: true });
297
+ });
298
+ });
@@ -0,0 +1,16 @@
1
+ import { Orm } from "@casekit/orm2";
2
+ export declare const pull: (db: Orm, schemas: string[]) => Promise<{
3
+ table: string;
4
+ schema: string;
5
+ column: string;
6
+ type: string;
7
+ nullable: boolean;
8
+ default: string | null;
9
+ ordinalPosition: number | null;
10
+ udtSchema: string;
11
+ udt: string;
12
+ elementType: string | null;
13
+ elementTypeSchema: string | null;
14
+ cardinality: number;
15
+ size: number | null;
16
+ }[]>;
package/build/pull.js ADDED
@@ -0,0 +1,4 @@
1
+ import { getTables } from "./pull/getTables.js";
2
+ export const pull = async (db, schemas) => {
3
+ return await db.query(getTables(schemas));
4
+ };
@@ -0,0 +1 @@
1
+ export declare const arrayToSqlArray: (values: unknown[]) => string;
@@ -0,0 +1,6 @@
1
+ export const arrayToSqlArray = (values) => {
2
+ const joined = values
3
+ .map((v) => (Array.isArray(v) ? arrayToSqlArray(v) : JSON.stringify(v)))
4
+ .join(", ");
5
+ return `{ ${joined} }`;
6
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,29 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { arrayToSqlArray } from "./arrayToSqlArray.js";
3
+ describe("arrayToSqlArray", () => {
4
+ test("converts a flat array to SQL array format", () => {
5
+ const input = [1, 2, 3];
6
+ const output = arrayToSqlArray(input);
7
+ expect(output).toBe("{ 1, 2, 3 }");
8
+ });
9
+ test("converts a nested array to SQL array format", () => {
10
+ const input = [1, [2, 3], 4];
11
+ const output = arrayToSqlArray(input);
12
+ expect(output).toBe("{ 1, { 2, 3 }, 4 }");
13
+ });
14
+ test("converts an array with strings to SQL array format", () => {
15
+ const input = ["a", "b", "c"];
16
+ const output = arrayToSqlArray(input);
17
+ expect(output).toBe('{ "a", "b", "c" }');
18
+ });
19
+ test("converts a mixed array to SQL array format", () => {
20
+ const input = [1, "b", [2, "c"]];
21
+ const output = arrayToSqlArray(input);
22
+ expect(output).toBe('{ 1, "b", { 2, "c" } }');
23
+ });
24
+ test("converts an empty array to SQL array format", () => {
25
+ const input = [];
26
+ const output = arrayToSqlArray(input);
27
+ expect(output).toBe("{ }");
28
+ });
29
+ });
@@ -0,0 +1,2 @@
1
+ import { SQLStatement } from "@casekit/orm2";
2
+ export declare const createExtensionsSql: (extension: string) => SQLStatement;
@@ -0,0 +1,4 @@
1
+ import { sql } from "@casekit/orm2";
2
+ export const createExtensionsSql = (extension) => {
3
+ return sql `CREATE EXTENSION IF NOT EXISTS ${sql.ident(extension)};`;
4
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,20 @@
1
+ import { afterAll, beforeAll, describe, expect, test } from "vitest";
2
+ import { orm } from "@casekit/orm2";
3
+ import { createExtensionsSql } from "./createExtensionSql.js";
4
+ describe("createExtensionsSql", () => {
5
+ const db = orm({ models: {} });
6
+ beforeAll(async () => {
7
+ await db.connect();
8
+ });
9
+ afterAll(async () => {
10
+ await db.close();
11
+ });
12
+ test("creates SQL statement for creating an extension", async () => {
13
+ const statement = createExtensionsSql("uuid-ossp");
14
+ expect(statement.text).toEqual('CREATE EXTENSION IF NOT EXISTS "uuid-ossp";');
15
+ expect(statement.values).toEqual([]);
16
+ await db.transact(async (db) => {
17
+ await expect(db.query(statement)).resolves.not.toThrow();
18
+ }, { rollback: true });
19
+ });
20
+ });
@@ -0,0 +1,3 @@
1
+ import { SQLStatement } from "@casekit/orm2";
2
+ import { NormalizedForeignKeyDefinition, NormalizedModelDefinition } from "@casekit/orm2-config";
3
+ export declare const createForeignKeyConstraintSql: (model: NormalizedModelDefinition, fk: NormalizedForeignKeyDefinition) => SQLStatement<import("pg").QueryResultRow>;
@@ -0,0 +1,17 @@
1
+ import { SQLStatement, sql } from "@casekit/orm2";
2
+ export const createForeignKeyConstraintSql = (model, fk) => {
3
+ const statement = sql `
4
+ ALTER TABLE ${sql.ident(model.schema)}.${sql.ident(model.table)}
5
+ ADD CONSTRAINT ${sql.ident(fk.name)}
6
+ FOREIGN KEY (${sql.join(fk.columns.map(sql.ident), ", ")})
7
+ REFERENCES ${sql.ident(fk.references.schema)}.${sql.ident(fk.references.table)} (${sql.join(fk.references.columns.map(sql.ident), ", ")})`;
8
+ if (fk.onDelete) {
9
+ statement.append `
10
+ ON DELETE ${new SQLStatement(fk.onDelete)}`;
11
+ }
12
+ if (fk.onUpdate) {
13
+ statement.append `
14
+ ON UPDATE ${new SQLStatement(fk.onUpdate)}`;
15
+ }
16
+ return statement;
17
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,142 @@
1
+ import { afterAll, beforeAll, describe, expect, test } from "vitest";
2
+ import { orm } from "@casekit/orm2";
3
+ import { unindent } from "@casekit/unindent";
4
+ import { createForeignKeyConstraintSql } from "./createForeignKeyConstraintSql.js";
5
+ describe("createForeignKeyConstraintSql", () => {
6
+ const db = orm({
7
+ schema: "orm",
8
+ models: {
9
+ foo: {
10
+ table: "foo",
11
+ fields: {
12
+ id: { type: "serial", primaryKey: true },
13
+ barId: { column: "bar_id", type: "integer" },
14
+ otherFooId: { column: "other_foo_id", type: "integer" },
15
+ },
16
+ },
17
+ bar: {
18
+ table: "bar",
19
+ fields: {
20
+ id: { type: "serial", primaryKey: true },
21
+ fooId: { column: "foo_id", type: "integer" },
22
+ },
23
+ },
24
+ baz: {
25
+ table: "baz",
26
+ fields: {
27
+ id: { type: "serial", primaryKey: true },
28
+ fooId: { column: "foo_id", type: "integer" },
29
+ barId: { column: "bar_id", type: "integer" },
30
+ },
31
+ },
32
+ },
33
+ });
34
+ const foo = db.config.models["foo"];
35
+ const baz = db.config.models["baz"];
36
+ beforeAll(async () => {
37
+ await db.connect();
38
+ });
39
+ afterAll(async () => {
40
+ await db.close();
41
+ });
42
+ test.for([
43
+ [
44
+ "basic foreign key",
45
+ foo,
46
+ {
47
+ name: "foo_other_foo_id_fk",
48
+ fields: ["otherFooId"],
49
+ columns: ["other_foo_id"],
50
+ references: {
51
+ model: "foo",
52
+ schema: "orm",
53
+ table: "foo",
54
+ fields: ["id"],
55
+ columns: ["id"],
56
+ },
57
+ onUpdate: null,
58
+ onDelete: null,
59
+ },
60
+ unindent `
61
+ ALTER TABLE "orm"."foo"
62
+ ADD CONSTRAINT "foo_other_foo_id_fk" FOREIGN KEY ("other_foo_id") REFERENCES "orm"."foo" ("id")
63
+ `,
64
+ ],
65
+ [
66
+ "on update cascade",
67
+ foo,
68
+ {
69
+ name: "foo_other_foo_id_fk",
70
+ fields: ["otherFooId"],
71
+ columns: ["other_foo_id"],
72
+ references: {
73
+ model: "foo",
74
+ schema: "orm",
75
+ table: "foo",
76
+ fields: ["id"],
77
+ columns: ["id"],
78
+ },
79
+ onUpdate: "CASCADE",
80
+ onDelete: null,
81
+ },
82
+ unindent `
83
+ ALTER TABLE "orm"."foo"
84
+ ADD CONSTRAINT "foo_other_foo_id_fk" FOREIGN KEY ("other_foo_id") REFERENCES "orm"."foo" ("id") ON UPDATE CASCADE
85
+ `,
86
+ ],
87
+ [
88
+ "on delete set null",
89
+ foo,
90
+ {
91
+ name: "foo_other_foo_id_fk",
92
+ fields: ["otherFooId"],
93
+ columns: ["other_foo_id"],
94
+ references: {
95
+ model: "foo",
96
+ schema: "orm",
97
+ table: "foo",
98
+ fields: ["id"],
99
+ columns: ["id"],
100
+ },
101
+ onUpdate: null,
102
+ onDelete: "SET NULL",
103
+ },
104
+ unindent `
105
+ ALTER TABLE "orm"."foo"
106
+ ADD CONSTRAINT "foo_other_foo_id_fk" FOREIGN KEY ("other_foo_id") REFERENCES "orm"."foo" ("id") ON DELETE SET NULL
107
+ `,
108
+ ],
109
+ [
110
+ "multi-column",
111
+ baz,
112
+ {
113
+ name: "baz_foo_id_bar_id_fk",
114
+ fields: ["fooId", "barId"],
115
+ columns: ["foo_id", "bar_id"],
116
+ references: {
117
+ model: "foo",
118
+ schema: "orm",
119
+ table: "foo",
120
+ fields: ["otherFooId", "barId"],
121
+ columns: ["other_foo_id", "bar_id"],
122
+ },
123
+ onUpdate: "RESTRICT",
124
+ onDelete: "SET NULL",
125
+ },
126
+ unindent `
127
+ ALTER TABLE "orm"."baz"
128
+ ADD CONSTRAINT "baz_foo_id_bar_id_fk" FOREIGN KEY ("foo_id", "bar_id") REFERENCES "orm"."foo" ("other_foo_id", "bar_id") ON DELETE SET NULL ON UPDATE RESTRICT
129
+ `,
130
+ ],
131
+ ])("%s", async ([_, model, fk, expected]) => {
132
+ const statement = createForeignKeyConstraintSql(model, fk);
133
+ expect(statement.pretty).toEqual(expected);
134
+ await db.transact(async (db) => {
135
+ await db.query `CREATE TABLE orm.foo (id SERIAL PRIMARY KEY, bar_id INTEGER, other_foo_id INTEGER)`;
136
+ await db.query `CREATE TABLE orm.bar (id SERIAL PRIMARY KEY, foo_id INTEGER)`;
137
+ await db.query `CREATE TABLE orm.baz (id SERIAL PRIMARY KEY, foo_id INTEGER, bar_id INTEGER)`;
138
+ await db.query `CREATE UNIQUE INDEX foo_other_foo_id_bar_id_key ON orm.foo (other_foo_id, bar_id)`;
139
+ await expect(db.query(statement)).resolves.not.toThrow();
140
+ }, { rollback: true });
141
+ });
142
+ });
@@ -0,0 +1 @@
1
+ export declare const createSchemaSql: (schema: string) => import("@casekit/sql").SQLStatement<import("pg").QueryResultRow>;
@@ -0,0 +1,7 @@
1
+ import { sql } from "@casekit/sql";
2
+ export const createSchemaSql = (schema) => {
3
+ if (schema === "") {
4
+ throw new Error("Cannot create schema with empty name");
5
+ }
6
+ return sql `CREATE SCHEMA IF NOT EXISTS ${sql.ident(schema)};`;
7
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,26 @@
1
+ import { afterAll, beforeAll, describe, expect, test } from "vitest";
2
+ import { orm } from "@casekit/orm2";
3
+ import { createSchemaSql } from "./createSchemaSql.js";
4
+ describe("createSchemaSql", () => {
5
+ const db = orm({ models: {} });
6
+ beforeAll(async () => {
7
+ await db.connect();
8
+ });
9
+ afterAll(async () => {
10
+ await db.close();
11
+ });
12
+ test.for([
13
+ ["basic schema name", "test_schema"],
14
+ ["schema name with spaces", "test schema"],
15
+ ["malicious", "test-schema;drop table"],
16
+ ])("%s", async ([, schema]) => {
17
+ const result = createSchemaSql(schema);
18
+ expect(result.text).toBe(`CREATE SCHEMA IF NOT EXISTS "${schema}";`);
19
+ await db.transact(async (db) => {
20
+ await expect(db.query(result)).resolves.not.toThrow();
21
+ }, { rollback: true });
22
+ });
23
+ test("should throw on empty schema name", () => {
24
+ expect(() => createSchemaSql("")).toThrowError("Cannot create schema with empty name");
25
+ });
26
+ });
@@ -0,0 +1,3 @@
1
+ import { SQLStatement } from "@casekit/orm2";
2
+ import { NormalizedModelDefinition } from "@casekit/orm2-config";
3
+ export declare const createTableSql: (model: NormalizedModelDefinition) => SQLStatement;
@@ -0,0 +1,37 @@
1
+ import { SQLStatement, sql } from "@casekit/orm2";
2
+ import { arrayToSqlArray } from "#push/arrayToSqlArray.js";
3
+ export const createTableSql = (model) => {
4
+ const fields = Object.values(model.fields);
5
+ const primaryKey = model.primaryKey;
6
+ const statement = sql `CREATE TABLE IF NOT EXISTS ${sql.ident(model.schema)}.${sql.ident(model.table)} (\n`;
7
+ for (const field of fields) {
8
+ statement.append ` ${sql.ident(field.column)} `;
9
+ // dangerously appending raw SQL here, but we need to,
10
+ // and this only comes from user-defined schemas, so should be ok
11
+ statement.push(new SQLStatement(field.type));
12
+ if (!field.nullable)
13
+ statement.append ` NOT NULL`;
14
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- let's do this check to be safe
15
+ if (field.default !== null || field.default === undefined) {
16
+ statement.append ` DEFAULT `;
17
+ if (field.default instanceof SQLStatement) {
18
+ statement.push(field.default);
19
+ }
20
+ else if (typeof field.default === "string") {
21
+ statement.push(sql.literal(field.default));
22
+ }
23
+ else if (Array.isArray(field.default)) {
24
+ statement.push(sql.literal(arrayToSqlArray(field.default)));
25
+ }
26
+ else {
27
+ statement.push(sql.literal(JSON.stringify(field.default)));
28
+ }
29
+ }
30
+ statement.append `,\n`;
31
+ }
32
+ if (primaryKey.length > 0) {
33
+ statement.append ` PRIMARY KEY (${sql.join(primaryKey.map((pk) => sql.ident(pk.column)), ", ")})`;
34
+ }
35
+ statement.append `\n);`;
36
+ return statement;
37
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,99 @@
1
+ import { afterAll, beforeAll, describe, expect, test } from "vitest";
2
+ import { orm, sql } from "@casekit/orm2";
3
+ import { normalizeConfig } from "@casekit/orm2-config";
4
+ import { config } from "@casekit/orm2-fixtures";
5
+ import { unindent } from "@casekit/unindent";
6
+ import { createTableSql } from "./createTableSql.js";
7
+ describe("createTableSql", () => {
8
+ const db = orm(config);
9
+ beforeAll(async () => {
10
+ await db.connect();
11
+ });
12
+ afterAll(async () => {
13
+ await db.close();
14
+ });
15
+ test.for([
16
+ [
17
+ "integer",
18
+ {},
19
+ unindent `
20
+ CREATE TABLE IF NOT EXISTS "public"."foo" (
21
+ "id" serial NOT NULL,
22
+ "value" integer NOT NULL,
23
+ PRIMARY KEY ("id")
24
+ );
25
+ `,
26
+ ],
27
+ [
28
+ "text",
29
+ { nullable: true },
30
+ unindent `
31
+ CREATE TABLE IF NOT EXISTS "public"."foo" (
32
+ "id" serial NOT NULL,
33
+ "value" text,
34
+ PRIMARY KEY ("id")
35
+ );
36
+ `,
37
+ ],
38
+ [
39
+ "integer",
40
+ { unique: true, default: 1312 },
41
+ unindent `
42
+ CREATE TABLE IF NOT EXISTS "public"."foo" (
43
+ "id" serial NOT NULL,
44
+ "value" integer NOT NULL DEFAULT '1312',
45
+ PRIMARY KEY ("id")
46
+ );
47
+ `,
48
+ ],
49
+ [
50
+ "text",
51
+ { unique: true, default: "foo" },
52
+ unindent `
53
+ CREATE TABLE IF NOT EXISTS "public"."foo" (
54
+ "id" serial NOT NULL,
55
+ "value" text NOT NULL DEFAULT 'foo',
56
+ PRIMARY KEY ("id")
57
+ );
58
+ `,
59
+ ],
60
+ [
61
+ "uuid",
62
+ { default: sql `uuid_generate_v4()` },
63
+ unindent `
64
+ CREATE TABLE IF NOT EXISTS "public"."foo" (
65
+ "id" serial NOT NULL,
66
+ "value" uuid NOT NULL DEFAULT uuid_generate_v4 (),
67
+ PRIMARY KEY ("id")
68
+ );
69
+ `,
70
+ ],
71
+ [
72
+ "text[][][]",
73
+ { default: [[[1, 2, 3]]] },
74
+ unindent `
75
+ CREATE TABLE IF NOT EXISTS "public"."foo" (
76
+ "id" serial NOT NULL,
77
+ "value" text[] [] [] NOT NULL DEFAULT '{ { { 1, 2, 3 } } }',
78
+ PRIMARY KEY ("id")
79
+ );
80
+ `,
81
+ ],
82
+ ])("%s %s", async ([type, options, expected]) => {
83
+ const config = normalizeConfig({
84
+ models: {
85
+ foo: {
86
+ fields: {
87
+ id: { type: "serial", primaryKey: true },
88
+ value: { type: type, ...options },
89
+ },
90
+ },
91
+ },
92
+ });
93
+ const statement = createTableSql(config.models["foo"]);
94
+ expect(statement.pretty).toEqual(expected);
95
+ await db.transact(async (db) => {
96
+ await expect(db.query(statement)).resolves.not.toThrow();
97
+ }, { rollback: true });
98
+ });
99
+ });
@@ -0,0 +1,2 @@
1
+ import { NormalizedModelDefinition, NormalizedUniqueConstraintDefinition } from "@casekit/orm2-config";
2
+ export declare const createUniqueConstraintSql: (model: NormalizedModelDefinition, constraint: NormalizedUniqueConstraintDefinition) => import("@casekit/orm2").SQLStatement<import("pg").QueryResultRow>;
@@ -0,0 +1,17 @@
1
+ import { sql } from "@casekit/orm2";
2
+ export const createUniqueConstraintSql = (model, constraint) => {
3
+ const statement = sql `
4
+ CREATE UNIQUE INDEX ${sql.ident(constraint.name)}
5
+ ON ${sql.ident(model.schema)}.${sql.ident(model.table)}
6
+ (${sql.join(constraint.columns.map(sql.ident), ", ")})
7
+ `;
8
+ if (constraint.nullsNotDistinct) {
9
+ statement.append `
10
+ NULLS NOT DISTINCT`;
11
+ }
12
+ if (constraint.where) {
13
+ statement.append `
14
+ WHERE ${constraint.where}`;
15
+ }
16
+ return statement;
17
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,105 @@
1
+ import { afterAll, beforeAll, describe, expect, test } from "vitest";
2
+ import { orm, sql } from "@casekit/orm2";
3
+ import { unindent } from "@casekit/unindent";
4
+ import { createUniqueConstraintSql } from "./createUniqueConstraintSql.js";
5
+ describe("createUniqueConstraintSql", () => {
6
+ const db = orm({
7
+ schema: "orm",
8
+ models: {
9
+ foo: {
10
+ table: "foo",
11
+ fields: {
12
+ id: { type: "serial", primaryKey: true },
13
+ fooValue: { column: "foo_value", type: "text" },
14
+ barValue: { column: "bar_value", type: "integer" },
15
+ },
16
+ },
17
+ },
18
+ });
19
+ const foo = db.config.models["foo"];
20
+ beforeAll(async () => {
21
+ await db.connect();
22
+ });
23
+ afterAll(async () => {
24
+ await db.close();
25
+ });
26
+ test.for([
27
+ [
28
+ "basic unique constraint",
29
+ {
30
+ name: "foo_foo_value_key",
31
+ fields: ["fooValue"],
32
+ columns: ["foo_value"],
33
+ nullsNotDistinct: false,
34
+ where: null,
35
+ },
36
+ unindent `
37
+ CREATE UNIQUE INDEX "foo_foo_value_key" ON "orm"."foo" ("foo_value")
38
+ `,
39
+ ],
40
+ [
41
+ "nulls not distinct",
42
+ {
43
+ name: "foo_foo_value_key",
44
+ fields: ["fooValue"],
45
+ columns: ["foo_value"],
46
+ nullsNotDistinct: true,
47
+ where: null,
48
+ },
49
+ unindent `
50
+ CREATE UNIQUE INDEX "foo_foo_value_key" ON "orm"."foo" ("foo_value") NULLS NOT DISTINCT
51
+ `,
52
+ ],
53
+ [
54
+ "where clause",
55
+ {
56
+ name: "foo_foo_value_key",
57
+ fields: ["fooValue"],
58
+ columns: ["foo_value"],
59
+ nullsNotDistinct: false,
60
+ where: sql `bar_value > 0`,
61
+ },
62
+ unindent `
63
+ CREATE UNIQUE INDEX "foo_foo_value_key" ON "orm"."foo" ("foo_value")
64
+ WHERE
65
+ bar_value > 0
66
+ `,
67
+ ],
68
+ [
69
+ "multi-column",
70
+ {
71
+ name: "foo_foo_value_bar_value_key",
72
+ fields: ["fooValue", "barValue"],
73
+ columns: ["foo_value", "bar_value"],
74
+ nullsNotDistinct: false,
75
+ where: null,
76
+ },
77
+ unindent `
78
+ CREATE UNIQUE INDEX "foo_foo_value_bar_value_key" ON "orm"."foo" ("foo_value", "bar_value")
79
+ `,
80
+ ],
81
+ [
82
+ "multi-column with nulls not distinct and where",
83
+ {
84
+ name: "foo_foo_value_bar_value_key",
85
+ fields: ["fooValue", "barValue"],
86
+ columns: ["foo_value", "bar_value"],
87
+ nullsNotDistinct: true,
88
+ where: sql `bar_value > 0 AND foo_value IS NOT NULL`,
89
+ },
90
+ unindent `
91
+ CREATE UNIQUE INDEX "foo_foo_value_bar_value_key" ON "orm"."foo" ("foo_value", "bar_value") NULLS NOT DISTINCT
92
+ WHERE
93
+ bar_value > 0
94
+ AND foo_value IS NOT NULL
95
+ `,
96
+ ],
97
+ ])("%s", async ([_, constraint, expected]) => {
98
+ const statement = createUniqueConstraintSql(foo, constraint);
99
+ expect(statement.pretty).toEqual(expected);
100
+ await db.transact(async (db) => {
101
+ await db.query `CREATE TABLE orm.foo (id SERIAL PRIMARY KEY, foo_value TEXT, bar_value INTEGER)`;
102
+ await expect(db.query(statement)).resolves.not.toThrow();
103
+ }, { rollback: true });
104
+ });
105
+ });
@@ -0,0 +1,2 @@
1
+ import { Orm } from "@casekit/orm2";
2
+ export declare const push: (db: Orm) => Promise<void>;
package/build/push.js ADDED
@@ -0,0 +1,36 @@
1
+ import { toSentence } from "@casekit/toolbox";
2
+ import { createExtensionsSql } from "#push/createExtensionSql.js";
3
+ import { createForeignKeyConstraintSql } from "#push/createForeignKeyConstraintSql.js";
4
+ import { createSchemaSql } from "#push/createSchemaSql.js";
5
+ import { createTableSql } from "#push/createTableSql.js";
6
+ import { createUniqueConstraintSql } from "#push/createUniqueConstraintSql.js";
7
+ export const push = async (db) => {
8
+ const schemas = new Set(Object.values(db.config.models).map((model) => model.schema));
9
+ console.log(`Pushing schemas ${toSentence(schemas)} to database`);
10
+ await db.transact(async (db) => {
11
+ for (const extension of db.config.extensions) {
12
+ console.log(` - Creating extension "${extension}"`);
13
+ await db.query(createExtensionsSql(extension));
14
+ }
15
+ for (const schema of schemas.values()) {
16
+ console.log(` - Creating schema "${schema}"`);
17
+ await db.query(createSchemaSql(schema));
18
+ }
19
+ for (const model of Object.values(db.config.models)) {
20
+ console.log(` - Creating table "${model.schema}"."${model.table}"`);
21
+ await db.query(createTableSql(model));
22
+ }
23
+ for (const model of Object.values(db.config.models)) {
24
+ for (const fk of model.foreignKeys) {
25
+ console.log(` - Creating foreign key constraint "${fk.name}" ON "${model.table}"`);
26
+ await db.query(createForeignKeyConstraintSql(model, fk));
27
+ }
28
+ }
29
+ for (const model of Object.values(db.config.models)) {
30
+ for (const constraint of model.uniqueConstraints) {
31
+ console.log(` - Creating unique constraint "${constraint.name}" ON "${model.schema}"."${model.table}"`);
32
+ await db.query(createUniqueConstraintSql(model, constraint));
33
+ }
34
+ }
35
+ });
36
+ };
@@ -0,0 +1,2 @@
1
+ export declare const isNumeric: (value: string) => boolean;
2
+ export declare const renderDefault: (d: string) => string;
@@ -0,0 +1,12 @@
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
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,23 @@
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
+ });
@@ -0,0 +1,2 @@
1
+ import { TableColumn } from "#pull/getTables.js";
2
+ export declare const generateModel: (columns: TableColumn[]) => string;
@@ -0,0 +1,35 @@
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
+ };
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@casekit/orm2-migrate",
3
+ "description": "",
4
+ "version": "0.0.0-20250322230249",
5
+ "author": "",
6
+ "dependencies": {
7
+ "camelcase": "^8.0.0",
8
+ "@casekit/orm2": "0.0.0-20250322230249",
9
+ "@casekit/orm2-schema": "0.0.0-20250322230249",
10
+ "@casekit/toolbox": "0.0.0-20250322230249",
11
+ "@casekit/orm2-config": "0.0.0-20250322230249",
12
+ "@casekit/sql": "0.0.0-20250322230249"
13
+ },
14
+ "devDependencies": {
15
+ "@casekit/unindent": "^1.0.5",
16
+ "@trivago/prettier-plugin-sort-imports": "^5.2.2",
17
+ "@types/node": "^22.13.11",
18
+ "@types/pg": "^8.11.11",
19
+ "@types/uuid": "^10.0.0",
20
+ "@vitest/coverage-v8": "^3.0.9",
21
+ "dotenv": "^16.4.7",
22
+ "prettier": "^3.5.3",
23
+ "prettier-plugin-svelte": "^3.3.3",
24
+ "typescript": "^5.8.2",
25
+ "uuid": "^11.1.0",
26
+ "vite-tsconfig-paths": "^5.1.4",
27
+ "vitest": "^3.0.9",
28
+ "@casekit/orm2-fixtures": "0.0.0-20250322230249",
29
+ "@casekit/tsconfig": "0.0.0-20250322230249",
30
+ "@casekit/prettier-config": "0.0.0-20250322230249"
31
+ },
32
+ "exports": {
33
+ ".": "./build/index.js"
34
+ },
35
+ "files": [
36
+ "/build"
37
+ ],
38
+ "imports": {
39
+ "#*": "./build/*"
40
+ },
41
+ "keywords": [],
42
+ "license": "ISC",
43
+ "main": "index.js",
44
+ "peerDependencies": {
45
+ "pg": "^8.13.1",
46
+ "zod": "^3.24.2"
47
+ },
48
+ "prettier": "@casekit/prettier-config",
49
+ "type": "module",
50
+ "scripts": {
51
+ "build": "rm -rf ./build && tsc",
52
+ "format:check": "prettier --check .",
53
+ "format": "prettier --write .",
54
+ "lint": "eslint src --max-warnings 0",
55
+ "test": "vitest --run --typecheck --coverage",
56
+ "test:watch": "vitest",
57
+ "typecheck": "tsc --noEmit"
58
+ }
59
+ }