litedbmodel 2.2.6 → 2.2.8
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 +146 -86
- package/dist/Column.d.ts +30 -6
- package/dist/Column.d.ts.map +1 -1
- package/dist/Column.js +30 -10
- package/dist/Column.js.map +1 -1
- package/dist/DBModel.d.ts +28 -16
- package/dist/DBModel.d.ts.map +1 -1
- package/dist/DBModel.js +60 -46
- package/dist/DBModel.js.map +1 -1
- package/dist/LazyRelation.d.ts +2 -2
- package/dist/LazyRelation.js +2 -2
- package/dist/TypeCast.d.ts +2 -2
- package/dist/TypeCast.d.ts.map +1 -1
- package/dist/TypeCast.js +7 -9
- package/dist/TypeCast.js.map +1 -1
- package/dist/decorators.d.ts +128 -60
- package/dist/decorators.d.ts.map +1 -1
- package/dist/decorators.js +393 -251
- package/dist/decorators.js.map +1 -1
- package/dist/drivers/MysqlHelper.d.ts +3 -3
- package/dist/drivers/MysqlHelper.d.ts.map +1 -1
- package/dist/drivers/MysqlHelper.js +3 -17
- package/dist/drivers/MysqlHelper.js.map +1 -1
- package/dist/drivers/MysqlSqlBuilder.d.ts.map +1 -1
- package/dist/drivers/MysqlSqlBuilder.js +2 -13
- package/dist/drivers/MysqlSqlBuilder.js.map +1 -1
- package/dist/drivers/PostgresHelper.d.ts +3 -2
- package/dist/drivers/PostgresHelper.d.ts.map +1 -1
- package/dist/drivers/PostgresHelper.js +3 -16
- package/dist/drivers/PostgresHelper.js.map +1 -1
- package/dist/drivers/PostgresSqlBuilder.d.ts.map +1 -1
- package/dist/drivers/PostgresSqlBuilder.js +2 -14
- package/dist/drivers/PostgresSqlBuilder.js.map +1 -1
- package/dist/drivers/SqliteHelper.d.ts +4 -2
- package/dist/drivers/SqliteHelper.d.ts.map +1 -1
- package/dist/drivers/SqliteHelper.js +4 -2
- package/dist/drivers/SqliteHelper.js.map +1 -1
- package/dist/drivers/SqliteSqlBuilder.d.ts.map +1 -1
- package/dist/drivers/SqliteSqlBuilder.js +2 -13
- package/dist/drivers/SqliteSqlBuilder.js.map +1 -1
- package/dist/index.cjs +861 -816
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/scp/coltype.d.ts +14 -5
- package/dist/scp/coltype.d.ts.map +1 -1
- package/dist/scp/coltype.js +35 -36
- package/dist/scp/coltype.js.map +1 -1
- package/dist/scp/decorator-adapter.d.ts +12 -15
- package/dist/scp/decorator-adapter.d.ts.map +1 -1
- package/dist/scp/decorator-adapter.js +13 -15
- package/dist/scp/decorator-adapter.js.map +1 -1
- package/dist/scp/index.cjs +237 -233
- package/dist/scp/index.mjs +237 -233
- package/dist/scp/makesql/json-array.d.ts +6 -0
- package/dist/scp/makesql/json-array.d.ts.map +1 -1
- package/dist/scp/makesql/json-array.js +17 -2
- package/dist/scp/makesql/json-array.js.map +1 -1
- package/dist/scp/makesql/tx.d.ts.map +1 -1
- package/dist/scp/makesql/tx.js +1 -3
- package/dist/scp/makesql/tx.js.map +1 -1
- package/dist/scp/relation.d.ts.map +1 -1
- package/dist/scp/relation.js +8 -10
- package/dist/scp/relation.js.map +1 -1
- package/docs/BENCHMARK.md +3 -3
- package/docs/COMPARISON.md +7 -7
- package/docs/api/README.md +18 -18
- package/docs/api/classes/Conditions.md +10 -10
- package/docs/api/classes/DBHandler.md +1 -1
- package/docs/api/classes/DBModel.md +84 -84
- package/docs/api/classes/LimitExceededError.md +1 -1
- package/docs/api/classes/Middleware.md +1 -1
- package/docs/api/classes/SqlRaw.md +1 -1
- package/docs/api/classes/SqlRef.md +1 -1
- package/docs/api/classes/Values.md +6 -6
- package/docs/api/classes/WriteInReadOnlyContextError.md +1 -1
- package/docs/api/classes/WriteOutsideTransactionError.md +1 -1
- package/docs/api/functions/belongsTo.md +10 -4
- package/docs/api/functions/closeAllPools.md +1 -1
- package/docs/api/functions/condsToRecord.md +4 -3
- package/docs/api/functions/createMiddleware.md +1 -1
- package/docs/api/functions/createPostgresDriver.md +1 -1
- package/docs/api/functions/createSqliteDriver.md +1 -1
- package/docs/api/functions/dbCast.md +1 -1
- package/docs/api/functions/dbCastIn.md +1 -1
- package/docs/api/functions/dbDynamic.md +1 -1
- package/docs/api/functions/dbFalse.md +1 -1
- package/docs/api/functions/dbImmediate.md +1 -1
- package/docs/api/functions/dbIn.md +1 -1
- package/docs/api/functions/dbNotNull.md +1 -1
- package/docs/api/functions/dbNow.md +1 -1
- package/docs/api/functions/dbNull.md +1 -1
- package/docs/api/functions/dbRaw.md +1 -1
- package/docs/api/functions/dbTrue.md +1 -1
- package/docs/api/functions/dbUuid.md +1 -1
- package/docs/api/functions/dbUuidIn.md +1 -1
- package/docs/api/functions/formatLocalDate.md +2 -2
- package/docs/api/functions/formatUTCDate.md +2 -2
- package/docs/api/functions/getDBConfig.md +1 -1
- package/docs/api/functions/getDBHandler.md +1 -1
- package/docs/api/functions/getTransactionConnection.md +2 -2
- package/docs/api/functions/getTransactionContext.md +2 -2
- package/docs/api/functions/hasMany.md +11 -5
- package/docs/api/functions/hasOne.md +10 -4
- package/docs/api/functions/initDBHandler.md +1 -1
- package/docs/api/functions/isAnySqlFragment.md +1 -1
- package/docs/api/functions/isConnectionError.md +1 -1
- package/docs/api/functions/isSqlCondition.md +1 -1
- package/docs/api/functions/isSqlFragment.md +1 -1
- package/docs/api/functions/isSqlRaw.md +1 -1
- package/docs/api/functions/isSqlRef.md +1 -1
- package/docs/api/functions/isSqlTypedFragment.md +1 -1
- package/docs/api/functions/model.md +8 -155
- package/docs/api/functions/parentRef.md +1 -1
- package/docs/api/functions/sql.md +1 -1
- package/docs/api/globals.md +99 -58
- package/docs/api/interfaces/Column.md +32 -31
- package/docs/api/interfaces/ColumnDecorator.md +81 -0
- package/docs/api/interfaces/ColumnOptions.md +6 -10
- package/docs/api/interfaces/CreatedMiddlewareClass.md +1 -1
- package/docs/api/interfaces/DBConfig.md +1 -1
- package/docs/api/interfaces/DBConfigOptions.md +1 -1
- package/docs/api/interfaces/DeleteOptions.md +1 -1
- package/docs/api/interfaces/InsertOptions.md +1 -1
- package/docs/api/interfaces/LimitConfig.md +1 -1
- package/docs/api/interfaces/MiddlewareConfig.md +1 -1
- package/docs/api/interfaces/ModelClassDecorator.md +64 -0
- package/docs/api/interfaces/ModelOptions.md +1 -1
- package/docs/api/interfaces/PkeyResult.md +1 -1
- package/docs/api/interfaces/RelationDecorator.md +72 -0
- package/docs/api/interfaces/SelectOptions.md +1 -1
- package/docs/api/interfaces/SqlCondition.md +1 -1
- package/docs/api/interfaces/SqlFragment.md +1 -1
- package/docs/api/interfaces/SqlTypedFragment.md +1 -1
- package/docs/api/interfaces/TransactionOptions.md +1 -1
- package/docs/api/interfaces/UpdateManyOptions.md +1 -1
- package/docs/api/interfaces/UpdateOptions.md +1 -1
- package/docs/api-guide.md +17 -17
- package/docs/architecture.md +14 -10
- package/docs/sql-tagged-template.md +5 -5
- package/eslint-plugin/rules/require-declare-for-relations.js +59 -4
- package/package.json +8 -7
package/README.md
CHANGED
|
@@ -127,6 +127,26 @@ npm install mysql2 # MySQL
|
|
|
127
127
|
npm install better-sqlite3 # SQLite
|
|
128
128
|
```
|
|
129
129
|
|
|
130
|
+
### TypeScript configuration
|
|
131
|
+
|
|
132
|
+
Models work under **either** decorator protocol, with no other compiler setting required:
|
|
133
|
+
|
|
134
|
+
```jsonc
|
|
135
|
+
// TC39 standard decorators — the TypeScript 5 default, and what esbuild / tsx / vite / Next.js emit.
|
|
136
|
+
{ "compilerOptions": { "target": "ES2022" } }
|
|
137
|
+
|
|
138
|
+
// Legacy decorators — also supported.
|
|
139
|
+
{ "compilerOptions": { "target": "ES2022", "experimentalDecorators": true } }
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
`emitDecoratorMetadata` is **not** used and does not need to be enabled. Every column states its own
|
|
143
|
+
type through its `@column.*` family, so the same model source produces the same model whether it is
|
|
144
|
+
compiled by `tsc`, esbuild, `tsx` or `vite`.
|
|
145
|
+
|
|
146
|
+
One difference the compiler forces, on relations only: standard decorators reject a decorated
|
|
147
|
+
`declare` field (`TS1206`), so write `posts!: Promise<Post[]>` there; legacy decorators keep
|
|
148
|
+
`declare posts: Promise<Post[]>`.
|
|
149
|
+
|
|
130
150
|
### Code Generation (optional)
|
|
131
151
|
|
|
132
152
|
[**litedbmodel-gen**](https://www.npmjs.com/package/litedbmodel-gen) generates model column definitions from SQL DDL (`schema.sql`). Column definitions inside markers are auto-updated when the schema changes; hand-written code (relations, methods, exports) is preserved.
|
|
@@ -147,10 +167,10 @@ import { DBModel, model, column } from 'litedbmodel';
|
|
|
147
167
|
// 1. Define model
|
|
148
168
|
@model('users')
|
|
149
169
|
class UserModel extends DBModel {
|
|
150
|
-
@column() id?: number;
|
|
151
|
-
@column() name?: string;
|
|
152
|
-
@column() email?: string;
|
|
153
|
-
@column() is_active?: boolean;
|
|
170
|
+
@column.number() id?: number;
|
|
171
|
+
@column.text() name?: string;
|
|
172
|
+
@column.text() email?: string;
|
|
173
|
+
@column.boolean() is_active?: boolean;
|
|
154
174
|
}
|
|
155
175
|
export const User = UserModel.asModel(); // Adds type-safe column references
|
|
156
176
|
|
|
@@ -195,9 +215,9 @@ The `@model` decorator accepts optional configuration for default behaviors:
|
|
|
195
215
|
updateTable: 'entries_writable', // UPDATE_TABLE_NAME (for views)
|
|
196
216
|
})
|
|
197
217
|
class EntryModel extends DBModel {
|
|
198
|
-
@column() id?: number;
|
|
199
|
-
@column() title?: string;
|
|
200
|
-
@column() created_at?:
|
|
218
|
+
@column.number() id?: number;
|
|
219
|
+
@column.text() title?: string;
|
|
220
|
+
@column.datetime() created_at?: string;
|
|
201
221
|
@column.boolean() is_deleted?: boolean;
|
|
202
222
|
}
|
|
203
223
|
export const Entry = EntryModel.asModel();
|
|
@@ -217,26 +237,81 @@ export const Entry = EntryModel.asModel();
|
|
|
217
237
|
|
|
218
238
|
## Column Decorators
|
|
219
239
|
|
|
220
|
-
###
|
|
221
|
-
|
|
222
|
-
|
|
240
|
+
### Declared Types
|
|
241
|
+
|
|
242
|
+
**A column states its own type.** The `@column.*` family is the single source of truth: it decides
|
|
243
|
+
both the TypeScript type you declare and the value `find()` gives back, on every toolchain and under
|
|
244
|
+
either decorator protocol. Nothing is inferred from the TypeScript type annotation.
|
|
245
|
+
|
|
246
|
+
| DB column type | Decorator | Declared TS type | Value `find()` returns |
|
|
247
|
+
|---|---|---|---|
|
|
248
|
+
| `TEXT` / `VARCHAR` / `CHAR` / `ENUM` | `@column.text()` | `string` | the driver string, uncast |
|
|
249
|
+
| `REAL` / `FLOAT` / `DOUBLE` | `@column.number()` | `number` | JS number (bc `float`) |
|
|
250
|
+
| `NUMERIC` / `DECIMAL` | `@column.decimal()` | `string` | the **exact decimal string** — a JS number destroys `NUMERIC(38,10)` |
|
|
251
|
+
| `MONEY` | `@column.text()` | `string` | the driver's money text (`$1.50`) |
|
|
252
|
+
| `BOOLEAN` | `@column.boolean()` | `boolean` | JS boolean |
|
|
253
|
+
| `INTEGER` / `BIGINT` / `SERIAL` … | `@column.bigint()` | `bigint` | a JS **bigint** — behavior-contracts' `int` value model (checked i64) |
|
|
254
|
+
| `TIMESTAMP` / `TIMESTAMPTZ` / `DATETIME` | `@column.datetime()` | `string` | the column's **own textual form**, never a TZ-shifted `Date` — with the offset when the column carries one (`timestamptz` → `2024-06-15 10:30:00+00`), without it when it does not (`timestamp` → `2024-06-15 10:30:00`) |
|
|
255
|
+
| `DATE` | `@column.date()` | `string` | `'YYYY-MM-DD'` — a calendar date has no timezone |
|
|
256
|
+
| `UUID` | `@column.uuid()` | `string` | the UUID string (adds `::uuid` casts on PostgreSQL) |
|
|
257
|
+
| `JSON` / `JSONB` | `@column.json<T>()` | `T` | parsed JSON |
|
|
258
|
+
| `TEXT[]` | `@column.stringArray()` | `string[]` | `string[]` |
|
|
259
|
+
| `INT[]` | `@column.intArray()` | `number[]` | `number[]` |
|
|
260
|
+
| `NUMERIC[]` | `@column.numericArray()` | `(number \| null)[]` | `(number \| null)[]` |
|
|
261
|
+
| `BOOLEAN[]` | `@column.booleanArray()` | `(boolean \| null)[]` | `(boolean \| null)[]` |
|
|
262
|
+
| `TIMESTAMP[]` | `@column.datetimeArray()` | `(string \| null)[]` | element-wise the same TZ-attached string `@column.datetime()` returns |
|
|
263
|
+
| `BYTEA` / `BLOB` / a vendor type | `@column.passthrough()` | `unknown` | the driver's value, uncast |
|
|
264
|
+
| anything else | `@column.custom(cast, serialize?)` | whatever `cast` returns | whatever `cast` returns |
|
|
223
265
|
|
|
224
266
|
```typescript
|
|
225
|
-
@
|
|
226
|
-
|
|
227
|
-
@column()
|
|
228
|
-
@column()
|
|
229
|
-
@column()
|
|
267
|
+
@model('users')
|
|
268
|
+
class UserModel extends DBModel {
|
|
269
|
+
@column.number({ primaryKey: true, autoIncrement: true }) id?: number;
|
|
270
|
+
@column.text() name?: string;
|
|
271
|
+
@column.text('mail_addr') email?: string; // property `email`, column `mail_addr`
|
|
272
|
+
@column.boolean() is_active?: boolean;
|
|
273
|
+
@column.datetime() created_at?: string; // reads back a string, so it is declared a string
|
|
274
|
+
@column.date() birth_date?: string; // 'YYYY-MM-DD'
|
|
275
|
+
@column.bigint() large_id?: bigint; // every integer width — bc's `int`
|
|
276
|
+
}
|
|
230
277
|
```
|
|
231
278
|
|
|
279
|
+
**An integer column is one type at every width.** behavior-contracts has a single integer type — `int`,
|
|
280
|
+
a JS `bigint` on the TS plane, checked i64 — and the drivers are configured to produce exactly that
|
|
281
|
+
(`configurePgDeboxTypeParsers`, `mysqlDeboxPoolOptions`, better-sqlite3 `safeIntegers`). A `SMALLINT`
|
|
282
|
+
and a `BIGINT` therefore read back the same way, and no value is ever rounded on the way in.
|
|
283
|
+
|
|
284
|
+
`JSON.stringify` refuses a `bigint` — that is JavaScript, not this library. Convert at the boundary:
|
|
285
|
+
|
|
286
|
+
```ts
|
|
287
|
+
JSON.stringify(row, (_k, v) => (typeof v === 'bigint' ? v.toString() : v));
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
Writes accept the natural JS value: `[[User.created_at, new Date()]]` serializes a `Date`, and
|
|
291
|
+
`[[User.large_id, 9007199254740993n]]` a `bigint`. Dates read back as strings so a row compares
|
|
292
|
+
without a timezone shift.
|
|
293
|
+
|
|
294
|
+
SQLite is the exception to the decimal row: it stores no fixed-precision type, so its driver hands
|
|
295
|
+
back a JS number whatever the column says, and the value is already rounded before litedbmodel sees it.
|
|
296
|
+
|
|
297
|
+
Parse a datetime when you need a `Date`: `new Date(user.created_at)`. PostgreSQL returns
|
|
298
|
+
`2024-06-15 10:30:00+00` — a space, not a `T`; `new Date()` accepts that form as-is. Do not "repair"
|
|
299
|
+
it into `2024-06-15T10:30:00+00`, which is an `Invalid Date` (a `T` form needs `+00:00`).
|
|
300
|
+
|
|
301
|
+
A string from a column with **no** timezone (`timestamp`, MySQL `datetime`) has no offset to parse, so
|
|
302
|
+
`new Date()` reads it as LOCAL time. Declare the column `timestamptz` when the value is an instant —
|
|
303
|
+
that is a schema decision no client-side conversion can substitute for.
|
|
304
|
+
|
|
232
305
|
### Column Options
|
|
233
306
|
|
|
307
|
+
Every family takes the same argument: a column name, or an options object.
|
|
308
|
+
|
|
234
309
|
```typescript
|
|
235
|
-
@column('db_column_name') prop?: string; // Custom column name (string shorthand)
|
|
236
|
-
@column({ columnName: 'db_col' }) prop?: string; // Custom column name (object form)
|
|
237
|
-
@column({ primaryKey: true }) id?: number;
|
|
238
|
-
@column({ primaryKey: true, columnName: 'user_id' }) id?: number; // Both options
|
|
239
|
-
@column({ primaryKey: true, autoIncrement: true }) id?: number; // Server-assigned key
|
|
310
|
+
@column.text('db_column_name') prop?: string; // Custom column name (string shorthand)
|
|
311
|
+
@column.text({ columnName: 'db_col' }) prop?: string; // Custom column name (object form)
|
|
312
|
+
@column.number({ primaryKey: true }) id?: number; // Mark as primary key
|
|
313
|
+
@column.number({ primaryKey: true, columnName: 'user_id' }) id?: number; // Both options
|
|
314
|
+
@column.number({ primaryKey: true, autoIncrement: true }) id?: number; // Server-assigned key
|
|
240
315
|
```
|
|
241
316
|
|
|
242
317
|
| Option | Type | Description |
|
|
@@ -250,47 +325,22 @@ whenever that is not true: on MySQL, which has no native `RETURNING`, a `returni
|
|
|
250
325
|
the rows it wrote from the declared key — by the auto-increment range for a server-assigned key, and
|
|
251
326
|
by the values the write itself bound for a UUID, a client-supplied, or a composite key.
|
|
252
327
|
|
|
253
|
-
### Explicit Types (for arrays/JSON/UUID)
|
|
254
|
-
|
|
255
|
-
Use explicit type decorators when auto-inference isn't sufficient:
|
|
256
|
-
|
|
257
|
-
```typescript
|
|
258
|
-
@column.date() birth_date?: string; // Date only (YYYY-MM-DD string)
|
|
259
|
-
@column.datetime() updated_at?: Date; // DateTime with timezone
|
|
260
|
-
@column.boolean() is_active?: boolean; // Explicit boolean
|
|
261
|
-
@column.number() amount?: number; // Explicit number
|
|
262
|
-
@column.uuid() id?: string; // UUID with auto-casting (PostgreSQL)
|
|
263
|
-
@column.stringArray() tags?: string[]; // String array
|
|
264
|
-
@column.intArray() scores?: number[]; // Integer array
|
|
265
|
-
@column.numericArray() prices?: number[]; // Numeric/decimal array
|
|
266
|
-
@column.booleanArray() flags?: boolean[]; // Boolean array
|
|
267
|
-
@column.datetimeArray() dates?: Date[]; // DateTime array
|
|
268
|
-
@column.json<Settings>() settings?: Settings; // JSON with type
|
|
269
|
-
```
|
|
270
|
-
|
|
271
328
|
### Date vs DateTime: Important Distinction
|
|
272
329
|
|
|
273
|
-
Databases distinguish
|
|
274
|
-
|
|
275
|
-
| DB Column Type | Decorator | TypeScript Type | Example |
|
|
276
|
-
|----------------|-----------|-----------------|---------|
|
|
277
|
-
| `TIMESTAMP` / `DATETIME` | `@column()` or `@column.datetime()` | `Date` | `created_at`, `updated_at` |
|
|
278
|
-
| `DATE` | `@column.date()` | `string` | `birth_date`, `settlement_date` |
|
|
279
|
-
|
|
280
|
-
**⚠️ Important**: `@column()` with `Date` type is treated as `TIMESTAMP/DATETIME`. For date-only columns (no time component), you **must** use `@column.date()` explicitly.
|
|
330
|
+
Databases distinguish date-only from datetime columns, and so do the decorators. Both read back as a
|
|
331
|
+
string, but a different one — pick the family that matches the column:
|
|
281
332
|
|
|
282
333
|
```typescript
|
|
283
334
|
// ✅ Correct usage
|
|
284
|
-
@column()
|
|
285
|
-
@column.
|
|
286
|
-
@column.date() settlement_date?: string; // DATE column (date only → 'YYYY-MM-DD')
|
|
335
|
+
@column.datetime() updated_at?: string; // TIMESTAMP column → '2024-06-15 10:30:00+00'
|
|
336
|
+
@column.date() settlement_date?: string; // DATE column → '2024-06-15'
|
|
287
337
|
|
|
288
|
-
// ❌ Wrong:
|
|
289
|
-
@column() settlement_date?:
|
|
338
|
+
// ❌ Wrong: a DATE column read through the datetime family carries a time that is not in the column
|
|
339
|
+
@column.datetime() settlement_date?: string;
|
|
290
340
|
```
|
|
291
341
|
|
|
292
|
-
**Serialization behavior** (PostgreSQL):
|
|
293
|
-
- `@column
|
|
342
|
+
**Serialization behavior on write** (PostgreSQL):
|
|
343
|
+
- `@column.datetime()` → ISO 8601 UTC: `2024-06-15T10:30:00.000Z`
|
|
294
344
|
- `@column.date()` → date string: `2024-06-15`
|
|
295
345
|
|
|
296
346
|
### Date Utility Functions
|
|
@@ -581,7 +631,7 @@ await User.find([
|
|
|
581
631
|
> **ESLint Plugin:** Use `litedbmodel/eslint-plugin` to catch mistakes that TypeScript cannot:
|
|
582
632
|
> - Wrong model columns (e.g., `User.find([[Post.id, 1]])`)
|
|
583
633
|
> - Hardcoded column names instead of `${Model.column}`
|
|
584
|
-
> -
|
|
634
|
+
> - The wrong relation declaration for your decorator protocol (`declare` vs `!`)
|
|
585
635
|
|
|
586
636
|
---
|
|
587
637
|
|
|
@@ -722,11 +772,12 @@ import { DBModel, model, column, hasMany, belongsTo, hasOne } from 'litedbmodel'
|
|
|
722
772
|
|
|
723
773
|
@model('users')
|
|
724
774
|
class UserModel extends DBModel {
|
|
725
|
-
@column() id?: number;
|
|
726
|
-
@column() name?: string;
|
|
775
|
+
@column.number() id?: number;
|
|
776
|
+
@column.text() name?: string;
|
|
727
777
|
|
|
728
|
-
//
|
|
729
|
-
//
|
|
778
|
+
// Legacy decorators: declare a relation with 'declare' (not '!'), so TypeScript emits no instance
|
|
779
|
+
// field to shadow the getter. Standard decorators reject a decorated 'declare' field (TS1206) —
|
|
780
|
+
// write `posts!: Promise<Post[]>` there; the library removes the shadowing field itself.
|
|
730
781
|
@hasMany(() => [User.id, Post.author_id])
|
|
731
782
|
declare posts: Promise<Post[]>;
|
|
732
783
|
|
|
@@ -737,9 +788,9 @@ export const User = UserModel.asModel();
|
|
|
737
788
|
|
|
738
789
|
@model('posts')
|
|
739
790
|
class PostModel extends DBModel {
|
|
740
|
-
@column() id?: number;
|
|
741
|
-
@column() author_id?: number;
|
|
742
|
-
@column() title?: string;
|
|
791
|
+
@column.number() id?: number;
|
|
792
|
+
@column.number() author_id?: number;
|
|
793
|
+
@column.text() title?: string;
|
|
743
794
|
|
|
744
795
|
@belongsTo(() => [Post.author_id, User.id])
|
|
745
796
|
declare author: Promise<User | null>;
|
|
@@ -755,7 +806,16 @@ const author = await post.author; // Lazy loaded
|
|
|
755
806
|
const comments = await post.comments; // Lazy loaded
|
|
756
807
|
```
|
|
757
808
|
|
|
758
|
-
> **Important
|
|
809
|
+
> **Important — the relation declaration depends on your decorator protocol:**
|
|
810
|
+
>
|
|
811
|
+
> - **Legacy decorators**: write `declare posts: Promise<Post[]>`. A field written `posts!: …` emits a
|
|
812
|
+
> class field that shadows the prototype getter, and the relation reads back as `undefined`.
|
|
813
|
+
> - **Standard (TC39) decorators**: write `posts!: Promise<Post[]>`. A decorated `declare` field does
|
|
814
|
+
> not compile (`TS1206`), and litedbmodel removes the shadowing field itself.
|
|
815
|
+
>
|
|
816
|
+
> `litedbmodel/eslint-plugin` enforces whichever applies. It reads `experimentalDecorators` from your
|
|
817
|
+
> TypeScript program when type-aware linting is on; otherwise set it explicitly:
|
|
818
|
+
> `"litedbmodel/require-declare-for-relations": ["error", { "decorators": "standard" }]`.
|
|
759
819
|
|
|
760
820
|
### With Options (order, where, limit)
|
|
761
821
|
|
|
@@ -787,9 +847,9 @@ This is more efficient than fetching all records and filtering in application co
|
|
|
787
847
|
```typescript
|
|
788
848
|
@model('tenant_posts')
|
|
789
849
|
class TenantPostModel extends DBModel {
|
|
790
|
-
@column({ primaryKey: true }) tenant_id?: number;
|
|
791
|
-
@column({ primaryKey: true }) id?: number;
|
|
792
|
-
@column() author_id?: number;
|
|
850
|
+
@column.number({ primaryKey: true }) tenant_id?: number;
|
|
851
|
+
@column.number({ primaryKey: true }) id?: number;
|
|
852
|
+
@column.number() author_id?: number;
|
|
793
853
|
|
|
794
854
|
@belongsTo(() => [
|
|
795
855
|
[TenantPost.tenant_id, TenantUser.tenant_id],
|
|
@@ -1034,10 +1094,10 @@ const activeUsers = await User.query(`
|
|
|
1034
1094
|
// Window functions, CTEs, recursive queries - anything PostgreSQL supports
|
|
1035
1095
|
@model('user_rankings')
|
|
1036
1096
|
class UserRankingModel extends DBModel {
|
|
1037
|
-
@column() user_id?: number;
|
|
1038
|
-
@column() score?: number;
|
|
1039
|
-
@column() rank?: number;
|
|
1040
|
-
@column() percentile?: number;
|
|
1097
|
+
@column.number() user_id?: number;
|
|
1098
|
+
@column.number() score?: number;
|
|
1099
|
+
@column.number() rank?: number;
|
|
1100
|
+
@column.number() percentile?: number;
|
|
1041
1101
|
}
|
|
1042
1102
|
const UserRanking = UserRankingModel.asModel();
|
|
1043
1103
|
|
|
@@ -1097,11 +1157,11 @@ import { DBModel, model, column } from 'litedbmodel';
|
|
|
1097
1157
|
|
|
1098
1158
|
@model('user_stats') // Alias for the CTE
|
|
1099
1159
|
class UserStatsModel extends DBModel {
|
|
1100
|
-
@column() id?: number;
|
|
1101
|
-
@column() name?: string;
|
|
1102
|
-
@column() post_count?: number;
|
|
1103
|
-
@column() comment_count?: number;
|
|
1104
|
-
@column() last_activity?:
|
|
1160
|
+
@column.number() id?: number;
|
|
1161
|
+
@column.text() name?: string;
|
|
1162
|
+
@column.number() post_count?: number;
|
|
1163
|
+
@column.number() comment_count?: number;
|
|
1164
|
+
@column.datetime() last_activity?: string;
|
|
1105
1165
|
|
|
1106
1166
|
// Define the base query
|
|
1107
1167
|
static QUERY = `
|
|
@@ -1160,11 +1220,11 @@ import { sql } from 'litedbmodel';
|
|
|
1160
1220
|
|
|
1161
1221
|
@model('sales_report')
|
|
1162
1222
|
class SalesReportModel extends DBModel {
|
|
1163
|
-
@column() product_id?: number;
|
|
1164
|
-
@column() product_name?: string;
|
|
1165
|
-
@column() total_quantity?: number;
|
|
1166
|
-
@column() total_revenue?: number;
|
|
1167
|
-
@column() order_count?: number;
|
|
1223
|
+
@column.number() product_id?: number;
|
|
1224
|
+
@column.text() product_name?: string;
|
|
1225
|
+
@column.number() total_quantity?: number;
|
|
1226
|
+
@column.number() total_revenue?: number;
|
|
1227
|
+
@column.number() order_count?: number;
|
|
1168
1228
|
|
|
1169
1229
|
static forPeriod(startDate: string, endDate: string) {
|
|
1170
1230
|
return this.withQuery(sql`
|
|
@@ -1204,9 +1264,9 @@ import { sql } from 'litedbmodel';
|
|
|
1204
1264
|
|
|
1205
1265
|
@model('user_activity')
|
|
1206
1266
|
class UserActivityModel extends DBModel {
|
|
1207
|
-
@column() user_id?: number;
|
|
1208
|
-
@column() user_name?: string;
|
|
1209
|
-
@column() total_posts?: number;
|
|
1267
|
+
@column.number() user_id?: number;
|
|
1268
|
+
@column.text() user_name?: string;
|
|
1269
|
+
@column.number() total_posts?: number;
|
|
1210
1270
|
|
|
1211
1271
|
static QUERY = sql`
|
|
1212
1272
|
SELECT
|
|
@@ -1340,15 +1400,15 @@ const CmsDB = DBModel.createDBBase({
|
|
|
1340
1400
|
// Models inherit from their respective database base class
|
|
1341
1401
|
@model('users')
|
|
1342
1402
|
class UserModel extends BaseDB {
|
|
1343
|
-
@column() id?: number;
|
|
1344
|
-
@column() name?: string;
|
|
1403
|
+
@column.number() id?: number;
|
|
1404
|
+
@column.text() name?: string;
|
|
1345
1405
|
}
|
|
1346
1406
|
export const User = UserModel.asModel();
|
|
1347
1407
|
|
|
1348
1408
|
@model('articles')
|
|
1349
1409
|
class ArticleModel extends CmsDB {
|
|
1350
|
-
@column() id?: number;
|
|
1351
|
-
@column() title?: string;
|
|
1410
|
+
@column.number() id?: number;
|
|
1411
|
+
@column.text() title?: string;
|
|
1352
1412
|
}
|
|
1353
1413
|
export const Article = ArticleModel.asModel();
|
|
1354
1414
|
```
|
package/dist/Column.d.ts
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
*/
|
|
20
20
|
import { DBNotNullValue, DBCast, DBCastArray } from './DBValues';
|
|
21
21
|
import { type SqlFragment, type SqlTypedFragment, type SqlCondition } from './SqlFragment';
|
|
22
|
+
import type { DriverTypeCast } from './drivers/types';
|
|
22
23
|
/**
|
|
23
24
|
* Sort direction for ORDER BY
|
|
24
25
|
* @internal
|
|
@@ -123,6 +124,8 @@ export interface Column<ValueType = unknown, ModelType = unknown> {
|
|
|
123
124
|
readonly _brand: 'Column';
|
|
124
125
|
/** SQL type for automatic casting in conditions (e.g., 'uuid') */
|
|
125
126
|
readonly sqlCast?: string;
|
|
127
|
+
/** The column family's serializer: a condition value binds exactly as a written value does */
|
|
128
|
+
readonly serialize?: (value: unknown, typeCast?: DriverTypeCast) => unknown;
|
|
126
129
|
/** Phantom type for model association (compile-time only, not used at runtime) */
|
|
127
130
|
readonly __model?: ModelType;
|
|
128
131
|
/**
|
|
@@ -242,6 +245,7 @@ export interface Column<ValueType = unknown, ModelType = unknown> {
|
|
|
242
245
|
* @param modelName - The model class name (for debugging)
|
|
243
246
|
* @param propertyName - The property name on the model (defaults to columnName)
|
|
244
247
|
* @param sqlCast - SQL type for automatic casting in conditions (e.g., 'uuid')
|
|
248
|
+
* @param serialize - The column family's serializer, applied to a condition value before it binds
|
|
245
249
|
* @returns A callable Column function with condition builder methods
|
|
246
250
|
*
|
|
247
251
|
* @example
|
|
@@ -257,7 +261,7 @@ export interface Column<ValueType = unknown, ModelType = unknown> {
|
|
|
257
261
|
*
|
|
258
262
|
* @internal
|
|
259
263
|
*/
|
|
260
|
-
export declare function createColumn<ValueType, ModelType = unknown>(columnName: string, tableName: string, modelName: string, propertyName?: string, sqlCast?: string): Column<ValueType, ModelType>;
|
|
264
|
+
export declare function createColumn<ValueType, ModelType = unknown>(columnName: string, tableName: string, modelName: string, propertyName?: string, sqlCast?: string, serialize?: (value: unknown, typeCast?: DriverTypeCast) => unknown): Column<ValueType, ModelType>;
|
|
261
265
|
/**
|
|
262
266
|
* Type guard to check if a value is a Column instance.
|
|
263
267
|
*
|
|
@@ -283,9 +287,9 @@ export declare function isColumn(value: unknown): value is Column;
|
|
|
283
287
|
* ```typescript
|
|
284
288
|
* @model('users')
|
|
285
289
|
* class User extends DBModel {
|
|
286
|
-
* @column() id?: number;
|
|
287
|
-
* @column() name?: string;
|
|
288
|
-
* @column() is_active?: boolean;
|
|
290
|
+
* @column.number() id?: number;
|
|
291
|
+
* @column.text() name?: string;
|
|
292
|
+
* @column.boolean() is_active?: boolean;
|
|
289
293
|
* }
|
|
290
294
|
*
|
|
291
295
|
* // Use asModel() to get type-safe Column references
|
|
@@ -484,8 +488,21 @@ export type CV<C extends Column<any, any>> = C extends Column<infer V, any> ? re
|
|
|
484
488
|
* @internal
|
|
485
489
|
*/
|
|
486
490
|
export type CVs<T extends readonly (readonly [Column<any, any>, any])[]> = {
|
|
487
|
-
[K in keyof T]: T[K] extends readonly [infer C, any] ? C extends Column<infer V, any> ? readonly [C, V | null | undefined | SkipType] : never : never;
|
|
491
|
+
[K in keyof T]: T[K] extends readonly [infer C, any] ? C extends Column<infer V, any> ? readonly [C, WriteValue<V> | null | undefined | SkipType] : never : never;
|
|
488
492
|
};
|
|
493
|
+
/**
|
|
494
|
+
* What a column ACCEPTS on write, given the type it READS BACK.
|
|
495
|
+
*
|
|
496
|
+
* A `@column.datetime()` / `.date()` / `.bigint()` column reads a string — the column's own textual
|
|
497
|
+
* form, or a BIGINT's exact decimal — but the write path still takes the JS value that string stands
|
|
498
|
+
* for: a `Date` goes through the driver's dialect-specific datetime serializer (MySQL wants a local
|
|
499
|
+
* `YYYY-MM-DD HH:mm:ss`, PostgreSQL an ISO instant, and getting that right is the library's job, not
|
|
500
|
+
* the caller's), and a `bigint` is written as its exact digits. TypeScript cannot tell those columns
|
|
501
|
+
* from a plain text one — all of them read `string` — so the widening applies to every string column.
|
|
502
|
+
*
|
|
503
|
+
* @category Column
|
|
504
|
+
*/
|
|
505
|
+
export type WriteValue<V> = V | (V extends string ? Date | bigint : never);
|
|
489
506
|
/**
|
|
490
507
|
* Sentinel value to skip a field in create/update operations.
|
|
491
508
|
* Use with conditional expressions to keep code as expressions instead of statements.
|
|
@@ -609,6 +626,13 @@ export declare function createOrCond<M>(condGroups: readonly CondsOf<M>[]): OrCo
|
|
|
609
626
|
* @internal
|
|
610
627
|
*/
|
|
611
628
|
export declare function isOrCond(cond: CondElement | CondElementOf<any>): cond is OrCond | OrCondOf<any>;
|
|
612
|
-
|
|
629
|
+
/**
|
|
630
|
+
* Bind a condition value the way the column's family writes it (`typeCast` is the driver's), so a
|
|
631
|
+
* value matches what a create / update stored — a `Date` on a `@column.datetime()` column included.
|
|
632
|
+
* An array is an IN list: each element binds on its own.
|
|
633
|
+
* @internal
|
|
634
|
+
*/
|
|
635
|
+
export declare function serializeCondValue(col: Column<any, any>, value: unknown, typeCast?: DriverTypeCast): unknown;
|
|
636
|
+
export declare function condsToRecord(conditions: Conds, typeCast?: DriverTypeCast): Record<string, unknown>;
|
|
613
637
|
export {};
|
|
614
638
|
//# sourceMappingURL=Column.d.ts.map
|
package/dist/Column.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Column.d.ts","sourceRoot":"","sources":["../src/Column.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,cAAc,EAAa,MAAM,EAAE,WAAW,EAAW,MAAM,YAAY,CAAC;AACrF,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,gBAAgB,EAAE,KAAK,YAAY,EAAwC,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"Column.d.ts","sourceRoot":"","sources":["../src/Column.ts"],"names":[],"mappings":"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"}
|
package/dist/Column.js
CHANGED
|
@@ -28,6 +28,7 @@ exports.columnsToNames = columnsToNames;
|
|
|
28
28
|
exports.pairsToRecord = pairsToRecord;
|
|
29
29
|
exports.createOrCond = createOrCond;
|
|
30
30
|
exports.isOrCond = isOrCond;
|
|
31
|
+
exports.serializeCondValue = serializeCondValue;
|
|
31
32
|
exports.condsToRecord = condsToRecord;
|
|
32
33
|
const DBValues_1 = require("./DBValues");
|
|
33
34
|
const SqlFragment_1 = require("./SqlFragment");
|
|
@@ -123,6 +124,7 @@ function orderToString(order) {
|
|
|
123
124
|
* @param modelName - The model class name (for debugging)
|
|
124
125
|
* @param propertyName - The property name on the model (defaults to columnName)
|
|
125
126
|
* @param sqlCast - SQL type for automatic casting in conditions (e.g., 'uuid')
|
|
127
|
+
* @param serialize - The column family's serializer, applied to a condition value before it binds
|
|
126
128
|
* @returns A callable Column function with condition builder methods
|
|
127
129
|
*
|
|
128
130
|
* @example
|
|
@@ -138,7 +140,7 @@ function orderToString(order) {
|
|
|
138
140
|
*
|
|
139
141
|
* @internal
|
|
140
142
|
*/
|
|
141
|
-
function createColumn(columnName, tableName, modelName, propertyName, sqlCast) {
|
|
143
|
+
function createColumn(columnName, tableName, modelName, propertyName, sqlCast, serialize) {
|
|
142
144
|
// The callable function itself - returns column name as string
|
|
143
145
|
const fn = function () {
|
|
144
146
|
return columnName;
|
|
@@ -176,6 +178,11 @@ function createColumn(columnName, tableName, modelName, propertyName, sqlCast) {
|
|
|
176
178
|
writable: false,
|
|
177
179
|
enumerable: true,
|
|
178
180
|
});
|
|
181
|
+
Object.defineProperty(fn, 'serialize', {
|
|
182
|
+
value: serialize,
|
|
183
|
+
writable: false,
|
|
184
|
+
enumerable: false,
|
|
185
|
+
});
|
|
179
186
|
// __model is a phantom type - not set at runtime, only used for TypeScript type checking
|
|
180
187
|
// Condition builder methods - wrap with DBCast if sqlCast is specified
|
|
181
188
|
if (sqlCast) {
|
|
@@ -451,11 +458,23 @@ function createOrCond(condGroups) {
|
|
|
451
458
|
function isOrCond(cond) {
|
|
452
459
|
return typeof cond === 'object' && cond !== null && '_type' in cond && cond._type === 'or';
|
|
453
460
|
}
|
|
461
|
+
/**
|
|
462
|
+
* Bind a condition value the way the column's family writes it (`typeCast` is the driver's), so a
|
|
463
|
+
* value matches what a create / update stored — a `Date` on a `@column.datetime()` column included.
|
|
464
|
+
* An array is an IN list: each element binds on its own.
|
|
465
|
+
* @internal
|
|
466
|
+
*/
|
|
467
|
+
function serializeCondValue(col, value, typeCast) {
|
|
468
|
+
const serialize = col.serialize;
|
|
469
|
+
if (serialize === undefined)
|
|
470
|
+
return value;
|
|
471
|
+
return Array.isArray(value) ? value.map(v => serialize(v, typeCast)) : serialize(value, typeCast);
|
|
472
|
+
}
|
|
454
473
|
/**
|
|
455
474
|
* Convert a single condition to key-value pair.
|
|
456
475
|
* Returns null if the condition should be skipped.
|
|
457
476
|
*/
|
|
458
|
-
function condToKeyValue(cond) {
|
|
477
|
+
function condToKeyValue(cond, typeCast) {
|
|
459
478
|
if (cond.length === 1) {
|
|
460
479
|
// No value condition like [${User.deleted_at} IS NULL]
|
|
461
480
|
return [cond[0], true]; // Use true as placeholder for DBConditions
|
|
@@ -468,7 +487,7 @@ function condToKeyValue(cond) {
|
|
|
468
487
|
// Handle composite key IN condition: [[Col1, Col2], [[v1, v2], [v3, v4]]]
|
|
469
488
|
if (Array.isArray(keyOrCol)) {
|
|
470
489
|
const columns = keyOrCol;
|
|
471
|
-
const tuples = value;
|
|
490
|
+
const tuples = value.map(t => t.map((v, i) => serializeCondValue(columns[i], v, typeCast)));
|
|
472
491
|
const columnNames = columns.map(col => col.columnName);
|
|
473
492
|
return ['__tuple__', (0, DBValues_1.dbTupleIn)(columnNames, tuples)];
|
|
474
493
|
}
|
|
@@ -486,14 +505,15 @@ function condToKeyValue(cond) {
|
|
|
486
505
|
if (value instanceof DBValues_1.DBToken) {
|
|
487
506
|
return [col.columnName, value];
|
|
488
507
|
}
|
|
508
|
+
const bound = serializeCondValue(col, value, typeCast);
|
|
489
509
|
if (sqlCast) {
|
|
490
510
|
// Wrap value with DBCast/DBCastArray for type casting
|
|
491
|
-
if (Array.isArray(
|
|
492
|
-
return [col.columnName, new DBValues_1.DBCastArray(
|
|
511
|
+
if (Array.isArray(bound)) {
|
|
512
|
+
return [col.columnName, new DBValues_1.DBCastArray(bound, sqlCast)];
|
|
493
513
|
}
|
|
494
|
-
return [col.columnName, new DBValues_1.DBCast(
|
|
514
|
+
return [col.columnName, new DBValues_1.DBCast(bound, sqlCast, '=')];
|
|
495
515
|
}
|
|
496
|
-
return [col.columnName,
|
|
516
|
+
return [col.columnName, bound];
|
|
497
517
|
}
|
|
498
518
|
/**
|
|
499
519
|
* Convert condition tuples to a ConditionObject.
|
|
@@ -515,7 +535,7 @@ function sqlFragmentToKeyValue(frag) {
|
|
|
515
535
|
// Multiple params (e.g., BETWEEN ? AND ?)
|
|
516
536
|
return [frag.sql, [...frag.params]];
|
|
517
537
|
}
|
|
518
|
-
function condsToRecord(conditions) {
|
|
538
|
+
function condsToRecord(conditions, typeCast) {
|
|
519
539
|
const result = {};
|
|
520
540
|
for (const cond of conditions) {
|
|
521
541
|
// Skip SKIP elements
|
|
@@ -523,7 +543,7 @@ function condsToRecord(conditions) {
|
|
|
523
543
|
continue;
|
|
524
544
|
}
|
|
525
545
|
if (isOrCond(cond)) {
|
|
526
|
-
const orGroups = cond.conditions.map(group => condsToRecord(group));
|
|
546
|
+
const orGroups = cond.conditions.map(group => condsToRecord(group, typeCast));
|
|
527
547
|
result['__or__'] = orGroups;
|
|
528
548
|
}
|
|
529
549
|
else if ((0, SqlFragment_1.isAnySqlFragment)(cond)) {
|
|
@@ -535,7 +555,7 @@ function condsToRecord(conditions) {
|
|
|
535
555
|
}
|
|
536
556
|
}
|
|
537
557
|
else {
|
|
538
|
-
const kv = condToKeyValue(cond);
|
|
558
|
+
const kv = condToKeyValue(cond, typeCast);
|
|
539
559
|
if (kv === null)
|
|
540
560
|
continue;
|
|
541
561
|
const [key, value] = kv;
|