neoorm 0.1.4 → 0.1.5
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/README.md +223 -12
- package/dist/bin/neoorm.js +50 -29
- package/dist/bin/neoorm.js.map +1 -1
- package/dist/codegen/diff-manifest.d.ts +4 -1
- package/dist/codegen/diff-manifest.d.ts.map +1 -1
- package/dist/codegen/diff-manifest.js +85 -4
- package/dist/codegen/diff-manifest.js.map +1 -1
- package/dist/codegen/generate-summary.d.ts +21 -0
- package/dist/codegen/generate-summary.d.ts.map +1 -0
- package/dist/codegen/generate-summary.js +101 -0
- package/dist/codegen/generate-summary.js.map +1 -0
- package/dist/codegen/generate.d.ts +6 -1
- package/dist/codegen/generate.d.ts.map +1 -1
- package/dist/codegen/generate.js +21 -8
- package/dist/codegen/generate.js.map +1 -1
- package/dist/codegen/manifest-relations.d.ts.map +1 -1
- package/dist/codegen/manifest-relations.js +9 -2
- package/dist/codegen/manifest-relations.js.map +1 -1
- package/dist/codegen/schema-to-manifest.d.ts +4 -1
- package/dist/codegen/schema-to-manifest.d.ts.map +1 -1
- package/dist/codegen/schema-to-manifest.js +58 -12
- package/dist/codegen/schema-to-manifest.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/dialect/postgres.d.ts +3 -0
- package/dist/dialect/postgres.d.ts.map +1 -1
- package/dist/dialect/postgres.js +51 -1
- package/dist/dialect/postgres.js.map +1 -1
- package/dist/dialect/types.d.ts +12 -1
- package/dist/dialect/types.d.ts.map +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/introspect/queries.d.ts +5 -0
- package/dist/introspect/queries.d.ts.map +1 -1
- package/dist/introspect/queries.js +17 -0
- package/dist/introspect/queries.js.map +1 -1
- package/dist/introspect/to-manifest.d.ts.map +1 -1
- package/dist/introspect/to-manifest.js +58 -14
- package/dist/introspect/to-manifest.js.map +1 -1
- package/dist/migrate/runner.d.ts +21 -0
- package/dist/migrate/runner.d.ts.map +1 -1
- package/dist/migrate/runner.js +99 -6
- package/dist/migrate/runner.js.map +1 -1
- package/dist/plugins/builtin.d.ts +35 -2
- package/dist/plugins/builtin.d.ts.map +1 -1
- package/dist/plugins/builtin.js +312 -1
- package/dist/plugins/builtin.js.map +1 -1
- package/dist/plugins/registry.d.ts.map +1 -1
- package/dist/plugins/registry.js +3 -1
- package/dist/plugins/registry.js.map +1 -1
- package/dist/runtime/client.d.ts +3 -0
- package/dist/runtime/client.d.ts.map +1 -1
- package/dist/runtime/client.js +11 -12
- package/dist/runtime/client.js.map +1 -1
- package/dist/runtime/executor.d.ts +1 -0
- package/dist/runtime/executor.d.ts.map +1 -1
- package/dist/runtime/executor.js +27 -4
- package/dist/runtime/executor.js.map +1 -1
- package/dist/runtime/query/compile.d.ts +6 -1
- package/dist/runtime/query/compile.d.ts.map +1 -1
- package/dist/runtime/query/compile.js +38 -6
- package/dist/runtime/query/compile.js.map +1 -1
- package/dist/runtime/query/create.d.ts +7 -0
- package/dist/runtime/query/create.d.ts.map +1 -1
- package/dist/runtime/query/create.js +50 -104
- package/dist/runtime/query/create.js.map +1 -1
- package/dist/runtime/query/delete.d.ts.map +1 -1
- package/dist/runtime/query/delete.js +9 -3
- package/dist/runtime/query/delete.js.map +1 -1
- package/dist/runtime/query/find.d.ts.map +1 -1
- package/dist/runtime/query/find.js +15 -11
- package/dist/runtime/query/find.js.map +1 -1
- package/dist/runtime/query/primary-key.d.ts +11 -1
- package/dist/runtime/query/primary-key.d.ts.map +1 -1
- package/dist/runtime/query/primary-key.js +55 -0
- package/dist/runtime/query/primary-key.js.map +1 -1
- package/dist/runtime/query/relation-writes.d.ts +24 -0
- package/dist/runtime/query/relation-writes.d.ts.map +1 -0
- package/dist/runtime/query/relation-writes.js +325 -0
- package/dist/runtime/query/relation-writes.js.map +1 -0
- package/dist/runtime/query/unique.d.ts.map +1 -1
- package/dist/runtime/query/unique.js +1 -5
- package/dist/runtime/query/unique.js.map +1 -1
- package/dist/runtime/query/update.d.ts.map +1 -1
- package/dist/runtime/query/update.js +45 -28
- package/dist/runtime/query/update.js.map +1 -1
- package/dist/runtime/types.d.ts +3 -2
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/schema/column-where.d.ts +18 -15
- package/dist/schema/column-where.d.ts.map +1 -1
- package/dist/schema/column.d.ts +2 -2
- package/dist/schema/column.d.ts.map +1 -1
- package/dist/schema/column.js +1 -1
- package/dist/schema/column.js.map +1 -1
- package/dist/schema/index.d.ts +4 -4
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +1 -1
- package/dist/schema/index.js.map +1 -1
- package/dist/schema/nested-relation-types.d.ts +60 -0
- package/dist/schema/nested-relation-types.d.ts.map +1 -0
- package/dist/schema/nested-relation-types.js +2 -0
- package/dist/schema/nested-relation-types.js.map +1 -0
- package/dist/schema/relation-types.d.ts +122 -22
- package/dist/schema/relation-types.d.ts.map +1 -1
- package/dist/schema/types.d.ts +27 -38
- package/dist/schema/types.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -43,6 +43,7 @@ export default defineConfig({
|
|
|
43
43
|
datasource: {
|
|
44
44
|
provider: "postgresql",
|
|
45
45
|
url: process.env.DATABASE_URL!,
|
|
46
|
+
enum: "check", // "check" (default) | "union" | "native"
|
|
46
47
|
},
|
|
47
48
|
});
|
|
48
49
|
```
|
|
@@ -81,20 +82,118 @@ Column field names use camelCase in TypeScript. By default, SQL column names are
|
|
|
81
82
|
|
|
82
83
|
### Column types
|
|
83
84
|
|
|
84
|
-
| Builder | SQL type | Notes |
|
|
85
|
-
|
|
86
|
-
| `id.primary()` | `TEXT` | App-generated string IDs (e.g. `user_a1b2c3d4`) |
|
|
87
|
-
| `uuid()` | `UUID` | Defaults to UUID v7; pass `{ version: 4 }` for v4 |
|
|
88
|
-
| `uuid().primary()` | `UUID` | Primary key; auto-generated on create if omitted |
|
|
89
|
-
| `
|
|
90
|
-
| `
|
|
91
|
-
| `
|
|
92
|
-
| `
|
|
85
|
+
| Builder | SQL type | TypeScript | Notes |
|
|
86
|
+
|---------|----------|------------|-------|
|
|
87
|
+
| `id.primary()` | `TEXT` | `string` | App-generated string IDs (e.g. `user_a1b2c3d4`) |
|
|
88
|
+
| `uuid()` | `UUID` | `string` | Defaults to UUID v7; pass `{ version: 4 }` for v4 |
|
|
89
|
+
| `uuid().primary()` | `UUID` | `string` | Primary key; auto-generated on create if omitted |
|
|
90
|
+
| `serial()` | `INTEGER GENERATED BY DEFAULT AS IDENTITY` | `number` | Auto-increment; omit on insert, DB assigns via `RETURNING` |
|
|
91
|
+
| `serial().primary()` | same | `number` | Integer PK with DB-generated values |
|
|
92
|
+
| `text()` | `TEXT` | `string \| null` | |
|
|
93
|
+
| `bool()` | `BOOLEAN` | `boolean \| null` | |
|
|
94
|
+
| `int()` | `INTEGER` | `number \| null` | |
|
|
95
|
+
| `timestamp()` | `TIMESTAMPTZ` | `Date \| null` | Use `.defaultNow()` for `DEFAULT NOW()` |
|
|
96
|
+
| `json()` | `JSON` | `unknown \| null` | Generic: `json<MyType>()` |
|
|
97
|
+
| `jsonb()` | `JSONB` | `unknown \| null` | Generic: `jsonb<MyType>()` |
|
|
98
|
+
| `decimal()` / `numeric()` | `NUMERIC` or `NUMERIC(p,s)` | `string \| null` | Use strings to avoid float precision loss |
|
|
99
|
+
| `enumType(["a", "b"])` | mode-dependent | union literals | See [Enum columns](#enum-columns) |
|
|
100
|
+
| `bytea()` | `BYTEA` | `Buffer \| null` | Binary data |
|
|
101
|
+
| `textArray()` | `TEXT[]` | `string[] \| null` | |
|
|
102
|
+
| `intArray()` | `INTEGER[]` | `number[] \| null` | |
|
|
103
|
+
| `citext()` | `CITEXT` | `string \| null` | Case-insensitive text; requires `citext` extension |
|
|
93
104
|
|
|
94
105
|
All column builders support `.notNull()`, `.unique()`, `.default(value)`, `.defaultNow()`, `.primary()`, and `.map(name)`.
|
|
95
106
|
|
|
96
107
|
Foreign keys use `fk("target_table.target_column", { as, inverse, nullable?, onDelete? })`.
|
|
97
108
|
|
|
109
|
+
### Enum columns
|
|
110
|
+
|
|
111
|
+
Define allowed values in the schema:
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
import { enumType, table } from "neoorm/schema";
|
|
115
|
+
|
|
116
|
+
posts: table("posts", {
|
|
117
|
+
status: enumType(["draft", "published", "archived"] as const)
|
|
118
|
+
.notNull()
|
|
119
|
+
.default("draft"),
|
|
120
|
+
})
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Control how enums are stored via `datasource.enum` in `neoorm.config.ts`:
|
|
124
|
+
|
|
125
|
+
| Mode | SQL | DB enforcement |
|
|
126
|
+
|------|-----|----------------|
|
|
127
|
+
| `check` (default) | `TEXT` + `CHECK (...)` | yes |
|
|
128
|
+
| `union` | `TEXT` | no (TypeScript union only) |
|
|
129
|
+
| `native` | Postgres `CREATE TYPE ... AS ENUM` | yes |
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
export default defineConfig({
|
|
133
|
+
schema: "./schema.ts",
|
|
134
|
+
out: "./neoorm",
|
|
135
|
+
datasource: {
|
|
136
|
+
provider: "postgresql",
|
|
137
|
+
url: process.env.DATABASE_URL!,
|
|
138
|
+
enum: "check", // or "union" | "native"
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Optional custom SQL type name for native mode: `enumType(["draft", "published"], { name: "post_status" })`.
|
|
144
|
+
|
|
145
|
+
Query and mutate enum columns like strings with compile-time union checking:
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
await db.posts.findMany({ where: { status: "published" } });
|
|
149
|
+
await db.posts.create({ data: { title: "Hello", status: "draft" } });
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### JSON and decimal columns
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
posts: table("posts", {
|
|
156
|
+
metadata: jsonb<{ tags: string[]; featured?: boolean }>(),
|
|
157
|
+
price: decimal({ precision: 10, scale: 2 }).default("0.00"),
|
|
158
|
+
})
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
// jsonb — equality match on the full object
|
|
163
|
+
await db.posts.findMany({
|
|
164
|
+
where: { metadata: { featured: true, category: "engineering" } },
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// decimal — compare as strings
|
|
168
|
+
await db.posts.findMany({
|
|
169
|
+
where: { price: { gte: "9.99", lte: "49.99" } },
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
await db.posts.create({
|
|
173
|
+
data: {
|
|
174
|
+
title: "Premium",
|
|
175
|
+
price: "19.99",
|
|
176
|
+
metadata: { featured: true, tags: ["orm"] },
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Serial primary keys
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
items: table("items", {
|
|
185
|
+
id: serial().primary(),
|
|
186
|
+
name: text().notNull(),
|
|
187
|
+
})
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Omit `id` on insert — the database assigns it and `RETURNING` populates the result:
|
|
191
|
+
|
|
192
|
+
```ts
|
|
193
|
+
const item = await db.items.create({ data: { name: "Widget" } });
|
|
194
|
+
// item.id is the DB-generated integer
|
|
195
|
+
```
|
|
196
|
+
|
|
98
197
|
### UUID columns
|
|
99
198
|
|
|
100
199
|
```ts
|
|
@@ -158,6 +257,55 @@ manyToMany(schema.posts, schema.tags, {
|
|
|
158
257
|
});
|
|
159
258
|
```
|
|
160
259
|
|
|
260
|
+
## Relation writes
|
|
261
|
+
|
|
262
|
+
`create` and `update` accept nested relation writes alongside scalar fields. Relation-only updates are supported (no scalar `SET` required).
|
|
263
|
+
|
|
264
|
+
| Relation kind | Operations |
|
|
265
|
+
|---------------|------------|
|
|
266
|
+
| **To-one** (outgoing FK) | `connect`, `create`, `disconnect` (nullable FK only) |
|
|
267
|
+
| **One-to-many** (inverse) | `create`, `connect`, `disconnect`, `set` |
|
|
268
|
+
| **Many-to-many** | `connect`, `connectOrCreate`, `disconnect`, `set` |
|
|
269
|
+
|
|
270
|
+
```ts
|
|
271
|
+
// To-one on update
|
|
272
|
+
await db.posts.update({
|
|
273
|
+
where: { id: postId },
|
|
274
|
+
data: { author: { connect: { id: userId } } },
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
// Nested create on one-to-many (full nested relation writes inside create items)
|
|
278
|
+
await db.posts.update({
|
|
279
|
+
where: { id: postId },
|
|
280
|
+
data: {
|
|
281
|
+
comments: {
|
|
282
|
+
create: [{ body: "Hi", author: { connect: { id: userId } } }],
|
|
283
|
+
connect: [{ id: commentId }],
|
|
284
|
+
disconnect: [{ id: oldCommentId }], // or `true` to unlink all
|
|
285
|
+
set: [{ id: commentId }], // replace all links
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
// M2M on create or update
|
|
291
|
+
await db.posts.update({
|
|
292
|
+
where: { id: postId },
|
|
293
|
+
data: {
|
|
294
|
+
tags: {
|
|
295
|
+
connect: [{ id: tagId }],
|
|
296
|
+
set: [{ id: tagId }],
|
|
297
|
+
connectOrCreate: [
|
|
298
|
+
{ where: { slug: "orm" }, create: { slug: "orm", name: "ORM" } },
|
|
299
|
+
],
|
|
300
|
+
},
|
|
301
|
+
},
|
|
302
|
+
});
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
Within a single relation field, operations run in order: `disconnect` → `set` → `connect` / `connectOrCreate` → `create`. Mixing `set` with `connect` or `create` is discouraged — `set` replaces the full link set.
|
|
306
|
+
|
|
307
|
+
`updateMany` does not support relation writes. Nested `delete` on relations is not supported yet.
|
|
308
|
+
|
|
161
309
|
## Where clauses
|
|
162
310
|
|
|
163
311
|
`findMany`, `findFirst`, `findUnique`, `count`, `update`, `updateMany`, `delete`, and `deleteMany` all accept a typed `where` argument.
|
|
@@ -266,14 +414,28 @@ const [user, post] = await db.$transaction([
|
|
|
266
414
|
(tx) => tx.posts.create({ data: { title: "Hello" } }),
|
|
267
415
|
]);
|
|
268
416
|
|
|
269
|
-
// Options
|
|
417
|
+
// Options (outermost transaction only)
|
|
270
418
|
await db.$transaction(fn, {
|
|
271
419
|
isolationLevel: "Serializable", // ReadUncommitted | ReadCommitted | RepeatableRead | Serializable
|
|
272
420
|
readOnly: true,
|
|
273
421
|
});
|
|
422
|
+
|
|
423
|
+
// Nested $transaction uses PostgreSQL savepoints on the same connection.
|
|
424
|
+
// A nested failure rolls back only that block; the outer transaction can continue.
|
|
425
|
+
await db.$transaction(async (tx) => {
|
|
426
|
+
await tx.users.create({ data: { email: "outer@example.com" } });
|
|
427
|
+
|
|
428
|
+
await tx.$transaction(async (nested) => {
|
|
429
|
+
await nested.posts.create({
|
|
430
|
+
data: { title: "Nested", body: "...", authorId: "user_1" },
|
|
431
|
+
});
|
|
432
|
+
}).catch(() => undefined);
|
|
433
|
+
|
|
434
|
+
// Outer writes are kept even if the nested block failed.
|
|
435
|
+
});
|
|
274
436
|
```
|
|
275
437
|
|
|
276
|
-
Nested `create` calls inside a transaction do not start a separate transaction.
|
|
438
|
+
Nested `create` calls inside a transaction do not start a separate transaction. Nested `$transaction` calls use savepoints; `readOnly` and `isolationLevel` apply only to the outermost `BEGIN`.
|
|
277
439
|
|
|
278
440
|
## CLI
|
|
279
441
|
|
|
@@ -282,10 +444,43 @@ Nested `create` calls inside a transaction do not start a separate transaction.
|
|
|
282
444
|
| `neoorm generate` | Emit manifest, typed client, models, includes, and migrations |
|
|
283
445
|
| `neoorm migrate dev` | Apply pending migrations, then generate a new one if the schema changed |
|
|
284
446
|
| `neoorm migrate deploy` | Apply pending migrations |
|
|
447
|
+
| `neoorm migrate status` | List applied vs pending migrations |
|
|
448
|
+
| `neoorm migrate reset --force` | Drop public schema and re-apply migrations (local dev) |
|
|
285
449
|
| `neoorm db push` | Push the current snapshot schema to the database |
|
|
286
450
|
| `neoorm db pull` | Introspect the database into a schema file |
|
|
287
451
|
|
|
288
|
-
|
|
452
|
+
### Generate outcomes
|
|
453
|
+
|
|
454
|
+
`neoorm generate` always refreshes generated TypeScript files (`client.ts`, `manifest.ts`, `models.ts`, etc.). It prints one of four outcomes:
|
|
455
|
+
|
|
456
|
+
| Outcome | Meaning |
|
|
457
|
+
|---------|---------|
|
|
458
|
+
| **Schema unchanged** | Snapshot hash matches — no manifest or migration changes |
|
|
459
|
+
| **Client regenerated** | Manifest changed but no database DDL was needed (e.g. `enumMode`, relation metadata) |
|
|
460
|
+
| **Migration created** | New `migrations/<timestamp>/migration.sql` written |
|
|
461
|
+
| **Migration blocked** | Destructive or manual changes prevented writing SQL |
|
|
462
|
+
|
|
463
|
+
When migration is blocked or skipped, the CLI explains why — for example unsupported type casts (`alter_column_type_manual`), enum value changes, or destructive drops. Re-run with `--accept-data-loss` to include destructive DDL, or write a manual migration for unsupported type changes.
|
|
464
|
+
|
|
465
|
+
```bash
|
|
466
|
+
neoorm generate --accept-data-loss
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
### Migration status and reset
|
|
470
|
+
|
|
471
|
+
```bash
|
|
472
|
+
neoorm migrate status
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
Shows applied migrations (with timestamps), pending folders on disk, and warnings for drift (applied in DB but missing on disk).
|
|
476
|
+
|
|
477
|
+
```bash
|
|
478
|
+
neoorm migrate reset --force
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
Drops the `public` schema and re-applies all migrations from disk. Requires `--force`. Use `--skip-apply` to only drop the schema without re-applying.
|
|
482
|
+
|
|
483
|
+
`generate` creates migration SQL only when the schema diff produces DDL changes (new tables/columns, column renames via `.map()`, etc.).
|
|
289
484
|
|
|
290
485
|
## Plugins
|
|
291
486
|
|
|
@@ -319,6 +514,22 @@ await db.places.findMany({
|
|
|
319
514
|
|
|
320
515
|
PostGIS columns are stored as geometry/geography in PostgreSQL and exposed as GeoJSON in TypeScript.
|
|
321
516
|
|
|
517
|
+
### Citext
|
|
518
|
+
|
|
519
|
+
`citext()` is registered as a separate plugin that enables the `citext` extension when used in a schema:
|
|
520
|
+
|
|
521
|
+
```ts
|
|
522
|
+
import { citext, table } from "neoorm/schema";
|
|
523
|
+
|
|
524
|
+
users: table("users", {
|
|
525
|
+
email: citext().notNull().unique(),
|
|
526
|
+
})
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
## Examples
|
|
530
|
+
|
|
531
|
+
The [blog example](examples/blog/schema.ts) demonstrates relations, many-to-many, `jsonb`, `decimal`, and `enumType` columns. See [queries.example.ts](examples/blog/queries.example.ts) for typed queries and mutations using those types.
|
|
532
|
+
|
|
322
533
|
## API surface
|
|
323
534
|
|
|
324
535
|
| Import | Purpose |
|
package/dist/bin/neoorm.js
CHANGED
|
@@ -3,8 +3,8 @@ import { Command } from "commander";
|
|
|
3
3
|
import { join, resolve } from "node:path";
|
|
4
4
|
import { Pool } from "pg";
|
|
5
5
|
import { loadConfig } from "../config.js";
|
|
6
|
-
import { generateFromSchema } from "../codegen/generate.js";
|
|
7
|
-
import { migrateDeploy, dbPush, dbPushWarnings } from "../migrate/runner.js";
|
|
6
|
+
import { generateFromSchema, formatGenerateSummary } from "../codegen/generate.js";
|
|
7
|
+
import { migrateDeploy, migrateReset, migrateStatus, formatMigrateStatus, dbPush, dbPushWarnings, } from "../migrate/runner.js";
|
|
8
8
|
import { introspectPostgres } from "../introspect/pull.js";
|
|
9
9
|
import { writeFile } from "node:fs/promises";
|
|
10
10
|
const program = new Command();
|
|
@@ -21,22 +21,12 @@ program
|
|
|
21
21
|
const config = await loadConfig(cwd);
|
|
22
22
|
const schemaPath = resolve(cwd, config.schema);
|
|
23
23
|
const outDir = resolve(cwd, config.out);
|
|
24
|
-
const {
|
|
24
|
+
const { warnings, summary } = await generateFromSchema(schemaPath, outDir, {
|
|
25
25
|
...(options.acceptDataLoss ? { acceptDataLoss: true } : {}),
|
|
26
|
+
...(config.datasource.enum ? { enumMode: config.datasource.enum } : {}),
|
|
26
27
|
});
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (migrationName) {
|
|
30
|
-
console.log(`Created migration: ${migrationName}`);
|
|
31
|
-
}
|
|
32
|
-
else if (destructiveBlocked) {
|
|
33
|
-
console.log("Destructive schema changes detected — no migration written");
|
|
34
|
-
}
|
|
35
|
-
else if (schemaChanged) {
|
|
36
|
-
console.log("Manifest updated (no migration SQL needed)");
|
|
37
|
-
}
|
|
38
|
-
else {
|
|
39
|
-
console.log("No schema changes detected");
|
|
28
|
+
for (const line of formatGenerateSummary(summary, outDir)) {
|
|
29
|
+
console.log(line);
|
|
40
30
|
}
|
|
41
31
|
for (const warning of warnings) {
|
|
42
32
|
console.warn(`Warning: ${warning}`);
|
|
@@ -45,8 +35,10 @@ program
|
|
|
45
35
|
program
|
|
46
36
|
.command("migrate")
|
|
47
37
|
.description("Run migrations")
|
|
48
|
-
.argument("[subcommand]", "dev | deploy")
|
|
38
|
+
.argument("[subcommand]", "dev | deploy | status | reset")
|
|
49
39
|
.option("--accept-data-loss", "Include destructive schema changes in generated migrations")
|
|
40
|
+
.option("--force", "Required for reset — drops the public schema and all data")
|
|
41
|
+
.option("--skip-apply", "With reset, only drop schema without re-applying migrations")
|
|
50
42
|
.action(async (subcommand, options) => {
|
|
51
43
|
const cwd = process.cwd();
|
|
52
44
|
const config = await loadConfig(cwd);
|
|
@@ -54,6 +46,33 @@ program
|
|
|
54
46
|
const migrationsDir = join(outDir, "migrations");
|
|
55
47
|
const pool = new Pool({ connectionString: config.datasource.url });
|
|
56
48
|
try {
|
|
49
|
+
if (subcommand === "status") {
|
|
50
|
+
const status = await migrateStatus(pool, migrationsDir);
|
|
51
|
+
for (const line of formatMigrateStatus(status, migrationsDir)) {
|
|
52
|
+
console.log(line);
|
|
53
|
+
}
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (subcommand === "reset") {
|
|
57
|
+
const { reapplied } = await migrateReset(pool, migrationsDir, {
|
|
58
|
+
force: options.force ?? false,
|
|
59
|
+
...(options.skipApply ? { skipApply: true } : {}),
|
|
60
|
+
});
|
|
61
|
+
console.log("✓ Database schema reset (public schema dropped and recreated)");
|
|
62
|
+
if (options.skipApply) {
|
|
63
|
+
console.log(" Skipped re-applying migrations (--skip-apply)");
|
|
64
|
+
}
|
|
65
|
+
else if (reapplied.length === 0) {
|
|
66
|
+
console.log(" No migrations on disk to apply");
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
console.log(` Re-applied ${reapplied.length} migration(s):`);
|
|
70
|
+
for (const name of reapplied) {
|
|
71
|
+
console.log(` - ${name}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
57
76
|
if (subcommand === "deploy" || subcommand === "dev") {
|
|
58
77
|
const applied = await migrateDeploy(pool, migrationsDir);
|
|
59
78
|
if (applied.length === 0) {
|
|
@@ -66,27 +85,28 @@ program
|
|
|
66
85
|
}
|
|
67
86
|
}
|
|
68
87
|
if (subcommand === "dev") {
|
|
69
|
-
const { generateFromSchema } = await import("../codegen/generate.js");
|
|
70
88
|
const schemaPath = resolve(cwd, config.schema);
|
|
71
|
-
const {
|
|
89
|
+
const { warnings, summary, migrationName } = await generateFromSchema(schemaPath, outDir, {
|
|
72
90
|
...(options.acceptDataLoss ? { acceptDataLoss: true } : {}),
|
|
91
|
+
...(config.datasource.enum ? { enumMode: config.datasource.enum } : {}),
|
|
73
92
|
});
|
|
93
|
+
for (const line of formatGenerateSummary(summary, outDir)) {
|
|
94
|
+
console.log(line);
|
|
95
|
+
}
|
|
74
96
|
for (const warning of warnings) {
|
|
75
97
|
console.warn(`Warning: ${warning}`);
|
|
76
98
|
}
|
|
77
99
|
if (migrationName) {
|
|
78
|
-
await migrateDeploy(pool, join(outDir, "migrations"));
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
console.log("Destructive schema changes detected — no migration created");
|
|
100
|
+
const newlyApplied = await migrateDeploy(pool, join(outDir, "migrations"));
|
|
101
|
+
if (newlyApplied.length > 0) {
|
|
102
|
+
console.log(`Applied new migration: ${newlyApplied.join(", ")}`);
|
|
103
|
+
}
|
|
83
104
|
}
|
|
84
105
|
}
|
|
106
|
+
return;
|
|
85
107
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
process.exit(1);
|
|
89
|
-
}
|
|
108
|
+
console.error("Usage: neoorm migrate dev | deploy | status | reset");
|
|
109
|
+
process.exit(1);
|
|
90
110
|
}
|
|
91
111
|
finally {
|
|
92
112
|
await pool.end();
|
|
@@ -111,7 +131,8 @@ program
|
|
|
111
131
|
console.error("Run neoorm generate first");
|
|
112
132
|
process.exit(1);
|
|
113
133
|
}
|
|
114
|
-
const { appliedStatements, destructiveBlocked } = await dbPush(pool, manifest, { ...(options.acceptDataLoss ? { acceptDataLoss: true } : {})
|
|
134
|
+
const { appliedStatements, destructiveBlocked } = await dbPush(pool, manifest, { ...(options.acceptDataLoss ? { acceptDataLoss: true } : {}),
|
|
135
|
+
});
|
|
115
136
|
for (const warning of dbPushWarnings(destructiveBlocked)) {
|
|
116
137
|
console.warn(`Warning: ${warning}`);
|
|
117
138
|
}
|
package/dist/bin/neoorm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"neoorm.js","sourceRoot":"","sources":["../../src/bin/neoorm.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"neoorm.js","sourceRoot":"","sources":["../../src/bin/neoorm.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import type { DestructiveChange, Manifest, ManifestColumn, ManifestDiff, TableDiff } from "../dialect/types.js";
|
|
2
2
|
export declare function columnSqlType(col: ManifestColumn, manifest?: Manifest): string;
|
|
3
3
|
export declare function columnsEqual(a: ManifestColumn, b: ManifestColumn, manifest?: Manifest): boolean;
|
|
4
|
-
export declare function buildMigrationSql(tableDiffs: TableDiff[], extensions?: string[], manifest?: Manifest
|
|
4
|
+
export declare function buildMigrationSql(tableDiffs: TableDiff[], extensions?: string[], manifest?: Manifest, newEnumTypes?: Record<string, {
|
|
5
|
+
values: readonly string[];
|
|
6
|
+
}>): string[];
|
|
5
7
|
export declare function diffManifest(prev: Manifest | null, next: Manifest): ManifestDiff;
|
|
6
8
|
export declare function resolveMigrationSql(diff: ManifestDiff, prev: Manifest | null, next: Manifest, acceptDataLoss: boolean): {
|
|
7
9
|
sql: string[];
|
|
8
10
|
blocked: DestructiveChange[];
|
|
9
11
|
};
|
|
12
|
+
export declare function explainNoMigrationSql(prev: Manifest | null, next: Manifest, diff: ManifestDiff): string[];
|
|
10
13
|
export declare function formatDestructiveWarnings(destructive: DestructiveChange[]): string[];
|
|
11
14
|
//# sourceMappingURL=diff-manifest.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diff-manifest.d.ts","sourceRoot":"","sources":["../../src/codegen/diff-manifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,iBAAiB,EAEjB,QAAQ,EACR,cAAc,EAGd,YAAY,EACZ,SAAS,EACV,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"diff-manifest.d.ts","sourceRoot":"","sources":["../../src/codegen/diff-manifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,iBAAiB,EAEjB,QAAQ,EACR,cAAc,EAGd,YAAY,EACZ,SAAS,EACV,MAAM,qBAAqB,CAAC;AAiB7B,wBAAgB,aAAa,CAC3B,GAAG,EAAE,cAAc,EACnB,QAAQ,CAAC,EAAE,QAAQ,GAClB,MAAM,CAER;AAuCD,wBAAgB,YAAY,CAC1B,CAAC,EAAE,cAAc,EACjB,CAAC,EAAE,cAAc,EACjB,QAAQ,CAAC,EAAE,QAAQ,GAClB,OAAO,CAaT;AA0bD,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,SAAS,EAAE,EACvB,UAAU,GAAE,MAAM,EAAO,EACzB,QAAQ,CAAC,EAAE,QAAQ,EACnB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC,GAC3D,MAAM,EAAE,CAyCV;AAiCD,wBAAgB,YAAY,CAC1B,IAAI,EAAE,QAAQ,GAAG,IAAI,EACrB,IAAI,EAAE,QAAQ,GACb,YAAY,CAoDd;AAED,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,QAAQ,GAAG,IAAI,EACrB,IAAI,EAAE,QAAQ,EACd,cAAc,EAAE,OAAO,GACtB;IAAE,GAAG,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,iBAAiB,EAAE,CAAA;CAAE,CAkDjD;AAcD,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,QAAQ,GAAG,IAAI,EACrB,IAAI,EAAE,QAAQ,EACd,IAAI,EAAE,YAAY,GACjB,MAAM,EAAE,CAoDV;AAED,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,iBAAiB,EAAE,GAC/B,MAAM,EAAE,CAyBV"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { postgresDialect, resolveFkConstraintName, resolveIndexSqlName, resolveUniqueConstraintName, canAutoCastType, resolveColumnSqlType, } from "../dialect/postgres.js";
|
|
1
|
+
import { postgresDialect, resolveFkConstraintName, resolveIndexSqlName, resolveUniqueConstraintName, canAutoCastType, resolveColumnSqlType, emitCreateEnumTypes, } from "../dialect/postgres.js";
|
|
2
2
|
function tablesBySqlName(manifest) {
|
|
3
3
|
return new Map(Object.values(manifest.tables).map((table) => [table.sqlName, table]));
|
|
4
4
|
}
|
|
@@ -36,6 +36,7 @@ export function columnsEqual(a, b, manifest) {
|
|
|
36
36
|
a.primary === b.primary &&
|
|
37
37
|
defaultsEqual(a, b) &&
|
|
38
38
|
typeOptionsEqual(a.typeOptions, b.typeOptions) &&
|
|
39
|
+
a.checkExpression === b.checkExpression &&
|
|
39
40
|
columnSqlType(a, manifest) === columnSqlType(b, manifest) &&
|
|
40
41
|
(a.kind !== "fk" ||
|
|
41
42
|
(a.fkTarget === b.fkTarget && a.onDelete === b.onDelete)));
|
|
@@ -172,6 +173,10 @@ function buildColumnAlter(prevCol, nextCol, prevTable, alterSqlName, prevManifes
|
|
|
172
173
|
if (prevCol.primary !== nextCol.primary) {
|
|
173
174
|
hasAlter = true;
|
|
174
175
|
}
|
|
176
|
+
if (prevCol.checkExpression !== nextCol.checkExpression) {
|
|
177
|
+
alter.setCheckExpression = nextCol.checkExpression ?? null;
|
|
178
|
+
hasAlter = true;
|
|
179
|
+
}
|
|
175
180
|
return hasAlter ? alter : null;
|
|
176
181
|
}
|
|
177
182
|
function diffColumns(prevTable, nextTable, prevManifest, nextManifest) {
|
|
@@ -372,11 +377,14 @@ function stripDestructiveFromDiff(diff) {
|
|
|
372
377
|
(stripped.fkChanges?.length ?? 0) > 0;
|
|
373
378
|
return hasChanges ? stripped : null;
|
|
374
379
|
}
|
|
375
|
-
export function buildMigrationSql(tableDiffs, extensions = [], manifest) {
|
|
380
|
+
export function buildMigrationSql(tableDiffs, extensions = [], manifest, newEnumTypes) {
|
|
376
381
|
const sql = [];
|
|
377
382
|
if (extensions.length > 0) {
|
|
378
383
|
sql.push(...postgresDialect.emitCreateExtensions(extensions));
|
|
379
384
|
}
|
|
385
|
+
if (newEnumTypes && Object.keys(newEnumTypes).length > 0) {
|
|
386
|
+
sql.push(...emitCreateEnumTypes(newEnumTypes));
|
|
387
|
+
}
|
|
380
388
|
const alterDiffs = tableDiffs.filter((d) => !d.create && !d.drop);
|
|
381
389
|
const dropDiffs = tableDiffs.filter((d) => d.drop);
|
|
382
390
|
const createDiffs = tableDiffs.filter((d) => d.create);
|
|
@@ -402,11 +410,36 @@ export function buildMigrationSql(tableDiffs, extensions = [], manifest) {
|
|
|
402
410
|
}
|
|
403
411
|
return sql;
|
|
404
412
|
}
|
|
413
|
+
function diffEnumTypes(prev, next) {
|
|
414
|
+
const destructive = [];
|
|
415
|
+
const newEnumTypes = {};
|
|
416
|
+
const prevEnums = prev?.enumTypes ?? {};
|
|
417
|
+
const nextEnums = next.enumTypes ?? {};
|
|
418
|
+
for (const [name, definition] of Object.entries(nextEnums)) {
|
|
419
|
+
const prevDefinition = prevEnums[name];
|
|
420
|
+
if (!prevDefinition) {
|
|
421
|
+
newEnumTypes[name] = definition;
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
if (JSON.stringify(prevDefinition.values) !== JSON.stringify(definition.values)) {
|
|
425
|
+
destructive.push({
|
|
426
|
+
kind: "alter_enum_manual",
|
|
427
|
+
table: name,
|
|
428
|
+
detail: `Enum type "${name}" values changed — manual migration required`,
|
|
429
|
+
sql: `-- ALTER TYPE ${name} ... (manual)`,
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
return { newEnumTypes, destructive };
|
|
434
|
+
}
|
|
405
435
|
export function diffManifest(prev, next) {
|
|
406
436
|
if (!prev) {
|
|
407
437
|
const sql = [];
|
|
408
438
|
const extensions = next.extensions ?? [];
|
|
409
439
|
sql.push(...postgresDialect.emitCreateExtensions(extensions));
|
|
440
|
+
if (next.enumTypes) {
|
|
441
|
+
sql.push(...emitCreateEnumTypes(next.enumTypes));
|
|
442
|
+
}
|
|
410
443
|
const tables = Object.values(next.tables);
|
|
411
444
|
for (const table of tables) {
|
|
412
445
|
sql.push(postgresDialect.emitCreateTable(table, { manifest: next }));
|
|
@@ -425,6 +458,7 @@ export function diffManifest(prev, next) {
|
|
|
425
458
|
const destructive = [];
|
|
426
459
|
const prevExtensions = new Set(prev.extensions ?? []);
|
|
427
460
|
const newExtensions = (next.extensions ?? []).filter((ext) => !prevExtensions.has(ext));
|
|
461
|
+
const { newEnumTypes, destructive: enumDestructive } = diffEnumTypes(prev, next);
|
|
428
462
|
for (const sqlName of allSqlNames) {
|
|
429
463
|
const prevTable = prevBySql.get(sqlName);
|
|
430
464
|
const nextTable = nextBySql.get(sqlName);
|
|
@@ -434,12 +468,14 @@ export function diffManifest(prev, next) {
|
|
|
434
468
|
tableDiffs.push(diff);
|
|
435
469
|
destructive.push(...classifyDestructive(diff, prevTable));
|
|
436
470
|
}
|
|
437
|
-
|
|
471
|
+
destructive.push(...enumDestructive);
|
|
472
|
+
const sql = buildMigrationSql(tableDiffs, newExtensions, next, newEnumTypes);
|
|
438
473
|
return { isInitial: false, sql, destructive };
|
|
439
474
|
}
|
|
440
475
|
export function resolveMigrationSql(diff, prev, next, acceptDataLoss) {
|
|
441
476
|
if (acceptDataLoss) {
|
|
442
|
-
const blocked = diff.destructive.filter((change) => change.kind === "alter_column_type_manual"
|
|
477
|
+
const blocked = diff.destructive.filter((change) => change.kind === "alter_column_type_manual" ||
|
|
478
|
+
change.kind === "alter_enum_manual");
|
|
443
479
|
if (blocked.length === 0) {
|
|
444
480
|
return { sql: diff.sql, blocked: [] };
|
|
445
481
|
}
|
|
@@ -477,6 +513,49 @@ export function resolveMigrationSql(diff, prev, next, acceptDataLoss) {
|
|
|
477
513
|
blocked: diff.destructive,
|
|
478
514
|
};
|
|
479
515
|
}
|
|
516
|
+
function relationsSignature(manifest) {
|
|
517
|
+
const parts = [];
|
|
518
|
+
for (const table of Object.values(manifest.tables)) {
|
|
519
|
+
for (const rel of table.relations) {
|
|
520
|
+
parts.push(`${table.sqlName}:${rel.name}:${rel.inverse}:${rel.targetAccessor}:${rel.cardinality}`);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
return parts.sort().join("|");
|
|
524
|
+
}
|
|
525
|
+
export function explainNoMigrationSql(prev, next, diff) {
|
|
526
|
+
const reasons = [];
|
|
527
|
+
if (!prev) {
|
|
528
|
+
return ["Initial schema — run generate again if no migration was expected."];
|
|
529
|
+
}
|
|
530
|
+
for (const change of diff.destructive) {
|
|
531
|
+
if (change.kind === "alter_column_type_manual" ||
|
|
532
|
+
change.kind === "alter_enum_manual") {
|
|
533
|
+
reasons.push(formatDestructiveWarnings([change])[0]);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
if (prev.enumMode !== next.enumMode) {
|
|
537
|
+
reasons.push(`enumMode changed (${prev.enumMode ?? "check"} → ${next.enumMode ?? "check"}) — no DDL emitted`);
|
|
538
|
+
}
|
|
539
|
+
const prevExtensions = new Set(prev.extensions ?? []);
|
|
540
|
+
const addedExtensions = (next.extensions ?? []).filter((ext) => !prevExtensions.has(ext));
|
|
541
|
+
if (addedExtensions.length === 0 &&
|
|
542
|
+
JSON.stringify(prev.extensions ?? []) !== JSON.stringify(next.extensions ?? [])) {
|
|
543
|
+
reasons.push("extensions metadata changed — no new CREATE EXTENSION statements");
|
|
544
|
+
}
|
|
545
|
+
if (JSON.stringify(prev.manyToMany) !== JSON.stringify(next.manyToMany)) {
|
|
546
|
+
reasons.push("manyToMany relation metadata changed — no DDL emitted");
|
|
547
|
+
}
|
|
548
|
+
if (relationsSignature(prev) !== relationsSignature(next)) {
|
|
549
|
+
reasons.push("relation names or inverses changed — no DDL emitted");
|
|
550
|
+
}
|
|
551
|
+
if (diff.destructive.length > 0 && diff.sql.length === 0) {
|
|
552
|
+
reasons.push("Destructive changes detected; safe subset produced no SQL. Re-run with --accept-data-loss or write a manual migration.");
|
|
553
|
+
}
|
|
554
|
+
if (reasons.length === 0) {
|
|
555
|
+
reasons.push("Manifest updated for codegen; database schema is already compatible.");
|
|
556
|
+
}
|
|
557
|
+
return reasons;
|
|
558
|
+
}
|
|
480
559
|
export function formatDestructiveWarnings(destructive) {
|
|
481
560
|
return destructive.map((change) => {
|
|
482
561
|
switch (change.kind) {
|
|
@@ -494,6 +573,8 @@ export function formatDestructiveWarnings(destructive) {
|
|
|
494
573
|
return `${change.detail} — this may affect referential integrity`;
|
|
495
574
|
case "alter_primary_key":
|
|
496
575
|
return `${change.detail} — primary key changes require manual migration`;
|
|
576
|
+
case "alter_enum_manual":
|
|
577
|
+
return `${change.detail} — enum value changes require manual migration`;
|
|
497
578
|
default: {
|
|
498
579
|
const _exhaustive = change.kind;
|
|
499
580
|
return _exhaustive;
|