neoorm 0.1.5 → 0.2.1

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 (149) hide show
  1. package/README.md +246 -23
  2. package/dist/bin/neoorm.js +70 -4
  3. package/dist/bin/neoorm.js.map +1 -1
  4. package/dist/codegen/diff-manifest.d.ts +3 -0
  5. package/dist/codegen/diff-manifest.d.ts.map +1 -1
  6. package/dist/codegen/diff-manifest.js +15 -0
  7. package/dist/codegen/diff-manifest.js.map +1 -1
  8. package/dist/codegen/emit-includes.d.ts.map +1 -1
  9. package/dist/codegen/emit-includes.js +7 -1
  10. package/dist/codegen/emit-includes.js.map +1 -1
  11. package/dist/codegen/emit-models.d.ts.map +1 -1
  12. package/dist/codegen/emit-models.js +28 -6
  13. package/dist/codegen/emit-models.js.map +1 -1
  14. package/dist/codegen/generate.d.ts +10 -4
  15. package/dist/codegen/generate.d.ts.map +1 -1
  16. package/dist/codegen/generate.js +22 -10
  17. package/dist/codegen/generate.js.map +1 -1
  18. package/dist/codegen/schema-to-manifest.d.ts +2 -3
  19. package/dist/codegen/schema-to-manifest.d.ts.map +1 -1
  20. package/dist/codegen/schema-to-manifest.js +25 -13
  21. package/dist/codegen/schema-to-manifest.js.map +1 -1
  22. package/dist/dialect/types.d.ts +2 -0
  23. package/dist/dialect/types.d.ts.map +1 -1
  24. package/dist/index.d.ts +8 -3
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +4 -2
  27. package/dist/index.js.map +1 -1
  28. package/dist/init/scaffold.d.ts +19 -0
  29. package/dist/init/scaffold.d.ts.map +1 -0
  30. package/dist/init/scaffold.js +92 -0
  31. package/dist/init/scaffold.js.map +1 -0
  32. package/dist/init/templates.d.ts +4 -0
  33. package/dist/init/templates.d.ts.map +1 -0
  34. package/dist/init/templates.js +44 -0
  35. package/dist/init/templates.js.map +1 -0
  36. package/dist/introspect/pull.d.ts.map +1 -1
  37. package/dist/introspect/pull.js +18 -7
  38. package/dist/introspect/pull.js.map +1 -1
  39. package/dist/migrate/runner.d.ts +5 -0
  40. package/dist/migrate/runner.d.ts.map +1 -1
  41. package/dist/migrate/runner.js +67 -1
  42. package/dist/migrate/runner.js.map +1 -1
  43. package/dist/plugins/builtin.d.ts.map +1 -1
  44. package/dist/plugins/builtin.js +6 -0
  45. package/dist/plugins/builtin.js.map +1 -1
  46. package/dist/plugins/json/operators.d.ts +3 -0
  47. package/dist/plugins/json/operators.d.ts.map +1 -0
  48. package/dist/plugins/json/operators.js +65 -0
  49. package/dist/plugins/json/operators.js.map +1 -0
  50. package/dist/plugins/types.d.ts +1 -0
  51. package/dist/plugins/types.d.ts.map +1 -1
  52. package/dist/runtime/client.d.ts +30 -3
  53. package/dist/runtime/client.d.ts.map +1 -1
  54. package/dist/runtime/client.js +46 -27
  55. package/dist/runtime/client.js.map +1 -1
  56. package/dist/runtime/errors.d.ts +20 -0
  57. package/dist/runtime/errors.d.ts.map +1 -0
  58. package/dist/runtime/errors.js +68 -0
  59. package/dist/runtime/errors.js.map +1 -0
  60. package/dist/runtime/pg-error.d.ts +19 -0
  61. package/dist/runtime/pg-error.d.ts.map +1 -0
  62. package/dist/runtime/pg-error.js +110 -0
  63. package/dist/runtime/pg-error.js.map +1 -0
  64. package/dist/runtime/query/aggregate.d.ts +11 -0
  65. package/dist/runtime/query/aggregate.d.ts.map +1 -0
  66. package/dist/runtime/query/aggregate.js +44 -0
  67. package/dist/runtime/query/aggregate.js.map +1 -0
  68. package/dist/runtime/query/compile.d.ts +15 -5
  69. package/dist/runtime/query/compile.d.ts.map +1 -1
  70. package/dist/runtime/query/compile.js +72 -13
  71. package/dist/runtime/query/compile.js.map +1 -1
  72. package/dist/runtime/query/count.d.ts +3 -3
  73. package/dist/runtime/query/count.d.ts.map +1 -1
  74. package/dist/runtime/query/count.js +7 -4
  75. package/dist/runtime/query/count.js.map +1 -1
  76. package/dist/runtime/query/create.d.ts +7 -4
  77. package/dist/runtime/query/create.d.ts.map +1 -1
  78. package/dist/runtime/query/create.js +33 -16
  79. package/dist/runtime/query/create.js.map +1 -1
  80. package/dist/runtime/query/cursor-codec.d.ts +3 -0
  81. package/dist/runtime/query/cursor-codec.d.ts.map +1 -0
  82. package/dist/runtime/query/cursor-codec.js +27 -0
  83. package/dist/runtime/query/cursor-codec.js.map +1 -0
  84. package/dist/runtime/query/cursor.d.ts +22 -0
  85. package/dist/runtime/query/cursor.d.ts.map +1 -0
  86. package/dist/runtime/query/cursor.js +84 -0
  87. package/dist/runtime/query/cursor.js.map +1 -0
  88. package/dist/runtime/query/delete.d.ts +4 -4
  89. package/dist/runtime/query/delete.d.ts.map +1 -1
  90. package/dist/runtime/query/delete.js +11 -7
  91. package/dist/runtime/query/delete.js.map +1 -1
  92. package/dist/runtime/query/execute.d.ts +16 -0
  93. package/dist/runtime/query/execute.d.ts.map +1 -0
  94. package/dist/runtime/query/execute.js +74 -0
  95. package/dist/runtime/query/execute.js.map +1 -0
  96. package/dist/runtime/query/find.d.ts +7 -5
  97. package/dist/runtime/query/find.d.ts.map +1 -1
  98. package/dist/runtime/query/find.js +142 -25
  99. package/dist/runtime/query/find.js.map +1 -1
  100. package/dist/runtime/query/paginate.d.ts +17 -0
  101. package/dist/runtime/query/paginate.d.ts.map +1 -0
  102. package/dist/runtime/query/paginate.js +35 -0
  103. package/dist/runtime/query/paginate.js.map +1 -0
  104. package/dist/runtime/query/relation-writes.d.ts +5 -4
  105. package/dist/runtime/query/relation-writes.d.ts.map +1 -1
  106. package/dist/runtime/query/relation-writes.js +117 -38
  107. package/dist/runtime/query/relation-writes.js.map +1 -1
  108. package/dist/runtime/query/update.d.ts +4 -4
  109. package/dist/runtime/query/update.d.ts.map +1 -1
  110. package/dist/runtime/query/update.js +63 -21
  111. package/dist/runtime/query/update.js.map +1 -1
  112. package/dist/runtime/query/updated-at.d.ts +6 -0
  113. package/dist/runtime/query/updated-at.d.ts.map +1 -0
  114. package/dist/runtime/query/updated-at.js +21 -0
  115. package/dist/runtime/query/updated-at.js.map +1 -0
  116. package/dist/runtime/query/upsert.d.ts +2 -2
  117. package/dist/runtime/query/upsert.d.ts.map +1 -1
  118. package/dist/runtime/query/upsert.js +12 -8
  119. package/dist/runtime/query/upsert.js.map +1 -1
  120. package/dist/runtime/types.d.ts +19 -10
  121. package/dist/runtime/types.d.ts.map +1 -1
  122. package/dist/schema/column-where.d.ts +14 -1
  123. package/dist/schema/column-where.d.ts.map +1 -1
  124. package/dist/schema/column.d.ts +4 -0
  125. package/dist/schema/column.d.ts.map +1 -1
  126. package/dist/schema/column.js +6 -0
  127. package/dist/schema/column.js.map +1 -1
  128. package/dist/schema/define-schema.d.ts +6 -2
  129. package/dist/schema/define-schema.d.ts.map +1 -1
  130. package/dist/schema/define-schema.js +5 -2
  131. package/dist/schema/define-schema.js.map +1 -1
  132. package/dist/schema/index.d.ts +3 -3
  133. package/dist/schema/index.d.ts.map +1 -1
  134. package/dist/schema/index.js.map +1 -1
  135. package/dist/schema/nested-relation-types.d.ts +1 -0
  136. package/dist/schema/nested-relation-types.d.ts.map +1 -1
  137. package/dist/schema/relation-types.d.ts +42 -0
  138. package/dist/schema/relation-types.d.ts.map +1 -1
  139. package/dist/schema/table.d.ts +7 -1
  140. package/dist/schema/table.d.ts.map +1 -1
  141. package/dist/schema/table.js +6 -2
  142. package/dist/schema/table.js.map +1 -1
  143. package/dist/schema/types.d.ts +60 -3
  144. package/dist/schema/types.d.ts.map +1 -1
  145. package/dist/utils/case.d.ts +2 -0
  146. package/dist/utils/case.d.ts.map +1 -1
  147. package/dist/utils/case.js +14 -0
  148. package/dist/utils/case.js.map +1 -1
  149. package/package.json +1 -1
