@lobb-js/core 0.15.1 → 0.17.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/api/meta/service.ts +1 -0
- package/src/index.ts +2 -0
- package/src/types/config/collectionFields.ts +32 -1
- package/src/workflows/coreWorkflows/index.ts +6 -0
- package/src/workflows/coreWorkflows/processors/defaultWorkflows.ts +26 -0
- package/src/workflows/coreWorkflows/processors/enumWorkflows.ts +49 -0
- package/src/workflows/coreWorkflows/processors/requiredWorkflows.ts +44 -0
package/package.json
CHANGED
package/src/api/meta/service.ts
CHANGED
|
@@ -63,6 +63,7 @@ export class MetaService {
|
|
|
63
63
|
field.type = fieldConfig.type;
|
|
64
64
|
field.label = fieldName;
|
|
65
65
|
field.key = fieldName;
|
|
66
|
+
field.enum = "enum" in fieldConfig ? fieldConfig.enum : undefined;
|
|
66
67
|
field.validators = fieldConfig.validators;
|
|
67
68
|
field.pre_processors = fieldConfig.pre_processors;
|
|
68
69
|
field.ui = fieldConfig.ui;
|
package/src/index.ts
CHANGED
|
@@ -28,6 +28,8 @@ export type { CollectionFieldsWithoutId } from "./types/config/collectionsConfig
|
|
|
28
28
|
export type {
|
|
29
29
|
CollectionField,
|
|
30
30
|
CollectionFieldBase,
|
|
31
|
+
EnumOption,
|
|
32
|
+
EnumLevel,
|
|
31
33
|
} from "./types/config/collectionFields.ts";
|
|
32
34
|
export type { Dashboard, Extension } from "./types/Extension.ts";
|
|
33
35
|
export { Field } from "./types/Field.ts";
|
|
@@ -2,6 +2,17 @@ import { RelationCollectionFieldSchema } from "./relations.ts";
|
|
|
2
2
|
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
|
|
5
|
+
export type EnumLevel = "success" | "warning" | "danger" | "info" | "neutral" | "muted";
|
|
6
|
+
|
|
7
|
+
export const EnumOptionSchema = z.object({
|
|
8
|
+
value: z.string(),
|
|
9
|
+
level: z.enum(["success", "warning", "danger", "info", "neutral", "muted"]),
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
export type EnumOption = z.infer<typeof EnumOptionSchema>;
|
|
13
|
+
|
|
14
|
+
const StringEnumSchema = z.union([z.array(z.string()), z.array(EnumOptionSchema)]);
|
|
15
|
+
|
|
5
16
|
// Define the UiInput schema
|
|
6
17
|
const UiInputSchema = z.object({
|
|
7
18
|
type: z.string(),
|
|
@@ -33,8 +44,9 @@ const FieldHooksSchema = z.object({
|
|
|
33
44
|
afterUpdate: z.custom<FieldHookFn>((val) => typeof val === "function").optional(),
|
|
34
45
|
}).optional();
|
|
35
46
|
|
|
36
|
-
// Base field schema
|
|
47
|
+
// Base field schema (no default — each specialized schema adds it with the correct type)
|
|
37
48
|
export const CollectionFieldBaseSchema = z.object({
|
|
49
|
+
required: z.boolean().optional(),
|
|
38
50
|
hooks: FieldHooksSchema,
|
|
39
51
|
validator: z.custom<FieldValidatorFn>((val) => typeof val === "function").optional(),
|
|
40
52
|
pre_processors: z.record(z.any()).optional(),
|
|
@@ -48,44 +60,63 @@ export type CollectionFieldBase = z.infer<typeof CollectionFieldBaseSchema>;
|
|
|
48
60
|
// Specialized field schemas
|
|
49
61
|
export const CollectionBoolFieldSchema = CollectionFieldBaseSchema.extend({
|
|
50
62
|
type: z.literal("bool"),
|
|
63
|
+
default: z.boolean().optional(),
|
|
51
64
|
});
|
|
52
65
|
|
|
53
66
|
export const CollectionDateFieldSchema = CollectionFieldBaseSchema.extend({
|
|
54
67
|
type: z.literal("date"),
|
|
68
|
+
default: z.union([z.string(), z.date()]).optional(),
|
|
69
|
+
enum: z.array(z.union([z.string(), z.date()])).optional(),
|
|
55
70
|
});
|
|
56
71
|
|
|
57
72
|
export const CollectionDateTimeFieldSchema = CollectionFieldBaseSchema.extend({
|
|
58
73
|
type: z.literal("datetime"),
|
|
74
|
+
default: z.union([z.string(), z.date()]).optional(),
|
|
75
|
+
enum: z.array(z.union([z.string(), z.date()])).optional(),
|
|
59
76
|
});
|
|
60
77
|
|
|
61
78
|
export const CollectionDecimalFieldSchema = CollectionFieldBaseSchema.extend({
|
|
62
79
|
type: z.literal("decimal"),
|
|
80
|
+
default: z.number().optional(),
|
|
81
|
+
enum: z.array(z.number()).optional(),
|
|
63
82
|
});
|
|
64
83
|
|
|
65
84
|
export const CollectionFloatFieldSchema = CollectionFieldBaseSchema.extend({
|
|
66
85
|
type: z.literal("float"),
|
|
86
|
+
default: z.number().optional(),
|
|
87
|
+
enum: z.array(z.number()).optional(),
|
|
67
88
|
});
|
|
68
89
|
|
|
69
90
|
export const CollectionIntegerFieldSchema = CollectionFieldBaseSchema.extend({
|
|
70
91
|
type: z.literal("integer"),
|
|
92
|
+
default: z.number().int().optional(),
|
|
93
|
+
enum: z.array(z.number().int()).optional(),
|
|
71
94
|
references: RelationCollectionFieldSchema.optional(),
|
|
72
95
|
});
|
|
73
96
|
|
|
74
97
|
export const CollectionLongFieldSchema = CollectionFieldBaseSchema.extend({
|
|
75
98
|
type: z.literal("long"),
|
|
99
|
+
default: z.number().optional(),
|
|
100
|
+
enum: z.array(z.number()).optional(),
|
|
76
101
|
});
|
|
77
102
|
|
|
78
103
|
export const CollectionStringFieldSchema = CollectionFieldBaseSchema.extend({
|
|
79
104
|
type: z.literal("string"),
|
|
80
105
|
length: z.number(),
|
|
106
|
+
default: z.string().optional(),
|
|
107
|
+
enum: StringEnumSchema.optional(),
|
|
81
108
|
});
|
|
82
109
|
|
|
83
110
|
export const CollectionTextFieldSchema = CollectionFieldBaseSchema.extend({
|
|
84
111
|
type: z.literal("text"),
|
|
112
|
+
default: z.string().optional(),
|
|
113
|
+
enum: StringEnumSchema.optional(),
|
|
85
114
|
});
|
|
86
115
|
|
|
87
116
|
export const CollectionTimeFieldSchema = CollectionFieldBaseSchema.extend({
|
|
88
117
|
type: z.literal("time"),
|
|
118
|
+
default: z.string().optional(),
|
|
119
|
+
enum: StringEnumSchema.optional(),
|
|
89
120
|
});
|
|
90
121
|
|
|
91
122
|
// Union type if you want a single schema for all
|
|
@@ -5,12 +5,18 @@ import { postOperationsWorkflows } from "./processors/postOperationsWorkflows.ts
|
|
|
5
5
|
import { preOperationsWorkflows } from "./processors/preOperationsWorkflows.ts";
|
|
6
6
|
import { hooksWorkflows } from "./processors/hooksWorkflows.ts";
|
|
7
7
|
import { validatorWorkflows } from "./processors/validatorWorkflows.ts";
|
|
8
|
+
import { requiredWorkflows } from "./processors/requiredWorkflows.ts";
|
|
9
|
+
import { defaultWorkflows } from "./processors/defaultWorkflows.ts";
|
|
10
|
+
import { enumWorkflows } from "./processors/enumWorkflows.ts";
|
|
8
11
|
import { coreWorkflowsCollectionWorkflows } from "./workflowsCollection/workflowsCollectionWorkflows.ts";
|
|
9
12
|
import { getQueryCoreWorkflows } from "./queryCoreWorkflows.ts";
|
|
10
13
|
|
|
11
14
|
export function getCoreWorkflows(): Array<Workflow> {
|
|
12
15
|
return [
|
|
13
16
|
...hooksWorkflows.filter((w) => w.name.startsWith("core_hooksBefore")),
|
|
17
|
+
...defaultWorkflows,
|
|
18
|
+
...requiredWorkflows,
|
|
19
|
+
...enumWorkflows,
|
|
14
20
|
...validatorWorkflows,
|
|
15
21
|
...preOperationsWorkflows,
|
|
16
22
|
...postOperationsWorkflows,
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Workflow } from "../../WorkflowSystem.ts";
|
|
2
|
+
import { Lobb } from "../../../Lobb.ts";
|
|
3
|
+
|
|
4
|
+
async function applyDefaults(input: any) {
|
|
5
|
+
const fields = Lobb.instance.configManager.getCollection(input.collectionName).fields;
|
|
6
|
+
const data = input.data;
|
|
7
|
+
|
|
8
|
+
for (const [fieldName, fieldConfig] of Object.entries(fields) as [string, any][]) {
|
|
9
|
+
if (fieldConfig.default === undefined) continue;
|
|
10
|
+
if (data[fieldName] === undefined) {
|
|
11
|
+
data[fieldName] = typeof fieldConfig.default === "function"
|
|
12
|
+
? fieldConfig.default()
|
|
13
|
+
: fieldConfig.default;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return input;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const defaultWorkflows: Workflow[] = [
|
|
21
|
+
{
|
|
22
|
+
name: "core_defaultOnCreate",
|
|
23
|
+
eventName: "core.store.preCreateOne",
|
|
24
|
+
handler: async (input) => applyDefaults(input),
|
|
25
|
+
},
|
|
26
|
+
];
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { Workflow } from "../../WorkflowSystem.ts";
|
|
2
|
+
import { Lobb } from "../../../Lobb.ts";
|
|
3
|
+
import { LobbError } from "../../../LobbError.ts";
|
|
4
|
+
|
|
5
|
+
async function runEnumCheck(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] as any;
|
|
14
|
+
if (!fieldConfig?.enum) continue;
|
|
15
|
+
|
|
16
|
+
const value = data[fieldName];
|
|
17
|
+
if (value === undefined || value === null) continue;
|
|
18
|
+
|
|
19
|
+
const allowedValues = fieldConfig.enum.map((item: any) =>
|
|
20
|
+
typeof item === "object" ? item.value : item
|
|
21
|
+
);
|
|
22
|
+
if (!allowedValues.includes(value)) {
|
|
23
|
+
errors[fieldName] = [`${fieldName} must be one of: ${allowedValues.join(", ")}`];
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (Object.keys(errors).length) {
|
|
28
|
+
throw new LobbError({
|
|
29
|
+
code: "BAD_REQUEST",
|
|
30
|
+
message: "Validation failed. Please check your inputs and try again.",
|
|
31
|
+
details: errors,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return input;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const enumWorkflows: Workflow[] = [
|
|
39
|
+
{
|
|
40
|
+
name: "core_enumOnCreate",
|
|
41
|
+
eventName: "core.store.preCreateOne",
|
|
42
|
+
handler: async (input) => runEnumCheck(input, true),
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: "core_enumOnUpdate",
|
|
46
|
+
eventName: "core.store.preUpdateOne",
|
|
47
|
+
handler: async (input) => runEnumCheck(input, false),
|
|
48
|
+
},
|
|
49
|
+
];
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Workflow } from "../../WorkflowSystem.ts";
|
|
2
|
+
import { Lobb } from "../../../Lobb.ts";
|
|
3
|
+
import { LobbError } from "../../../LobbError.ts";
|
|
4
|
+
|
|
5
|
+
async function runRequiredCheck(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?.required) continue;
|
|
15
|
+
|
|
16
|
+
const value = data[fieldName];
|
|
17
|
+
if (value === undefined || value === null || value === "") {
|
|
18
|
+
errors[fieldName] = [`${fieldName} is required`];
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (Object.keys(errors).length) {
|
|
23
|
+
throw new LobbError({
|
|
24
|
+
code: "BAD_REQUEST",
|
|
25
|
+
message: "Validation failed. Please check your inputs and try again.",
|
|
26
|
+
details: errors,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return input;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const requiredWorkflows: Workflow[] = [
|
|
34
|
+
{
|
|
35
|
+
name: "core_requiredOnCreate",
|
|
36
|
+
eventName: "core.store.preCreateOne",
|
|
37
|
+
handler: async (input) => runRequiredCheck(input, true),
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: "core_requiredOnUpdate",
|
|
41
|
+
eventName: "core.store.preUpdateOne",
|
|
42
|
+
handler: async (input) => runRequiredCheck(input, false),
|
|
43
|
+
},
|
|
44
|
+
];
|