rads-db 3.2.28 → 3.2.30
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/dist/config.d.ts +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/{types-4fe0833c.d.ts → types-3f6b8235.d.ts} +12 -12
- package/drivers/memory.d.ts +3 -3
- package/drivers/restApi.d.ts +1 -1
- package/drivers/sql.d.ts +1 -1
- package/features/cache.d.ts +11 -2
- package/features/eventSourcing.d.ts +1 -1
- package/features/softDelete.d.ts +4 -1
- package/fileUploadDrivers/azureStorageBlob.d.ts +1 -0
- package/fileUploadDrivers/memory.d.ts +1 -0
- package/fileUploadDrivers/restApi.d.ts +1 -1
- package/fileUploadDrivers/supabaseStorage.d.ts +1 -0
- package/llms.txt +409 -0
- package/package.json +3 -2
package/dist/config.d.ts
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { E as EntityDecoratorArgs, U as UiDecoratorArgs, a as UiFieldDecoratorArgs, V as ValidateEntityDecoratorArgs, b as ValidateFieldDecoratorArgs, F as FieldDecoratorArgs, C as ComputedDecoratorArgs, S as Schema, D as DriverConstructor, c as Driver, d as ComputedContext, R as RadsRequestContext, e as CreateRadsDbArgs, f as CreateRadsDbClientArgs } from './types-
|
|
2
|
-
export { O as Change, x as ComputedContextGlobal, k as CreateRadsArgsDrivers, m as CreateRadsDbArgsNormalized, ak as DeepKeys, af as DeepPartial, ad as DeepPartialWithNulls, ae as DeepPartialWithNullsItem, al as EntityMethods, w as EnumDefinition, v as FieldDefinition, J as FileSystemNode, u as FileUploadArgs, q as FileUploadDriver, p as FileUploadResult, H as GenerateClientNormalizedOptions, B as GenerateClientOptions, a9 as Get, $ as GetAggArgs, a0 as GetAggArgsAgg, a3 as GetAggArgsAny, a6 as GetAggResponse, _ as GetArgs, a2 as GetArgsAny, a5 as GetArgsInclude, Q as GetManyArgs, a1 as GetManyArgsAny, a7 as GetManyResponse, a8 as GetResponse, aa as GetResponseInclude, ab as GetResponseIncludeSelect, ac as GetResponseNoInclude, s as GetRestRoutesArgs, G as GetRestRoutesOptions, t as GetRestRoutesResponse, ah as InverseRelation, aj as JsonPutOperations, M as MinimalDriver, ai as Put, P as PutEffect, g as RadsConfig, h as RadsConfigDataSource, r as RadsDbInstance, N as RadsFeature, y as RadsHookDoc, L as RadsUiSlotDefinition, K as RadsUiSlotName, I as RadsVitePluginOptions, ag as Relation, i as RequiredFields, l as RestDriverOptions, A as RestFileUploadDriverOptions, n as SchemaLoadResult, o as SchemaValidators, z as SqlDriverOptions, T as TypeDefinition, j as ValidateStringDecoratorArgs, X as VerifyManyArgs, a4 as VerifyManyArgsAny, Y as VerifyManyResponse, Z as Where, W as WhereJsonContains } from './types-
|
|
1
|
+
import { E as EntityDecoratorArgs, U as UiDecoratorArgs, a as UiFieldDecoratorArgs, V as ValidateEntityDecoratorArgs, b as ValidateFieldDecoratorArgs, F as FieldDecoratorArgs, C as ComputedDecoratorArgs, S as Schema, D as DriverConstructor, c as Driver, d as ComputedContext, R as RadsRequestContext, e as CreateRadsDbArgs, f as CreateRadsDbClientArgs } from './types-3f6b8235.js';
|
|
2
|
+
export { O as Change, x as ComputedContextGlobal, k as CreateRadsArgsDrivers, m as CreateRadsDbArgsNormalized, ak as DeepKeys, af as DeepPartial, ad as DeepPartialWithNulls, ae as DeepPartialWithNullsItem, al as EntityMethods, w as EnumDefinition, v as FieldDefinition, J as FileSystemNode, u as FileUploadArgs, q as FileUploadDriver, p as FileUploadResult, H as GenerateClientNormalizedOptions, B as GenerateClientOptions, a9 as Get, $ as GetAggArgs, a0 as GetAggArgsAgg, a3 as GetAggArgsAny, a6 as GetAggResponse, _ as GetArgs, a2 as GetArgsAny, a5 as GetArgsInclude, Q as GetManyArgs, a1 as GetManyArgsAny, a7 as GetManyResponse, a8 as GetResponse, aa as GetResponseInclude, ab as GetResponseIncludeSelect, ac as GetResponseNoInclude, s as GetRestRoutesArgs, G as GetRestRoutesOptions, t as GetRestRoutesResponse, ah as InverseRelation, aj as JsonPutOperations, M as MinimalDriver, ai as Put, P as PutEffect, g as RadsConfig, h as RadsConfigDataSource, r as RadsDbInstance, N as RadsFeature, y as RadsHookDoc, L as RadsUiSlotDefinition, K as RadsUiSlotName, I as RadsVitePluginOptions, ag as Relation, i as RequiredFields, l as RestDriverOptions, A as RestFileUploadDriverOptions, n as SchemaLoadResult, o as SchemaValidators, z as SqlDriverOptions, T as TypeDefinition, j as ValidateStringDecoratorArgs, X as VerifyManyArgs, a4 as VerifyManyArgsAny, Y as VerifyManyResponse, Z as Where, W as WhereJsonContains } from './types-3f6b8235.js';
|
|
3
3
|
import { RadsDb } from '_rads-db';
|
|
4
4
|
export { RadsDb } from '_rads-db';
|
|
5
5
|
import 'mssql';
|
|
@@ -45,18 +45,18 @@ type VerifyManyArgsAny = VerifyManyArgs<any>;
|
|
|
45
45
|
type RelationsAndNestedObjects<EN extends keyof EntityMeta> = EntityMeta[EN]['relations'] & EntityMeta[EN]['nestedObjects'];
|
|
46
46
|
type JsonFieldsInclude<EN extends keyof EntityMeta> = [EntityMeta[EN]['jsonFields']] extends [never] ? unknown : {
|
|
47
47
|
[K in EntityMeta[EN]['jsonFields'] & string]?: {
|
|
48
|
-
_pick?: string[];
|
|
48
|
+
_pick?: readonly string[];
|
|
49
49
|
};
|
|
50
50
|
};
|
|
51
51
|
type GetArgsInclude<EN extends keyof EntityMeta, R extends keyof RelationsAndNestedObjects<EN> = keyof RelationsAndNestedObjects<EN>> = [R] extends [never] ? {
|
|
52
|
-
_pick?: (EntityMeta[EN]['primitives'] | EntityMeta[EN]['jsonFields'])[];
|
|
52
|
+
_pick?: readonly (EntityMeta[EN]['primitives'] | EntityMeta[EN]['jsonFields'])[];
|
|
53
53
|
} & JsonFieldsInclude<EN> : {
|
|
54
|
-
_pick?: (EntityMeta[EN]['primitives'] | EntityMeta[EN]['jsonFields'] | R)[];
|
|
54
|
+
_pick?: readonly (EntityMeta[EN]['primitives'] | EntityMeta[EN]['jsonFields'] | R)[];
|
|
55
55
|
} & {
|
|
56
56
|
[K in R]?: GetArgsInclude<RelationsAndNestedObjects<EN>[K]['entityName']>;
|
|
57
57
|
} & JsonFieldsInclude<EN>;
|
|
58
58
|
type GetAggResponse<EN extends keyof EntityMeta, A extends GetAggArgs<EN>> = {
|
|
59
|
-
[K in A['agg'][
|
|
59
|
+
[K in A['agg'][number]]: K extends '_count' ? number : number | undefined;
|
|
60
60
|
};
|
|
61
61
|
interface GetManyResponse<EN extends keyof EntityMeta, A extends GetArgs<EN>> {
|
|
62
62
|
nodes: GetResponse<EN, A>[];
|
|
@@ -75,12 +75,12 @@ type Get<EntityName extends keyof EntityMeta, Include extends keyof EntityMeta[E
|
|
|
75
75
|
type RelationData<EN extends keyof EntityMeta, K extends keyof EntityMeta[EN]['relations']> = Pick<EntityMeta[EN]['relations'][K]['entity'], EntityMeta[EN]['relations'][K]['denormFields']>;
|
|
76
76
|
type KeepArray<TMaybeArray, TType> = NonNullable<TMaybeArray> extends any[] ? TType[] : TType;
|
|
77
77
|
type GetResponseInclude<EN extends keyof EntityMeta, I extends GetArgsInclude<EN>> = I extends {
|
|
78
|
-
_pick: string[];
|
|
78
|
+
_pick: readonly string[];
|
|
79
79
|
} ? GetResponseIncludeSelect<EN, I> : {
|
|
80
80
|
[K in keyof EntityMeta[EN]['type']]: K extends keyof EntityMeta[EN]['relations'] ? K extends keyof I ? KeepArray<EntityMeta[EN]['type'][K], GetResponseInclude<EntityMeta[EN]['relations'][K]['entityName'], I[K]>> : KeepArray<EntityMeta[EN]['type'][K], RelationData<EN, K>> : EntityMeta[EN]['type'][K];
|
|
81
81
|
};
|
|
82
82
|
type GetResponseIncludeSelect<EN extends keyof EntityMeta, I extends GetArgsInclude<EN>> = I extends {
|
|
83
|
-
_pick: (infer P)[];
|
|
83
|
+
_pick: readonly (infer P)[];
|
|
84
84
|
} ? Pick<{
|
|
85
85
|
[K in keyof EntityMeta[EN]['type']]: K extends keyof EntityMeta[EN]['relations'] ? K extends keyof Omit<I, '_pick'> ? KeepArray<EntityMeta[EN]['type'][K], GetResponseInclude<EntityMeta[EN]['relations'][K]['entityName'], Omit<I, '_pick'>[K]>> : KeepArray<EntityMeta[EN]['type'][K], RelationData<EN, K>> : K extends EntityMeta[EN]['jsonFields'] ? K extends keyof Omit<I, '_pick'> ? Omit<I, '_pick'>[K] extends {
|
|
86
86
|
_pick: (infer JP)[];
|
|
@@ -126,14 +126,14 @@ interface EntityMethods<E, EN extends keyof EntityMeta> {
|
|
|
126
126
|
/** Used to access underlying mechanism of storage directly.
|
|
127
127
|
* Warning: bypasses all rads features - schema won't be validated, default values won't be filled, etc. */
|
|
128
128
|
driver: Driver;
|
|
129
|
-
get<A extends GetArgs<EN>>(args: A, ctx?: RadsRequestContext): MaybePromise$1<GetResponse<EN, A>>;
|
|
130
|
-
getMany<A extends GetManyArgs<EN>>(args?: A, ctx?: RadsRequestContext): MaybePromise$1<GetManyResponse<EN, A>>;
|
|
131
|
-
getAgg<A extends GetAggArgs<EN>>(args: A, ctx?: RadsRequestContext): MaybePromise$1<GetAggResponse<EN, A>>;
|
|
132
|
-
getAll<A extends GetManyArgs<EN>>(args?: A, ctx?: RadsRequestContext): MaybePromise$1<GetManyResponse<EN, A>['nodes']>;
|
|
129
|
+
get<const A extends GetArgs<EN>>(args: A, ctx?: RadsRequestContext): MaybePromise$1<GetResponse<EN, A>>;
|
|
130
|
+
getMany<const A extends GetManyArgs<EN>>(args?: A, ctx?: RadsRequestContext): MaybePromise$1<GetManyResponse<EN, A>>;
|
|
131
|
+
getAgg<const A extends GetAggArgs<EN>>(args: A, ctx?: RadsRequestContext): MaybePromise$1<GetAggResponse<EN, A>>;
|
|
132
|
+
getAll<const A extends GetManyArgs<EN>>(args?: A, ctx?: RadsRequestContext): MaybePromise$1<GetManyResponse<EN, A>['nodes']>;
|
|
133
133
|
put(data: Put<EN>, ctx?: RadsRequestContext): MaybePromise$1<GetResponseNoInclude<EN>>;
|
|
134
134
|
putMany(data: Put<EN>[], ctx?: RadsRequestContext): MaybePromise$1<GetResponseNoInclude<EN>[]>;
|
|
135
|
-
verifyMany<A extends VerifyManyArgs<EN>>(args?: A, ctx?: RadsRequestContext): MaybePromise$1<VerifyManyResponse>;
|
|
136
|
-
verifyAll<A extends VerifyManyArgs<EN>>(args?: A, ctx?: RadsRequestContext): MaybePromise$1<Pick<VerifyManyResponse, 'correctCount' | 'incorrectCount'>>;
|
|
135
|
+
verifyMany<const A extends VerifyManyArgs<EN>>(args?: A, ctx?: RadsRequestContext): MaybePromise$1<VerifyManyResponse>;
|
|
136
|
+
verifyAll<const A extends VerifyManyArgs<EN>>(args?: A, ctx?: RadsRequestContext): MaybePromise$1<Pick<VerifyManyResponse, 'correctCount' | 'incorrectCount'>>;
|
|
137
137
|
}
|
|
138
138
|
type Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ...0[]];
|
|
139
139
|
type Primitive = string | number | boolean | symbol | null | undefined;
|
package/drivers/memory.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Schema } from '
|
|
2
|
-
import type { GetAggArgsAny, GetManyArgsAny } from '
|
|
1
|
+
import type { MinimalDriver, Schema } from '../types';
|
|
2
|
+
import type { GetAggArgsAny, GetManyArgsAny } from '../typesClientEngine';
|
|
3
3
|
interface MemoryDriverOptions {
|
|
4
4
|
}
|
|
5
5
|
declare const _default: (options?: MemoryDriverOptions) => (schema: Schema, entity: string) => MinimalDriver;
|
|
@@ -7,6 +7,6 @@ export default _default;
|
|
|
7
7
|
export declare function getAggFromArray(array: any[], args: GetAggArgsAny): Record<string, any>;
|
|
8
8
|
export declare function queryArray(array: any[], args: GetManyArgsAny): {
|
|
9
9
|
nodes: any;
|
|
10
|
-
cursor:
|
|
10
|
+
cursor: string | null;
|
|
11
11
|
};
|
|
12
12
|
export declare function getFilter(where: Record<string, any>, namePrefix?: string): ((x: any) => boolean | undefined) | null;
|
package/drivers/restApi.d.ts
CHANGED
package/drivers/sql.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { SqlDriverOptions } from '
|
|
1
|
+
import type { SqlDriverOptions } from '../types';
|
|
2
2
|
import type { Schema } from 'rads-db';
|
|
3
3
|
declare const _default: (options: SqlDriverOptions) => (schema: Schema, entity: string) => MinimalDriver;
|
|
4
4
|
export default _default;
|
package/features/cache.d.ts
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
|
-
import type { DriverConstructor } from '
|
|
2
|
-
declare const _default: (options: CacheOptions) =>
|
|
1
|
+
import type { DriverConstructor } from '../types';
|
|
2
|
+
declare const _default: (options: CacheOptions) => {
|
|
3
|
+
name: string;
|
|
4
|
+
init(db: Record<string, any>, context: import("../types").ComputedContextGlobal): void;
|
|
5
|
+
beforeGet(args: import("../typesClientEngine").GetArgsAny, ctx: import("../types").RadsRequestContext, context: import("../types").ComputedContext): Promise<Record<string, any> | Record<string, any>[] | {
|
|
6
|
+
nodes: Record<string, any>[];
|
|
7
|
+
cursor: string | undefined;
|
|
8
|
+
} | undefined>;
|
|
9
|
+
afterGet(items: any[], args: import("../typesClientEngine").GetArgsAny, ctx: import("../types").RadsRequestContext, context: import("../types").ComputedContext): Promise<void>;
|
|
10
|
+
afterPut(items: import("../types").RadsHookDoc[], ctx: import("../types").RadsRequestContext, computedContext: import("../types").ComputedContext): Promise<void>;
|
|
11
|
+
};
|
|
3
12
|
export default _default;
|
|
4
13
|
export interface EntityCacheOptions {
|
|
5
14
|
driver?: DriverConstructor;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ComputedContext, RadsFeature, RadsRequestContext } from '
|
|
1
|
+
import type { ComputedContext, RadsFeature, RadsRequestContext } from '../types';
|
|
2
2
|
export interface EventSourcingFeatureOptions {
|
|
3
3
|
applyEventIf?: (event: any, context: ComputedContext, ctx: RadsRequestContext) => boolean;
|
|
4
4
|
freezeEvent?: (event: any, context: ComputedContext, ctx: RadsRequestContext) => boolean;
|
package/features/softDelete.d.ts
CHANGED
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
declare const _default: () =>
|
|
1
|
+
declare const _default: () => {
|
|
2
|
+
name: string;
|
|
3
|
+
beforeGet(args: import("../typesClientEngine").GetArgsAny, ctx: import("../types").RadsRequestContext, context: import("../types").ComputedContext): void;
|
|
4
|
+
};
|
|
2
5
|
export default _default;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { SupabaseClient } from '@supabase/supabase-js';
|
|
2
|
+
import type { FileUploadDriver } from '../types';
|
|
2
3
|
declare const _default: (options: SupabaseFileUploadDriverOptions) => FileUploadDriver;
|
|
3
4
|
export default _default;
|
|
4
5
|
interface SupabaseFileUploadDriverOptions {
|
package/llms.txt
ADDED
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
# rads-db
|
|
2
|
+
|
|
3
|
+
> TypeScript-first universal data access library. Define entities as decorated classes, get strongly-typed CRUD methods, filtering, pagination, JOINs across any storage backend. Works on both server and browser.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install rads-db
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Create `rads.config.ts` at the project root:
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { defineRadsConfig, schemaFromFiles } from 'rads-db/config'
|
|
15
|
+
|
|
16
|
+
export default defineRadsConfig({
|
|
17
|
+
dataSources: {
|
|
18
|
+
db: {
|
|
19
|
+
schema: schemaFromFiles('./entities'), // folder with your entity classes
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
})
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Run codegen after every schema change (generates TypeScript types and metadata):
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npx rads
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Automate in `package.json`:
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"scripts": {
|
|
36
|
+
"dev": "npx rads && <your-dev-command>",
|
|
37
|
+
"build": "npx rads && <your-build-command>"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Defining Entities
|
|
43
|
+
|
|
44
|
+
Each entity is a TypeScript class in the entities folder. Use decorators from `rads-db`:
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import { entity, field, ui, validate, computed, precomputed, keepHistory } from 'rads-db'
|
|
48
|
+
import type { Relation, InverseRelation } from 'rads-db'
|
|
49
|
+
import type { TcRole } from './TcRole'
|
|
50
|
+
import type { TcPost } from './TcPost'
|
|
51
|
+
|
|
52
|
+
@entity()
|
|
53
|
+
@ui<typeof TcUser>({ name: 'User', namePlural: 'Users', nameField: 'name', captionFields: ['id', 'name'], group: 'People' })
|
|
54
|
+
export class TcUser {
|
|
55
|
+
id!: string
|
|
56
|
+
|
|
57
|
+
@validate<TcUser, any, string>({ preset: 'email' })
|
|
58
|
+
@ui({ name: 'Email Address', hint: 'Used for login' })
|
|
59
|
+
email!: string
|
|
60
|
+
|
|
61
|
+
name!: string
|
|
62
|
+
age?: number
|
|
63
|
+
isActive?: boolean
|
|
64
|
+
tags: string[] = []
|
|
65
|
+
|
|
66
|
+
// Relation: only { id } is stored; use include: { tcRole: {} } to load full object
|
|
67
|
+
tcRole!: Relation<TcRole>
|
|
68
|
+
|
|
69
|
+
// Relation with denormalized fields stored alongside the id
|
|
70
|
+
tcRoleWithName!: Relation<TcRole, 'name'>
|
|
71
|
+
|
|
72
|
+
// Computed inverse relation — not stored, derived at query time
|
|
73
|
+
tcPosts?: InverseRelation<'TcPost'>
|
|
74
|
+
|
|
75
|
+
@computed()
|
|
76
|
+
fullLabel?: string // computed server-side, not stored
|
|
77
|
+
|
|
78
|
+
@precomputed({ preset: 'eventSourcing' })
|
|
79
|
+
status?: string // precomputed and stored when source events change
|
|
80
|
+
|
|
81
|
+
@keepHistory()
|
|
82
|
+
statusHistory?: string // keeps full history of changes
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Decorator reference
|
|
87
|
+
|
|
88
|
+
| Decorator | Target | Purpose |
|
|
89
|
+
|-----------|--------|---------|
|
|
90
|
+
| `@entity(args?)` | class | Marks class as a database entity; `args.driver` overrides default driver |
|
|
91
|
+
| `@ui(args)` | class or field | Display hints for rads-ui (name, icon, captionFields, nameField, etc.) |
|
|
92
|
+
| `@validate(args)` | class or field | Validation rules (presets, regex, min/maxLength, etc.) |
|
|
93
|
+
| `@field(args?)` | field | Marks a relation field; `args.relation` points to the related class |
|
|
94
|
+
| `@computed()` | field | Computed at query time, never stored |
|
|
95
|
+
| `@precomputed()` | field | Computed and stored; recalculated when source data changes |
|
|
96
|
+
| `@keepHistory()` | field | Stores full change history |
|
|
97
|
+
|
|
98
|
+
### Relation types
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
Relation<T> // stores { id } only
|
|
102
|
+
Relation<T, 'field1'|'field2'> // stores { id, field1, field2 } (denormalized)
|
|
103
|
+
InverseRelation<'EntityName'> // reverse lookup — computed, not stored
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Field validation presets (string)
|
|
107
|
+
|
|
108
|
+
`text` | `html` | `markdown` | `alpha` | `alphanum` | `number` | `decimalNumber` | `email` | `icon` | `imageUrl` | `fileUrl` | `absoluteUrl` | `relativeUrl` | `phoneNumber` | `datetime` | `date` | `time` | `timeInterval` | `duration` | `hexColor`
|
|
109
|
+
|
|
110
|
+
## Creating the DB instance
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
import { createRadsDb } from 'rads-db'
|
|
114
|
+
|
|
115
|
+
const db = createRadsDb('db')
|
|
116
|
+
// db.tcUser, db.tcPost, db.tcRole, … — one property per entity (lowerFirst of class name)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## CRUD Methods
|
|
120
|
+
|
|
121
|
+
Every entity exposes the same set of methods on `db.<entityHandle>`:
|
|
122
|
+
|
|
123
|
+
### get — fetch one record
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
const user = await db.tcUser.get({ where: { id: 'user1' } })
|
|
127
|
+
// returns undefined if not found
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### getAll — fetch all matching records
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
const users = await db.tcUser.getAll({ where: { isActive: true } })
|
|
134
|
+
// returns T[]
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### getMany — paginated fetch
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
const page = await db.tcUser.getMany({
|
|
141
|
+
where: { isActive: true },
|
|
142
|
+
maxItemCount: 20,
|
|
143
|
+
cursor: previousPage.cursor, // pass cursor from previous response for next page
|
|
144
|
+
orderByArray: ['name_asc', 'age_desc'],
|
|
145
|
+
})
|
|
146
|
+
// returns { nodes: T[], cursor: string | undefined }
|
|
147
|
+
// cursor === undefined means no more pages
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### getAgg — aggregate queries
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
const agg = await db.tcUser.getAgg({
|
|
154
|
+
where: { isActive: true },
|
|
155
|
+
agg: ['_count', 'age_min', 'age_max', 'age_sum'],
|
|
156
|
+
})
|
|
157
|
+
// agg._count: number; agg.age_min: number | undefined; etc.
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### put — create or update one record (upsert by id)
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
const saved = await db.tcUser.put({ id: 'user1', name: 'Alice', tcRole: { id: 'role1' } })
|
|
164
|
+
// returns the full saved record
|
|
165
|
+
// missing optional fields stay unchanged; null erases the value
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### putMany — batch upsert
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
const saved = await db.tcUser.putMany([
|
|
172
|
+
{ id: 'user1', name: 'Alice', tcRole: { id: 'role1' } },
|
|
173
|
+
{ id: 'user2', name: 'Bob', tcRole: { id: 'role1' } },
|
|
174
|
+
])
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### construct — create in-memory object with defaults + a new UUID
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
const newUser = db.tcUser.construct({ name: 'Draft' })
|
|
181
|
+
// newUser.id is a fresh UUID; defaults from field declarations are applied
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Where Filters
|
|
185
|
+
|
|
186
|
+
Where clauses are fully typed. Every primitive field supports operators via suffix:
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
await db.tcUser.getAll({
|
|
190
|
+
where: {
|
|
191
|
+
name: 'Alice', // exact match
|
|
192
|
+
name_startsWith: 'Al',
|
|
193
|
+
name_istartsWith: 'al', // case-insensitive
|
|
194
|
+
name_contains: 'lic',
|
|
195
|
+
name_icontains: 'LIC',
|
|
196
|
+
name_endsWith: 'ice',
|
|
197
|
+
name_in: ['Alice', 'Bob'],
|
|
198
|
+
name_notIn: ['Charlie'],
|
|
199
|
+
age_gt: 18,
|
|
200
|
+
age_gte: 18,
|
|
201
|
+
age_lt: 65,
|
|
202
|
+
age_lte: 65,
|
|
203
|
+
isActive: true,
|
|
204
|
+
|
|
205
|
+
// Logical combinators
|
|
206
|
+
_and: [{ isActive: true }, { age_gte: 18 }],
|
|
207
|
+
_or: [{ name: 'Alice' }, { name: 'Bob' }],
|
|
208
|
+
_not: { isActive: false },
|
|
209
|
+
|
|
210
|
+
// Nested object filter
|
|
211
|
+
tcRole: { id: 'role1' },
|
|
212
|
+
},
|
|
213
|
+
})
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Include (JOINs)
|
|
217
|
+
|
|
218
|
+
Load related entities inline. Works across different databases/drivers:
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
const post = await db.tcPost.get({
|
|
222
|
+
where: { id: 'post1' },
|
|
223
|
+
include: {
|
|
224
|
+
tcUser: {}, // load full TcUser object
|
|
225
|
+
tcUser: { tcRole: {} }, // nested: load tcUser and its tcRole
|
|
226
|
+
tcComments: { _pick: ['id', 'text'] }, // load only specific fields of relation
|
|
227
|
+
},
|
|
228
|
+
})
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Field selection with `_pick`
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
const users = await db.tcUser.getAll({
|
|
235
|
+
include: { _pick: ['id', 'name'] }, // return only id and name
|
|
236
|
+
})
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## TypeScript utility types
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
import type { Get, Put } from 'rads-db'
|
|
243
|
+
|
|
244
|
+
// Get — shape returned by read operations (all fields non-nullable)
|
|
245
|
+
type UserView = Get<'TcUser'>
|
|
246
|
+
// With relations and field selection:
|
|
247
|
+
type UserWithRole = Get<'TcUser', { tcRole: {}; _pick: ['id', 'name'] }>
|
|
248
|
+
|
|
249
|
+
// Put — shape accepted by write operations (all fields except id are optional; null erases a value)
|
|
250
|
+
type UserWrite = Put<'TcUser'>
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
**Form state pattern** — use `Put` for editable state; `Get` is assignable to `Put`:
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
const data = await db.tcUser.get({ where: { id } })
|
|
257
|
+
const form: Put<'TcUser'> = { ...data } // Get is assignable to Put ✓
|
|
258
|
+
form.age = null // null = erase the field
|
|
259
|
+
await db.tcUser.put(form)
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## Context (second argument to every method)
|
|
263
|
+
|
|
264
|
+
Pass `RadsRequestContext` as the second argument to control behaviour per-request:
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
import type { RadsRequestContext } from 'rads-db'
|
|
268
|
+
|
|
269
|
+
const ctx: RadsRequestContext = {
|
|
270
|
+
getUser: () => ({ id: 'user1', role: 'admin' }),
|
|
271
|
+
dryRun: true, // no changes written; effects returned as logs
|
|
272
|
+
noCache: true, // bypass caching layer
|
|
273
|
+
excludeFeatures: ['softDelete'], // skip specific features
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const result = await db.tcUser.put(data, ctx)
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
## Features
|
|
280
|
+
|
|
281
|
+
Features hook into the get/put pipeline. Pass them to `createRadsDb`:
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
import { createRadsDb } from 'rads-db'
|
|
285
|
+
import softDelete from 'rads-db/features/softDelete'
|
|
286
|
+
import eventSourcing from 'rads-db/features/eventSourcing'
|
|
287
|
+
import cache from 'rads-db/features/cache'
|
|
288
|
+
|
|
289
|
+
const db = createRadsDb('db', {
|
|
290
|
+
features: [
|
|
291
|
+
softDelete(),
|
|
292
|
+
eventSourcing(),
|
|
293
|
+
cache(),
|
|
294
|
+
],
|
|
295
|
+
})
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Built-in features
|
|
299
|
+
|
|
300
|
+
| Feature | Import | What it does |
|
|
301
|
+
|---------|--------|-------------|
|
|
302
|
+
| `softDelete` | `rads-db/features/softDelete` | Filters out records where `isDeleted: true` automatically |
|
|
303
|
+
| `eventSourcing` | `rads-db/features/eventSourcing` | Rebuilds aggregate from event log; entity needs `@precomputed({ preset: 'eventSourcing' })` |
|
|
304
|
+
| `cache` | `rads-db/features/cache` | Adds a caching layer; bypass with `ctx.noCache = true` |
|
|
305
|
+
|
|
306
|
+
### Custom feature
|
|
307
|
+
|
|
308
|
+
```typescript
|
|
309
|
+
import type { RadsFeature } from 'rads-db'
|
|
310
|
+
|
|
311
|
+
const auditLog = (): RadsFeature => ({
|
|
312
|
+
name: 'auditLog',
|
|
313
|
+
afterPut(items, ctx, computedContext) {
|
|
314
|
+
for (const { doc, oldDoc } of items) {
|
|
315
|
+
console.log('changed', computedContext.typeName, doc.id)
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
})
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## Drivers
|
|
322
|
+
|
|
323
|
+
Configure per data-source in `rads.config.ts` or pass to `createRadsDb`:
|
|
324
|
+
|
|
325
|
+
| Import | Storage |
|
|
326
|
+
|--------|---------|
|
|
327
|
+
| `rads-db/drivers/memory` | In-memory (default; great for tests) |
|
|
328
|
+
| `rads-db/drivers/sql` | MSSQL / MySQL / PostgreSQL (via mssql / mysql2 / pg) |
|
|
329
|
+
| `rads-db/drivers/azureCosmos` | Azure Cosmos DB |
|
|
330
|
+
| `rads-db/drivers/azureStorageBlob` | Azure Blob Storage |
|
|
331
|
+
| `rads-db/drivers/indexedDb` | Browser IndexedDB |
|
|
332
|
+
| `rads-db/drivers/restApi` | REST API (used by `createRadsDbClient`) |
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
import sql from 'rads-db/drivers/sql'
|
|
336
|
+
|
|
337
|
+
const db = createRadsDb('db', {
|
|
338
|
+
driver: sql({
|
|
339
|
+
server: 'localhost',
|
|
340
|
+
database: 'mydb',
|
|
341
|
+
authentication: { type: 'default', options: { userName: 'sa', password: 'pass' } },
|
|
342
|
+
}),
|
|
343
|
+
})
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
## Client-side usage (browser)
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
import { createRadsDbClient } from 'rads-db'
|
|
350
|
+
|
|
351
|
+
// Uses fetch to talk to the server REST API; same methods as server-side db
|
|
352
|
+
const db = createRadsDbClient('db', { driver: { baseUrl: '/api' } })
|
|
353
|
+
|
|
354
|
+
const users = await db.tcUser.getAll({ where: { isActive: true } })
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
## REST API / Server endpoints
|
|
358
|
+
|
|
359
|
+
Expose all entities over HTTP using `getRestRoutes`:
|
|
360
|
+
|
|
361
|
+
```typescript
|
|
362
|
+
// h3 / Nitro (Nuxt)
|
|
363
|
+
import { defineEventHandler, getMethod, readBody } from 'h3'
|
|
364
|
+
import { createRadsDb } from 'rads-db'
|
|
365
|
+
import { getRestRoutes } from 'rads-db/integrations/restEndpoints'
|
|
366
|
+
|
|
367
|
+
const db = createRadsDb('db')
|
|
368
|
+
const routes = getRestRoutes({ db, prefix: '/api/' })
|
|
369
|
+
|
|
370
|
+
export default defineEventHandler(async event => {
|
|
371
|
+
const path = event.path.split('?')[0]
|
|
372
|
+
const method = getMethod(event)
|
|
373
|
+
const handler = routes[path]?.[method]
|
|
374
|
+
if (!handler) return
|
|
375
|
+
const body = method !== 'GET' ? await readBody(event) : undefined
|
|
376
|
+
return handler({ body, context: event, headers: {} })
|
|
377
|
+
})
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
Generated routes per entity (e.g. `TcUser` → handle `tcUser`):
|
|
381
|
+
|
|
382
|
+
| Path | Method | Description |
|
|
383
|
+
|------|--------|-------------|
|
|
384
|
+
| `/api/tcUser` | `POST` | `db.tcUser.get(body)` |
|
|
385
|
+
| `/api/tcUser` | `PUT` | `db.tcUser.put(body.data)` |
|
|
386
|
+
| `/api/tcUser/list` | `POST` | `db.tcUser.getMany(body)` |
|
|
387
|
+
| `/api/tcUser/list` | `PUT` | `db.tcUser.putMany(body.data)` |
|
|
388
|
+
| `/api/tcUser/agg` | `POST` | `db.tcUser.getAgg(body)` |
|
|
389
|
+
|
|
390
|
+
## OpenAPI / Swagger
|
|
391
|
+
|
|
392
|
+
```typescript
|
|
393
|
+
import { getOpenApiSpec } from 'rads-db/integrations/getOpenApiSpec'
|
|
394
|
+
|
|
395
|
+
const spec = getOpenApiSpec(
|
|
396
|
+
{ db, prefix: '/api/' },
|
|
397
|
+
{ title: 'My App API', version: '1.0.0' },
|
|
398
|
+
)
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
## Utilities
|
|
402
|
+
|
|
403
|
+
```typescript
|
|
404
|
+
import { merge, diff, cleanEntity } from 'rads-db'
|
|
405
|
+
|
|
406
|
+
merge(target, source) // deep merge source into target
|
|
407
|
+
diff(oldObj, newObj) // returns changed fields only
|
|
408
|
+
cleanEntity(entity) // strips undefined/null fields
|
|
409
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rads-db",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.30",
|
|
4
4
|
"description": "Say goodbye to boilerplate code and hello to efficient and elegant syntax.",
|
|
5
5
|
"author": "",
|
|
6
6
|
"license": "ISC",
|
|
@@ -48,7 +48,8 @@
|
|
|
48
48
|
"drivers",
|
|
49
49
|
"fileUploadDrivers",
|
|
50
50
|
"integrations",
|
|
51
|
-
"features"
|
|
51
|
+
"features",
|
|
52
|
+
"llms.txt"
|
|
52
53
|
],
|
|
53
54
|
"peerDependencies": {
|
|
54
55
|
"@azure/cosmos": ">=3",
|