package/README.md CHANGED
@@ -12,7 +12,44 @@ Requires Node.js 20+ and PostgreSQL.
12
12
 
13
13
  ## Quick start
14
14
 
15
- **1. Define a schema** (`schema.ts`):
15
+ **1. Scaffold a new project**
16
+
17
+ ```bash
18
+ npx neoorm init
19
+ ```
20
+
21
+ This creates `neoorm.config.ts`, `schema.ts`, `.env.example`, generates `neoorm/client.ts` (and related files), and writes the first migration under `neoorm/migrations/`.
22
+
23
+ **2. Set your database URL**
24
+
25
+ ```bash
26
+ cp .env.example .env
27
+ # edit DATABASE_URL in .env
28
+ ```
29
+
30
+ **3. Apply migrations**
31
+
32
+ ```bash
33
+ npx neoorm migrate deploy
34
+ ```
35
+
36
+ **4. Query**
37
+
38
+ ```ts
39
+ import { db } from "./neoorm/client.js";
40
+
41
+ const user = await db.users.findById(userId, {
42
+ with: {
43
+ posts: { orderBy: { createdAt: "desc" }, limit: 10 },
44
+ },
45
+ });
46
+ ```
47
+
48
+ ### Manual setup
49
+
50
+ If you prefer to write files yourself instead of `neoorm init`:
51
+
52
+ **Define a schema** (`schema.ts`):
16
53
 
