@lobb-js/core 0.14.0 → 0.15.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/package.json +1 -1
- package/src/types/config/collectionFields.ts +20 -0
- package/src/types/config/collectionsConfig.ts +13 -0
- package/src/workflows/coreWorkflows/index.ts +5 -0
- package/src/workflows/coreWorkflows/processors/hooksWorkflows.ts +60 -0
- package/src/workflows/coreWorkflows/processors/utils.ts +1 -0
- package/src/workflows/coreWorkflows/processors/validatorWorkflows.ts +54 -0
package/package.json
CHANGED
|
@@ -15,8 +15,28 @@ const UiSchema = z.object({
|
|
|
15
15
|
input: UiInputSchema.optional(),
|
|
16
16
|
});
|
|
17
17
|
|
|
18
|
+
export type FieldHookFn = (ctx: {
|
|
19
|
+
data: Record<string, any>;
|
|
20
|
+
context?: any;
|
|
21
|
+
}) => any;
|
|
22
|
+
|
|
23
|
+
export type FieldValidatorFn = (ctx: {
|
|
24
|
+
value: any;
|
|
25
|
+
data: Record<string, any>;
|
|
26
|
+
context?: any;
|
|
27
|
+
}) => string | undefined | Promise<string | undefined>;
|
|
28
|
+
|
|
29
|
+
const FieldHooksSchema = z.object({
|
|
30
|
+
beforeCreate: z.custom<FieldHookFn>((val) => typeof val === "function").optional(),
|
|
31
|
+
beforeUpdate: z.custom<FieldHookFn>((val) => typeof val === "function").optional(),
|
|
32
|
+
afterCreate: z.custom<FieldHookFn>((val) => typeof val === "function").optional(),
|
|
33
|
+
afterUpdate: z.custom<FieldHookFn>((val) => typeof val === "function").optional(),
|
|
34
|
+
}).optional();
|
|
35
|
+
|
|
18
36
|
// Base field schema
|
|
19
37
|
export const CollectionFieldBaseSchema = z.object({
|
|
38
|
+
hooks: FieldHooksSchema,
|
|
39
|
+
validator: z.custom<FieldValidatorFn>((val) => typeof val === "function").optional(),
|
|
20
40
|
pre_processors: z.record(z.any()).optional(),
|
|
21
41
|
post_processors: z.record(z.any()).optional(),
|
|
22
42
|
validators: z.record(z.any()).optional(),
|
|
@@ -4,6 +4,18 @@ import {
|
|
|
4
4
|
CollectionIntegerFieldSchema,
|
|
5
5
|
} from "./collectionFields.ts";
|
|
6
6
|
|
|
7
|
+
export type CollectionHookFn = (ctx: {
|
|
8
|
+
data: Record<string, any>;
|
|
9
|
+
context?: any;
|
|
10
|
+
}) => void | Promise<void>;
|
|
11
|
+
|
|
12
|
+
const CollectionHooksSchema = z.object({
|
|
13
|
+
beforeCreate: z.custom<CollectionHookFn>((val) => typeof val === "function").optional(),
|
|
14
|
+
afterCreate: z.custom<CollectionHookFn>((val) => typeof val === "function").optional(),
|
|
15
|
+
beforeUpdate: z.custom<CollectionHookFn>((val) => typeof val === "function").optional(),
|
|
16
|
+
afterUpdate: z.custom<CollectionHookFn>((val) => typeof val === "function").optional(),
|
|
17
|
+
}).optional();
|
|
18
|
+
|
|
7
19
|
// CollectionIndexField
|
|
8
20
|
export const CollectionIndexFieldSchema = z.object({
|
|
9
21
|
order: z.enum(["asc", "desc"]),
|
|
@@ -33,6 +45,7 @@ export const CollectionFieldsSchema = z.intersection(
|
|
|
33
45
|
export const CollectionConfigSchema = z.object({
|
|
34
46
|
category: z.string().optional(),
|
|
35
47
|
singleton: z.boolean().optional(),
|
|
48
|
+
hooks: CollectionHooksSchema,
|
|
36
49
|
indexes: CollectionIndexesSchema,
|
|
37
50
|
fields: CollectionFieldsSchema,
|
|
38
51
|
});
|
|
@@ -3,13 +3,18 @@ import { getCollectionsTableWorkflows } from "./collectionsTable/index.ts";
|
|
|
3
3
|
import { getUtilsCoreWorkflows } from "./utilsCoreWorkflows.ts";
|
|
4
4
|
import { postOperationsWorkflows } from "./processors/postOperationsWorkflows.ts";
|
|
5
5
|
import { preOperationsWorkflows } from "./processors/preOperationsWorkflows.ts";
|
|
6
|
+
import { hooksWorkflows } from "./processors/hooksWorkflows.ts";
|
|
7
|
+
import { validatorWorkflows } from "./processors/validatorWorkflows.ts";
|
|
6
8
|
import { coreWorkflowsCollectionWorkflows } from "./workflowsCollection/workflowsCollectionWorkflows.ts";
|
|
7
9
|
import { getQueryCoreWorkflows } from "./queryCoreWorkflows.ts";
|
|
8
10
|
|
|
9
11
|
export function getCoreWorkflows(): Array<Workflow> {
|
|
10
12
|
return [
|
|
13
|
+
...hooksWorkflows.filter((w) => w.name.startsWith("core_hooksBefore")),
|
|
14
|
+
...validatorWorkflows,
|
|
11
15
|
...preOperationsWorkflows,
|
|
12
16
|
...postOperationsWorkflows,
|
|
17
|
+
...hooksWorkflows.filter((w) => w.name.startsWith("core_hooksAfter")),
|
|
13
18
|
...coreWorkflowsCollectionWorkflows,
|
|
14
19
|
...getCollectionsTableWorkflows(),
|
|
15
20
|
...getQueryCoreWorkflows(),
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { Workflow } from "../../WorkflowSystem.ts";
|
|
2
|
+
import { Lobb } from "../../../Lobb.ts";
|
|
3
|
+
|
|
4
|
+
async function runFieldHooks(
|
|
5
|
+
hookName: "beforeCreate" | "beforeUpdate" | "afterCreate" | "afterUpdate",
|
|
6
|
+
input: any,
|
|
7
|
+
) {
|
|
8
|
+
const fields = Lobb.instance.configManager.getCollection(input.collectionName).fields;
|
|
9
|
+
for (const [fieldName, fieldConfig] of Object.entries(fields)) {
|
|
10
|
+
const hook = fieldConfig.hooks?.[hookName];
|
|
11
|
+
if (!hook) continue;
|
|
12
|
+
const result = await hook({ data: input.data, context: input.context });
|
|
13
|
+
if (result !== undefined) {
|
|
14
|
+
input.data[fieldName] = result;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const hooksWorkflows: Workflow[] = [
|
|
20
|
+
{
|
|
21
|
+
name: "core_hooksBeforeCreate",
|
|
22
|
+
eventName: "core.store.preCreateOne",
|
|
23
|
+
handler: async (input) => {
|
|
24
|
+
const hooks = Lobb.instance.configManager.getCollection(input.collectionName).hooks;
|
|
25
|
+
await hooks?.beforeCreate?.({ data: input.data, context: input.context });
|
|
26
|
+
await runFieldHooks("beforeCreate", input);
|
|
27
|
+
return input;
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: "core_hooksBeforeUpdate",
|
|
32
|
+
eventName: "core.store.preUpdateOne",
|
|
33
|
+
handler: async (input) => {
|
|
34
|
+
const hooks = Lobb.instance.configManager.getCollection(input.collectionName).hooks;
|
|
35
|
+
await hooks?.beforeUpdate?.({ data: input.data, context: input.context });
|
|
36
|
+
await runFieldHooks("beforeUpdate", input);
|
|
37
|
+
return input;
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: "core_hooksAfterCreate",
|
|
42
|
+
eventName: "core.store.createOne",
|
|
43
|
+
handler: async (input) => {
|
|
44
|
+
const hooks = Lobb.instance.configManager.getCollection(input.collectionName).hooks;
|
|
45
|
+
await hooks?.afterCreate?.({ data: input.data, context: input.context });
|
|
46
|
+
await runFieldHooks("afterCreate", input);
|
|
47
|
+
return input;
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: "core_hooksAfterUpdate",
|
|
52
|
+
eventName: "core.store.updateOne",
|
|
53
|
+
handler: async (input) => {
|
|
54
|
+
const hooks = Lobb.instance.configManager.getCollection(input.collectionName).hooks;
|
|
55
|
+
await hooks?.afterUpdate?.({ data: input.data, context: input.context });
|
|
56
|
+
await runFieldHooks("afterUpdate", input);
|
|
57
|
+
return input;
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
];
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { Workflow } from "../../WorkflowSystem.ts";
|
|
2
|
+
import { Lobb } from "../../../Lobb.ts";
|
|
3
|
+
import { LobbError } from "../../../LobbError.ts";
|
|
4
|
+
|
|
5
|
+
async function runFieldValidators(input: any, processAllFields: boolean) {
|
|
6
|
+
const fields = Lobb.instance.configManager.getCollection(input.collectionName).fields;
|
|
7
|
+
const data = input.data;
|
|
8
|
+
const errors: Record<string, string[]> = {};
|
|
9
|
+
|
|
10
|
+
const fieldNames = processAllFields ? Object.keys(fields) : Object.keys(data);
|
|
11
|
+
|
|
12
|
+
for (const fieldName of fieldNames) {
|
|
13
|
+
const fieldConfig = fields[fieldName];
|
|
14
|
+
if (!fieldConfig?.validator) continue;
|
|
15
|
+
|
|
16
|
+
const error = await fieldConfig.validator({
|
|
17
|
+
value: data[fieldName],
|
|
18
|
+
data,
|
|
19
|
+
context: input.context,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
if (error) {
|
|
23
|
+
if (!errors[fieldName]) errors[fieldName] = [];
|
|
24
|
+
errors[fieldName].push(error);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (Object.keys(errors).length) {
|
|
29
|
+
throw new LobbError({
|
|
30
|
+
code: "BAD_REQUEST",
|
|
31
|
+
message: "Validation failed. Please check your inputs and try again.",
|
|
32
|
+
details: errors,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return input;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const validatorWorkflows: Workflow[] = [
|
|
40
|
+
{
|
|
41
|
+
name: "core_validatorOnCreate",
|
|
42
|
+
eventName: "core.store.preCreateOne",
|
|
43
|
+
handler: async (input) => {
|
|
44
|
+
return await runFieldValidators(input, true);
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: "core_validatorOnUpdate",
|
|
49
|
+
eventName: "core.store.preUpdateOne",
|
|
50
|
+
handler: async (input) => {
|
|
51
|
+
return await runFieldValidators(input, false);
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
];
|