rads-db 3.0.83 → 3.1.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/dist/config.cjs +636 -0
- package/dist/config.d.ts +23 -0
- package/{integrations/lib.mjs → dist/config.mjs} +45 -3
- package/dist/index.d.ts +4 -433
- package/dist/types-7e792d1f.d.ts +449 -0
- package/integrations/cli.cjs +1 -16
- package/integrations/cli.mjs +1 -16
- package/integrations/node.cjs +54 -62
- package/integrations/node.d.ts +5 -4
- package/integrations/node.mjs +56 -52
- package/package.json +8 -2
- package/integrations/lib.cjs +0 -603
- package/integrations/lib.d.ts +0 -1
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
import { EntityMeta, RadsDb } from '_rads-db';
|
|
2
|
+
|
|
3
|
+
type MaybePromise$1<T> = Promise<T> | T;
|
|
4
|
+
type Change<T> = {
|
|
5
|
+
[K in keyof T]?: T[K] extends any[] ? T[K] : T[K] extends {} ? Change<T[K]> : T[K];
|
|
6
|
+
};
|
|
7
|
+
interface GetManyArgs<EN extends keyof EntityMeta> extends GetArgs<EN> {
|
|
8
|
+
cursor?: string | null;
|
|
9
|
+
maxItemCount?: number;
|
|
10
|
+
orderBy?: string;
|
|
11
|
+
}
|
|
12
|
+
interface VerifyManyArgs<EN extends keyof EntityMeta> extends GetManyArgs<EN> {
|
|
13
|
+
recompute?: string[];
|
|
14
|
+
dryRun?: boolean;
|
|
15
|
+
}
|
|
16
|
+
interface VerifyManyResponse {
|
|
17
|
+
cursor: string | null;
|
|
18
|
+
correctCount: number;
|
|
19
|
+
incorrectCount: number;
|
|
20
|
+
incorrectDocs: {
|
|
21
|
+
id: string;
|
|
22
|
+
diff: any;
|
|
23
|
+
toRemove?: any;
|
|
24
|
+
}[];
|
|
25
|
+
}
|
|
26
|
+
type Where<EN extends keyof EntityMeta> = EntityMeta[EN]['whereType'] & {
|
|
27
|
+
_not?: Where<EN>;
|
|
28
|
+
_and?: Where<EN>[];
|
|
29
|
+
_or?: Where<EN>[];
|
|
30
|
+
};
|
|
31
|
+
interface GetArgs<EN extends keyof EntityMeta> {
|
|
32
|
+
where?: Where<EN>;
|
|
33
|
+
include?: GetArgsInclude<EN>;
|
|
34
|
+
}
|
|
35
|
+
interface GetAggArgs<EN extends keyof EntityMeta> {
|
|
36
|
+
where?: Where<EN>;
|
|
37
|
+
agg: GetAggArgsAgg<EN>;
|
|
38
|
+
}
|
|
39
|
+
type GetAggArgsAgg<EN extends keyof EntityMeta, F extends string = EntityMeta[EN]['aggregates']> = ('_count' | `${F}_min` | `${F}_max` | `${F}_sum`)[];
|
|
40
|
+
type GetManyArgsAny = GetManyArgs<any>;
|
|
41
|
+
type GetArgsAny = GetArgs<any>;
|
|
42
|
+
type GetAggArgsAny = GetAggArgs<any>;
|
|
43
|
+
type VerifyManyArgsAny = VerifyManyArgs<any>;
|
|
44
|
+
type RelationsAndNestedObjects<EN extends keyof EntityMeta> = EntityMeta[EN]['relations'] & EntityMeta[EN]['nestedObjects'];
|
|
45
|
+
type GetArgsInclude<EN extends keyof EntityMeta, R extends keyof RelationsAndNestedObjects<EN> = keyof RelationsAndNestedObjects<EN>> = [R] extends [never] ? {
|
|
46
|
+
_pick?: EntityMeta[EN]['primitives'][];
|
|
47
|
+
} : {
|
|
48
|
+
_pick?: EntityMeta[EN]['primitives'][];
|
|
49
|
+
} & {
|
|
50
|
+
[K in R]?: GetArgsInclude<RelationsAndNestedObjects<EN>[K]['entityName']>;
|
|
51
|
+
};
|
|
52
|
+
type GetAggResponse<EN extends keyof EntityMeta, A extends GetAggArgs<EN>> = {
|
|
53
|
+
[K in A['agg'][0]]: K extends '_count' ? number : number | undefined;
|
|
54
|
+
};
|
|
55
|
+
interface GetManyResponse<EN extends keyof EntityMeta, A extends GetArgs<EN>> {
|
|
56
|
+
nodes: GetResponse<EN, A>[];
|
|
57
|
+
cursor: string | null;
|
|
58
|
+
}
|
|
59
|
+
type GetResponse<EN extends keyof EntityMeta, A extends GetArgs<EN>> = A extends {
|
|
60
|
+
include: any;
|
|
61
|
+
} ? GetResponseInclude<EN, A['include']> : GetResponseNoInclude<EN>;
|
|
62
|
+
type Get<EntityName extends keyof EntityMeta, Include extends keyof EntityMeta[EntityName]['relations'] | GetArgsInclude<EntityName> = {}> = [Include] extends [Record<string, any>] ? GetResponse<EntityName, {
|
|
63
|
+
include: Include;
|
|
64
|
+
}> : GetResponse<EntityName, {
|
|
65
|
+
include: {
|
|
66
|
+
[K in Include]: {};
|
|
67
|
+
};
|
|
68
|
+
}>;
|
|
69
|
+
type RelationData<EN extends keyof EntityMeta, K extends keyof EntityMeta[EN]['relations']> = Pick<EntityMeta[EN]['relations'][K]['entity'], EntityMeta[EN]['relations'][K]['denormFields']>;
|
|
70
|
+
type KeepArray<TMaybeArray, TType> = NonNullable<TMaybeArray> extends any[] ? TType[] : TType;
|
|
71
|
+
type GetResponseInclude<EN extends keyof EntityMeta, I extends GetArgsInclude<EN>> = I extends {
|
|
72
|
+
_pick: string[];
|
|
73
|
+
} ? GetResponseIncludeSelect<EN, I> : {
|
|
74
|
+
[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];
|
|
75
|
+
};
|
|
76
|
+
interface GetResponseIncludeSelect<EN extends keyof EntityMeta, I> {
|
|
77
|
+
}
|
|
78
|
+
type GetResponseNoInclude<EN extends keyof EntityMeta> = {
|
|
79
|
+
[K in keyof EntityMeta[EN]['type']]: K extends keyof EntityMeta[EN]['relations'] ? KeepArray<EntityMeta[EN]['type'][K], RelationData<EN, K>> : EntityMeta[EN]['type'][K];
|
|
80
|
+
};
|
|
81
|
+
type DeepPartialWithNulls<T> = {
|
|
82
|
+
[K in keyof T]?: NonNullable<T[K]> extends any[] ? DeepPartialWithNullsItem<NonNullable<T[K]>[number]>[] : DeepPartialWithNullsItem<NonNullable<T[K]>>;
|
|
83
|
+
};
|
|
84
|
+
type DeepPartialWithNullsItem<T> = T extends {
|
|
85
|
+
id: string;
|
|
86
|
+
} ? {
|
|
87
|
+
id: string;
|
|
88
|
+
} | null : T extends Record<string, any> ? DeepPartialWithNulls<T> | null : T | null;
|
|
89
|
+
type DeepPartial<T> = {
|
|
90
|
+
[K in keyof T]?: NonNullable<T[K]> extends any[] ? DeepPartial<NonNullable<T[K]>[number]>[] : NonNullable<T[K]> extends Record<string, any> ? DeepPartial<T[K]> : T[K];
|
|
91
|
+
};
|
|
92
|
+
/** Indicates that this field is a relation to another entity in the database.
|
|
93
|
+
* Only id will be stored in the database.
|
|
94
|
+
* If you want to store additional fields, please, pass them as the second type argument */
|
|
95
|
+
type Relation<T extends {
|
|
96
|
+
id: any;
|
|
97
|
+
}, K extends Exclude<keyof T, 'id'> = never> = Pick<T, K | 'id'>;
|
|
98
|
+
/** Indicates that this is computed field - all documents that point to this document via Relation<>.
|
|
99
|
+
* Note: this field is not stored in the database at all. Returns up to 100 items and doesn't support pagination
|
|
100
|
+
* If you need more control, please, use separate request instead.
|
|
101
|
+
*/
|
|
102
|
+
type InverseRelation<EN extends keyof EntityMeta, _Field extends keyof EntityMeta[EN]['relations']> = EntityMeta[EN]['type'];
|
|
103
|
+
type PutArgs<T> = {
|
|
104
|
+
id: string;
|
|
105
|
+
} & DeepPartialWithNulls<T>;
|
|
106
|
+
type Put<EntityName extends keyof EntityMeta> = PutArgs<EntityMeta[EntityName]['type']>;
|
|
107
|
+
type DeepKeys<T> = T extends object ? {
|
|
108
|
+
[K in (string | number) & keyof T]: `${`.${K}` | (`${K}` extends `${number}` ? `[${K}]` : never)}${'' | DeepKeys<T[K]>}`;
|
|
109
|
+
}[(string | number) & keyof T] : never;
|
|
110
|
+
interface EntityMethods<E, EN extends keyof EntityMeta> {
|
|
111
|
+
/** Returns object with with random UUID as `id` and prefilled default values */
|
|
112
|
+
construct(defaultValues?: DeepPartial<E>): E;
|
|
113
|
+
/** Used to access underlying mechanism of storage directly.
|
|
114
|
+
* Warning: bypasses all rads features - schema won't be validated, default values won't be filled, etc. */
|
|
115
|
+
driver: Driver;
|
|
116
|
+
get<A extends GetArgs<EN>>(args: A, ctx?: RadsRequestContext): MaybePromise$1<GetResponse<EN, A>>;
|
|
117
|
+
getMany<A extends GetManyArgs<EN>>(args?: A, ctx?: RadsRequestContext): MaybePromise$1<GetManyResponse<EN, A>>;
|
|
118
|
+
getAgg<A extends GetAggArgs<EN>>(args: A, ctx?: RadsRequestContext): MaybePromise$1<GetAggResponse<EN, A>>;
|
|
119
|
+
getAll<A extends GetManyArgs<EN>>(args?: A, ctx?: RadsRequestContext): MaybePromise$1<GetManyResponse<EN, A>['nodes']>;
|
|
120
|
+
put(data: PutArgs<E>, ctx?: RadsRequestContext): MaybePromise$1<GetResponseNoInclude<EN>>;
|
|
121
|
+
putMany(data: PutArgs<E>[], ctx?: RadsRequestContext): MaybePromise$1<GetResponseNoInclude<EN>[]>;
|
|
122
|
+
verifyMany<A extends VerifyManyArgs<EN>>(args?: A, ctx?: RadsRequestContext): MaybePromise$1<VerifyManyResponse>;
|
|
123
|
+
verifyAll<A extends VerifyManyArgs<EN>>(args?: A, ctx?: RadsRequestContext): MaybePromise$1<Pick<VerifyManyResponse, 'correctCount' | 'incorrectCount'>>;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
interface RadsConfig {
|
|
127
|
+
dataSources: Record<string, RadsConfigDataSource>;
|
|
128
|
+
}
|
|
129
|
+
interface RadsConfigDataSource {
|
|
130
|
+
schema: () => MaybePromise<SchemaLoadResult>;
|
|
131
|
+
}
|
|
132
|
+
type MaybePromise<T> = Promise<T> | T;
|
|
133
|
+
type RequiredFields<T, K extends keyof T> = T & Required<Pick<T, K>>;
|
|
134
|
+
type ValidateFieldDecoratorArgs<T> = T extends string ? ValidateStringDecoratorArgs : T extends number ? ValidateNumberDecoratorArgs : ValidateAnyDecoratorArgs;
|
|
135
|
+
interface ValidateEntityDecoratorArgs {
|
|
136
|
+
}
|
|
137
|
+
interface ValidateAnyDecoratorArgs {
|
|
138
|
+
}
|
|
139
|
+
interface ValidateNumberDecoratorArgs {
|
|
140
|
+
isWhole?: boolean;
|
|
141
|
+
min?: number;
|
|
142
|
+
max?: number;
|
|
143
|
+
}
|
|
144
|
+
interface ValidateStringDecoratorArgs {
|
|
145
|
+
preset?: 'text' | 'html' | 'markdown' | 'alpha' | 'alphanum' | 'number' | 'decimalNumber' | 'email' | 'icon' | 'imageUrl' | 'fileUrl' | 'absoluteUrl' | 'relativeUrl' | 'phoneNumber' | 'datetime' | 'date' | 'time' | 'timeInterval' | 'duration';
|
|
146
|
+
regex?: RegExp | string;
|
|
147
|
+
minLength?: number;
|
|
148
|
+
maxLength?: number;
|
|
149
|
+
}
|
|
150
|
+
interface UiFieldDecoratorArgs {
|
|
151
|
+
/** User-friendly name of the entity (singular).
|
|
152
|
+
* Used as editor field label and column name in exports.
|
|
153
|
+
* Defaults to `_.startCase(fieldName)` */
|
|
154
|
+
name?: string;
|
|
155
|
+
/** Icon to display as editor field icon. E.g. `mdi-user`. */
|
|
156
|
+
icon?: string;
|
|
157
|
+
/** Free text - hint to display under editor field in UI */
|
|
158
|
+
hint?: string;
|
|
159
|
+
/** If true, field is hidden from the rads-ui */
|
|
160
|
+
isHidden?: boolean;
|
|
161
|
+
/** If true, field is readonly in the rads-ui */
|
|
162
|
+
isReadonly?: boolean;
|
|
163
|
+
/** If true, field can be edited only during creation */
|
|
164
|
+
isImmutable?: boolean;
|
|
165
|
+
/** TODO: Preferred component to display or edit this value */
|
|
166
|
+
component?: string;
|
|
167
|
+
}
|
|
168
|
+
interface UiDecoratorArgs<T = any> {
|
|
169
|
+
/** User-friendly name of the entity (singular).
|
|
170
|
+
* Defaults to `_.startCase(entityName).split(' ').slice(1).join(' ')` (i.e. removes the prefix) */
|
|
171
|
+
name?: string;
|
|
172
|
+
/** User-friendly name of the entity (plural)
|
|
173
|
+
* Defaults to `pluralize(entityName)` */
|
|
174
|
+
namePlural?: string;
|
|
175
|
+
/** Icon to display near this entity. Defaults to `mdi-file-outline` */
|
|
176
|
+
icon?: string;
|
|
177
|
+
/** Field that contains "name" value.
|
|
178
|
+
* Used by Rads UI to set up default search and display behaviour
|
|
179
|
+
* Defaults to (if field with such name exists) `name` > `title` > `id` */
|
|
180
|
+
nameField?: keyof T;
|
|
181
|
+
/** Some important-to-know fields, like "id" or "code" or "description".
|
|
182
|
+
* Used by Rads UI to display in the list view
|
|
183
|
+
* Defaults to `["id"]` */
|
|
184
|
+
captionFields?: (keyof T)[];
|
|
185
|
+
/** Field that contains value that points to either:
|
|
186
|
+
* - icon (e.g. 'mdi-test')
|
|
187
|
+
* - image URL (e.g. 'https://radsjs.com/icon.png'). Make sure it's not too big (around 200x200px)
|
|
188
|
+
* Used by Rads UI to display it in the list view.
|
|
189
|
+
* Defaults to (if exists) `image` > `photo` > `thumbnail` > `icon` > undefined */
|
|
190
|
+
iconField?: keyof T;
|
|
191
|
+
/** Search operator that's used in simple search by name. Defaults to 'istartsWith' */
|
|
192
|
+
searchOperator?: 'startsWith' | 'istartsWith' | 'contains' | 'icontains';
|
|
193
|
+
/** Defines section name in rads-ui where this entity will be grouped under */
|
|
194
|
+
group?: string;
|
|
195
|
+
/** If true, field is hidden from the rads-ui */
|
|
196
|
+
isHidden?: boolean;
|
|
197
|
+
}
|
|
198
|
+
interface EntityDecoratorArgs {
|
|
199
|
+
driver?: string;
|
|
200
|
+
}
|
|
201
|
+
interface ComputedDecoratorArgs {
|
|
202
|
+
preset: string;
|
|
203
|
+
}
|
|
204
|
+
interface FieldDecoratorArgs {
|
|
205
|
+
relation?: Function;
|
|
206
|
+
}
|
|
207
|
+
type DriverConstructor = (schema: Schema, entity: string) => MinimalDriver;
|
|
208
|
+
interface CreateRadsArgsDrivers {
|
|
209
|
+
default: DriverConstructor;
|
|
210
|
+
[driverName: string]: DriverConstructor;
|
|
211
|
+
}
|
|
212
|
+
interface RestDriverOptions {
|
|
213
|
+
/** @default '/api' */
|
|
214
|
+
baseUrl?: string;
|
|
215
|
+
fetch?: (url: string, options?: {
|
|
216
|
+
body?: any;
|
|
217
|
+
headers?: any;
|
|
218
|
+
method?: string;
|
|
219
|
+
}) => any;
|
|
220
|
+
getHeaders?: (radsDbRequest: {
|
|
221
|
+
args?: any;
|
|
222
|
+
context?: RadsRequestContext;
|
|
223
|
+
}) => Record<string, string> | undefined;
|
|
224
|
+
}
|
|
225
|
+
interface CreateRadsDbArgs {
|
|
226
|
+
schema?: Schema;
|
|
227
|
+
driver?: DriverConstructor | CreateRadsArgsDrivers;
|
|
228
|
+
fileUploadDriver?: FileUploadDriver;
|
|
229
|
+
noComputed?: boolean;
|
|
230
|
+
noCustomDrivers?: boolean;
|
|
231
|
+
computed?: Record<string, Record<string, Function> | {
|
|
232
|
+
_entity?: {
|
|
233
|
+
compute: Function;
|
|
234
|
+
recomputeWhen: Record<string, (args: any) => boolean>;
|
|
235
|
+
};
|
|
236
|
+
}>;
|
|
237
|
+
beforeGet?: (args: GetArgsAny, ctx: RadsRequestContext, context: ComputedContext) => MaybePromise<void>;
|
|
238
|
+
context?: Record<string, any> & Omit<RadsRequestContext, 'dryRun' | 'silent'>;
|
|
239
|
+
features?: RadsFeature[];
|
|
240
|
+
keepNulls?: boolean;
|
|
241
|
+
}
|
|
242
|
+
type CreateRadsDbArgsNormalized = RequiredFields<CreateRadsDbArgs, 'computed' | 'features'> & {
|
|
243
|
+
driver: CreateRadsArgsDrivers;
|
|
244
|
+
};
|
|
245
|
+
type CreateRadsDbClientArgs = Omit<CreateRadsDbArgs, 'noComputed' | 'noCustomDrivers' | 'driver'> & {
|
|
246
|
+
driver?: RestDriverOptions;
|
|
247
|
+
};
|
|
248
|
+
type Schema = Record<string, TypeDefinition>;
|
|
249
|
+
interface SchemaLoadResult {
|
|
250
|
+
schema: Schema;
|
|
251
|
+
entitiesDir?: string;
|
|
252
|
+
}
|
|
253
|
+
type SchemaValidators = Record<string, (item: any) => any>;
|
|
254
|
+
interface TypeDefinition {
|
|
255
|
+
name: string;
|
|
256
|
+
decorators: Record<string, Record<string, any>>;
|
|
257
|
+
comment?: string;
|
|
258
|
+
precomputedFields?: string[];
|
|
259
|
+
computedFields?: string[];
|
|
260
|
+
nestedTypeFields?: string[];
|
|
261
|
+
fields?: Record<string, FieldDefinition>;
|
|
262
|
+
enumValues?: Record<string, EnumDefinition>;
|
|
263
|
+
handle?: string;
|
|
264
|
+
handlePlural?: string;
|
|
265
|
+
isExtending?: string;
|
|
266
|
+
keepHistoryFields?: string[];
|
|
267
|
+
sourceFile?: string;
|
|
268
|
+
}
|
|
269
|
+
interface FileUploadResult {
|
|
270
|
+
url: string;
|
|
271
|
+
}
|
|
272
|
+
interface FileUploadDriver {
|
|
273
|
+
driverName: string;
|
|
274
|
+
client?: any;
|
|
275
|
+
uploadFile(args: FileUploadArgs, ctx?: RadsRequestContext): Promise<FileUploadResult>;
|
|
276
|
+
}
|
|
277
|
+
type RadsDbInstance = RadsDb[keyof RadsDb];
|
|
278
|
+
interface GetRestRoutesOptions {
|
|
279
|
+
/** Instance of RadsDb */
|
|
280
|
+
db: RadsDb[keyof RadsDb];
|
|
281
|
+
/** Prefix to use for generated routes. Defaults to "/api/" */
|
|
282
|
+
prefix?: string;
|
|
283
|
+
/** By default, rest endpoints for manipulating local files are exposed when NODE_ENV === 'development'. Set to true or false to override. */
|
|
284
|
+
exposeFilesystem?: boolean;
|
|
285
|
+
}
|
|
286
|
+
interface GetRestRoutesArgs {
|
|
287
|
+
body?: any;
|
|
288
|
+
context: any;
|
|
289
|
+
headers: Record<string, string | undefined>;
|
|
290
|
+
}
|
|
291
|
+
type GetRestRoutesResponse = Record<string, Partial<Record<string, (args: GetRestRoutesArgs, ctx?: RadsRequestContext) => Promise<any>>>>;
|
|
292
|
+
interface FileUploadArgs {
|
|
293
|
+
blob: Blob;
|
|
294
|
+
fileName: string;
|
|
295
|
+
containerName?: string;
|
|
296
|
+
options?: any;
|
|
297
|
+
}
|
|
298
|
+
interface MinimalDriver {
|
|
299
|
+
driverName: string;
|
|
300
|
+
/** raw underlying data access service - e.g. sql client or azure storage client */
|
|
301
|
+
client?: any;
|
|
302
|
+
putMany: (item: Record<string, any>[], ctx?: RadsRequestContext) => MaybePromise<void>;
|
|
303
|
+
getMany: (args?: GetManyArgsAny, ctx?: RadsRequestContext) => MaybePromise<{
|
|
304
|
+
nodes: Record<string, any>[];
|
|
305
|
+
cursor: string | null;
|
|
306
|
+
}>;
|
|
307
|
+
get?: (args: GetArgsAny, ctx?: RadsRequestContext) => MaybePromise<Record<string, any> | null>;
|
|
308
|
+
getAll?: (args: GetManyArgsAny, ctx?: RadsRequestContext) => MaybePromise<Record<string, any>[]>;
|
|
309
|
+
getAgg?: (args: GetAggArgsAny, ctx?: RadsRequestContext) => MaybePromise<Record<string, any>>;
|
|
310
|
+
deleteMany?: (args: GetManyArgsAny, ctx?: RadsRequestContext) => MaybePromise<{
|
|
311
|
+
nodes: Record<string, any>[];
|
|
312
|
+
cursor: string | null;
|
|
313
|
+
}>;
|
|
314
|
+
deleteAll?: (args: GetManyArgsAny, ctx?: RadsRequestContext) => MaybePromise<Record<string, any>[]>;
|
|
315
|
+
put?: (data: Record<string, any>, ctx?: RadsRequestContext) => MaybePromise<void>;
|
|
316
|
+
verifyMany?: (args?: VerifyManyArgsAny, ctx?: RadsRequestContext) => MaybePromise<VerifyManyResponse>;
|
|
317
|
+
}
|
|
318
|
+
type Driver = Required<Omit<MinimalDriver, 'verifyMany' | 'client'>> & Pick<MinimalDriver, 'verifyMany' | 'client'>;
|
|
319
|
+
interface FieldDefinition {
|
|
320
|
+
name: string;
|
|
321
|
+
type: string;
|
|
322
|
+
defaultValue?: any;
|
|
323
|
+
defaultValueClass?: string;
|
|
324
|
+
defaultValueCopyFrom?: string;
|
|
325
|
+
isRequired?: boolean;
|
|
326
|
+
isArray?: boolean;
|
|
327
|
+
isRelation?: boolean;
|
|
328
|
+
isInverseRelation?: boolean;
|
|
329
|
+
isChange?: boolean;
|
|
330
|
+
inverseRelationField?: string;
|
|
331
|
+
relationDenormFields?: string[];
|
|
332
|
+
comment?: string;
|
|
333
|
+
decorators?: Record<string, Record<string, any>>;
|
|
334
|
+
}
|
|
335
|
+
interface EnumDefinition {
|
|
336
|
+
name: string;
|
|
337
|
+
comment?: string;
|
|
338
|
+
decorators?: Record<string, Record<string, any>>;
|
|
339
|
+
}
|
|
340
|
+
interface ComputedContextGlobal {
|
|
341
|
+
db: RadsDbInstance;
|
|
342
|
+
schema: Schema;
|
|
343
|
+
validators: SchemaValidators;
|
|
344
|
+
options: CreateRadsDbArgs;
|
|
345
|
+
drivers: Record<string, Driver>;
|
|
346
|
+
effects: Record<string, PutEffect[]>;
|
|
347
|
+
}
|
|
348
|
+
interface ComputedContext extends ComputedContextGlobal {
|
|
349
|
+
typeName: string;
|
|
350
|
+
handle: string;
|
|
351
|
+
}
|
|
352
|
+
interface RadsHookDoc {
|
|
353
|
+
/** Updated document that will be saved to the database */
|
|
354
|
+
doc: any;
|
|
355
|
+
/** Previous version of document - i.e. one that is currently in the database (before saving) */
|
|
356
|
+
oldDoc: any;
|
|
357
|
+
/** If current entity is event sourcing aggregate, you can access all events in the chronological order */
|
|
358
|
+
events?: any;
|
|
359
|
+
}
|
|
360
|
+
interface PutEffect {
|
|
361
|
+
beforePut?: (computedContext: ComputedContext, docs: RadsHookDoc[], ctx: RadsRequestContext) => MaybePromise<any>;
|
|
362
|
+
afterPut: (computedContext: ComputedContext, docs: RadsHookDoc[], beforePutResult: any, ctx: RadsRequestContext) => MaybePromise<any>;
|
|
363
|
+
}
|
|
364
|
+
interface RestFileUploadDriverOptions {
|
|
365
|
+
/** @default '/api' */
|
|
366
|
+
baseUrl?: string;
|
|
367
|
+
fetch?: (url: string, options?: {
|
|
368
|
+
body?: any;
|
|
369
|
+
headers?: any;
|
|
370
|
+
method?: string;
|
|
371
|
+
}) => any;
|
|
372
|
+
/** If true, "multipart/form-data" body will be used (sometimes causes problems in serverless environments).
|
|
373
|
+
* Otherwise, "application/json" body (with base64-encoded binary content) will be used */
|
|
374
|
+
useFormData?: boolean;
|
|
375
|
+
}
|
|
376
|
+
type GenerateClientOptions = GenerateClientNormalizedOptions;
|
|
377
|
+
interface GenerateClientNormalizedOptions {
|
|
378
|
+
/** For servers - path to your entities directory, e.g. "./entities" */
|
|
379
|
+
entitiesDir?: string;
|
|
380
|
+
/** For client-only integrations - base url of your API, e.g. "https://my-website/api" */
|
|
381
|
+
apiUrl?: string;
|
|
382
|
+
}
|
|
383
|
+
interface RadsVitePluginOptions extends GenerateClientOptions {
|
|
384
|
+
/** To simplify debugging, makes modules available on window.
|
|
385
|
+
* For example, "/src/stores/userStore.ts" will be available at window.stores.userStore.
|
|
386
|
+
* You can pass the regex - if filename satisfies it, it will be exposed.
|
|
387
|
+
* Path inside window is determined by first matching group in said regex.
|
|
388
|
+
* If `true`, default regex is used - `/\/src\/((services|stores)\/(.+))\.[tj]s$/` */
|
|
389
|
+
exposeModulesInDev?: boolean | RegExp;
|
|
390
|
+
}
|
|
391
|
+
interface RadsRequestContext {
|
|
392
|
+
_logs?: any[];
|
|
393
|
+
log?: (requestInfo: any) => void;
|
|
394
|
+
getUser?: () => {
|
|
395
|
+
id: string;
|
|
396
|
+
role: string;
|
|
397
|
+
} | undefined;
|
|
398
|
+
/** If true, no changes made to the database - instead, all changes are returned as logs */
|
|
399
|
+
dryRun?: boolean;
|
|
400
|
+
/** (Not supported yet) If true, updatedAt/updatedBy properties are not updated, and log records are not created */
|
|
401
|
+
silent?: boolean;
|
|
402
|
+
/** Mostly for internal use. When entity name is specified, indicates that calling put* on this entity is allowed, even if it's marked as @precomputed or @readonly */
|
|
403
|
+
skipPrecomputedCheckFor?: string;
|
|
404
|
+
/** Method that was called - get, getAll, getMany, getAgg, put, putMany, etc */
|
|
405
|
+
method?: 'get' | 'getAll' | 'getMany' | 'getAgg' | 'put' | 'putMany' | 'deleteMany' | 'deleteAll';
|
|
406
|
+
/** if true, all cache layers will be bypassed */
|
|
407
|
+
noCache?: boolean;
|
|
408
|
+
[key: string]: any;
|
|
409
|
+
}
|
|
410
|
+
interface FileSystemNode {
|
|
411
|
+
path: string;
|
|
412
|
+
size?: number;
|
|
413
|
+
type: 'blob' | 'tree';
|
|
414
|
+
}
|
|
415
|
+
interface WhereJsonContains {
|
|
416
|
+
/** Checks for existence of this path inside json object
|
|
417
|
+
* Example: "meta.countries[0].name". */
|
|
418
|
+
path: string;
|
|
419
|
+
/** Checks if value from "path" is exactly null. Note: it is not the same as not having the key in the object. */
|
|
420
|
+
isNull?: boolean;
|
|
421
|
+
/** Checks if value from "path" equals provided value */
|
|
422
|
+
value?: number | string | boolean;
|
|
423
|
+
}
|
|
424
|
+
type RadsUiSlotName = 'entityListActions' | 'entityActions' | 'fieldActions';
|
|
425
|
+
interface RadsUiSlotDefinition {
|
|
426
|
+
entityName?: string | {
|
|
427
|
+
regExp: string;
|
|
428
|
+
};
|
|
429
|
+
fieldName?: string | {
|
|
430
|
+
regExp: string;
|
|
431
|
+
};
|
|
432
|
+
label: string;
|
|
433
|
+
icon?: string;
|
|
434
|
+
endpoint: string;
|
|
435
|
+
}
|
|
436
|
+
interface RadsFeature {
|
|
437
|
+
name: string;
|
|
438
|
+
radsUiSlots?: Record<RadsUiSlotName, RadsUiSlotDefinition[]>;
|
|
439
|
+
init?: (db: Record<string, any>, context: ComputedContextGlobal) => void;
|
|
440
|
+
enhanceEntityMethods?: (context: ComputedContext, entityMethodsObj: EntityMethods<any, any>) => void;
|
|
441
|
+
beforeGet?: (args: GetArgsAny, ctx: RadsRequestContext, context: ComputedContext) => MaybePromise<any>;
|
|
442
|
+
afterGet?: (items: any[], args: GetArgsAny, ctx: RadsRequestContext, context: ComputedContext) => MaybePromise<void>;
|
|
443
|
+
beforePut?: (items: RadsHookDoc[], ctx: RadsRequestContext, computedContext: ComputedContext) => void;
|
|
444
|
+
afterPut?: (items: RadsHookDoc[], ctx: RadsRequestContext, computedContext: ComputedContext) => void;
|
|
445
|
+
beforeUploadFile?: (args: FileUploadArgs) => MaybePromise<any>;
|
|
446
|
+
afterUploadFile?: (result: FileUploadResult, args: FileUploadArgs) => MaybePromise<void>;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
export { GetAggArgsAgg as $, GenerateClientOptions as A, GenerateClientNormalizedOptions as B, ComputedDecoratorArgs as C, DriverConstructor as D, EntityDecoratorArgs as E, FieldDecoratorArgs as F, GetRestRoutesOptions as G, RadsVitePluginOptions as H, FileSystemNode as I, RadsUiSlotName as J, RadsUiSlotDefinition as K, RadsFeature as L, MinimalDriver as M, Change as N, GetManyArgs as O, PutEffect as P, VerifyManyArgs as Q, RadsRequestContext as R, Schema as S, TypeDefinition as T, UiDecoratorArgs as U, ValidateEntityDecoratorArgs as V, WhereJsonContains as W, VerifyManyResponse as X, Where as Y, GetArgs as Z, GetAggArgs as _, UiFieldDecoratorArgs as a, GetManyArgsAny as a0, GetArgsAny as a1, GetAggArgsAny as a2, VerifyManyArgsAny as a3, GetArgsInclude as a4, GetAggResponse as a5, GetManyResponse as a6, GetResponse as a7, Get as a8, GetResponseInclude as a9, GetResponseIncludeSelect as aa, GetResponseNoInclude as ab, DeepPartialWithNulls as ac, DeepPartialWithNullsItem as ad, DeepPartial as ae, Relation as af, InverseRelation as ag, PutArgs as ah, Put as ai, DeepKeys as aj, EntityMethods as ak, ValidateFieldDecoratorArgs as b, Driver as c, ComputedContext as d, CreateRadsDbArgs as e, CreateRadsDbClientArgs as f, RadsConfig as g, RadsConfigDataSource as h, RequiredFields as i, ValidateStringDecoratorArgs as j, CreateRadsArgsDrivers as k, RestDriverOptions as l, CreateRadsDbArgsNormalized as m, SchemaLoadResult as n, SchemaValidators as o, FileUploadResult as p, FileUploadDriver as q, RadsDbInstance as r, GetRestRoutesArgs as s, GetRestRoutesResponse as t, FileUploadArgs as u, FieldDefinition as v, EnumDefinition as w, ComputedContextGlobal as x, RadsHookDoc as y, RestFileUploadDriverOptions as z };
|
package/integrations/cli.cjs
CHANGED
|
@@ -1,20 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
|
-
var _nodeUtil = require("node:util");
|
|
5
4
|
var _node = require("./node.cjs");
|
|
6
|
-
|
|
7
|
-
options: {
|
|
8
|
-
entitiesDir: {
|
|
9
|
-
type: "string",
|
|
10
|
-
short: "d"
|
|
11
|
-
},
|
|
12
|
-
apiUrl: {
|
|
13
|
-
type: "string",
|
|
14
|
-
short: "u"
|
|
15
|
-
}
|
|
16
|
-
},
|
|
17
|
-
allowPositionals: false,
|
|
18
|
-
strict: true
|
|
19
|
-
});
|
|
20
|
-
(0, _node.generateClient)(args.values);
|
|
5
|
+
(0, _node.generateClient)();
|
package/integrations/cli.mjs
CHANGED
|
@@ -1,18 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { parseArgs } from "node:util";
|
|
3
2
|
import { generateClient } from "./node.mjs";
|
|
4
|
-
|
|
5
|
-
options: {
|
|
6
|
-
entitiesDir: {
|
|
7
|
-
type: "string",
|
|
8
|
-
short: "d"
|
|
9
|
-
},
|
|
10
|
-
apiUrl: {
|
|
11
|
-
type: "string",
|
|
12
|
-
short: "u"
|
|
13
|
-
}
|
|
14
|
-
},
|
|
15
|
-
allowPositionals: false,
|
|
16
|
-
strict: true
|
|
17
|
-
});
|
|
18
|
-
generateClient(args.values);
|
|
3
|
+
generateClient();
|
package/integrations/node.cjs
CHANGED
|
@@ -5,18 +5,18 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.generateClient = generateClient;
|
|
7
7
|
exports.getEntityTypesStrFromSchema = getEntityTypesStrFromSchema;
|
|
8
|
-
exports.
|
|
9
|
-
exports.
|
|
8
|
+
exports.getIndexDtsInner = getIndexDtsInner;
|
|
9
|
+
exports.getSchemas = getSchemas;
|
|
10
10
|
var _promises = _interopRequireDefault(require("node:fs/promises"));
|
|
11
11
|
var _nodeFs = _interopRequireDefault(require("node:fs"));
|
|
12
12
|
var _nodePath = _interopRequireDefault(require("node:path"));
|
|
13
13
|
var _lodash = _interopRequireDefault(require("lodash"));
|
|
14
|
-
var
|
|
14
|
+
var _jiti = require("jiti");
|
|
15
15
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
16
|
-
async function generateClient(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const
|
|
16
|
+
async function generateClient() {
|
|
17
|
+
console.time('"node_modules/_rads-db" generated in');
|
|
18
|
+
const config = await loadConfig();
|
|
19
|
+
const schemas = await getSchemas(config);
|
|
20
20
|
const nodeModulesPath = _nodePath.default.resolve("./node_modules");
|
|
21
21
|
const radsDbPath = _nodePath.default.join(nodeModulesPath, "./_rads-db");
|
|
22
22
|
if (!_nodeFs.default.existsSync(nodeModulesPath)) {
|
|
@@ -25,14 +25,15 @@ async function generateClient(options) {
|
|
|
25
25
|
if (!_nodeFs.default.existsSync(radsDbPath)) {
|
|
26
26
|
await _promises.default.mkdir(radsDbPath);
|
|
27
27
|
}
|
|
28
|
+
const schemasOnly = _lodash.default.mapValues(schemas, x => x.schema);
|
|
28
29
|
const indexJsText = `
|
|
29
30
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
-
exports.
|
|
31
|
+
exports.schemas=${JSON.stringify(schemasOnly)}
|
|
31
32
|
`.trim();
|
|
32
33
|
const indexMjsText = `
|
|
33
|
-
export const
|
|
34
|
+
export const schemas = ${JSON.stringify(schemasOnly)}
|
|
34
35
|
`.trim();
|
|
35
|
-
const indexDtsText = getIndexDts(
|
|
36
|
+
const indexDtsText = getIndexDts(schemas, config);
|
|
36
37
|
await _promises.default.writeFile(_nodePath.default.join(radsDbPath, "./index.js"), indexJsText);
|
|
37
38
|
await _promises.default.writeFile(_nodePath.default.join(radsDbPath, "./index.mjs"), indexMjsText);
|
|
38
39
|
await _promises.default.writeFile(_nodePath.default.join(radsDbPath, "./index.d.ts"), indexDtsText);
|
|
@@ -43,71 +44,62 @@ export const schema = ${JSON.stringify(schema)}
|
|
|
43
44
|
main: "./index.js",
|
|
44
45
|
module: "./index.mjs",
|
|
45
46
|
types: "./index.d.ts"
|
|
46
|
-
// browser: 'index-browser.js',
|
|
47
47
|
}, null, 2));
|
|
48
|
-
console.timeEnd("
|
|
48
|
+
console.timeEnd('"node_modules/_rads-db" generated in');
|
|
49
49
|
}
|
|
50
|
-
async function
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (!_nodeFs.default.existsSync(entitiesDir)) await _promises.default.mkdir(entitiesDir, {
|
|
58
|
-
recursive: true
|
|
59
|
-
});
|
|
60
|
-
const response = await _promises.default.readdir(entitiesDir, {
|
|
61
|
-
withFileTypes: true
|
|
62
|
-
});
|
|
63
|
-
const entities = {};
|
|
64
|
-
for (const file of response) {
|
|
65
|
-
if (!file.isFile()) continue;
|
|
66
|
-
if (!file.name.endsWith(".ts")) continue;
|
|
67
|
-
const text = await _promises.default.readFile(_nodePath.default.resolve(entitiesDir, file.name), "utf-8");
|
|
68
|
-
entities[file.name.slice(0, -3)] = text;
|
|
69
|
-
}
|
|
70
|
-
schema = (0, _lib.parseSchema)(entities);
|
|
71
|
-
} else if (apiUrl) {
|
|
72
|
-
const url = new URL("radsTunnel", apiUrl);
|
|
73
|
-
const fetchResponse = await fetch(url.href, {
|
|
74
|
-
method: "POST",
|
|
75
|
-
headers: {
|
|
76
|
-
"content-type": "application/json"
|
|
77
|
-
},
|
|
78
|
-
body: JSON.stringify({
|
|
79
|
-
method: "_schema"
|
|
80
|
-
})
|
|
50
|
+
async function loadConfig() {
|
|
51
|
+
const jiti = (0, _jiti.createJiti)(process.cwd());
|
|
52
|
+
const paths = ["./rads.config.ts", "./rads.config.js"];
|
|
53
|
+
let config;
|
|
54
|
+
for (const p of paths) {
|
|
55
|
+
config = await jiti.import(p, {
|
|
56
|
+
try: true
|
|
81
57
|
});
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
58
|
+
if (config) break;
|
|
59
|
+
}
|
|
60
|
+
if (!config) {
|
|
61
|
+
throw new Error('File "./rads.config.ts" was not found. Please make sure it is present in the root of your project.');
|
|
62
|
+
}
|
|
63
|
+
return config;
|
|
64
|
+
}
|
|
65
|
+
async function getSchemas(config) {
|
|
66
|
+
const result = {};
|
|
67
|
+
for (const key in config.dataSources) {
|
|
68
|
+
if (!config.dataSources[key].schema) {
|
|
69
|
+
throw new Error(`Data source "${key}" does not have a schema. Please, specify it in rads.config.ts`);
|
|
85
70
|
}
|
|
86
|
-
|
|
87
|
-
|
|
71
|
+
result[key] = await config.dataSources[key].schema();
|
|
72
|
+
}
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
function getIndexDts(schemas, config) {
|
|
76
|
+
const parts = [];
|
|
77
|
+
const rootTypeFields = [];
|
|
78
|
+
for (const key in schemas) {
|
|
79
|
+
let rootTypeName = _lodash.default.camelCase(key);
|
|
80
|
+
rootTypeName = rootTypeName[0].toUpperCase() + rootTypeName.slice(1);
|
|
81
|
+
const indexDtsStr = getIndexDtsInner(schemas[key].schema, rootTypeName, schemas[key].entitiesDir);
|
|
82
|
+
rootTypeFields.push(`${key}: ${rootTypeName}`);
|
|
83
|
+
parts.push(indexDtsStr);
|
|
88
84
|
}
|
|
89
|
-
|
|
85
|
+
parts.push(`
|
|
86
|
+
export interface RadsDb {
|
|
87
|
+
${rootTypeFields.join("\n")}
|
|
90
88
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (options.entitiesDir && options.apiUrl) throw new Error('You cannot specify both "apiUrl" and "entitiesDir". Choose one.');
|
|
94
|
-
if (options.apiUrl) return options;
|
|
95
|
-
return {
|
|
96
|
-
entitiesDir: "./entities",
|
|
97
|
-
...options
|
|
98
|
-
};
|
|
89
|
+
`.trim());
|
|
90
|
+
return parts.join("\n\n");
|
|
99
91
|
}
|
|
100
|
-
function
|
|
92
|
+
function getIndexDtsInner(schema, rootTypeName, entitiesDir) {
|
|
101
93
|
const imports = [];
|
|
102
94
|
const rootFields = [];
|
|
103
95
|
const whereTypes = [];
|
|
104
96
|
const entityMeta = [];
|
|
105
|
-
const schemaTypesStr =
|
|
97
|
+
const schemaTypesStr = entitiesDir ? "" : getEntityTypesStrFromSchema(schema);
|
|
106
98
|
for (const key in schema) {
|
|
107
99
|
const type = schema[key];
|
|
108
100
|
if (!type.fields) continue;
|
|
109
|
-
if (
|
|
110
|
-
imports.push(`import type { ${type.name} } from '../../${
|
|
101
|
+
if (entitiesDir && type.sourceFile) {
|
|
102
|
+
imports.push(`import type { ${type.name} } from '../../${entitiesDir}/${type.sourceFile}'`);
|
|
111
103
|
}
|
|
112
104
|
if (type.decorators?.entity) {
|
|
113
105
|
rootFields.push(`${type.handle}: EntityMethods<${type.name}, '${type.name}'>`);
|
|
@@ -161,7 +153,7 @@ interface Reference_Where {
|
|
|
161
153
|
|
|
162
154
|
${whereTypes.join("\n\n")}
|
|
163
155
|
|
|
164
|
-
export interface
|
|
156
|
+
export interface ${rootTypeName} {
|
|
165
157
|
_schema: any
|
|
166
158
|
uploadFile: (args: FileUploadArgs, ctx?: RadsRequestContext) => Promise<{ url: string }>
|
|
167
159
|
${rootFields.join("\n")}
|
package/integrations/node.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
export declare function
|
|
4
|
-
export declare function
|
|
1
|
+
import type { RadsConfig, Schema } from '@/types';
|
|
2
|
+
/** Functions in this file are used by CLI and all other integrations */
|
|
3
|
+
export declare function generateClient(): Promise<void>;
|
|
4
|
+
export declare function getSchemas(config: RadsConfig): Promise<Record<string, SchemaLoadResult>>;
|
|
5
|
+
export declare function getIndexDtsInner(schema: Schema, rootTypeName: string, entitiesDir?: string): string;
|
|
5
6
|
export declare function getEntityTypesStrFromSchema(schema: Schema): string;
|