17
54
  ```ts
18
55
  import { defineSchema, table, uuid, text, timestamp, fk } from "neoorm/schema";
@@ -56,18 +93,6 @@ npx neoorm generate
56
93
 
57
94
  This writes `client.ts`, `manifest.ts`, `models.ts`, `includes.ts`, and migration SQL when the schema changed.
58
95
 
59
- **4. Query**
60
-
61
- ```ts
62
- import { db } from "./neoorm/client.js";
63
-
64
- const user = await db.users.findById(userId, {
65
- with: {
66
- posts: { orderBy: { createdAt: "desc" }, limit: 10 },
67
- },
68
- });
69
- ```
70
-
71
96
  ## Schema DSL
72
97
 
73
98
  ### Tables and accessors
@@ -78,7 +103,7 @@ const user = await db.users.findById(userId, {
78
103
  users: table("user", { ... }) // db.users → SQL table "user"
79
104
  ```
80
105
 
81
- Column field names use camelCase in TypeScript. By default, SQL column names are snake_case (`createdAt` → `created_at`).
106
+ Column field names use camelCase in TypeScript. By default, SQL column names are snake_case (`createdAt` → `created_at`). You can change that globally or per table with `columnNaming`.
82
107
 
83
108
  ### Column types
84
109
 
@@ -92,7 +117,16 @@ Column field names use camelCase in TypeScript. By default, SQL column names are
92
117
  | `text()` | `TEXT` | `string \| null` | |
93
118
  | `bool()` | `BOOLEAN` | `boolean \| null` | |
94
119
  | `int()` | `INTEGER` | `number \| null` | |
95
- | `timestamp()` | `TIMESTAMPTZ` | `Date \| null` | Use `.defaultNow()` for `DEFAULT NOW()` |
120
+ | `timestamp()` | `TIMESTAMPTZ` | `Date \| null` | Use `.defaultNow()` for `DEFAULT NOW()`; use `.updatedAt()` for auto-update on ORM writes |
121
+
122
+ Pair audit timestamps on mutable tables:
123
+
124
+ ```ts
125
+ createdAt: timestamp().notNull().defaultNow(),
126
+ updatedAt: timestamp().notNull().defaultNow().updatedAt(),
127
+ ```
128
+
129
+ `.updatedAt()` is ORM metadata only (no DB trigger). `update`, `updateById`, `updateMany`, and `upsert` always set the column to `NOW()` in SQL; user-provided values in `data` are ignored.
96
130
  | `json()` | `JSON` | `unknown \| null` | Generic: `json<MyType>()` |
