@monorise/base 4.1.0-dev.2 → 4.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/index.d.ts +54 -110
- package/dist/index.js +1 -3
- package/dist/index.js.map +1 -1
- package/package.json +6 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,53 +1,10 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
|
|
3
|
-
type WhereOperator = {
|
|
4
|
-
$eq: string | number | boolean;
|
|
5
|
-
} | {
|
|
6
|
-
$ne: string | number | boolean;
|
|
7
|
-
} | {
|
|
8
|
-
$gt: number;
|
|
9
|
-
} | {
|
|
10
|
-
$lt: number;
|
|
11
|
-
} | {
|
|
12
|
-
$gte: number;
|
|
13
|
-
} | {
|
|
14
|
-
$lte: number;
|
|
15
|
-
} | {
|
|
16
|
-
$exists: boolean;
|
|
17
|
-
} | {
|
|
18
|
-
$beginsWith: string;
|
|
19
|
-
};
|
|
20
|
-
type WhereClause = WhereOperator | string | number | boolean;
|
|
21
|
-
type WhereConditions = Record<string, WhereClause>;
|
|
22
|
-
type AdjustmentConditionFn<B extends z.ZodRawShape = z.ZodRawShape> = (data: Partial<z.infer<z.ZodObject<B>>>, adjustments: Record<string, number>) => WhereConditions;
|
|
23
|
-
type AdjustmentCondition<B extends z.ZodRawShape = z.ZodRawShape> = WhereConditions | AdjustmentConditionFn<B>;
|
|
24
|
-
type UpdateConditionFn<B extends z.ZodRawShape = z.ZodRawShape> = (data: Partial<z.infer<z.ZodObject<B>>>) => WhereConditions;
|
|
25
|
-
type UpdateCondition<B extends z.ZodRawShape = z.ZodRawShape> = WhereConditions | UpdateConditionFn<B>;
|
|
26
|
-
|
|
27
3
|
declare enum Entity {
|
|
28
4
|
}
|
|
29
5
|
interface EntitySchemaMap {
|
|
30
6
|
[key: string]: Record<string, any>;
|
|
31
7
|
}
|
|
32
|
-
/**
|
|
33
|
-
* @description Configuration for a mutual relationship between two entities.
|
|
34
|
-
* Defines the schema for mutualData validation. Define once, reference from both entity configs.
|
|
35
|
-
*
|
|
36
|
-
* @example
|
|
37
|
-
* ```ts
|
|
38
|
-
* const enrollmentMutual = createMutualConfig({
|
|
39
|
-
* entities: [Entity.STUDENT, Entity.COURSE],
|
|
40
|
-
* mutualDataSchema: z.object({
|
|
41
|
-
* role: z.enum(['student', 'auditor']),
|
|
42
|
-
* enrolledAt: z.string().datetime(),
|
|
43
|
-
* }),
|
|
44
|
-
* });
|
|
45
|
-
* ```
|
|
46
|
-
*/
|
|
47
|
-
interface MutualConfig<MD extends z.ZodRawShape = z.ZodRawShape> {
|
|
48
|
-
entities: [Entity, Entity];
|
|
49
|
-
mutualDataSchema: z.ZodObject<MD>;
|
|
50
|
-
}
|
|
51
8
|
type DraftEntity<T extends Entity = Entity> = T extends keyof EntitySchemaMap ? EntitySchemaMap[T] : never;
|
|
52
9
|
type NumericFields<T> = {
|
|
53
10
|
[K in keyof T as T[K] extends number ? K : never]?: number;
|
|
@@ -152,11 +109,6 @@ interface MonoriseEntityConfig<T extends Entity = Entity, B extends z.ZodRawShap
|
|
|
152
109
|
* @returns the final state of `mutualData` to be stored in the mutual record. Must be an object.
|
|
153
110
|
*/
|
|
154
111
|
mutualDataProcessor?: (mutualIds: string[], currentMutual: any, customContext?: Record<string, any>) => Record<string, any>;
|
|
155
|
-
/**
|
|
156
|
-
* @description (Optional) Reference to a mutual config created by `createMutualConfig`.
|
|
157
|
-
* Provides mutualData schema validation for create/update operations on this mutual relationship.
|
|
158
|
-
*/
|
|
159
|
-
mutual?: MutualConfig;
|
|
160
112
|
};
|
|
161
113
|
};
|
|
162
114
|
/**
|
|
@@ -261,13 +213,53 @@ interface MonoriseEntityConfig<T extends Entity = Entity, B extends z.ZodRawShap
|
|
|
261
213
|
sortValue?: string;
|
|
262
214
|
}[];
|
|
263
215
|
}[];
|
|
216
|
+
/**
|
|
217
|
+
* @description (Optional) Configure a DynamoDB TTL for this entity. When set, `expiresAt`
|
|
218
|
+
* is computed via `processor` on create, and recomputed on every update/upsert. Once past,
|
|
219
|
+
* DynamoDB automatically deletes the item (and its derived index rows, since they all carry
|
|
220
|
+
* the same `expiresAt`).
|
|
221
|
+
*
|
|
222
|
+
* `processor` returning `undefined` means "no TTL" for that record. On updates, an `undefined`
|
|
223
|
+
* result leaves any existing `expiresAt` untouched rather than clearing it.
|
|
224
|
+
*
|
|
225
|
+
* @example
|
|
226
|
+
* ```ts
|
|
227
|
+
* // fixed 30-day TTL from creation/each update
|
|
228
|
+
* ttl: {
|
|
229
|
+
* processor: () => Math.floor(Date.now() / 1000) + 30 * 24 * 60 * 60,
|
|
230
|
+
* }
|
|
231
|
+
* ```
|
|
232
|
+
*
|
|
233
|
+
* @example
|
|
234
|
+
* ```ts
|
|
235
|
+
* // data-driven TTL, eg. expire when a subscription ends
|
|
236
|
+
* ttl: {
|
|
237
|
+
* processor: (entity) => {
|
|
238
|
+
* return entity.data.subscriptionEndsAt
|
|
239
|
+
* ? new Date(entity.data.subscriptionEndsAt)
|
|
240
|
+
* : undefined;
|
|
241
|
+
* },
|
|
242
|
+
* }
|
|
243
|
+
* ```
|
|
244
|
+
*/
|
|
245
|
+
ttl?: {
|
|
246
|
+
/**
|
|
247
|
+
* @description Returns the expiry as epoch seconds (absolute) or a `Date`.
|
|
248
|
+
* Return `undefined` for no expiry.
|
|
249
|
+
*/
|
|
250
|
+
processor: (entity: {
|
|
251
|
+
entityId: string;
|
|
252
|
+
entityType: string;
|
|
253
|
+
data: Record<string, any>;
|
|
254
|
+
createdAt: string;
|
|
255
|
+
updatedAt: string;
|
|
256
|
+
}) => number | Date | undefined;
|
|
257
|
+
};
|
|
264
258
|
/**
|
|
265
259
|
* @description (Optional) Constraints for `adjustEntity` operations.
|
|
266
260
|
* When adjusting numeric fields, these constraints are enforced at the database level.
|
|
267
261
|
* If an adjustment would violate a constraint, the operation is rejected.
|
|
268
262
|
*
|
|
269
|
-
* @deprecated Use `conditions` instead. Will be removed in a future version.
|
|
270
|
-
*
|
|
271
263
|
* @example
|
|
272
264
|
* ```ts
|
|
273
265
|
* {
|
|
@@ -303,59 +295,10 @@ interface MonoriseEntityConfig<T extends Entity = Entity, B extends z.ZodRawShap
|
|
|
303
295
|
};
|
|
304
296
|
};
|
|
305
297
|
};
|
|
306
|
-
/**
|
|
307
|
-
* @description Named conditions for adjustEntity operations.
|
|
308
|
-
* Each condition is either a static `WhereConditions` object or a function
|
|
309
|
-
* `(data, adjustments) => WhereConditions` that receives the entity's current data
|
|
310
|
-
* and the adjustment deltas.
|
|
311
|
-
*
|
|
312
|
-
* When defined, `$condition` is **required** in the adjustEntity request body.
|
|
313
|
-
* The client sends a condition name (string), the server resolves it to a
|
|
314
|
-
* DynamoDB ConditionExpression.
|
|
315
|
-
*
|
|
316
|
-
* @example
|
|
317
|
-
* ```ts
|
|
318
|
-
* {
|
|
319
|
-
* adjustmentConditions: {
|
|
320
|
-
* withdraw: (data, adjustments) => ({
|
|
321
|
-
* balance: { $gte: (data.minBalance ?? 0) + Math.abs(adjustments?.balance ?? 0) },
|
|
322
|
-
* }),
|
|
323
|
-
* deposit: (data, adjustments) => ({
|
|
324
|
-
* balance: { $lte: 1000000 - (adjustments.balance ?? 0) },
|
|
325
|
-
* }),
|
|
326
|
-
* }
|
|
327
|
-
* }
|
|
328
|
-
* ```
|
|
329
|
-
*/
|
|
330
|
-
adjustmentConditions?: {
|
|
331
|
-
[conditionName: string]: WhereConditions | ((data: Partial<z.infer<z.ZodObject<B>>>, adjustments: Record<string, number>) => WhereConditions);
|
|
332
|
-
};
|
|
333
|
-
/**
|
|
334
|
-
* @description Named conditions for updateEntity operations.
|
|
335
|
-
* Each condition is either a static `WhereConditions` object or a function
|
|
336
|
-
* `(data) => WhereConditions` that receives the entity's current data.
|
|
337
|
-
*
|
|
338
|
-
* `$condition` is always **optional** for updateEntity.
|
|
339
|
-
* The client sends a condition name (string), the server resolves it to a
|
|
340
|
-
* DynamoDB ConditionExpression. Replaces raw `$where` (deprecated).
|
|
341
|
-
*
|
|
342
|
-
* @example
|
|
343
|
-
* ```ts
|
|
344
|
-
* {
|
|
345
|
-
* updateConditions: {
|
|
346
|
-
* publish: { status: { $eq: 'draft' } },
|
|
347
|
-
* archive: (data) => ({ status: { $ne: 'archived' } }),
|
|
348
|
-
* }
|
|
349
|
-
* }
|
|
350
|
-
* ```
|
|
351
|
-
*/
|
|
352
|
-
updateConditions?: {
|
|
353
|
-
[conditionName: string]: WhereConditions | ((data: Partial<z.infer<z.ZodObject<B>>>) => WhereConditions);
|
|
354
|
-
};
|
|
355
298
|
}
|
|
356
299
|
|
|
357
300
|
declare const createEntityConfig: <T extends Entity, B extends z.ZodRawShape, C extends z.ZodRawShape, M extends z.ZodRawShape, CO extends z.ZodObject<C> | undefined = undefined, MO extends z.ZodObject<M> | undefined = undefined>(config: MonoriseEntityConfig<T, B, C, M, CO, MO>) => {
|
|
358
|
-
finalSchema: (CO extends z.AnyZodObject ? MO extends z.AnyZodObject ? z.ZodObject<
|
|
301
|
+
finalSchema: (CO extends z.AnyZodObject ? MO extends z.AnyZodObject ? z.ZodObject<MO["shape"] & CO["shape"], z.UnknownKeysParam, z.ZodTypeAny, z.objectUtil.addQuestionMarks<z.baseObjectOutputType<MO["shape"] & CO["shape"]>, any> extends infer T_1 ? { [k in keyof T_1]: T_1[k]; } : never, z.baseObjectInputType<MO["shape"] & CO["shape"]> extends infer T_2 ? { [k_1 in keyof T_2]: T_2[k_1]; } : never> : CO : MO extends z.AnyZodObject ? z.ZodObject<MO["shape"] & B, z.UnknownKeysParam, z.ZodTypeAny, z.objectUtil.addQuestionMarks<z.baseObjectOutputType<MO["shape"] & B>, any> extends infer T_3 ? { [k_2 in keyof T_3]: T_3[k_2]; } : never, z.baseObjectInputType<MO["shape"] & B> extends infer T_4 ? { [k_3 in keyof T_4]: T_4[k_3]; } : never> : z.ZodObject<B, z.UnknownKeysParam, z.ZodTypeAny, z.objectUtil.addQuestionMarks<z.baseObjectOutputType<B>, any> extends infer T_5 ? { [k_4 in keyof T_5]: T_5[k_4]; } : never, z.baseObjectInputType<B> extends infer T_6 ? { [k_5 in keyof T_6]: T_6[k_5]; } : never>) | z.ZodEffects<CO extends z.AnyZodObject ? MO extends z.AnyZodObject ? z.ZodObject<MO["shape"] & CO["shape"], z.UnknownKeysParam, z.ZodTypeAny, z.objectUtil.addQuestionMarks<z.baseObjectOutputType<MO["shape"] & CO["shape"]>, any> extends infer T_7 ? { [k in keyof T_7]: T_7[k]; } : never, z.baseObjectInputType<MO["shape"] & CO["shape"]> extends infer T_8 ? { [k_1 in keyof T_8]: T_8[k_1]; } : never> : CO : MO extends z.AnyZodObject ? z.ZodObject<MO["shape"] & B, z.UnknownKeysParam, z.ZodTypeAny, z.objectUtil.addQuestionMarks<z.baseObjectOutputType<MO["shape"] & B>, any> extends infer T_9 ? { [k_2 in keyof T_9]: T_9[k_2]; } : never, z.baseObjectInputType<MO["shape"] & B> extends infer T_10 ? { [k_3 in keyof T_10]: T_10[k_3]; } : never> : z.ZodObject<B, z.UnknownKeysParam, z.ZodTypeAny, z.objectUtil.addQuestionMarks<z.baseObjectOutputType<B>, any> extends infer T_11 ? { [k_4 in keyof T_11]: T_11[k_4]; } : never, z.baseObjectInputType<B> extends infer T_12 ? { [k_5 in keyof T_12]: T_12[k_5]; } : never>, z.output<CO extends z.AnyZodObject ? MO extends z.AnyZodObject ? z.ZodObject<MO["shape"] & CO["shape"], z.UnknownKeysParam, z.ZodTypeAny, z.objectUtil.addQuestionMarks<z.baseObjectOutputType<MO["shape"] & CO["shape"]>, any> extends infer T_13 ? { [k in keyof T_13]: T_13[k]; } : never, z.baseObjectInputType<MO["shape"] & CO["shape"]> extends infer T_14 ? { [k_1 in keyof T_14]: T_14[k_1]; } : never> : CO : MO extends z.AnyZodObject ? z.ZodObject<MO["shape"] & B, z.UnknownKeysParam, z.ZodTypeAny, z.objectUtil.addQuestionMarks<z.baseObjectOutputType<MO["shape"] & B>, any> extends infer T_15 ? { [k_2 in keyof T_15]: T_15[k_2]; } : never, z.baseObjectInputType<MO["shape"] & B> extends infer T_16 ? { [k_3 in keyof T_16]: T_16[k_3]; } : never> : z.ZodObject<B, z.UnknownKeysParam, z.ZodTypeAny, z.objectUtil.addQuestionMarks<z.baseObjectOutputType<B>, any> extends infer T_17 ? { [k_4 in keyof T_17]: T_17[k_4]; } : never, z.baseObjectInputType<B> extends infer T_18 ? { [k_5 in keyof T_18]: T_18[k_5]; } : never>>, z.input<CO extends z.AnyZodObject ? MO extends z.AnyZodObject ? z.ZodObject<MO["shape"] & CO["shape"], z.UnknownKeysParam, z.ZodTypeAny, z.objectUtil.addQuestionMarks<z.baseObjectOutputType<MO["shape"] & CO["shape"]>, any> extends infer T_19 ? { [k in keyof T_19]: T_19[k]; } : never, z.baseObjectInputType<MO["shape"] & CO["shape"]> extends infer T_20 ? { [k_1 in keyof T_20]: T_20[k_1]; } : never> : CO : MO extends z.AnyZodObject ? z.ZodObject<MO["shape"] & B, z.UnknownKeysParam, z.ZodTypeAny, z.objectUtil.addQuestionMarks<z.baseObjectOutputType<MO["shape"] & B>, any> extends infer T_21 ? { [k_2 in keyof T_21]: T_21[k_2]; } : never, z.baseObjectInputType<MO["shape"] & B> extends infer T_22 ? { [k_3 in keyof T_22]: T_22[k_3]; } : never> : z.ZodObject<B, z.UnknownKeysParam, z.ZodTypeAny, z.objectUtil.addQuestionMarks<z.baseObjectOutputType<B>, any> extends infer T_23 ? { [k_4 in keyof T_23]: T_23[k_4]; } : never, z.baseObjectInputType<B> extends infer T_24 ? { [k_5 in keyof T_24]: T_24[k_5]; } : never>>>;
|
|
359
302
|
name: string | T;
|
|
360
303
|
displayName: string;
|
|
361
304
|
authMethod?: {
|
|
@@ -363,7 +306,7 @@ declare const createEntityConfig: <T extends Entity, B extends z.ZodRawShape, C
|
|
|
363
306
|
tokenExpiresIn: number;
|
|
364
307
|
};
|
|
365
308
|
};
|
|
366
|
-
baseSchema: z.ZodObject<B, z.UnknownKeysParam, z.ZodTypeAny, z.objectUtil.addQuestionMarks<z.baseObjectOutputType<B>, any> extends infer
|
|
309
|
+
baseSchema: z.ZodObject<B, z.UnknownKeysParam, z.ZodTypeAny, z.objectUtil.addQuestionMarks<z.baseObjectOutputType<B>, any> extends infer T_25 ? { [k_4 in keyof T_25]: T_25[k_4]; } : never, z.baseObjectInputType<B> extends infer T_26 ? { [k_5 in keyof T_26]: T_26[k_5]; } : never>;
|
|
367
310
|
createSchema?: CO | undefined;
|
|
368
311
|
searchableFields?: (keyof B)[] | undefined;
|
|
369
312
|
uniqueFields?: (keyof B)[] | undefined;
|
|
@@ -377,7 +320,6 @@ declare const createEntityConfig: <T extends Entity, B extends z.ZodRawShape, C
|
|
|
377
320
|
entityType: Entity;
|
|
378
321
|
toMutualIds?: (context: any) => string[];
|
|
379
322
|
mutualDataProcessor?: (mutualIds: string[], currentMutual: any, customContext?: Record<string, any>) => Record<string, any>;
|
|
380
|
-
mutual?: MutualConfig;
|
|
381
323
|
};
|
|
382
324
|
};
|
|
383
325
|
prejoins?: {
|
|
@@ -404,6 +346,15 @@ declare const createEntityConfig: <T extends Entity, B extends z.ZodRawShape, C
|
|
|
404
346
|
sortValue?: string;
|
|
405
347
|
}[];
|
|
406
348
|
}[];
|
|
349
|
+
ttl?: {
|
|
350
|
+
processor: (entity: {
|
|
351
|
+
entityId: string;
|
|
352
|
+
entityType: string;
|
|
353
|
+
data: Record<string, any>;
|
|
354
|
+
createdAt: string;
|
|
355
|
+
updatedAt: string;
|
|
356
|
+
}) => number | Date | undefined;
|
|
357
|
+
};
|
|
407
358
|
adjustmentConstraints?: {
|
|
408
359
|
[fieldName: string]: {
|
|
409
360
|
min?: number;
|
|
@@ -412,13 +363,6 @@ declare const createEntityConfig: <T extends Entity, B extends z.ZodRawShape, C
|
|
|
412
363
|
maxField?: (keyof { [K_2 in keyof B as B[K_2] extends z.ZodNumber | z.ZodOptional<z.ZodNumber> ? K_2 : never]: K_2; } extends never ? string : keyof { [K_3 in keyof B as B[K_3] extends z.ZodNumber | z.ZodOptional<z.ZodNumber> ? K_3 : never]: K_3; }) | undefined;
|
|
413
364
|
};
|
|
414
365
|
} | undefined;
|
|
415
|
-
adjustmentConditions?: {
|
|
416
|
-
[conditionName: string]: undefined | ((data: Partial<z.objectUtil.addQuestionMarks<z.baseObjectOutputType<B>, any> extends infer T_35 ? { [k_6 in keyof T_35]: T_35[k_6]; } : never>, adjustments: Record<string, number>) => undefined);
|
|
417
|
-
} | undefined;
|
|
418
|
-
updateConditions?: {
|
|
419
|
-
[conditionName: string]: undefined | ((data: Partial<z.objectUtil.addQuestionMarks<z.baseObjectOutputType<B>, any> extends infer T_35 ? { [k_6 in keyof T_35]: T_35[k_6]; } : never>) => undefined);
|
|
420
|
-
} | undefined;
|
|
421
366
|
};
|
|
422
|
-
declare const createMutualConfig: <MD extends z.ZodRawShape>(config: MutualConfig<MD>) => MutualConfig<MD>;
|
|
423
367
|
|
|
424
|
-
export { type
|
|
368
|
+
export { type CreatedEntity, type DraftEntity, Entity, type EntitySchemaMap, type MonoriseEntityConfig, type NumericFields, createEntityConfig };
|
package/dist/index.js
CHANGED
|
@@ -37,10 +37,8 @@ function makeSchema(config) {
|
|
|
37
37
|
var createEntityConfig = (config) => __spreadProps(__spreadValues({}, config), {
|
|
38
38
|
finalSchema: makeSchema(config)
|
|
39
39
|
});
|
|
40
|
-
var createMutualConfig = (config) => config;
|
|
41
40
|
export {
|
|
42
41
|
Entity,
|
|
43
|
-
createEntityConfig
|
|
44
|
-
createMutualConfig
|
|
42
|
+
createEntityConfig
|
|
45
43
|
};
|
|
46
44
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../types/monorise.type.ts","../utils/index.ts"],"sourcesContent":["import type { z } from 'zod';\nimport type { WhereConditions } from './conditions.type';\n\nexport enum Entity {}\n\nexport interface EntitySchemaMap {\n [key: string]: Record<string, any>;\n}\n\n/**\n * @description Configuration for a mutual relationship between two entities.\n * Defines the schema for mutualData validation. Define once, reference from both entity configs.\n *\n * @example\n * ```ts\n * const enrollmentMutual = createMutualConfig({\n * entities: [Entity.STUDENT, Entity.COURSE],\n * mutualDataSchema: z.object({\n * role: z.enum(['student', 'auditor']),\n * enrolledAt: z.string().datetime(),\n * }),\n * });\n * ```\n */\nexport interface MutualConfig<\n MD extends z.ZodRawShape = z.ZodRawShape,\n> {\n entities: [Entity, Entity];\n mutualDataSchema: z.ZodObject<MD>;\n}\n\nexport type DraftEntity<T extends Entity = Entity> =\n T extends keyof EntitySchemaMap ? EntitySchemaMap[T] : never;\n\nexport type NumericFields<T> = {\n [K in keyof T as T[K] extends number ? K : never]?: number;\n};\n\nexport type CreatedEntity<T extends Entity = Entity> = {\n entityId: string;\n entityType: string;\n data: T extends keyof EntitySchemaMap ? EntitySchemaMap[T] : never;\n createdAt: string;\n updatedAt: string;\n};\n\n/**\n * @description Configuration for a monorise entity, a shared configuration that is used across frontend and backend.\n * This can be served as a single source of truth for the entity configuration.\n * It is used to define the schema, and mutual relationships between this entity and other entities.\n *\n * @example\n * ```ts\n * const baseSchema = z.object({\n * title: z.string(),\n * }).partial();\n *\n * const createSchema = baseSchema.extend({\n * title: z.string(),\n * })\n *\n * const config = createEntityConfig({\n * name: 'learner',\n * displayName: 'Learner',\n * baseSchema,\n * createSchema,\n * });\n * ```\n */\nexport interface MonoriseEntityConfig<\n T extends Entity = Entity,\n B extends z.ZodRawShape = z.ZodRawShape,\n C extends z.ZodRawShape = z.ZodRawShape,\n M extends z.ZodRawShape = z.ZodRawShape,\n CO extends z.ZodObject<C> | undefined = undefined,\n MO extends z.ZodObject<M> | undefined = undefined,\n> {\n /**\n * @description Name of the entity. Must be in **lower-kebab-case** and **unique** across all entities\n *\n * @example `learner`, `learning-activity`\n */\n name: string | T;\n\n /**\n * @description Display name of the entity. It is not required to be unique\n */\n displayName: string;\n\n /**\n * @description (DEPRECATED) Use `uniqueFields` instead, Monorise should not handle auth mechanism\n * @description (Optional) Specify the authentication method to be used for the entity\n */\n authMethod?: {\n /**\n * @description Authentication method using email\n *\n * Note: The email used for authentication is unique per entity.\n * For example, if `johndoe@mail.com` is used for `learner` entity,\n * it can be reused again on `admin` entity. However, the same email\n * address cannot be repeated for the same entity.\n */\n email: {\n /**\n * @description Number of milliseconds before the token expires\n */\n tokenExpiresIn: number;\n };\n };\n\n /**\n * @description Base schema for the entity\n */\n baseSchema: z.ZodObject<B>;\n\n /**\n * @description Minimal schema required to create an entity\n */\n createSchema?: CO;\n searchableFields?: (keyof B)[];\n uniqueFields?: (keyof B)[];\n\n /**\n * @description Define mutual relationship of this entity with other entities\n */\n mutual?: {\n /**\n * @description Subscribes to update events from specified entities in the array.\n * These events will be used to run prejoin processor.\n */\n subscribes?: { entityType: Entity }[];\n /**\n * @description Virtual schema for mutual relationship. The schema is only used for validation purpose, but these fields are not stored in the database\n */\n mutualSchema: MO;\n\n /**\n * @description Keys of `mutualFields` are fields defined in `mutualSchema`.\n * Each field is a mutual relationship between this entity and another entity.\n */\n mutualFields: {\n [key: string]: {\n entityType: Entity;\n toMutualIds?: (context: any) => string[];\n /**\n * @description (Optional) Custom function to process `mutualData`. If not provided, `mutualData` will be empty.\n *\n * @returns the final state of `mutualData` to be stored in the mutual record. Must be an object.\n */\n mutualDataProcessor?: (\n mutualIds: string[],\n currentMutual: any,\n customContext?: Record<string, any>,\n ) => Record<string, any>;\n /**\n * @description (Optional) Reference to a mutual config created by `createMutualConfig`.\n * Provides mutualData schema validation for create/update operations on this mutual relationship.\n */\n mutual?: MutualConfig;\n };\n };\n\n /**\n * @description (Optional) Better known as tree processor\n * This is used to prejoin entities that are not directly related as mutual.\n * For example, if `learner` entity is related to `course` entity, and `course` entity is related to `module` entity,\n * prejoins can be used to join `learner` and `module` entities.\n * With this, the `learner` entity can access the `module` entity without having to go through the `course` entity,\n * hence reducing the number of queries.\n *\n * DynamoDB best practices: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-general-normalization.html\n *\n */\n prejoins?: {\n mutualField: string;\n targetEntityType: Entity;\n entityPaths: {\n skipCache?: boolean;\n entityType: Entity;\n processor?: (items: any[], context: Record<string, any>) => any;\n }[];\n }[];\n };\n /**\n * Use this function to perform side effects on the final schema for example refine/superRefine the schema\n *\n * @param schema The final schema of the entity (the combination of `baseSchema`/`createSchema` and `mutualSchema` if specified)\n * @returns void\n *\n * @example\n * ```ts\n * effect: (schema) => {\n * schema.refine(\n * // refinement logic here\n * )\n * }\n */\n effect?: (\n schema: z.ZodObject<z.ZodRawShape>,\n ) => z.ZodEffects<z.ZodObject<z.ZodRawShape>>;\n\n /**\n * @description (Optional) Use tags to create additional access patterns for the entity.\n * Time complexity for retrieving tagged entities is O(1).\n *\n * The following configuration will create a tag named `region` for the `organization` entity grouped by `region`.\n * You would then be able to retrieve all organizations in a specific region by:\n * GET `/core/tag/organization/region?group={region_name}`\n *\n * @example\n *\n * ```ts\n * {\n * name: 'organization',\n * tags: [\n * {\n * name: 'region',\n * processor: (entity) => {\n * return [\n * {\n * group: entity.data.region\n * }\n * ]\n * },\n * }\n * ]\n * }\n * ```\n *\n * @description\n *\n * The following configuration will create a tag named `dob` for the `user` entity sorted by `dob`.\n * You would then be able to retrieve all users sorted by `dob` by:\n * GET `/core/tag/user/dob?start=2000-01-01&end=2020-12-31`\n *\n * @example\n * ```ts\n * {\n * name: 'user',\n * tags: [\n * {\n * name: 'dob',\n * processor: (entity) => {\n * return [\n * {\n * sortValue: entity.data.dob\n * }\n * ]\n * },\n * }\n * ]\n * }\n * ```\n */\n tags?: {\n name: string;\n processor: (entity: { entityId: string; entityType: string; data: Record<string, any>; createdAt: string; updatedAt: string }) => {\n group?: string;\n sortValue?: string;\n }[];\n }[];\n\n /**\n * @description (Optional) Constraints for `adjustEntity` operations.\n * When adjusting numeric fields, these constraints are enforced at the database level.\n * If an adjustment would violate a constraint, the operation is rejected.\n *\n * @deprecated Use `conditions` instead. Will be removed in a future version.\n *\n * @example\n * ```ts\n * {\n * adjustmentConstraints: {\n * // Static: same for all entities of this type\n * balance: { min: 0 },\n * credits: { min: 0, max: 10000 },\n *\n * // Dynamic: reads constraint value from entity's own data\n * balance: { minField: 'minBalance' },\n * credits: { min: 0, maxField: 'creditLimit' },\n * }\n * }\n * ```\n */\n adjustmentConstraints?: {\n [fieldName: string]: {\n /** Static minimum value */\n min?: number;\n /** Static maximum value */\n max?: number;\n /** Field name on the entity whose value is used as the minimum (must be a numeric field) */\n minField?: keyof {\n [K in keyof B as B[K] extends z.ZodNumber | z.ZodOptional<z.ZodNumber> ? K : never]: K;\n } extends never ? string : keyof {\n [K in keyof B as B[K] extends z.ZodNumber | z.ZodOptional<z.ZodNumber> ? K : never]: K;\n };\n /** Field name on the entity whose value is used as the maximum (must be a numeric field) */\n maxField?: keyof {\n [K in keyof B as B[K] extends z.ZodNumber | z.ZodOptional<z.ZodNumber> ? K : never]: K;\n } extends never ? string : keyof {\n [K in keyof B as B[K] extends z.ZodNumber | z.ZodOptional<z.ZodNumber> ? K : never]: K;\n };\n };\n };\n\n /**\n * @description Named conditions for adjustEntity operations.\n * Each condition is either a static `WhereConditions` object or a function\n * `(data, adjustments) => WhereConditions` that receives the entity's current data\n * and the adjustment deltas.\n *\n * When defined, `$condition` is **required** in the adjustEntity request body.\n * The client sends a condition name (string), the server resolves it to a\n * DynamoDB ConditionExpression.\n *\n * @example\n * ```ts\n * {\n * adjustmentConditions: {\n * withdraw: (data, adjustments) => ({\n * balance: { $gte: (data.minBalance ?? 0) + Math.abs(adjustments?.balance ?? 0) },\n * }),\n * deposit: (data, adjustments) => ({\n * balance: { $lte: 1000000 - (adjustments.balance ?? 0) },\n * }),\n * }\n * }\n * ```\n */\n adjustmentConditions?: {\n [conditionName: string]:\n | WhereConditions\n | ((\n data: Partial<z.infer<z.ZodObject<B>>>,\n adjustments: Record<string, number>,\n ) => WhereConditions);\n };\n\n /**\n * @description Named conditions for updateEntity operations.\n * Each condition is either a static `WhereConditions` object or a function\n * `(data) => WhereConditions` that receives the entity's current data.\n *\n * `$condition` is always **optional** for updateEntity.\n * The client sends a condition name (string), the server resolves it to a\n * DynamoDB ConditionExpression. Replaces raw `$where` (deprecated).\n *\n * @example\n * ```ts\n * {\n * updateConditions: {\n * publish: { status: { $eq: 'draft' } },\n * archive: (data) => ({ status: { $ne: 'archived' } }),\n * }\n * }\n * ```\n */\n updateConditions?: {\n [conditionName: string]:\n | WhereConditions\n | ((data: Partial<z.infer<z.ZodObject<B>>>) => WhereConditions);\n };\n}\n","import type { Entity, MonoriseEntityConfig, MutualConfig } from '../types/monorise.type';\nimport { z } from 'zod';\n\nfunction makeSchema<\n T extends Entity,\n B extends z.ZodRawShape,\n C extends z.ZodRawShape,\n M extends z.ZodRawShape,\n CO extends z.ZodObject<C> | undefined = undefined,\n MO extends z.ZodObject<M> | undefined = undefined,\n>(config: MonoriseEntityConfig<T, B, C, M, CO, MO>) {\n const { baseSchema, createSchema, mutual, effect } = config;\n const { mutualSchema } = mutual || {};\n\n type FinalSchemaType = CO extends z.AnyZodObject\n ? MO extends z.AnyZodObject\n ? z.ZodObject<B & CO['shape'] & MO['shape']>\n : z.ZodObject<B & CO['shape']>\n : MO extends z.AnyZodObject\n ? z.ZodObject<B & MO['shape']>\n : z.ZodObject<B>;\n\n const finalSchema = z.object({\n ...baseSchema.shape,\n ...createSchema?.shape,\n ...mutualSchema?.shape,\n }) as FinalSchemaType;\n\n if (effect) {\n return effect(finalSchema) as z.ZodEffects<FinalSchemaType>;\n }\n\n return finalSchema;\n}\n\nconst createEntityConfig = <\n T extends Entity,\n B extends z.ZodRawShape,\n C extends z.ZodRawShape,\n M extends z.ZodRawShape,\n CO extends z.ZodObject<C> | undefined = undefined,\n MO extends z.ZodObject<M> | undefined = undefined,\n>(\n config: MonoriseEntityConfig<T, B, C, M, CO, MO>,\n) => ({\n ...config,\n finalSchema: makeSchema(config),\n});\n\nconst createMutualConfig = <MD extends z.ZodRawShape>(\n config: MutualConfig<MD>,\n) => config;\n\nexport { createEntityConfig, createMutualConfig };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAGO,IAAK,SAAL,kBAAKA,YAAL;AAAK,SAAAA;AAAA,GAAA;;;ACFZ,SAAS,SAAS;AAElB,SAAS,WAOP,QAAkD;AAClD,QAAM,EAAE,YAAY,cAAc,QAAQ,OAAO,IAAI;AACrD,QAAM,EAAE,aAAa,IAAI,UAAU,CAAC;AAUpC,QAAM,cAAc,EAAE,OAAO,iDACxB,WAAW,QACX,6CAAc,QACd,6CAAc,MAClB;AAED,MAAI,QAAQ;AACV,WAAO,OAAO,WAAW;AAAA,EAC3B;AAEA,SAAO;AACT;AAEA,IAAM,qBAAqB,CAQzB,WACI,iCACD,SADC;AAAA,EAEJ,aAAa,WAAW,MAAM;AAChC;AAEA,IAAM,qBAAqB,CACzB,WACG;","names":["Entity"]}
|
|
1
|
+
{"version":3,"sources":["../types/monorise.type.ts","../utils/index.ts"],"sourcesContent":["import type { z } from 'zod';\n\nexport enum Entity {}\n\nexport interface EntitySchemaMap {\n [key: string]: Record<string, any>;\n}\n\nexport type DraftEntity<T extends Entity = Entity> =\n T extends keyof EntitySchemaMap ? EntitySchemaMap[T] : never;\n\nexport type NumericFields<T> = {\n [K in keyof T as T[K] extends number ? K : never]?: number;\n};\n\nexport type CreatedEntity<T extends Entity = Entity> = {\n entityId: string;\n entityType: string;\n data: T extends keyof EntitySchemaMap ? EntitySchemaMap[T] : never;\n createdAt: string;\n updatedAt: string;\n};\n\n/**\n * @description Configuration for a monorise entity, a shared configuration that is used across frontend and backend.\n * This can be served as a single source of truth for the entity configuration.\n * It is used to define the schema, and mutual relationships between this entity and other entities.\n *\n * @example\n * ```ts\n * const baseSchema = z.object({\n * title: z.string(),\n * }).partial();\n *\n * const createSchema = baseSchema.extend({\n * title: z.string(),\n * })\n *\n * const config = createEntityConfig({\n * name: 'learner',\n * displayName: 'Learner',\n * baseSchema,\n * createSchema,\n * });\n * ```\n */\nexport interface MonoriseEntityConfig<\n T extends Entity = Entity,\n B extends z.ZodRawShape = z.ZodRawShape,\n C extends z.ZodRawShape = z.ZodRawShape,\n M extends z.ZodRawShape = z.ZodRawShape,\n CO extends z.ZodObject<C> | undefined = undefined,\n MO extends z.ZodObject<M> | undefined = undefined,\n> {\n /**\n * @description Name of the entity. Must be in **lower-kebab-case** and **unique** across all entities\n *\n * @example `learner`, `learning-activity`\n */\n name: string | T;\n\n /**\n * @description Display name of the entity. It is not required to be unique\n */\n displayName: string;\n\n /**\n * @description (DEPRECATED) Use `uniqueFields` instead, Monorise should not handle auth mechanism\n * @description (Optional) Specify the authentication method to be used for the entity\n */\n authMethod?: {\n /**\n * @description Authentication method using email\n *\n * Note: The email used for authentication is unique per entity.\n * For example, if `johndoe@mail.com` is used for `learner` entity,\n * it can be reused again on `admin` entity. However, the same email\n * address cannot be repeated for the same entity.\n */\n email: {\n /**\n * @description Number of milliseconds before the token expires\n */\n tokenExpiresIn: number;\n };\n };\n\n /**\n * @description Base schema for the entity\n */\n baseSchema: z.ZodObject<B>;\n\n /**\n * @description Minimal schema required to create an entity\n */\n createSchema?: CO;\n searchableFields?: (keyof B)[];\n uniqueFields?: (keyof B)[];\n\n /**\n * @description Define mutual relationship of this entity with other entities\n */\n mutual?: {\n /**\n * @description Subscribes to update events from specified entities in the array.\n * These events will be used to run prejoin processor.\n */\n subscribes?: { entityType: Entity }[];\n /**\n * @description Virtual schema for mutual relationship. The schema is only used for validation purpose, but these fields are not stored in the database\n */\n mutualSchema: MO;\n\n /**\n * @description Keys of `mutualFields` are fields defined in `mutualSchema`.\n * Each field is a mutual relationship between this entity and another entity.\n */\n mutualFields: {\n [key: string]: {\n entityType: Entity;\n toMutualIds?: (context: any) => string[];\n /**\n * @description (Optional) Custom function to process `mutualData`. If not provided, `mutualData` will be empty.\n *\n * @returns the final state of `mutualData` to be stored in the mutual record. Must be an object.\n */\n mutualDataProcessor?: (\n mutualIds: string[],\n currentMutual: any,\n customContext?: Record<string, any>,\n ) => Record<string, any>;\n };\n };\n\n /**\n * @description (Optional) Better known as tree processor\n * This is used to prejoin entities that are not directly related as mutual.\n * For example, if `learner` entity is related to `course` entity, and `course` entity is related to `module` entity,\n * prejoins can be used to join `learner` and `module` entities.\n * With this, the `learner` entity can access the `module` entity without having to go through the `course` entity,\n * hence reducing the number of queries.\n *\n * DynamoDB best practices: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-general-normalization.html\n *\n */\n prejoins?: {\n mutualField: string;\n targetEntityType: Entity;\n entityPaths: {\n skipCache?: boolean;\n entityType: Entity;\n processor?: (items: any[], context: Record<string, any>) => any;\n }[];\n }[];\n };\n /**\n * Use this function to perform side effects on the final schema for example refine/superRefine the schema\n *\n * @param schema The final schema of the entity (the combination of `baseSchema`/`createSchema` and `mutualSchema` if specified)\n * @returns void\n *\n * @example\n * ```ts\n * effect: (schema) => {\n * schema.refine(\n * // refinement logic here\n * )\n * }\n */\n effect?: (\n schema: z.ZodObject<z.ZodRawShape>,\n ) => z.ZodEffects<z.ZodObject<z.ZodRawShape>>;\n\n /**\n * @description (Optional) Use tags to create additional access patterns for the entity.\n * Time complexity for retrieving tagged entities is O(1).\n *\n * The following configuration will create a tag named `region` for the `organization` entity grouped by `region`.\n * You would then be able to retrieve all organizations in a specific region by:\n * GET `/core/tag/organization/region?group={region_name}`\n *\n * @example\n *\n * ```ts\n * {\n * name: 'organization',\n * tags: [\n * {\n * name: 'region',\n * processor: (entity) => {\n * return [\n * {\n * group: entity.data.region\n * }\n * ]\n * },\n * }\n * ]\n * }\n * ```\n *\n * @description\n *\n * The following configuration will create a tag named `dob` for the `user` entity sorted by `dob`.\n * You would then be able to retrieve all users sorted by `dob` by:\n * GET `/core/tag/user/dob?start=2000-01-01&end=2020-12-31`\n *\n * @example\n * ```ts\n * {\n * name: 'user',\n * tags: [\n * {\n * name: 'dob',\n * processor: (entity) => {\n * return [\n * {\n * sortValue: entity.data.dob\n * }\n * ]\n * },\n * }\n * ]\n * }\n * ```\n */\n tags?: {\n name: string;\n processor: (entity: { entityId: string; entityType: string; data: Record<string, any>; createdAt: string; updatedAt: string }) => {\n group?: string;\n sortValue?: string;\n }[];\n }[];\n\n /**\n * @description (Optional) Configure a DynamoDB TTL for this entity. When set, `expiresAt`\n * is computed via `processor` on create, and recomputed on every update/upsert. Once past,\n * DynamoDB automatically deletes the item (and its derived index rows, since they all carry\n * the same `expiresAt`).\n *\n * `processor` returning `undefined` means \"no TTL\" for that record. On updates, an `undefined`\n * result leaves any existing `expiresAt` untouched rather than clearing it.\n *\n * @example\n * ```ts\n * // fixed 30-day TTL from creation/each update\n * ttl: {\n * processor: () => Math.floor(Date.now() / 1000) + 30 * 24 * 60 * 60,\n * }\n * ```\n *\n * @example\n * ```ts\n * // data-driven TTL, eg. expire when a subscription ends\n * ttl: {\n * processor: (entity) => {\n * return entity.data.subscriptionEndsAt\n * ? new Date(entity.data.subscriptionEndsAt)\n * : undefined;\n * },\n * }\n * ```\n */\n ttl?: {\n /**\n * @description Returns the expiry as epoch seconds (absolute) or a `Date`.\n * Return `undefined` for no expiry.\n */\n processor: (entity: {\n entityId: string;\n entityType: string;\n data: Record<string, any>;\n createdAt: string;\n updatedAt: string;\n }) => number | Date | undefined;\n };\n\n /**\n * @description (Optional) Constraints for `adjustEntity` operations.\n * When adjusting numeric fields, these constraints are enforced at the database level.\n * If an adjustment would violate a constraint, the operation is rejected.\n *\n * @example\n * ```ts\n * {\n * adjustmentConstraints: {\n * // Static: same for all entities of this type\n * balance: { min: 0 },\n * credits: { min: 0, max: 10000 },\n *\n * // Dynamic: reads constraint value from entity's own data\n * balance: { minField: 'minBalance' },\n * credits: { min: 0, maxField: 'creditLimit' },\n * }\n * }\n * ```\n */\n adjustmentConstraints?: {\n [fieldName: string]: {\n /** Static minimum value */\n min?: number;\n /** Static maximum value */\n max?: number;\n /** Field name on the entity whose value is used as the minimum (must be a numeric field) */\n minField?: keyof {\n [K in keyof B as B[K] extends z.ZodNumber | z.ZodOptional<z.ZodNumber> ? K : never]: K;\n } extends never ? string : keyof {\n [K in keyof B as B[K] extends z.ZodNumber | z.ZodOptional<z.ZodNumber> ? K : never]: K;\n };\n /** Field name on the entity whose value is used as the maximum (must be a numeric field) */\n maxField?: keyof {\n [K in keyof B as B[K] extends z.ZodNumber | z.ZodOptional<z.ZodNumber> ? K : never]: K;\n } extends never ? string : keyof {\n [K in keyof B as B[K] extends z.ZodNumber | z.ZodOptional<z.ZodNumber> ? K : never]: K;\n };\n };\n };\n}\n","import type { Entity, MonoriseEntityConfig } from '../types/monorise.type';\nimport { z } from 'zod';\n\nfunction makeSchema<\n T extends Entity,\n B extends z.ZodRawShape,\n C extends z.ZodRawShape,\n M extends z.ZodRawShape,\n CO extends z.ZodObject<C> | undefined = undefined,\n MO extends z.ZodObject<M> | undefined = undefined,\n>(config: MonoriseEntityConfig<T, B, C, M, CO, MO>) {\n const { baseSchema, createSchema, mutual, effect } = config;\n const { mutualSchema } = mutual || {};\n\n type FinalSchemaType = CO extends z.AnyZodObject\n ? MO extends z.AnyZodObject\n ? z.ZodObject<MO['shape'] & CO['shape']>\n : CO\n : MO extends z.AnyZodObject\n ? z.ZodObject<MO['shape'] & B>\n : z.ZodObject<B>;\n\n const finalSchema = z.object({\n ...baseSchema.shape,\n ...createSchema?.shape,\n ...mutualSchema?.shape,\n }) as FinalSchemaType;\n\n if (effect) {\n return effect(finalSchema) as z.ZodEffects<FinalSchemaType>;\n }\n\n return finalSchema;\n}\n\nconst createEntityConfig = <\n T extends Entity,\n B extends z.ZodRawShape,\n C extends z.ZodRawShape,\n M extends z.ZodRawShape,\n CO extends z.ZodObject<C> | undefined = undefined,\n MO extends z.ZodObject<M> | undefined = undefined,\n>(\n config: MonoriseEntityConfig<T, B, C, M, CO, MO>,\n) => ({\n ...config,\n finalSchema: makeSchema(config),\n});\n\nexport { createEntityConfig };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAEO,IAAK,SAAL,kBAAKA,YAAL;AAAK,SAAAA;AAAA,GAAA;;;ACDZ,SAAS,SAAS;AAElB,SAAS,WAOP,QAAkD;AAClD,QAAM,EAAE,YAAY,cAAc,QAAQ,OAAO,IAAI;AACrD,QAAM,EAAE,aAAa,IAAI,UAAU,CAAC;AAUpC,QAAM,cAAc,EAAE,OAAO,iDACxB,WAAW,QACX,6CAAc,QACd,6CAAc,MAClB;AAED,MAAI,QAAQ;AACV,WAAO,OAAO,WAAW;AAAA,EAC3B;AAEA,SAAO;AACT;AAEA,IAAM,qBAAqB,CAQzB,WACI,iCACD,SADC;AAAA,EAEJ,aAAa,WAAW,MAAM;AAChC;","names":["Entity"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@monorise/base",
|
|
3
|
-
"version": "4.1.0
|
|
3
|
+
"version": "4.1.0",
|
|
4
|
+
"repository": {
|
|
5
|
+
"type": "git",
|
|
6
|
+
"url": "git+https://github.com/monorist/monorise.git",
|
|
7
|
+
"directory": "packages/base"
|
|
8
|
+
},
|
|
4
9
|
"description": "",
|
|
5
10
|
"type": "module",
|
|
6
11
|
"main": "dist/index.js",
|