neoorm 0.7.4 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/bin/neoorm.js +36 -27
- package/dist/bin/neoorm.js.map +1 -1
- package/dist/codegen/diff-manifest.d.ts.map +1 -1
- package/dist/codegen/diff-manifest.js +55 -30
- package/dist/codegen/diff-manifest.js.map +1 -1
- package/dist/codegen/emit-includes.d.ts.map +1 -1
- package/dist/codegen/emit-includes.js +6 -6
- package/dist/codegen/emit-includes.js.map +1 -1
- package/dist/codegen/emit-models.d.ts.map +1 -1
- package/dist/codegen/emit-models.js +14 -14
- package/dist/codegen/emit-models.js.map +1 -1
- package/dist/codegen/generate.d.ts +11 -1
- package/dist/codegen/generate.d.ts.map +1 -1
- package/dist/codegen/generate.js +30 -11
- package/dist/codegen/generate.js.map +1 -1
- package/dist/codegen/manifest-relations.d.ts +2 -1
- package/dist/codegen/manifest-relations.d.ts.map +1 -1
- package/dist/codegen/manifest-relations.js +4 -27
- package/dist/codegen/manifest-relations.js.map +1 -1
- package/dist/codegen/schema-to-manifest.d.ts +3 -2
- package/dist/codegen/schema-to-manifest.d.ts.map +1 -1
- package/dist/codegen/schema-to-manifest.js +9 -38
- package/dist/codegen/schema-to-manifest.js.map +1 -1
- package/dist/config.d.ts +6 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +21 -13
- package/dist/config.js.map +1 -1
- package/dist/datasource-provider.d.ts +7 -0
- package/dist/datasource-provider.d.ts.map +1 -0
- package/dist/datasource-provider.js +11 -0
- package/dist/datasource-provider.js.map +1 -0
- package/dist/dialect/fk.d.ts.map +1 -1
- package/dist/dialect/fk.js +4 -2
- package/dist/dialect/fk.js.map +1 -1
- package/dist/dialect/postgres.d.ts.map +1 -1
- package/dist/dialect/postgres.js +11 -15
- package/dist/dialect/postgres.js.map +1 -1
- package/dist/dialect/shared.d.ts +3 -1
- package/dist/dialect/shared.d.ts.map +1 -1
- package/dist/dialect/shared.js +4 -0
- package/dist/dialect/shared.js.map +1 -1
- package/dist/dialect/sqlite.d.ts.map +1 -1
- package/dist/dialect/sqlite.js +15 -18
- package/dist/dialect/sqlite.js.map +1 -1
- package/dist/dialect/sqlite.test.d.ts +2 -0
- package/dist/dialect/sqlite.test.d.ts.map +1 -0
- package/dist/dialect/sqlite.test.js +122 -0
- package/dist/dialect/sqlite.test.js.map +1 -0
- package/dist/dialect/types.d.ts +2 -1
- package/dist/dialect/types.d.ts.map +1 -1
- package/dist/docs/render.d.ts.map +1 -1
- package/dist/docs/render.js.map +1 -1
- package/dist/docs/search.d.ts.map +1 -1
- package/dist/docs/search.js +4 -4
- package/dist/docs/search.js.map +1 -1
- package/dist/docs/server.d.ts.map +1 -1
- package/dist/docs/server.js +3 -3
- package/dist/docs/server.js.map +1 -1
- package/dist/index.d.ts +12 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/dist/init/scaffold.d.ts.map +1 -1
- package/dist/init/scaffold.js +3 -1
- package/dist/init/scaffold.js.map +1 -1
- package/dist/introspect/pull.d.ts.map +1 -1
- package/dist/introspect/pull.js +209 -154
- package/dist/introspect/pull.js.map +1 -1
- package/dist/introspect/queries.d.ts +6 -0
- package/dist/introspect/queries.d.ts.map +1 -1
- package/dist/introspect/queries.js +19 -0
- package/dist/introspect/queries.js.map +1 -1
- package/dist/introspect/sqlite/to-manifest.d.ts.map +1 -1
- package/dist/introspect/sqlite/to-manifest.js +6 -9
- package/dist/introspect/sqlite/to-manifest.js.map +1 -1
- package/dist/introspect/to-manifest.d.ts +8 -1
- package/dist/introspect/to-manifest.d.ts.map +1 -1
- package/dist/introspect/to-manifest.js +96 -21
- package/dist/introspect/to-manifest.js.map +1 -1
- package/dist/migrate/runner.d.ts +16 -0
- package/dist/migrate/runner.d.ts.map +1 -1
- package/dist/migrate/runner.js +147 -24
- package/dist/migrate/runner.js.map +1 -1
- package/dist/plugins/builtin.d.ts.map +1 -1
- package/dist/plugins/builtin.js +15 -16
- package/dist/plugins/builtin.js.map +1 -1
- package/dist/plugins/json/operators.d.ts.map +1 -1
- package/dist/plugins/json/operators.js +100 -27
- package/dist/plugins/json/operators.js.map +1 -1
- package/dist/plugins/postgis/columns.d.ts +18 -4
- package/dist/plugins/postgis/columns.d.ts.map +1 -1
- package/dist/plugins/postgis/columns.js +69 -10
- package/dist/plugins/postgis/columns.js.map +1 -1
- package/dist/plugins/postgis/columns.test.d.ts +2 -0
- package/dist/plugins/postgis/columns.test.d.ts.map +1 -0
- package/dist/plugins/postgis/columns.test.js +67 -0
- package/dist/plugins/postgis/columns.test.js.map +1 -0
- package/dist/plugins/postgis/index.d.ts +4 -2
- package/dist/plugins/postgis/index.d.ts.map +1 -1
- package/dist/plugins/postgis/index.js +2 -1
- package/dist/plugins/postgis/index.js.map +1 -1
- package/dist/plugins/postgis/where-types.d.ts +18 -0
- package/dist/plugins/postgis/where-types.d.ts.map +1 -0
- package/dist/plugins/postgis/where-types.js +2 -0
- package/dist/plugins/postgis/where-types.js.map +1 -0
- package/dist/plugins/registry.d.ts.map +1 -1
- package/dist/plugins/registry.js +20 -5
- package/dist/plugins/registry.js.map +1 -1
- package/dist/plugins/types.d.ts +1 -1
- package/dist/plugins/types.d.ts.map +1 -1
- package/dist/runtime/client.d.ts +60 -11
- package/dist/runtime/client.d.ts.map +1 -1
- package/dist/runtime/client.js +85 -39
- package/dist/runtime/client.js.map +1 -1
- package/dist/runtime/client.test.d.ts +2 -0
- package/dist/runtime/client.test.d.ts.map +1 -0
- package/dist/runtime/client.test.js +170 -0
- package/dist/runtime/client.test.js.map +1 -0
- package/dist/runtime/compile-error.d.ts +14 -0
- package/dist/runtime/compile-error.d.ts.map +1 -0
- package/dist/runtime/compile-error.js +14 -0
- package/dist/runtime/compile-error.js.map +1 -0
- package/dist/runtime/driver-sqlite.test.d.ts +2 -0
- package/dist/runtime/driver-sqlite.test.d.ts.map +1 -0
- package/dist/runtime/driver-sqlite.test.js +195 -0
- package/dist/runtime/driver-sqlite.test.js.map +1 -0
- package/dist/runtime/driver.d.ts +16 -1
- package/dist/runtime/driver.d.ts.map +1 -1
- package/dist/runtime/driver.js +118 -96
- package/dist/runtime/driver.js.map +1 -1
- package/dist/runtime/error-builders.d.ts +7 -6
- package/dist/runtime/error-builders.d.ts.map +1 -1
- package/dist/runtime/error-builders.js +2 -2
- package/dist/runtime/error-builders.js.map +1 -1
- package/dist/runtime/error-codes.d.ts +50 -0
- package/dist/runtime/error-codes.d.ts.map +1 -0
- package/dist/runtime/error-codes.js +54 -0
- package/dist/runtime/error-codes.js.map +1 -0
- package/dist/runtime/error-hints.d.ts +1 -0
- package/dist/runtime/error-hints.d.ts.map +1 -1
- package/dist/runtime/error-hints.js +13 -1
- package/dist/runtime/error-hints.js.map +1 -1
- package/dist/runtime/errors.d.ts +55 -8
- package/dist/runtime/errors.d.ts.map +1 -1
- package/dist/runtime/errors.js +143 -12
- package/dist/runtime/errors.js.map +1 -1
- package/dist/runtime/errors.test.d.ts +2 -0
- package/dist/runtime/errors.test.d.ts.map +1 -0
- package/dist/runtime/errors.test.js +115 -0
- package/dist/runtime/errors.test.js.map +1 -0
- package/dist/runtime/executor.d.ts +22 -3
- package/dist/runtime/executor.d.ts.map +1 -1
- package/dist/runtime/executor.js +93 -39
- package/dist/runtime/executor.js.map +1 -1
- package/dist/runtime/parse-db-json.d.ts +8 -0
- package/dist/runtime/parse-db-json.d.ts.map +1 -0
- package/dist/runtime/parse-db-json.js +33 -0
- package/dist/runtime/parse-db-json.js.map +1 -0
- package/dist/runtime/pg-error.d.ts.map +1 -1
- package/dist/runtime/pg-error.js +16 -16
- package/dist/runtime/pg-error.js.map +1 -1
- package/dist/runtime/pg-rollback.test.d.ts +2 -0
- package/dist/runtime/pg-rollback.test.d.ts.map +1 -0
- package/dist/runtime/pg-rollback.test.js +39 -0
- package/dist/runtime/pg-rollback.test.js.map +1 -0
- package/dist/runtime/query/aggregate.d.ts.map +1 -1
- package/dist/runtime/query/aggregate.js +15 -4
- package/dist/runtime/query/aggregate.js.map +1 -1
- package/dist/runtime/query/compile-aggregate.d.ts +39 -0
- package/dist/runtime/query/compile-aggregate.d.ts.map +1 -0
- package/dist/runtime/query/compile-aggregate.js +456 -0
- package/dist/runtime/query/compile-aggregate.js.map +1 -0
- package/dist/runtime/query/compile-where.d.ts +32 -0
- package/dist/runtime/query/compile-where.d.ts.map +1 -0
- package/dist/runtime/query/compile-where.js +708 -0
- package/dist/runtime/query/compile-where.js.map +1 -0
- package/dist/runtime/query/compile-write.d.ts +53 -0
- package/dist/runtime/query/compile-write.d.ts.map +1 -0
- package/dist/runtime/query/compile-write.js +467 -0
- package/dist/runtime/query/compile-write.js.map +1 -0
- package/dist/runtime/query/compile.d.ts +3 -114
- package/dist/runtime/query/compile.d.ts.map +1 -1
- package/dist/runtime/query/compile.js +3 -1536
- package/dist/runtime/query/compile.js.map +1 -1
- package/dist/runtime/query/count.d.ts +1 -0
- package/dist/runtime/query/count.d.ts.map +1 -1
- package/dist/runtime/query/count.js +12 -14
- package/dist/runtime/query/count.js.map +1 -1
- package/dist/runtime/query/create.d.ts.map +1 -1
- package/dist/runtime/query/create.js +20 -49
- package/dist/runtime/query/create.js.map +1 -1
- package/dist/runtime/query/cursor-codec.d.ts.map +1 -1
- package/dist/runtime/query/cursor-codec.js +8 -2
- package/dist/runtime/query/cursor-codec.js.map +1 -1
- package/dist/runtime/query/cursor.d.ts.map +1 -1
- package/dist/runtime/query/cursor.js +31 -7
- package/dist/runtime/query/cursor.js.map +1 -1
- package/dist/runtime/query/delete.d.ts.map +1 -1
- package/dist/runtime/query/delete.js +17 -17
- package/dist/runtime/query/delete.js.map +1 -1
- package/dist/runtime/query/execute.d.ts.map +1 -1
- package/dist/runtime/query/execute.js +11 -12
- package/dist/runtime/query/execute.js.map +1 -1
- package/dist/runtime/query/find-or-create.d.ts +4 -2
- package/dist/runtime/query/find-or-create.d.ts.map +1 -1
- package/dist/runtime/query/find-or-create.js +53 -43
- package/dist/runtime/query/find-or-create.js.map +1 -1
- package/dist/runtime/query/find-or-create.test.d.ts +2 -0
- package/dist/runtime/query/find-or-create.test.d.ts.map +1 -0
- package/dist/runtime/query/find-or-create.test.js +189 -0
- package/dist/runtime/query/find-or-create.test.js.map +1 -0
- package/dist/runtime/query/find.d.ts.map +1 -1
- package/dist/runtime/query/find.js +86 -58
- package/dist/runtime/query/find.js.map +1 -1
- package/dist/runtime/query/group-by.d.ts.map +1 -1
- package/dist/runtime/query/group-by.js +3 -2
- package/dist/runtime/query/group-by.js.map +1 -1
- package/dist/runtime/query/manifest-lookup.d.ts.map +1 -1
- package/dist/runtime/query/manifest-lookup.js +1 -1
- package/dist/runtime/query/manifest-lookup.js.map +1 -1
- package/dist/runtime/query/map-row.d.ts.map +1 -1
- package/dist/runtime/query/map-row.js +1 -1
- package/dist/runtime/query/map-row.js.map +1 -1
- package/dist/runtime/query/paginate.d.ts +3 -0
- package/dist/runtime/query/paginate.d.ts.map +1 -1
- package/dist/runtime/query/paginate.js +49 -11
- package/dist/runtime/query/paginate.js.map +1 -1
- package/dist/runtime/query/primary-key.d.ts +1 -1
- package/dist/runtime/query/primary-key.d.ts.map +1 -1
- package/dist/runtime/query/primary-key.js +45 -8
- package/dist/runtime/query/primary-key.js.map +1 -1
- package/dist/runtime/query/projection.d.ts +1 -0
- package/dist/runtime/query/projection.d.ts.map +1 -1
- package/dist/runtime/query/projection.js +23 -7
- package/dist/runtime/query/projection.js.map +1 -1
- package/dist/runtime/query/relation-planner.d.ts.map +1 -1
- package/dist/runtime/query/relation-planner.js +82 -53
- package/dist/runtime/query/relation-planner.js.map +1 -1
- package/dist/runtime/query/relation-writes.d.ts +3 -1
- package/dist/runtime/query/relation-writes.d.ts.map +1 -1
- package/dist/runtime/query/relation-writes.js +145 -54
- package/dist/runtime/query/relation-writes.js.map +1 -1
- package/dist/runtime/query/serialize-dialect.test.d.ts +2 -0
- package/dist/runtime/query/serialize-dialect.test.d.ts.map +1 -0
- package/dist/runtime/query/serialize-dialect.test.js +79 -0
- package/dist/runtime/query/serialize-dialect.test.js.map +1 -0
- package/dist/runtime/query/sqlite-constraint-errors.test.d.ts +2 -0
- package/dist/runtime/query/sqlite-constraint-errors.test.d.ts.map +1 -0
- package/dist/runtime/query/sqlite-constraint-errors.test.js +60 -0
- package/dist/runtime/query/sqlite-constraint-errors.test.js.map +1 -0
- package/dist/runtime/query/strip.d.ts.map +1 -1
- package/dist/runtime/query/strip.js.map +1 -1
- package/dist/runtime/query/table-index.d.ts.map +1 -1
- package/dist/runtime/query/table-index.js +4 -3
- package/dist/runtime/query/table-index.js.map +1 -1
- package/dist/runtime/query/unique.d.ts +8 -1
- package/dist/runtime/query/unique.d.ts.map +1 -1
- package/dist/runtime/query/unique.js +89 -4
- package/dist/runtime/query/unique.js.map +1 -1
- package/dist/runtime/query/unique.test.d.ts +2 -0
- package/dist/runtime/query/unique.test.d.ts.map +1 -0
- package/dist/runtime/query/unique.test.js +231 -0
- package/dist/runtime/query/unique.test.js.map +1 -0
- package/dist/runtime/query/update.d.ts.map +1 -1
- package/dist/runtime/query/update.js +30 -35
- package/dist/runtime/query/update.js.map +1 -1
- package/dist/runtime/query/update.test.d.ts +2 -0
- package/dist/runtime/query/update.test.d.ts.map +1 -0
- package/dist/runtime/query/update.test.js +186 -0
- package/dist/runtime/query/update.test.js.map +1 -0
- package/dist/runtime/query/updated-at.d.ts.map +1 -1
- package/dist/runtime/query/updated-at.js.map +1 -1
- package/dist/runtime/query/upsert.d.ts.map +1 -1
- package/dist/runtime/query/upsert.js +5 -7
- package/dist/runtime/query/upsert.js.map +1 -1
- package/dist/runtime/schema-error.d.ts.map +1 -1
- package/dist/runtime/schema-error.js +6 -2
- package/dist/runtime/schema-error.js.map +1 -1
- package/dist/runtime/sqlite-error.d.ts.map +1 -1
- package/dist/runtime/sqlite-error.js +11 -12
- package/dist/runtime/sqlite-error.js.map +1 -1
- package/dist/runtime/sqlite-open.d.ts +2 -0
- package/dist/runtime/sqlite-open.d.ts.map +1 -1
- package/dist/runtime/sqlite-open.js +18 -1
- package/dist/runtime/sqlite-open.js.map +1 -1
- package/dist/runtime/transaction.d.ts +4 -0
- package/dist/runtime/transaction.d.ts.map +1 -1
- package/dist/runtime/transaction.js +31 -1
- package/dist/runtime/transaction.js.map +1 -1
- package/dist/runtime/transaction.test.d.ts +2 -0
- package/dist/runtime/transaction.test.d.ts.map +1 -0
- package/dist/runtime/transaction.test.js +43 -0
- package/dist/runtime/transaction.test.js.map +1 -0
- package/dist/runtime/types.d.ts +50 -17
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/schema/column-where.d.ts +11 -1
- package/dist/schema/column-where.d.ts.map +1 -1
- package/dist/schema/column.d.ts +1 -1
- 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/define-schema.d.ts +1 -1
- package/dist/schema/define-schema.d.ts.map +1 -1
- package/dist/schema/define-schema.js +5 -23
- package/dist/schema/define-schema.js.map +1 -1
- package/dist/schema/index.d.ts +2 -2
- 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/relation-types.d.ts +13 -17
- package/dist/schema/relation-types.d.ts.map +1 -1
- package/dist/schema/relation.d.ts.map +1 -1
- package/dist/schema/relation.js +6 -4
- package/dist/schema/relation.js.map +1 -1
- package/dist/schema/table-registry.d.ts.map +1 -1
- package/dist/schema/table-registry.js +8 -2
- package/dist/schema/table-registry.js.map +1 -1
- package/dist/schema/table.d.ts +6 -3
- package/dist/schema/table.d.ts.map +1 -1
- package/dist/schema/table.js +30 -31
- package/dist/schema/table.js.map +1 -1
- package/dist/schema/types.d.ts +33 -25
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/sql/builder.d.ts +5 -1
- package/dist/sql/builder.d.ts.map +1 -1
- package/dist/sql/builder.js +23 -3
- package/dist/sql/builder.js.map +1 -1
- package/dist/sql/index.d.ts +5 -2
- package/dist/sql/index.d.ts.map +1 -1
- package/dist/sql/index.js +5 -2
- package/dist/sql/index.js.map +1 -1
- package/dist/utils/case.d.ts +4 -0
- package/dist/utils/case.d.ts.map +1 -1
- package/dist/utils/case.js +11 -0
- package/dist/utils/case.js.map +1 -1
- package/dist/utils/dotenv.d.ts +8 -0
- package/dist/utils/dotenv.d.ts.map +1 -0
- package/dist/utils/dotenv.js +70 -0
- package/dist/utils/dotenv.js.map +1 -0
- package/dist/utils/dotenv.test.d.ts +2 -0
- package/dist/utils/dotenv.test.d.ts.map +1 -0
- package/dist/utils/dotenv.test.js +32 -0
- package/dist/utils/dotenv.test.js.map +1 -0
- package/dist/utils/inflect.d.ts +6 -0
- package/dist/utils/inflect.d.ts.map +1 -0
- package/dist/utils/inflect.js +80 -0
- package/dist/utils/inflect.js.map +1 -0
- package/docs/cli.md +6 -2
- package/docs/configuration.md +3 -1
- package/docs/errors.md +61 -6
- package/docs/examples.md +63 -13
- package/docs/getting-started.md +24 -1
- package/docs/migrations.md +15 -2
- package/docs/plugins.md +13 -1
- package/docs/queries.md +109 -10
- package/docs/relations.md +5 -1
- package/docs/schema.md +12 -5
- package/docs/sqlite.md +24 -10
- package/docs/transactions.md +13 -2
- package/package.json +4 -2
package/docs/queries.md
CHANGED
|
@@ -19,14 +19,36 @@ const users = await db.users.findMany({
|
|
|
19
19
|
skip: 40,
|
|
20
20
|
});
|
|
21
21
|
|
|
22
|
+
// First match after skip, or with DISTINCT ON (null if not found)
|
|
23
|
+
await db.users.findFirst({ orderBy: { email: "asc" }, skip: 1 });
|
|
24
|
+
await db.users.findFirst({
|
|
25
|
+
distinct: ["email"],
|
|
26
|
+
orderBy: { email: "asc" },
|
|
27
|
+
});
|
|
28
|
+
|
|
22
29
|
// Unique constraint lookup
|
|
23
30
|
const user = await db.users.findUnique({ where: { slug: "hello" } });
|
|
24
31
|
```
|
|
25
32
|
|
|
33
|
+
Unique `where` (`findUnique`, `upsert`, `findOrCreate`, singular `update`/`delete`) is scalar equality per unique field — `{ email: "a@b.com" }`, not a filter. `{ equals: value }` is accepted and unwrapped to the scalar. Other operators (`contains`, `in`, `mode: "insensitive"`) throw `unique_where_invalid`.
|
|
34
|
+
|
|
26
35
|
## Create
|
|
27
36
|
|
|
28
37
|
```ts
|
|
29
|
-
const
|
|
38
|
+
const created = await db.users.create({ data: { email: "a@b.com" } });
|
|
39
|
+
created.id; // primary key is always returned
|
|
40
|
+
|
|
41
|
+
// Pass returnCreated: true to get the full row (defaults, timestamps, …)
|
|
42
|
+
const user = await db.users.create({
|
|
43
|
+
data: { email: "a@b.com" },
|
|
44
|
+
returnCreated: true,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// `with` also returns the full row (plus relations)
|
|
48
|
+
const post = await db.posts.create({
|
|
49
|
+
data: { title: "Hello", body: "…", authorId: user.id },
|
|
50
|
+
with: { author: true },
|
|
51
|
+
});
|
|
30
52
|
|
|
31
53
|
// Bulk insert (returns count)
|
|
32
54
|
const count = await db.users.createMany({
|
|
@@ -47,13 +69,24 @@ const users = await db.users.createManyAndReturn({
|
|
|
47
69
|
});
|
|
48
70
|
```
|
|
49
71
|
|
|
72
|
+
Unknown keys in `data` fail at compile time (`unknown_column`), same as `where`.
|
|
73
|
+
|
|
50
74
|
## Update
|
|
51
75
|
|
|
76
|
+
Singular `update` requires a unique `where` (primary key, `@unique` column, or composite unique index), same as `findUnique`. Use `updateMany` when the filter can match multiple rows.
|
|
77
|
+
|
|
52
78
|
```ts
|
|
53
|
-
//
|
|
79
|
+
// Returns {} on success, null if no row matched
|
|
80
|
+
const updated = await db.users.update({
|
|
81
|
+
where: { id: userId },
|
|
82
|
+
data: { email: "new@b.com" },
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Pass returnUpdated: true to get the updated row back
|
|
54
86
|
const user = await db.users.update({
|
|
55
87
|
where: { id: userId },
|
|
56
88
|
data: { email: "new@b.com" },
|
|
89
|
+
returnUpdated: true,
|
|
57
90
|
});
|
|
58
91
|
|
|
59
92
|
// By ID (scalar PK — string; composite PK — object)
|
|
@@ -73,6 +106,12 @@ const updated = await db.users.updateManyAndReturn({
|
|
|
73
106
|
where: { email: { contains: "@old" } },
|
|
74
107
|
data: { status: "archived" },
|
|
75
108
|
});
|
|
109
|
+
|
|
110
|
+
const post = await db.posts.update({
|
|
111
|
+
where: { id: postId },
|
|
112
|
+
data: { title: "New" },
|
|
113
|
+
with: { author: true }, // full row plus relations; no returnUpdated needed
|
|
114
|
+
});
|
|
76
115
|
```
|
|
77
116
|
|
|
78
117
|
Numeric columns (`int`, `serial`, `decimal`, `bigint`) also accept `{ increment }`, `{ decrement }`, `{ multiply }`, and `{ set }`. These compile to `col = col ±/* $n` so concurrent updates do not clobber each other. `{ set: value }` is the same assignment as a plain value. `NULL + 1` stays SQL NULL.
|
|
@@ -91,6 +130,8 @@ await db.posts.updateMany({
|
|
|
91
130
|
|
|
92
131
|
## Delete
|
|
93
132
|
|
|
133
|
+
Singular `delete` requires a unique `where`. Use `deleteMany` for bulk deletes.
|
|
134
|
+
|
|
94
135
|
```ts
|
|
95
136
|
// Returns {} on success, null if no row matched
|
|
96
137
|
await db.users.delete({ where: { id: userId } });
|
|
@@ -101,6 +142,12 @@ const deleted = await db.users.delete({
|
|
|
101
142
|
returnDeleted: true,
|
|
102
143
|
});
|
|
103
144
|
|
|
145
|
+
// `with` also returns the full row (plus relations), even without returnDeleted
|
|
146
|
+
const post = await db.posts.delete({
|
|
147
|
+
where: { id: postId },
|
|
148
|
+
with: { author: true },
|
|
149
|
+
});
|
|
150
|
+
|
|
104
151
|
// Scalar PK — pass a string
|
|
105
152
|
await db.users.deleteById(userId);
|
|
106
153
|
|
|
@@ -132,6 +179,10 @@ Atomically insert a row or return an existing one when a unique constraint match
|
|
|
132
179
|
|
|
133
180
|
`where` must identify a unique constraint (same rules as `findUnique` and `upsert`).
|
|
134
181
|
|
|
182
|
+
Postgres issues `INSERT … ON CONFLICT DO UPDATE` with a no-op assignment so `RETURNING` always includes the row — including when a concurrent session already inserted it. `created` is `(xmax = 0)` (inserted vs existing). A no-op update can fire `UPDATE` triggers; column values are not changed.
|
|
183
|
+
|
|
184
|
+
Under `SERIALIZABLE` (and sometimes `REPEATABLE READ`), a concurrent insert can still abort the transaction with a serialization failure. Retry the whole `$transaction`. SQLite uses find-then-insert and retries the find after a unique violation.
|
|
185
|
+
|
|
135
186
|
```ts
|
|
136
187
|
const { record, created } = await db.tags.findOrCreate({
|
|
137
188
|
where: { slug: "orm" },
|
|
@@ -158,22 +209,31 @@ await db.posts.findFirst({
|
|
|
158
209
|
});
|
|
159
210
|
```
|
|
160
211
|
|
|
212
|
+
Unknown column names and operators fail at compile time (`unknown_column`, with “did you mean” hints). JSON columns may still use a plain object as equality (`{ metadata: { featured: true } }`).
|
|
213
|
+
|
|
161
214
|
| Type | Operators |
|
|
162
215
|
|------|-----------|
|
|
163
216
|
| String | `equals`, `contains`, `startsWith`, `endsWith`, `search`, `in`, `notIn`, `mode` |
|
|
164
217
|
| Numeric / Date | `equals`, `gt`, `gte`, `lt`, `lte`, `in`, `notIn` |
|
|
165
218
|
| JSON | `jsonContains`, `hasKey`, `hasAnyKeys`, `hasAllKeys`, `path` |
|
|
219
|
+
| PostGIS (`geometry`, `geography`, `point`) | `intersects`, `within`, `dWithin` |
|
|
166
220
|
| All nullable | `isNull`, `isNotNull` |
|
|
167
221
|
|
|
168
|
-
`contains`, `startsWith`, and `
|
|
222
|
+
`contains`, `startsWith`, `endsWith`, and `equals` compile to `LIKE` / `=`. Pass sibling `mode: "insensitive"` for case-folding (`ILIKE` on Postgres, `LOWER(col) LIKE LOWER($n)` on SQLite). SQLite `LIKE` is ASCII case-insensitive even in default mode.
|
|
169
223
|
|
|
170
224
|
`search` is POSIX regex (`~`, or `~*` with `mode: "insensitive"`). It is PostgreSQL-only; SQLite throws.
|
|
171
225
|
|
|
226
|
+
JSON operators on PostgreSQL use `@>`, `?`, and `#>` / `#>>`. On SQLite they compile to `json_patch` (object containment), `json_each` (key existence), and `json_extract` (path).
|
|
227
|
+
|
|
172
228
|
```ts
|
|
173
229
|
await db.posts.findMany({
|
|
174
230
|
where: { title: { contains: "orm", mode: "insensitive" } },
|
|
175
231
|
});
|
|
176
232
|
|
|
233
|
+
await db.users.findMany({
|
|
234
|
+
where: { email: { equals: "Ada@Example.com", mode: "insensitive" } },
|
|
235
|
+
});
|
|
236
|
+
|
|
177
237
|
await db.posts.findMany({
|
|
178
238
|
where: { title: { search: "^Neo", mode: "insensitive" } },
|
|
179
239
|
});
|
|
@@ -194,6 +254,8 @@ await db.users.findMany({
|
|
|
194
254
|
});
|
|
195
255
|
```
|
|
196
256
|
|
|
257
|
+
Empty `OR: []` matches no rows. Empty `AND: []` matches every row. Empty `in: []` is false; `{ OR: [{ id: { in: [] } }, { name: "Ada" }] }` still matches the other branch.
|
|
258
|
+
|
|
197
259
|
### Relation filters
|
|
198
260
|
|
|
199
261
|
```ts
|
|
@@ -218,7 +280,7 @@ await db.posts.findMany({
|
|
|
218
280
|
| `authorId: "user_1"` | Filter by FK column value |
|
|
219
281
|
| `author: { email: "a@b.c" }` | Filter by related record (to-one) |
|
|
220
282
|
| `posts: { some: { ... } }` | At least one related record matches (to-many) |
|
|
221
|
-
| `posts: { every: { ... } }` | All related records match (to-many) |
|
|
283
|
+
| `posts: { every: { ... } }` | All related records match (to-many). Compiled as `NOT EXISTS` of related rows that fail the nested filter. |
|
|
222
284
|
| `posts: { none: { ... } }` | No related records match (to-many) |
|
|
223
285
|
|
|
224
286
|
Relation filters compile to SQL `EXISTS` subqueries, so they work with `findMany`, `count`, `updateMany`, and `deleteMany` without duplicate rows.
|
|
@@ -226,7 +288,7 @@ Relation filters compile to SQL `EXISTS` subqueries, so they work with `findMany
|
|
|
226
288
|
### JSON and decimal filters
|
|
227
289
|
|
|
228
290
|
```ts
|
|
229
|
-
// jsonb — partial / subset match (
|
|
291
|
+
// jsonb — partial / subset match (`@>` on PostgreSQL, `json_patch` on SQLite)
|
|
230
292
|
await db.posts.findMany({
|
|
231
293
|
where: { metadata: { jsonContains: { featured: true } } },
|
|
232
294
|
});
|
|
@@ -250,7 +312,7 @@ await db.posts.findMany({
|
|
|
250
312
|
|
|
251
313
|
### `distinct`
|
|
252
314
|
|
|
253
|
-
PostgreSQL `DISTINCT ON` — `orderBy` must lead with the same columns:
|
|
315
|
+
PostgreSQL `DISTINCT ON` — `orderBy` must lead with the same columns (key order is the ORDER BY prefix):
|
|
254
316
|
|
|
255
317
|
```ts
|
|
256
318
|
await db.users.findMany({
|
|
@@ -270,9 +332,10 @@ Relations are eagerly loaded in `findMany`, `findFirst`, and `findById` via the
|
|
|
270
332
|
NeoORM uses a relation-loading planner that picks the fewest round-trips per `with` tree:
|
|
271
333
|
|
|
272
334
|
- **LEFT JOIN** — to-one relations where the parent table owns the foreign key are resolved in the main query. No additional round-trips.
|
|
335
|
+
- **JOIN + `json_agg`** — a single inverse has-many include may use one collection `LEFT JOIN` with `GROUP BY`. A second collection is a correlated subquery so sizes N and M do not become N×M rows.
|
|
273
336
|
- **Correlated `json_agg` subquery** — inverse has-many relations (and simple linear nested chains) are embedded in the main `SELECT` as correlated subqueries. Multiple top-level relations can be inlined in the same query alongside JOINs.
|
|
274
|
-
- **Inline `COUNT` subquery** — simple `_count` on has-many relations is embedded in the main `SELECT`.
|
|
275
|
-
- **Batch query** — many-to-many, nested `with` on to-one, sibling-heavy trees, and other complex shapes fall back to batched `WHERE IN (...)` queries (one per relation level). Sibling batch queries at the same level run in parallel.
|
|
337
|
+
- **Inline `COUNT` subquery** — simple `_count` on has-many relations is embedded in the main `SELECT`. At most one `_count` collection uses a `LEFT JOIN`; additional counts are correlated `COUNT(*)` subqueries.
|
|
338
|
+
- **Batch query** — many-to-many, nested `with` on to-one, sibling-heavy trees, and other complex shapes fall back to batched `WHERE IN (...)` queries (one per relation level). Sibling batch queries at the same level run in parallel. Nested `take`/`skip` on those batches apply **per parent** (window ranking), not to the whole `IN` list.
|
|
276
339
|
|
|
277
340
|
Typical query counts:
|
|
278
341
|
|
|
@@ -395,7 +458,8 @@ for (;;) {
|
|
|
395
458
|
```
|
|
396
459
|
|
|
397
460
|
- `orderBy` is required; scalar `id` is appended automatically when omitted.
|
|
398
|
-
- `
|
|
461
|
+
- `select`, `omit`, and `includeHidden` work like `findMany`. Cursor fields used by `orderBy` stay in the SQL (and in `nextCursor` / `prevCursor`) even when omitted from `items`.
|
|
462
|
+
- `take` is the page size; `hasMore` / `hasPrevious` use a `take + 1` probe in the query direction. A `before` page also probes once forward so `hasMore` is not assumed true. After an `after` page, `hasPrevious` is true because `after` was passed.
|
|
399
463
|
- `after` is a typed cursor (`nextCursor` from the previous page). `before` walks the other way (`prevCursor`).
|
|
400
464
|
- `after` and `before` can be combined as exclusive window bounds; pass both on every windowed call.
|
|
401
465
|
- For HTTP APIs, encode cursors with `encodeCursor` / `decodeCursor` from `neoorm`.
|
|
@@ -441,7 +505,7 @@ const stats = await db.posts.aggregate({
|
|
|
441
505
|
|
|
442
506
|
`_count: true` is still `COUNT(*)` and returns `_count: number`. A field map uses `COUNT(col)` (nulls are skipped) — not `DISTINCT`.
|
|
443
507
|
|
|
444
|
-
Single-table grouped stats use `groupBy()`. Cross-table dashboards still use `db.sql` or `sqlBuilder`.
|
|
508
|
+
Single-table grouped stats use `groupBy()`. Cross-table dashboards still use `db.sql` (same compiler as `neoorm/sql`) or `sqlBuilder` composed into `db.sql`.
|
|
445
509
|
|
|
446
510
|
### `groupBy()`
|
|
447
511
|
|
|
@@ -478,3 +542,38 @@ const taken = await db.users.exists({ where: { email: "a@b.com" } });
|
|
|
478
542
|
`count()` returns a number unless `select` is set. `distinct` is `COUNT(DISTINCT col)` on one column and cannot be combined with `select`. Field `select` / `_count` maps are non-null `COUNT(col)`, not distinct.
|
|
479
543
|
|
|
480
544
|
`exists()` runs `SELECT 1 … LIMIT 1` and returns a boolean. Use `count()` when you need the actual number of matches.
|
|
545
|
+
|
|
546
|
+
## Raw SQL
|
|
547
|
+
|
|
548
|
+
`db.sql` and `import { sql } from "neoorm/sql"` share one compiler. Interpolate values, `sqlId("table")`, nested `sql\`...\`` fragments, or `sqlBuilder.compile()`. `sqlBuilder` itself has no WHERE, LIMIT, or params — add those in the `db.sql` tag.
|
|
549
|
+
|
|
550
|
+
```ts
|
|
551
|
+
import { sql, sqlBuilder, sqlId } from "neoorm/sql";
|
|
552
|
+
|
|
553
|
+
const ident = sqlId("users");
|
|
554
|
+
const filter = sql`email = ${"a@b.com"}`;
|
|
555
|
+
const rows = await db.sql`SELECT * FROM ${ident} WHERE ${filter}`;
|
|
556
|
+
|
|
557
|
+
const grouped = sqlBuilder
|
|
558
|
+
.selectFrom("users")
|
|
559
|
+
.select(["id", "email"])
|
|
560
|
+
.groupBy("id", "email")
|
|
561
|
+
.compile();
|
|
562
|
+
await db.sql`${grouped} LIMIT ${10}`;
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
`db.execute({ text, params })` runs already-compiled SQL. Raw SQL is not rewritten for tenant `schema` — qualify identifiers yourself.
|
|
566
|
+
|
|
567
|
+
## Logging SQL
|
|
568
|
+
|
|
569
|
+
Pass `beforeQuery` / `afterQuery` when creating the client. They run for repository methods and `db.sql` / `db.execute`, including queries inside `$transaction`. BEGIN/COMMIT/SAVEPOINT are not included.
|
|
570
|
+
|
|
571
|
+
```ts
|
|
572
|
+
const db = createNeoOrmClient(manifest, {
|
|
573
|
+
afterQuery: ({ sql, params, durationMs, error }) => {
|
|
574
|
+
if (error || durationMs > 100) {
|
|
575
|
+
console.warn({ sql, params, durationMs, error });
|
|
576
|
+
}
|
|
577
|
+
},
|
|
578
|
+
});
|
|
579
|
+
```
|
package/docs/relations.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Relation writes
|
|
2
2
|
|
|
3
|
-
`create` and `update` accept nested relation writes alongside scalar fields. Relation-only updates are supported (no scalar `SET` required).
|
|
3
|
+
`create` and `update` accept nested relation writes alongside scalar fields. Relation-only updates are supported (no scalar `SET` required). Nested to-one `create` on `update` (and `create`) runs in the same transaction as the parent row so a failed parent write does not leave an orphan.
|
|
4
4
|
|
|
5
5
|
## Supported operations
|
|
6
6
|
|
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
| **One-to-many** (inverse) | `create`, `connect`, `disconnect`, `set`, `delete` |
|
|
11
11
|
| **Many-to-many** | `connect`, `connectOrCreate`, `disconnect`, `set`, `delete` |
|
|
12
12
|
|
|
13
|
+
`connect`, `set`, `disconnect`, and `delete` identify related rows by the **target table's scalar primary key** (`{ id: ... }` when the PK is `id`, `{ userId: ... }` when it is `userId`). Composite-PK connect is not supported.
|
|
14
|
+
|
|
15
|
+
A relation field must be a **pure write bag**: every key is one of those operations. Mixing a write op with extra fields (`{ create: { name: "A" }, foo: 1 }`), passing a scalar, or passing `{}` throws `invalid_nested_write` — the parent row is not written.
|
|
16
|
+
|
|
13
17
|
## Examples
|
|
14
18
|
|
|
15
19
|
### To-one on update
|
package/docs/schema.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Schema DSL
|
|
2
2
|
|
|
3
|
-
NeoOrm **0.
|
|
3
|
+
NeoOrm **0.8.0** uses accessor-based identity: schema keys, `fk("users")`, `many("tags")`, and `through: "postTags"` all refer to **accessors**, not raw SQL names. `schemaToManifest` resolves accessors to SQL before migrations and the typed client are generated.
|
|
4
4
|
|
|
5
5
|
## Tables and accessors
|
|
6
6
|
|
|
@@ -26,6 +26,8 @@ export const schema = defineSchema({
|
|
|
26
26
|
- `table({ columns, extras? })` — SQL name is filled in from the accessor (`users` → `"users"`).
|
|
27
27
|
- `table("sql_name", { columns }, extras?)` — override the SQL name (`postTags` → `"post_tags"`).
|
|
28
28
|
|
|
29
|
+
Generated row types are the English singular of the accessor: `users` → `User`, `status` → `Status` (not `Statu`).
|
|
30
|
+
|
|
29
31
|
Column field names use camelCase in TypeScript. By default SQL column names are snake_case (`createdAt` → `created_at`). See [column naming](#column-naming-strategy).
|
|
30
32
|
|
|
31
33
|
## Column types
|
|
@@ -41,7 +43,7 @@ Column field names use camelCase in TypeScript. By default SQL column names are
|
|
|
41
43
|
| `bool()` | `BOOLEAN` | `boolean \| null` | |
|
|
42
44
|
| `int()` | `INTEGER` | `number \| null` | |
|
|
43
45
|
| `bigint()` | `BIGINT` | `bigint \| null` | Maps to `TEXT` on SQLite |
|
|
44
|
-
| `timestamp()` | `TIMESTAMPTZ` | `Date \| null` | `.defaultNow()` / `.updatedAt()` on timestamp builders only |
|
|
46
|
+
| `timestamp()` | `TIMESTAMPTZ` | `Date \| null` | `.defaultNow()` / `.updatedAt()` on timestamp builders only. Maps to `TEXT` on SQLite |
|
|
45
47
|
| `json()` / `jsonb()` | `JSON` / `JSONB` | `unknown \| null` | Generic: `jsonb<MyType>()` |
|
|
46
48
|
| `decimal()` / `numeric()` | `NUMERIC` | `string \| null` | Use strings to avoid float loss |
|
|
47
49
|
| `enumType(["a", "b"])` | mode-dependent | union literals | See [Enum columns](#enum-columns) |
|
|
@@ -119,6 +121,8 @@ postTags: table("post_tags", {
|
|
|
119
121
|
}),
|
|
120
122
|
```
|
|
121
123
|
|
|
124
|
+
Multiple `.primary()` columns infer a composite key. `primaryKey(t.colA, t.colB)` in extras is optional when you want to name the key without marking every column.
|
|
125
|
+
|
|
122
126
|
### Self-relations
|
|
123
127
|
|
|
124
128
|
```ts
|
|
@@ -168,15 +172,18 @@ posts: table(
|
|
|
168
172
|
),
|
|
169
173
|
```
|
|
170
174
|
|
|
171
|
-
Helpers: `unique(...cols)`, `index(...cols)`, `primaryKey(...cols)`.
|
|
175
|
+
Helpers: `unique(...cols)`, `index(...cols)`, `primaryKey(...cols)`. `unique()` and `index()` both support `.where()` for partial indexes.
|
|
172
176
|
|
|
173
177
|
### Partial indexes
|
|
174
178
|
|
|
175
179
|
```ts
|
|
176
|
-
(t) => [
|
|
180
|
+
(t) => [
|
|
181
|
+
index(t.title).where({ published: true }),
|
|
182
|
+
unique(t.email).where({ deleted: false }),
|
|
183
|
+
],
|
|
177
184
|
```
|
|
178
185
|
|
|
179
|
-
Equality map of column refs → values; compiled to `WHERE "published" = true` (or `= 1` on SQLite).
|
|
186
|
+
Equality map of column refs → values; compiled to `WHERE "published" = true` (or `= 1` on SQLite). Partial uniques emit as `CREATE UNIQUE INDEX ... WHERE ...`, not table-level `UNIQUE (...)`. They are not usable as `findUnique` / `upsert` targets.
|
|
180
187
|
|
|
181
188
|
## Many-to-many
|
|
182
189
|
|
package/docs/sqlite.md
CHANGED
|
@@ -4,13 +4,13 @@ SQLite is a first-class dialect for local development, testing, and single-file
|
|
|
4
4
|
|
|
5
5
|
## Requirements
|
|
6
6
|
|
|
7
|
-
No SQLite package is required —
|
|
7
|
+
NeoOrm requires **Node.js 22.5+** or **Bun**. No SQLite package is required — auto-open uses the driver bundled with your runtime:
|
|
8
8
|
|
|
9
9
|
| Runtime | Driver |
|
|
10
10
|
|---------|--------|
|
|
11
11
|
| Node.js 22.5+ | built-in `node:sqlite` (`DatabaseSync`) |
|
|
12
12
|
| Bun | `bun:sqlite` |
|
|
13
|
-
|
|
|
13
|
+
| Custom | pass your own `db` instance (see [Custom driver](#custom-driver)) |
|
|
14
14
|
|
|
15
15
|
If none is available, creating a client throws: `No SQLite driver available. Provide a db instance, or run on Bun or Node.js 22.5+`.
|
|
16
16
|
|
|
@@ -48,19 +48,33 @@ const db = createNeoOrmClient(manifest, {
|
|
|
48
48
|
|
|
49
49
|
Use `databasePath: ":memory:"` for an in-memory database (one connection, no persistence).
|
|
50
50
|
|
|
51
|
+
Wrapping a connection sets `PRAGMA foreign_keys = ON`, `busy_timeout = 5000`, and `journal_mode = WAL` (WAL is a no-op for `:memory:`). Opt out per client:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
const db = createNeoOrmClient(manifest, {
|
|
55
|
+
provider: "sqlite",
|
|
56
|
+
databasePath: "./dev.db",
|
|
57
|
+
sqlite: { wal: false, busyTimeout: false },
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
`busyTimeout` can also be a millisecond wait (`sqlite: { busyTimeout: 10_000 }`).
|
|
62
|
+
|
|
51
63
|
### Custom driver
|
|
52
64
|
|
|
53
65
|
Pass any object implementing `prepare(sql)`, `exec(sql)`, and `close()` — the `node:sqlite` and `bun:sqlite` APIs both match. Useful for sandboxed runtimes or existing database handles:
|
|
54
66
|
|
|
55
67
|
```ts
|
|
56
68
|
import { DatabaseSync } from "node:sqlite";
|
|
57
|
-
import {
|
|
69
|
+
import { createNeoOrmClientFromSqlite } from "neoorm";
|
|
58
70
|
import { manifest } from "./neoorm/manifest.js";
|
|
59
71
|
|
|
60
72
|
const database = new DatabaseSync("./dev.db");
|
|
61
|
-
const db =
|
|
73
|
+
const db = createNeoOrmClientFromSqlite(manifest, database);
|
|
62
74
|
```
|
|
63
75
|
|
|
76
|
+
`$disconnect()` does not close the handle — call `database.close()` yourself. `createNeoOrmClient(manifest, { db: database })` is the same ownership model.
|
|
77
|
+
|
|
64
78
|
## CLI
|
|
65
79
|
|
|
66
80
|
The full CLI works against SQLite:
|
|
@@ -73,7 +87,7 @@ bunx neoorm migrate status
|
|
|
73
87
|
bunx neoorm migrate reset --force
|
|
74
88
|
```
|
|
75
89
|
|
|
76
|
-
- The migration ledger table is `_neoorm_migrations` (`id INTEGER PRIMARY KEY AUTOINCREMENT`).
|
|
90
|
+
- The migration ledger table is `_neoorm_migrations` (`id INTEGER PRIMARY KEY AUTOINCREMENT`, `name`, `checksum`, `applied_at`). `migrate deploy` wraps the run in `BEGIN IMMEDIATE` and refuses an edited `migration.sql` whose checksum no longer matches the ledger.
|
|
77
91
|
- `migrate reset` drops all non-`sqlite_` tables (there is no schema concept).
|
|
78
92
|
- `db pull` introspects `sqlite_master` back into a schema file.
|
|
79
93
|
|
|
@@ -86,11 +100,11 @@ bunx neoorm migrate reset --force
|
|
|
86
100
|
| `bigint` | `TEXT` |
|
|
87
101
|
| `serial().primary()` | `INTEGER PRIMARY KEY AUTOINCREMENT` |
|
|
88
102
|
| `bool` | `BOOLEAN` (stored as 0/1) |
|
|
89
|
-
| `timestamp` | `
|
|
103
|
+
| `timestamp` | `TEXT` (ISO-8601) |
|
|
90
104
|
| `bytea` | `BLOB` |
|
|
91
105
|
| `fk` | the target column's type (default `TEXT`) |
|
|
92
106
|
|
|
93
|
-
`defaultNow()` compiles to `CURRENT_TIMESTAMP`. Structural table changes that SQLite cannot do in place (column type changes, FK changes) are applied via a table-rebuild strategy: create `__neoorm_<table>_new`, copy rows, drop the old table, rename.
|
|
107
|
+
`defaultNow()` compiles to `CURRENT_TIMESTAMP`. Structural table changes that SQLite cannot do in place (column type changes, FK changes, nullability) are applied via a table-rebuild strategy: `PRAGMA foreign_keys = OFF`, then in one transaction create `__neoorm_<table>_new`, copy rows, drop the old table, rename, and `PRAGMA foreign_keys = ON`. Foreign keys must be disabled *before* `BEGIN` — SQLite ignores that pragma inside a transaction. The client always enables `foreign_keys` at connect time, so inbound FKs (e.g. `posts.user_id` → `users`) would otherwise block `DROP TABLE` of the parent.
|
|
94
108
|
|
|
95
109
|
## Differences from PostgreSQL
|
|
96
110
|
|
|
@@ -99,7 +113,7 @@ bunx neoorm migrate reset --force
|
|
|
99
113
|
| `distinct` (`DISTINCT ON`) | supported | throws `distinct is not supported on SQLite` |
|
|
100
114
|
| `datasource.schema` | multi-schema | not applicable |
|
|
101
115
|
| `enum: "native"` | `CREATE TYPE ... AS ENUM` | not applicable (TEXT + CHECK) |
|
|
102
|
-
| transaction options (`readOnly`, `isolationLevel`) |
|
|
103
|
-
| JSON operators | `@>`, `?`,
|
|
116
|
+
| transaction options (`readOnly`, `isolationLevel`) | `BEGIN READ ONLY` / `ISOLATION LEVEL` | `readOnly` → `PRAGMA query_only`; `RepeatableRead`/`Serializable` → `BEGIN IMMEDIATE`; other isolation → `BEGIN` |
|
|
117
|
+
| JSON operators | `@>`, `?`, `#>` | `json_patch` / `json_each` / `json_extract` |
|
|
104
118
|
|
|
105
|
-
Everything else — relations, nested writes, cursor pagination, aggregates, `groupBy`, upsert, `findOrCreate`, savepoint-based nested transactions — behaves identically.
|
|
119
|
+
Everything else — relations, nested writes, cursor pagination, aggregates, `groupBy`, upsert, `findOrCreate`, savepoint-based nested transactions — behaves identically.
|
package/docs/transactions.md
CHANGED
|
@@ -49,6 +49,17 @@ await db.$transaction(async (tx) => {
|
|
|
49
49
|
});
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
Nested `
|
|
52
|
+
Nested `$transaction` uses a savepoint on the same connection, not a new `BEGIN`. `readOnly` and `isolationLevel` are only valid on the outermost `$transaction`; passing them on a nested call throws.
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
### SQLite
|
|
55
|
+
|
|
56
|
+
SQLite has no `BEGIN READ ONLY` and no SQL isolation levels. Outer `$transaction` options map as follows:
|
|
57
|
+
|
|
58
|
+
| Option | SQLite |
|
|
59
|
+
|--------|--------|
|
|
60
|
+
| `readOnly: true` | `PRAGMA query_only = ON` for the duration of the transaction (writes fail) |
|
|
61
|
+
| `isolationLevel: "RepeatableRead"` or `"Serializable"` | `BEGIN IMMEDIATE` (reserved lock before the first statement) |
|
|
62
|
+
| `isolationLevel: "ReadUncommitted"` or `"ReadCommitted"` | `BEGIN` (deferred; default) |
|
|
63
|
+
| nested `$transaction` | `SAVEPOINT` — `readOnly` / `isolationLevel` are rejected |
|
|
64
|
+
|
|
65
|
+
After the transaction commits or rolls back, `query_only` is turned off so later writes succeed.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "neoorm",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "TypeScript-first PostgreSQL and SQLite ORM with schema DSL, codegen, and typed relations",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -73,12 +73,14 @@
|
|
|
73
73
|
"database"
|
|
74
74
|
],
|
|
75
75
|
"engines": {
|
|
76
|
-
"node": ">=
|
|
76
|
+
"node": ">=22.5.0"
|
|
77
77
|
},
|
|
78
78
|
"scripts": {
|
|
79
79
|
"build": "bunx tsc -p tsconfig.build.json",
|
|
80
80
|
"typecheck": "bunx tsc -p tsconfig.json --noEmit",
|
|
81
81
|
"dev": "bunx tsc -w -p tsconfig.build.json",
|
|
82
|
+
"lint": "biome check .",
|
|
83
|
+
"format": "biome format --write .",
|
|
82
84
|
"test": "bunx vitest run",
|
|
83
85
|
"prepublishOnly": "bun run build && bun run test"
|
|
84
86
|
},
|