97
131
  | `jsonb()` | `JSONB` | `unknown \| null` | Generic: `jsonb<MyType>()` |
98
132
  | `decimal()` / `numeric()` | `NUMERIC` or `NUMERIC(p,s)` | `string \| null` | Use strings to avoid float precision loss |
@@ -102,7 +136,7 @@ Column field names use camelCase in TypeScript. By default, SQL column names are
102
136
  | `intArray()` | `INTEGER[]` | `number[] \| null` | |
103
137
  | `citext()` | `CITEXT` | `string \| null` | Case-insensitive text; requires `citext` extension |
104
138
 
105
- All column builders support `.notNull()`, `.unique()`, `.default(value)`, `.defaultNow()`, `.primary()`, and `.map(name)`.
139
+ All column builders support `.notNull()`, `.unique()`, `.default(value)`, `.defaultNow()`, `.updatedAt()` (timestamp only), `.primary()`, and `.map(name)`.
106
140
 
107
141
  Foreign keys use `fk("target_table.target_column", { as, inverse, nullable?, onDelete? })`.
108
142
 
@@ -159,11 +193,27 @@ posts: table("posts", {
159
193
  ```
160
194
 
161
195
  ```ts
162
- // jsonb — equality match on the full object
196
+ // jsonb — exact equality on the full object (all keys must match)
163
197
  await db.posts.findMany({
164
198
  where: { metadata: { featured: true, category: "engineering" } },
165
199
  });
166
200
 
201
+ // jsonb — partial / subset match (@> containment)
202
+ await db.posts.findMany({
203
+ where: { metadata: { jsonContains: { featured: true } } },
204
+ });
205
+
206
+ // jsonb — key existence and path filters
207
+ await db.posts.findMany({
208
+ where: {
209
+ metadata: {
210
+ hasKey: "featured",
211
+ hasAnyKeys: ["category", "tags"],
212
+ path: { segments: ["category"], equals: "engineering" },
213
+ },
214
+ },
215
+ });
216
+
167
217
  // decimal — compare as strings
168
218
  await db.posts.findMany({
169
219
  where: { price: { gte: "9.99", lte: "49.99" } },
@@ -192,6 +242,11 @@ Omit `id` on insert — the database assigns it and `RETURNING` populates the re
192
242
  ```ts
193
243
  const item = await db.items.create({ data: { name: "Widget" } });
194
244
  // item.id is the DB-generated integer
245
+
246
+ // Bulk insert with returned rows (serial IDs, UUIDs, defaults materialized)
247
+ const items = await db.items.createManyAndReturn({
248
+ data: [{ name: "A" }, { name: "B" }],
249
+ });
195
250
  ```
196
251
 
197
252
  ### UUID columns
@@ -216,7 +271,45 @@ emailAddress: text().notNull().map("email"),
216
271
  authorId: fk("users.id", { as: "author", inverse: "posts" }).map("author_ref"),
217
272
  ```
218
273
 
219
- `.map()` only produces a migration when the SQL name actually changes. Mapping to the default snake_case name (e.g. `.map("email_verified")` on `emailVerified`) is a no-op — `neoorm generate` warns about this.
274
+ `.map()` only produces a migration when the SQL name actually changes. Mapping to the active naming strategy's default name (e.g. `.map("email_verified")` on `emailVerified` in a snake_case table) is a no-op — `neoorm generate` warns about this.
275
+
276
+ ### Column naming strategy
277
+
278
+ Use `columnNaming` when an entire schema or table follows camelCase SQL column names instead of the default snake_case:
279
+
280
+ ```ts
281
+ export const schema = defineSchema(
282
+ {
283
+ users: table("users", {
284
+ emailAddress: text().notNull(), // SQL: email_address
285
+ }),
286
+
287
+ legacyUsers: table(
288
+ "legacy_users",
289
+ {
290
+ emailAddress: text().notNull(), // SQL: emailAddress
291
+ },
292
+ { columnNaming: "camelCase" },
293
+ ),
294
+ },
295
+ { columnNaming: "snakeCase" },
296
+ );
297
+ ```
298
+
299
+ Per-table options can also include `extras`:
300
+
301
+ ```ts
302
+ posts: table(
303
+ "posts",
304
+ { authorId: fk("users.id", { as: "author", inverse: "posts" }) },
305
+ {
306
+ columnNaming: "camelCase",
307
+ extras: (t) => ({ authorIdx: index().on(t.authorId) }),
308
+ },
309
+ )
310
+ ```
311
+
312
+ `columnNaming` affects SQL column names only. TypeScript keys stay exactly as written in the schema, and `.map("exact_name")` still overrides the strategy for individual columns.
220
313
 
221
314
  ### Indexes and composite keys
222
315
 
@@ -264,8 +357,8 @@ manyToMany(schema.posts, schema.tags, {
264
357
  | Relation kind | Operations |
265
358
  |---------------|------------|
266
359
  | **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` |
360
+ | **One-to-many** (inverse) | `create`, `connect`, `disconnect`, `set`, `delete` |
361
+ | **Many-to-many** | `connect`, `connectOrCreate`, `disconnect`, `set`, `delete` |
269
362
 
270
363
  ```ts
271
364
  // To-one on update
@@ -283,6 +376,7 @@ await db.posts.update({
283
376
  connect: [{ id: commentId }],
284
377
  disconnect: [{ id: oldCommentId }], // or `true` to unlink all
285
378
  set: [{ id: commentId }], // replace all links
379
+ delete: [{ id: commentId }], // or `true` to delete all linked children
286
380
  },
287
381
  },
288
382
  });
@@ -297,14 +391,100 @@ await db.posts.update({
297
391
  connectOrCreate: [
298
392
  { where: { slug: "orm" }, create: { slug: "orm", name: "ORM" } },
299
393
  ],
394
+ delete: [{ id: tagId }], // removes junction links, then deletes tag rows
300
395
  },
301
396
  },
302
397
  });
398
+
399
+ // updateMany applies scalar SET once, then nested writes per matched parent
400
+ await db.posts.updateMany({
401
+ where: { published: true },
402
+ data: {
403
+ status: "archived",
404
+ tags: { connect: [{ id: tagId }] },
405
+ },
406
+ });
303
407
  ```
304
408
 
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.
409
+ Within a single relation field, operations run in order: `delete` → `disconnect` → `set` → `connect` / `connectOrCreate` → `create`. Mixing `set` with `connect` or `create` is discouraged — `set` replaces the full link set.
410
+
411
+ ## Cursor pagination
412
+
413
+ For feeds, infinite scroll, and large tables, use `paginate` instead of `limit`/`offset`. It uses **keyset pagination** on your `orderBy` columns plus the table primary key as a stable tiebreaker (for example `(createdAt, id)`).
414
+
415
+ ```ts
416
+ let cursor: { createdAt: string; id: string } | null = null;
417
+
418
+ for (;;) {
419
+ const page = await db.posts.paginate({
420
+ where: { published: true },
421
+ orderBy: { createdAt: "desc" },
422
+ take: 20,
423
+ ...(cursor ? { after: cursor } : {}),
424
+ with: { author: true },
425
+ });
306
426
 
307
- `updateMany` does not support relation writes. Nested `delete` on relations is not supported yet.
427
+ for (const post of page.items) {
428
+ // render post
429
+ }
430
+
431
+ if (!page.hasMore) break;
432
+ cursor = page.nextCursor;
433
+ }
434
+ ```
435
+
436
+ - `orderBy` is required; scalar `id` is appended automatically when omitted.
437
+ - `take` is the page size; `hasMore` uses a `take + 1` probe row.
438
+ - `after` is a typed cursor object (`nextCursor` from the previous page).
439
+ - For HTTP APIs, encode cursors with `encodeCursor` / `decodeCursor` from `neoorm`.
440
+
441
+ On feed tables, add a composite index on the sort columns (for example `index().on(t.createdAt, t.id)`).
442
+
443
+ ## Aggregates
444
+
445
+ ### Relation counts in `with`
446
+
447
+ Load counts without fetching full related rows:
448
+
449
+ ```ts
450
+ const users = await db.users.findMany({
451
+ with: {
452
+ _count: { posts: true },
453
+ profile: true,
454
+ },
455
+ });
456
+ // users[0]._count.posts === number
457
+ ```
458
+
459
+ Optional per-relation filter: `_count: { posts: { where: { published: true } } }`.
460
+
461
+ ### Table-level `aggregate()`
462
+
463
+ ```ts
464
+ const stats = await db.posts.aggregate({
465
+ where: { published: true },
466
+ _count: true,
467
+ _avg: { views: true },
468
+ });
469
+ // { _count: number, _avg: { views: number | null } }
470
+ ```
471
+
472
+ For grouped dashboards, use `db.sql` or the `sqlBuilder` helper.
473
+
474
+ ## Bulk insert
475
+
476
+ `createMany` returns the number of rows inserted. Use `createManyAndReturn` when you need serial IDs, generated UUIDs, or default values in the result:
477
+
478
+ ```ts
479
+ const users = await db.users.createManyAndReturn({
480
+ data: [
481
+ { email: "a@example.com", name: "A" },
482
+ { email: "b@example.com", name: "B" },
483
+ ],
484
+ });
485
+ ```
486
+
487
+ Scalar fields only — no nested relation writes (same as `createMany`).
308
488
 
309
489
  ## Where clauses
310
490
 
@@ -328,7 +508,29 @@ await db.posts.findFirst({
328
508
  });
329
509
  ```
330
510
 
331
- String columns support `equals`, `contains`, `startsWith`, `endsWith`, `in`, and `notIn`. Numeric, boolean, and date columns support `equals`, `gt`, `gte`, `lt`, `lte`, `in`, and `notIn`. All nullable columns also support `isNull` and `isNotNull`.
511
+ String columns support `equals`, `contains`, `startsWith`, `endsWith`, `in`, and `notIn`. Numeric, boolean, and date columns support `equals`, `gt`, `gte`, `lt`, `lte`, `in`, and `notIn`. JSON columns support `jsonContains`, `hasKey`, `hasAnyKeys`, `hasAllKeys`, and `path`. All nullable columns also support `isNull` and `isNotNull`.
512
+
513
+ ### `distinct`
514
+
515
+ PostgreSQL `DISTINCT ON` — `orderBy` must lead with the same columns:
516
+
517
+ ```ts
518
+ await db.users.findMany({
519
+ distinct: ["email"],
520
+ orderBy: { email: "asc" },
521
+ });
522
+ ```
523
+
524
+ ### Selective `with` return types
525
+
526
+ Relation `select` narrows the TypeScript return type at compile time:
527
+
528
+ ```ts
529
+ const users = await db.users.findMany({
530
+ with: { posts: { select: { title: true } } },
531
+ });
532
+ // users[0].posts[0].title is string; .body is excluded from the type
533
+ ```
332
534
 
333
535
  ```ts
334
536
  await db.users.findMany({
@@ -441,10 +643,12 @@ Nested `create` calls inside a transaction do not start a separate transaction.
441
643
 
442
644
  | Command | Description |
443
645
  |---------|-------------|
646
+ | `neoorm init` | Scaffold `neoorm.config.ts`, `schema.ts`, `.env.example`, generate client, and first migration |
444
647
  | `neoorm generate` | Emit manifest, typed client, models, includes, and migrations |
445
648
  | `neoorm migrate dev` | Apply pending migrations, then generate a new one if the schema changed |
446
649
  | `neoorm migrate deploy` | Apply pending migrations |
447
650
  | `neoorm migrate status` | List applied vs pending migrations |
651
+ | `neoorm migrate down [--steps N]` | Roll back the last N applied migrations (default 1) |
448
652
  | `neoorm migrate reset --force` | Drop public schema and re-apply migrations (local dev) |
449
653
  | `neoorm db push` | Push the current snapshot schema to the database |
450
654
  | `neoorm db pull` | Introspect the database into a schema file |
@@ -480,6 +684,25 @@ neoorm migrate reset --force
480
684
 
481
685
  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
686
 
687
+ ### Rolling back migrations
688
+
689
+ ```bash
690
+ neoorm migrate down
691
+ neoorm migrate down --steps 2
692
+ ```
693
+
694
+ Rolls back the most recently applied migration(s) by running each migration folder's `down.sql`, removing the ledger entry from `_neoorm_migrations`, and restoring `snapshot.json` from `snapshot.before.json` (the manifest state before that migration was applied).
695
+
696
+ `down.sql` and `snapshot.before.json` are written automatically when `neoorm generate` or `neoorm migrate dev` creates a migration. The down SQL is the reverse schema diff (`next → prev`), with destructive changes accepted so rollbacks can drop columns or tables added in the forward migration.
697
+
698
+ Legacy migrations without `down.sql` cannot be rolled back — re-generate the migration or add `down.sql` manually.
699
+
700
+ Migration folders are not deleted on rollback (same as Prisma). Re-run `neoorm migrate deploy` to re-apply rolled-back migrations.
701
+
702
+ After rollback, `schema.ts` may still describe a newer schema than the restored snapshot; `neoorm migrate dev` may generate a new forward migration. `db push` and `migrate down` are independent — push ignores the migration ledger.
703
+
704
+ For a full wipe during local development, use `neoorm migrate reset --force` instead.
705
+
483
706
  `generate` creates migration SQL only when the schema diff produces DDL changes (new tables/columns, column renames via `.map()`, etc.).
484
707
 
485
708
  ## Plugins
@@ -4,14 +4,58 @@ import { join, resolve } from "node:path";
4
4
  import { Pool } from "pg";
5
5
  import { loadConfig } from "../config.js";
6
6
  import { generateFromSchema, formatGenerateSummary } from "../codegen/generate.js";
7
- import { migrateDeploy, migrateReset, migrateStatus, formatMigrateStatus, dbPush, dbPushWarnings, } from "../migrate/runner.js";
7
+ import { runInit, formatInitNextSteps } from "../init/scaffold.js";
8
+ import { migrateDeploy, migrateDown, migrateReset, migrateStatus, formatMigrateStatus, dbPush, dbPushWarnings, } from "../migrate/runner.js";
8
9
  import { introspectPostgres } from "../introspect/pull.js";
9
10
  import { writeFile } from "node:fs/promises";
11
+ import packageJson from "../../package.json" with { type: "json" };
10
12
  const program = new Command();
11
13
  program
12
14
  .name("neoorm")
13
15
  .description("NeoOrm CLI")
14
- .version("0.1.0");
16
+ .version(packageJson.version);
17
+ program
18
+ .command("init")
19
+ .description("Scaffold neoorm.config.ts, schema.ts, .env.example, and run initial generate")
20
+ .option("--force", "Overwrite existing scaffold files")
21
+ .option("--schema <path>", "Schema file path", "./schema.ts")
22
+ .option("--out <dir>", "Generated output directory", "./neoorm")
23
+ .action(async (options) => {
24
+ const cwd = process.cwd();
25
+ try {
26
+ const result = await runInit({
27
+ cwd,
28
+ schemaPath: options.schema,
29
+ outDir: options.out,
30
+ ...(options.force ? { force: true } : {}),
31
+ });
32
+ if (result.written.length > 0) {
33
+ console.log("Scaffolded:");
34
+ for (const file of result.written) {
35
+ console.log(` + ${file}`);
36
+ }
37
+ }
38
+ if (result.skipped.length > 0) {
39
+ console.log("Skipped (already exists):");
40
+ for (const file of result.skipped) {
41
+ console.log(` - ${file}`);
42
+ }
43
+ }
44
+ for (const line of formatGenerateSummary(result.summary, result.outDir)) {
45
+ console.log(line);
46
+ }
47
+ for (const warning of result.warnings) {
48
+ console.warn(`Warning: ${warning}`);
49
+ }
50
+ for (const line of formatInitNextSteps(cwd, options.schema, options.out)) {
51
+ console.log(line);
52
+ }
53
+ }
54
+ catch (err) {
55
+ console.error(err instanceof Error ? err.message : String(err));
56
+ process.exit(1);
57
+ }
58
+ });
15
59
  program
16
60
  .command("generate")
17
61
  .description("Generate manifest, client, and migrations from schema")
@@ -35,10 +79,11 @@ program
35
79
  program
36
80
  .command("migrate")
37
81
  .description("Run migrations")
38
- .argument("[subcommand]", "dev | deploy | status | reset")
82
+ .argument("[subcommand]", "dev | deploy | status | reset | down")
39
83
  .option("--accept-data-loss", "Include destructive schema changes in generated migrations")
40
84
  .option("--force", "Required for reset — drops the public schema and all data")
41
85
  .option("--skip-apply", "With reset, only drop schema without re-applying migrations")
86
+ .option("--steps <n>", "Number of migrations to roll back (down)", "1")
42
87
  .action(async (subcommand, options) => {
43
88
  const cwd = process.cwd();
44
89
  const config = await loadConfig(cwd);
@@ -73,6 +118,27 @@ program
73
118
  }
74
119
  return;
75
120
  }
121
+ if (subcommand === "down") {
122
+ const steps = Number.parseInt(options.steps ?? "1", 10);
123
+ if (!Number.isFinite(steps) || steps < 1) {
124
+ console.error("--steps must be a positive integer");
125
+ process.exit(1);
126
+ }
127
+ const reverted = await migrateDown(pool, migrationsDir, {
128
+ steps,
129
+ outDir,
130
+ });
131
+ if (reverted.length === 0) {
132
+ console.log("No migrations rolled back");
133
+ }
134
+ else {
135
+ console.log(`Rolled back ${reverted.length} migration(s):`);
136
+ for (const name of reverted) {
137
+ console.log(` - ${name}`);
138
+ }
139
+ }
140
+ return;
141
+ }
76
142
  if (subcommand === "deploy" || subcommand === "dev") {
77
143
  const applied = await migrateDeploy(pool, migrationsDir);
78
144
  if (applied.length === 0) {
@@ -105,7 +171,7 @@ program
105
171
  }
106
172
  return;
107
173
  }
108
- console.error("Usage: neoorm migrate dev | deploy | status | reset");
174
+ console.error("Usage: neoorm migrate dev | deploy | status | reset | down");
109
175
  process.exit(1);
110
176
  }
111
177
  finally {
@@ -1 +1 @@
1
- {"version":3,"file":"neoorm.js","sourceRoot":"","sources":["../../src/bin/neoorm.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"neoorm.js","sourceRoot":"","sources":["../../src/bin/neoorm.ts"],"names":[],"mappings":"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"}
@@ -5,6 +5,9 @@ export declare function buildMigrationSql(tableDiffs: TableDiff[], extensions?:
5
5
  values: readonly string[];
6
6
  }>): string[];
7
7
  export declare function diffManifest(prev: Manifest | null, next: Manifest): ManifestDiff;
8
+ export declare function emptyManifest(): Manifest;
9
+ /** SQL to roll back a forward migration (next → prev). */
10
+ export declare function buildDownSql(prev: Manifest | null, next: Manifest): string[];
8
11
  export declare function resolveMigrationSql(diff: ManifestDiff, prev: Manifest | null, next: Manifest, acceptDataLoss: boolean): {
9
12
  sql: string[];
10
13
  blocked: DestructiveChange[];
@@ -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;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
+ {"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,CAcT;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,aAAa,IAAI,QAAQ,CAMxC;AAED,0DAA0D;AAC1D,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,EAAE,IAAI,EAAE,QAAQ,GAAG,MAAM,EAAE,CAK5E;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"}
@@ -34,6 +34,7 @@ export function columnsEqual(a, b, manifest) {
34
34
  effectiveNullable(a) === effectiveNullable(b) &&
35
35
  a.unique === b.unique &&
36
36
  a.primary === b.primary &&
37
+ !!a.updatedAt === !!b.updatedAt &&
37
38
  defaultsEqual(a, b) &&
38
39
  typeOptionsEqual(a.typeOptions, b.typeOptions) &&
39
40
  a.checkExpression === b.checkExpression &&
@@ -472,6 +473,20 @@ export function diffManifest(prev, next) {
472
473
  const sql = buildMigrationSql(tableDiffs, newExtensions, next, newEnumTypes);
473
474
  return { isInitial: false, sql, destructive };
474
475
  }
476
+ export function emptyManifest() {
477
+ return {
478
+ version: 1,
479
+ tables: {},
480
+ manyToMany: [],
481
+ };
482
+ }
483
+ /** SQL to roll back a forward migration (next → prev). */
484
+ export function buildDownSql(prev, next) {
485
+ const target = prev ?? emptyManifest();
486
+ const diff = diffManifest(next, target);
487
+ const { sql } = resolveMigrationSql(diff, next, target, true);
488
+ return sql;
489
+ }
475
490
  export function resolveMigrationSql(diff, prev, next, acceptDataLoss) {
476
491
  if (acceptDataLoss) {
477
492
  const blocked = diff.destructive.filter((change) => change.kind === "alter_column_type_manual" ||