Skip to content

Commit 1e5d8fe

Browse files
committed
Add type for hook functions
1 parent d645e3d commit 1e5d8fe

File tree

3 files changed

+36
-54
lines changed

3 files changed

+36
-54
lines changed

index.d.ts

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,18 @@ declare module "@moleculer/database" {
6969

7070
export type EntityChangedEventType = "broadcast" | "emit" | null;
7171

72+
// Hook function type for field lifecycle hooks
73+
export type HookCustomFunctionArgument<TValue = any, TEntity = any> = {
74+
ctx: Context<any, any>;
75+
value: TValue;
76+
params: any;
77+
field: BaseFieldDefinition;
78+
id?: any;
79+
operation: "create" | "update" | "replace" | "remove";
80+
entity?: TEntity;
81+
root?: any;
82+
};
83+
7284
export interface PopulateDefinition {
7385
/** Service name for populating */
7486
service?: string;
@@ -121,7 +133,7 @@ declare module "@moleculer/database" {
121133
/** Transformation function when getting value */
122134
get?: (value: any, entity: any, field: BaseFieldDefinition, ctx: Context<any, any>) => any;
123135
/** Transformation function when setting value */
124-
set?: (value: any, entity: any, field: BaseFieldDefinition, ctx: Context<any, any>) => any;
136+
set?: (args: HookCustomFunctionArgument) => any;
125137
/** Custom validation function */
126138
validate?:
127139
| string
@@ -132,33 +144,13 @@ declare module "@moleculer/database" {
132144
ctx: Context<any, any>
133145
) => Promise<boolean | string>);
134146
/** Lifecycle hook: called when entity is created */
135-
onCreate?: (
136-
value: any,
137-
entity: any,
138-
field: BaseFieldDefinition,
139-
ctx: Context<any, any>
140-
) => any;
147+
onCreate?: (args: HookCustomFunctionArgument) => any | any;
141148
/** Lifecycle hook: called when entity is updated */
142-
onUpdate?: (
143-
value: any,
144-
entity: any,
145-
field: BaseFieldDefinition,
146-
ctx: Context<any, any>
147-
) => any;
149+
onUpdate?: (args: HookCustomFunctionArgument) => any | any;
148150
/** Lifecycle hook: called when entity is replaced */
149-
onReplace?: (
150-
value: any,
151-
entity: any,
152-
field: BaseFieldDefinition,
153-
ctx: Context<any, any>
154-
) => any;
151+
onReplace?: (args: HookCustomFunctionArgument) => any | any;
155152
/** Lifecycle hook: called when entity is removed (enables soft delete) */
156-
onRemove?: (
157-
value: any,
158-
entity: any,
159-
field: BaseFieldDefinition,
160-
ctx: Context<any, any>
161-
) => any;
153+
onRemove?: (args: HookCustomFunctionArgument) => any | any;
162154
}
163155

164156
export interface StringFieldDefinition extends BaseFieldDefinition {

src/validation.js

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -406,24 +406,6 @@ module.exports = function (mixinOpts) {
406406
// Virtual field
407407
if (field.virtual) return;
408408

409-
// Custom formatter (can be async)
410-
// Syntax: `set: (value, entity, field, ctx) => value.toUpperCase()`
411-
if (field.set) {
412-
value = await this._callCustomFunction(field.set, [
413-
{
414-
ctx,
415-
value,
416-
params,
417-
field,
418-
id: opts.id,
419-
operation: type,
420-
entity: oldEntity,
421-
root: opts.root || params
422-
}
423-
]);
424-
return setValue(field, value);
425-
}
426-
427409
const customArgs = [
428410
{
429411
ctx,
@@ -437,6 +419,13 @@ module.exports = function (mixinOpts) {
437419
}
438420
];
439421

422+
// Custom formatter (can be async)
423+
// Syntax: `set: (value, entity, field, ctx) => value.toUpperCase()`
424+
if (field.set) {
425+
value = await this._callCustomFunction(field.set, customArgs);
426+
return setValue(field, value);
427+
}
428+
440429
// Handlers
441430
if (!opts.skipOnHooks) {
442431
if (type == "create" && field.onCreate) {

test/typescript/index.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ import {
4343
generateFieldValidatorSchema,
4444
DatabaseServiceSettings,
4545
DatabaseMethods,
46-
DatabaseLocalVariables
46+
DatabaseLocalVariables,
47+
HookCustomFunctionArgument
4748
} from "@moleculer/database";
4849

4950
// =============================================================================
@@ -155,12 +156,12 @@ const customField: CustomFieldDefinition = {
155156
// Test field definitions with lifecycle hooks
156157
const fieldWithHooks: FieldDefinition = {
157158
type: "string",
158-
onCreate: (value: any, entity: any, field: FieldDefinition, ctx: Context) => value || "default",
159-
onUpdate: (value: any, entity: any, field: FieldDefinition, ctx: Context) => value,
160-
onReplace: (value: any, entity: any, field: FieldDefinition, ctx: Context) => value,
161-
onRemove: (value: any, entity: any, field: FieldDefinition, ctx: Context) => null,
159+
onCreate: ({ value }) => value || "default",
160+
onUpdate: ({ value }) => value,
161+
onReplace: ({ value }) => value,
162+
onRemove: () => null,
162163
get: (value: any, entity: any, field: FieldDefinition, ctx: Context) => value,
163-
set: (value: any, entity: any, field: FieldDefinition, ctx: Context) => value,
164+
set: ({ value }) => value,
164165
validate: async (value: any, entity: any, field: FieldDefinition, ctx: Context) => {
165166
return typeof value === "string" && value.length > 0;
166167
}
@@ -850,7 +851,7 @@ const multiTenantServiceSchema: ServiceSchema<
850851
tenantId: {
851852
type: "string",
852853
required: true,
853-
set: (value: any, entity: any, field: FieldDefinition, ctx: Context) => {
854+
set: ({ value, ctx }) => {
854855
return (ctx.meta as any)?.tenantId || value;
855856
}
856857
}
@@ -896,7 +897,7 @@ const complexFields: Fields = {
896897
trim: true,
897898
lowercase: true,
898899
alphanum: false,
899-
set: (value: any) => value?.toString().toLowerCase().replace(/\s+/g, "-")
900+
set: ({ value }) => value?.toString().toLowerCase().replace(/\s+/g, "-")
900901
},
901902

902903
// Number field with validation
@@ -975,18 +976,18 @@ const complexFields: Fields = {
975976
auditInfo: {
976977
type: "object",
977978
readonly: true,
978-
onCreate: (value: any, entity: any, field: FieldDefinition, ctx: Context) => ({
979+
onCreate: ({ ctx }: HookCustomFunctionArgument) => ({
979980
createdBy: (ctx.meta as any)?.user?.id,
980981
createdAt: Date.now(),
981982
version: 1
982983
}),
983-
onUpdate: (value: any, entity: any, field: FieldDefinition, ctx: Context) => ({
984+
onUpdate: ({ value, ctx }: HookCustomFunctionArgument) => ({
984985
...value,
985986
updatedBy: (ctx.meta as any)?.user?.id,
986987
updatedAt: Date.now(),
987988
version: (value.version || 0) + 1
988989
}),
989-
onRemove: (value: any, entity: any, field: FieldDefinition, ctx: Context) => ({
990+
onRemove: ({ value, ctx }: HookCustomFunctionArgument) => ({
990991
...value,
991992
deletedBy: (ctx.meta as any)?.user?.id,
992993
deletedAt: Date.now()

0 commit comments

Comments
 (0)