@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobb-js/core",
3
- "version": "0.15.1",
3
+ "version": "0.17.0",
4
4
  "license": "AGPL-3.0-only",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -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
+ ];