@lobb-js/core 0.22.0 → 0.23.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.
Files changed (36) hide show
  1. package/package.json +1 -1
  2. package/src/Lobb.ts +1 -0
  3. package/src/api/meta/service.ts +0 -2
  4. package/src/config/ConfigManager.ts +25 -0
  5. package/src/coreCollections/collectionsCollection.ts +4 -10
  6. package/src/coreCollections/workflowsCollection.ts +6 -16
  7. package/src/index.ts +0 -2
  8. package/src/types/config/collectionFields.ts +1 -3
  9. package/src/types/index.ts +0 -2
  10. package/src/utils/lockCollectionToObject.ts +7 -15
  11. package/src/utils/utils.ts +0 -73
  12. package/src/workflows/coreWorkflows/index.ts +2 -4
  13. package/src/workflows/coreWorkflows/processors/coerceAndValidateWorkflows.ts +108 -0
  14. package/src/fields/ArrayField.ts +0 -33
  15. package/src/fields/BoolField.ts +0 -34
  16. package/src/fields/DateField.ts +0 -13
  17. package/src/fields/DateTimeField.ts +0 -13
  18. package/src/fields/DecimalField.ts +0 -13
  19. package/src/fields/FieldUtils.ts +0 -56
  20. package/src/fields/FloatField.ts +0 -13
  21. package/src/fields/IntegerField.ts +0 -13
  22. package/src/fields/LongField.ts +0 -13
  23. package/src/fields/ObjectField.ts +0 -15
  24. package/src/fields/StringField.ts +0 -13
  25. package/src/fields/TextField.ts +0 -13
  26. package/src/fields/TimeField.ts +0 -13
  27. package/src/types/Field.ts +0 -29
  28. package/src/types/workflows/processors.ts +0 -1
  29. package/src/workflows/coreWorkflows/processors/postOperationsWorkflows.ts +0 -46
  30. package/src/workflows/coreWorkflows/processors/preOperationsWorkflows.ts +0 -27
  31. package/src/workflows/coreWorkflows/processors/processorForDB.ts +0 -13
  32. package/src/workflows/coreWorkflows/processors/processors/processor.ts +0 -23
  33. package/src/workflows/coreWorkflows/processors/processors/processorsFunctions.ts +0 -47
  34. package/src/workflows/coreWorkflows/processors/utils.ts +0 -103
  35. package/src/workflows/coreWorkflows/processors/validator/validator.ts +0 -19
  36. package/src/workflows/coreWorkflows/processors/validator/validatorsFunction.ts +0 -52
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lobb-js/core",
3
3
  "license": "UNLICENSED",
4
- "version": "0.22.0",
4
+ "version": "0.23.0",
5
5
  "type": "module",
6
6
  "publishConfig": {
7
7
  "access": "public"
package/src/Lobb.ts CHANGED
@@ -144,6 +144,7 @@ export class Lobb {
144
144
  process.chdir(config.project.context ?? ".");
145
145
 
146
146
  await Lobb.instance.extensionSystem.loadExtensionsCollections();
147
+ this.configManager.addUniqueIndexesFromFields();
147
148
  this.configManager.addRelationsFromIntegerRefrences();
148
149
  }
149
150
  }
@@ -68,8 +68,6 @@ export class MetaService {
68
68
  field.label = fieldName;
69
69
  field.key = fieldName;
70
70
  field.enum = "enum" in fieldConfig ? fieldConfig.enum : undefined;
71
- field.validators = "validators" in fieldConfig ? fieldConfig.validators : undefined;
72
- field.pre_processors = "pre_processors" in fieldConfig ? fieldConfig.pre_processors : undefined;
73
71
  field.ui = fieldConfig.ui;
74
72
 
75
73
  collections[collectionName].fields[fieldName] = field;
@@ -42,6 +42,31 @@ export class ConfigManager {
42
42
  };
43
43
  }
44
44
 
45
+ public addUniqueIndexesFromFields() {
46
+ for (
47
+ const [collectionName, collectionValue] of Object.entries(
48
+ this.config.collections,
49
+ )
50
+ ) {
51
+ if (collectionValue.virtual) continue;
52
+ for (
53
+ const [fieldName, fieldValue] of Object.entries(
54
+ (collectionValue as any).fields,
55
+ )
56
+ ) {
57
+ if (fieldValue.unique) {
58
+ const indexName = `${collectionName}_${fieldName}_unique_index`;
59
+ (collectionValue as any).indexes[indexName] = {
60
+ unique: true,
61
+ fields: {
62
+ [fieldName]: { order: "asc" },
63
+ },
64
+ };
65
+ }
66
+ }
67
+ }
68
+ }
69
+
45
70
  public addRelationsFromIntegerRefrences() {
46
71
  for (
47
72
  const [collectionName, collectionValue] of Object.entries(
@@ -24,19 +24,13 @@ export function getCollectionsCollection(): CollectionConfig {
24
24
  "name": {
25
25
  "type": "string",
26
26
  "length": 255,
27
- "validators": {
28
- "required": true,
29
- },
27
+ "required": true,
30
28
  },
31
29
  "value": {
32
30
  "type": "text",
33
- "validators": {
34
- "required": true,
35
- },
36
- "pre_processors": {
37
- "default":
38
- 'async function collection(): Promise<Collection> {\n\treturn {\n\t\tindexes: {},\n\t\tfields: {\n\t\t\tid: {\n\t\t\t\ttype: "integer",\n\t\t\t},\n\t\t},\n\t};\n}',
39
- },
31
+ "required": true,
32
+ "default":
33
+ 'async function collection(): Promise<Collection> {\n\treturn {\n\t\tindexes: {},\n\t\tfields: {\n\t\t\tid: {\n\t\t\t\ttype: "integer",\n\t\t\t},\n\t\t},\n\t};\n}',
40
34
  "ui": {
41
35
  "input": {
42
36
  "type": "code",
@@ -9,9 +9,7 @@ export const workflowsCollection: CollectionConfig = {
9
9
  "name": {
10
10
  "type": "string",
11
11
  "length": 255,
12
- "validators": {
13
- "required": true,
14
- },
12
+ "required": true,
15
13
  },
16
14
  "event_name": {
17
15
  "type": "string",
@@ -23,9 +21,7 @@ export const workflowsCollection: CollectionConfig = {
23
21
  },
24
22
  "input_schema": {
25
23
  "type": "text",
26
- "pre_processors": {
27
- "default": "function schema(z) {\n\treturn z.object({});\n}",
28
- },
24
+ "default": "function schema(z) {\n\treturn z.object({});\n}",
29
25
  "ui": {
30
26
  "input": {
31
27
  "type": "code",
@@ -38,9 +34,7 @@ export const workflowsCollection: CollectionConfig = {
38
34
  },
39
35
  "output_schema": {
40
36
  "type": "text",
41
- "pre_processors": {
42
- "default": "function schema(z) {\n\treturn z.object({});\n}",
43
- },
37
+ "default": "function schema(z) {\n\treturn z.object({});\n}",
44
38
  "ui": {
45
39
  "input": {
46
40
  "type": "code",
@@ -53,13 +47,9 @@ export const workflowsCollection: CollectionConfig = {
53
47
  },
54
48
  "handler": {
55
49
  "type": "text",
56
- "validators": {
57
- "required": true,
58
- },
59
- "pre_processors": {
60
- "default":
61
- "async function workflow(input: Input, ctx: Context): Promise<Output> {\n\treturn input;\n}",
62
- },
50
+ "required": true,
51
+ "default":
52
+ "async function workflow(input: Input, ctx: Context): Promise<Output> {\n\treturn input;\n}",
63
53
  "ui": {
64
54
  "input": {
65
55
  "type": "code",
package/src/index.ts CHANGED
@@ -32,7 +32,6 @@ export type {
32
32
  EnumColor,
33
33
  } from "./types/config/collectionFields.ts";
34
34
  export type { Dashboard, Extension } from "./types/Extension.ts";
35
- export { Field } from "./types/Field.ts";
36
35
  export type { Migration, MigrationProps, Migrations } from "./types/migrations.ts";
37
36
  export type { Filter } from "./types/filterSchema.ts";
38
37
  export {
@@ -52,4 +51,3 @@ export type {
52
51
  RequestMessage,
53
52
  SubscribeMessage,
54
53
  } from "./types/websockets.ts";
55
- export type { ProcessorType } from "./types/workflows/processors.ts";
@@ -54,11 +54,9 @@ const FieldHooksSchema = z.object({
54
54
  export const CollectionFieldBaseSchema = z.object({
55
55
  virtual: z.never().optional(),
56
56
  required: z.boolean().optional(),
57
+ unique: z.boolean().optional(),
57
58
  hooks: FieldHooksSchema,
58
59
  validator: z.custom<FieldValidatorFn>((val) => typeof val === "function").optional(),
59
- pre_processors: z.record(z.any()).optional(),
60
- post_processors: z.record(z.any()).optional(),
61
- validators: z.record(z.any()).optional(),
62
60
  ui: UiSchema.optional(),
63
61
  });
64
62
 
@@ -16,7 +16,6 @@ export type {
16
16
  CollectionFieldBase,
17
17
  } from "./config/collectionFields.ts";
18
18
  export type { Dashboard, Extension } from "./Extension.ts";
19
- export { Field } from "./Field.ts";
20
19
  export type { Migration, MigrationProps, Migrations } from "./migrations.ts";
21
20
  export type { Filter } from "./filterSchema.ts";
22
21
  export {
@@ -36,4 +35,3 @@ export type {
36
35
  RequestMessage,
37
36
  SubscribeMessage,
38
37
  } from "./websockets.ts";
39
- export type { ProcessorType } from "./workflows/processors.ts";
@@ -37,36 +37,28 @@ function generateSchemasForLockedCollections(
37
37
  "key": {
38
38
  "type": "string",
39
39
  "length": 255,
40
- "validators": {
41
- "required": true,
42
- },
40
+ "required": true,
43
41
  },
44
42
  "value": {
45
43
  "type": "text",
46
- "validators": {
47
- "required": true,
48
- },
49
- "pre_processors": {
50
- "default": "{}",
51
- },
44
+ "required": true,
45
+ "default": "{}",
52
46
  "ui": {
53
47
  "input": {
54
48
  "type": "code",
55
49
  "args": {
56
50
  "type": "json",
57
51
  "height": 200,
58
- // "types": "type Value = any",
59
52
  },
60
53
  },
61
54
  },
62
55
  },
63
56
  "static": {
64
57
  "type": "bool",
65
- "pre_processors": {
66
- "overwrite": false,
67
- },
68
- "validators": {
69
- "required": true,
58
+ "required": true,
59
+ "hooks": {
60
+ "beforeCreate": () => false,
61
+ "beforeUpdate": () => false,
70
62
  },
71
63
  },
72
64
  },
@@ -1,7 +1,5 @@
1
1
  import type { Context } from "hono";
2
2
  import type { ApiCollectionAction } from "../types/index.ts";
3
- import type { ProcessorType } from "../types/index.ts";
4
- import type { CollectionFieldBase } from "../types/index.ts";
5
3
  import type { CollectionConfig, CollectionsConfig } from "../types/index.ts";
6
4
  import type { PoolClient } from "pg";
7
5
 
@@ -9,13 +7,8 @@ import _ from "lodash";
9
7
  import nunjucks from "nunjucks";
10
8
  import { transform } from "sucrase";
11
9
  import { LobbError } from "../LobbError.ts";
12
- import { FieldUtils } from "../fields/FieldUtils.ts";
13
10
  import { coreCollections } from "../coreCollections/index.ts";
14
11
  import { Lobb } from "../Lobb.ts";
15
- import {
16
- processPayloadWithSchema,
17
- validatePayloadWithSchema,
18
- } from "../workflows/coreWorkflows/processors/utils.ts";
19
12
  import { createTypeAlias, printNode, zodToTs } from "zod-to-ts";
20
13
  import {
21
14
  innerLockCollectionToObject,
@@ -194,44 +187,6 @@ export const Utils = {
194
187
 
195
188
  return array;
196
189
  },
197
- processDocumentsForDb(
198
- type: "encode" | "decode",
199
- collectionName: string,
200
- data: any,
201
- ) {
202
- const isArray = Array.isArray(data);
203
- const entries = isArray ? data : [data];
204
- for (let index = 0; index < entries.length; index++) {
205
- for (const fieldName of Object.keys(entries[index])) {
206
- const fieldValue = entries[index][fieldName];
207
- if (fieldValue === null) {
208
- continue;
209
- }
210
- const doesFieldExists = Lobb.instance.configManager.fieldExists(
211
- fieldName,
212
- collectionName,
213
- );
214
- if (!doesFieldExists) {
215
- continue;
216
- }
217
- const field = FieldUtils.getFieldInstance(
218
- fieldName,
219
- collectionName,
220
- );
221
- let result;
222
- if (type === "encode") {
223
- result = field.encodeFieldValue(fieldValue);
224
- } else {
225
- result = field.decodeFieldValue(fieldValue);
226
- }
227
- if (result !== undefined) {
228
- entries[index][fieldName] = result;
229
- }
230
- }
231
- }
232
-
233
- return isArray ? entries : entries[0];
234
- },
235
190
  getCollectionOwner(collectionName: string) {
236
191
  const lobbInstance = Lobb.instance;
237
192
  const owner = lobbInstance.extensionSystem.getCollectionOwner(
@@ -246,34 +201,6 @@ export const Utils = {
246
201
  }
247
202
  return "__project";
248
203
  },
249
- processPayloadWithSchema(
250
- type: ProcessorType,
251
- data: any,
252
- fieldsSchema: Record<string, CollectionFieldBase>,
253
- processAllFields: boolean = false,
254
- context: any,
255
- ) {
256
- return processPayloadWithSchema(
257
- type,
258
- data,
259
- fieldsSchema,
260
- processAllFields,
261
- context,
262
- );
263
- },
264
- validatePayloadWithSchema(
265
- data: any,
266
- fieldsSchema: Record<string, CollectionFieldBase>,
267
- processAllFields: boolean,
268
- context: any,
269
- ): Promise<any> {
270
- return validatePayloadWithSchema(
271
- data,
272
- fieldsSchema,
273
- processAllFields,
274
- context,
275
- );
276
- },
277
204
  parseFunction(functionString: string) {
278
205
  const { code: vanillaJsFunctionString } = transform(functionString, {
279
206
  transforms: ["typescript"],
@@ -1,25 +1,23 @@
1
1
  import type { Workflow } from "../WorkflowSystem.ts";
2
2
  import { getCollectionsTableWorkflows } from "./collectionsTable/index.ts";
3
3
  import { getUtilsCoreWorkflows } from "./utilsCoreWorkflows.ts";
4
- import { postOperationsWorkflows } from "./processors/postOperationsWorkflows.ts";
5
- import { preOperationsWorkflows } from "./processors/preOperationsWorkflows.ts";
6
4
  import { hooksWorkflows } from "./processors/hooksWorkflows.ts";
7
5
  import { validatorWorkflows } from "./processors/validatorWorkflows.ts";
8
6
  import { requiredWorkflows } from "./processors/requiredWorkflows.ts";
9
7
  import { defaultWorkflows } from "./processors/defaultWorkflows.ts";
10
8
  import { enumWorkflows } from "./processors/enumWorkflows.ts";
9
+ import { coerceAndValidateWorkflows } from "./processors/coerceAndValidateWorkflows.ts";
11
10
  import { coreWorkflowsCollectionWorkflows } from "./workflowsCollection/workflowsCollectionWorkflows.ts";
12
11
  import { getQueryCoreWorkflows } from "./queryCoreWorkflows.ts";
13
12
 
14
13
  export function getCoreWorkflows(): Array<Workflow> {
15
14
  return [
15
+ ...coerceAndValidateWorkflows,
16
16
  ...hooksWorkflows.filter((w) => w.name.startsWith("core_hooksBefore")),
17
17
  ...defaultWorkflows,
18
18
  ...requiredWorkflows,
19
19
  ...enumWorkflows,
20
20
  ...validatorWorkflows,
21
- ...preOperationsWorkflows,
22
- ...postOperationsWorkflows,
23
21
  ...hooksWorkflows.filter((w) => w.name.startsWith("core_hooksAfter")),
24
22
  ...coreWorkflowsCollectionWorkflows,
25
23
  ...getCollectionsTableWorkflows(),
@@ -0,0 +1,108 @@
1
+ import type { Workflow } from "../../WorkflowSystem.ts";
2
+ import { Lobb } from "../../../Lobb.ts";
3
+ import { LobbError } from "../../../LobbError.ts";
4
+
5
+ async function coerceAndValidate(input: any) {
6
+ if (Lobb.instance.configManager.isCollectionVirtual(input.collectionName)) return input;
7
+ const fields = Lobb.instance.configManager.getNormalCollection(input.collectionName).fields;
8
+ const data = input.data;
9
+ const errors: Record<string, string[]> = {};
10
+
11
+ for (const [fieldName, fieldConfig] of Object.entries(fields) as [string, any][]) {
12
+ if (!(fieldName in data)) continue;
13
+ const value = data[fieldName];
14
+
15
+ try {
16
+ if (value === null || value === undefined) continue;
17
+
18
+ if (fieldConfig.type === "string") {
19
+ if (typeof value !== "string") {
20
+ data[fieldName] = String(value);
21
+ }
22
+ } else if (fieldConfig.type === "text") {
23
+ if (typeof value !== "string") {
24
+ data[fieldName] = String(value);
25
+ }
26
+ } else if (fieldConfig.type === "integer") {
27
+ if (value === "") {
28
+ data[fieldName] = null;
29
+ } else {
30
+ const int = Number(value);
31
+ if (!Number.isFinite(int)) throw new Error(`${fieldName} must be a valid integer`);
32
+ data[fieldName] = Math.trunc(int);
33
+ }
34
+ } else if (fieldConfig.type === "long") {
35
+ if (value === "") {
36
+ data[fieldName] = null;
37
+ } else if (typeof value === "string" && !/^-?\d+$/.test(value.trim())) {
38
+ throw new Error(`${fieldName} must be a valid integer`);
39
+ }
40
+ } else if (fieldConfig.type === "float") {
41
+ if (value === "") {
42
+ data[fieldName] = null;
43
+ } else {
44
+ const float = Number(value);
45
+ if (!Number.isFinite(float)) throw new Error(`${fieldName} must be a valid number`);
46
+ data[fieldName] = float;
47
+ }
48
+ } else if (fieldConfig.type === "decimal") {
49
+ if (value === "") {
50
+ data[fieldName] = null;
51
+ } else if (typeof value === "string" && isNaN(Number(value))) {
52
+ throw new Error(`${fieldName} must be a valid decimal number`);
53
+ }
54
+ } else if (fieldConfig.type === "bool") {
55
+ if (value === "") {
56
+ data[fieldName] = null;
57
+ } else if (typeof value === "boolean") {
58
+ // already correct, nothing to do
59
+ } else if (typeof value === "string" && value.toLowerCase() === "true" || value === "1" || value === 1) {
60
+ data[fieldName] = true;
61
+ } else if (typeof value === "string" && value.toLowerCase() === "false" || value === "0" || value === 0) {
62
+ data[fieldName] = false;
63
+ } else {
64
+ throw new Error(`${fieldName} must be a valid boolean`);
65
+ }
66
+ } else if (fieldConfig.type === "date") {
67
+ if (value === "") {
68
+ data[fieldName] = null;
69
+ }
70
+ } else if (fieldConfig.type === "datetime") {
71
+ if (value === "") {
72
+ data[fieldName] = null;
73
+ }
74
+ } else if (fieldConfig.type === "time") {
75
+ if (value === "") {
76
+ data[fieldName] = null;
77
+ }
78
+ } else {
79
+ throw new Error(`coerceAndValidate: type "${fieldConfig.type}" is not implemented`);
80
+ }
81
+ } catch (e: any) {
82
+ errors[fieldName] = [e.message];
83
+ }
84
+ }
85
+
86
+ if (Object.keys(errors).length) {
87
+ throw new LobbError({
88
+ code: "BAD_REQUEST",
89
+ message: "Validation failed. Please check your inputs and try again.",
90
+ details: errors,
91
+ });
92
+ }
93
+
94
+ return input;
95
+ }
96
+
97
+ export const coerceAndValidateWorkflows: Workflow[] = [
98
+ {
99
+ name: "core_coerceAndValidateOnCreate",
100
+ eventName: "core.store.preCreateOne",
101
+ handler: async (input) => coerceAndValidate(input),
102
+ },
103
+ {
104
+ name: "core_coerceAndValidateOnUpdate",
105
+ eventName: "core.store.preUpdateOne",
106
+ handler: async (input) => coerceAndValidate(input),
107
+ },
108
+ ];
@@ -1,33 +0,0 @@
1
- // import { Field, type FieldOperatorsType } from "./Field.ts";
2
-
3
- // export class ArrayField extends Field {
4
- // public static override type: string = "array";
5
- // protected fieldName: string;
6
- // protected collectionName: string;
7
- // public fieldOperatorsType: FieldOperatorsType = "string";
8
-
9
- // constructor(fieldName: string, collectionName: string) {
10
- // super();
11
- // this.fieldName = fieldName;
12
- // this.collectionName = collectionName;
13
- // }
14
-
15
- // public override encodeFieldValue(value: any): any[] | null {
16
- // if (value === null || value === undefined) {
17
- // return null;
18
- // }
19
-
20
- // if (typeof value === "string") {
21
- // try {
22
- // value = JSON.parse(value);
23
- // } catch (_error) {
24
- // this.throwIncompatibleValueTypeError();
25
- // }
26
- // }
27
-
28
- // if (!Array.isArray(value)) {
29
- // this.throwIncompatibleValueTypeError();
30
- // }
31
- // return value;
32
- // }
33
- // }
@@ -1,34 +0,0 @@
1
- import { Field } from "../types/index.ts";
2
-
3
- export class BoolField extends Field {
4
- public static override type: string = "bool";
5
- protected fieldName: string;
6
- protected collectionName: string;
7
-
8
- constructor(fieldName: string, collectionName: string) {
9
- super();
10
- this.fieldName = fieldName;
11
- this.collectionName = collectionName;
12
- }
13
-
14
- public override encodeFieldValue(value: any): boolean | null {
15
- if (value === null || value === undefined || value === "null") {
16
- return null;
17
- }
18
-
19
- if (typeof value === "boolean") {
20
- return value;
21
- }
22
-
23
- if (typeof value === "string") {
24
- const lowerCaseValue = value.toLowerCase().trim();
25
- if (lowerCaseValue === "true" || lowerCaseValue === "1") {
26
- return true;
27
- } else if (lowerCaseValue === "false" || lowerCaseValue === "0") {
28
- return false;
29
- }
30
- }
31
-
32
- this.throwIncompatibleValueTypeError();
33
- }
34
- }
@@ -1,13 +0,0 @@
1
- import { Field } from "../types/index.ts";
2
-
3
- export class DateField extends Field {
4
- public static override type: string = "date";
5
- protected fieldName: string;
6
- protected collectionName: string;
7
-
8
- constructor(fieldName: string, collectionName: string) {
9
- super();
10
- this.fieldName = fieldName;
11
- this.collectionName = collectionName;
12
- }
13
- }
@@ -1,13 +0,0 @@
1
- import { Field } from "../types/index.ts";
2
-
3
- export class DateTimeField extends Field {
4
- public static override type: string = "datetime";
5
- protected fieldName: string;
6
- protected collectionName: string;
7
-
8
- constructor(fieldName: string, collectionName: string) {
9
- super();
10
- this.fieldName = fieldName;
11
- this.collectionName = collectionName;
12
- }
13
- }
@@ -1,13 +0,0 @@
1
- import { Field } from "../types/index.ts";
2
-
3
- export class DecimalField extends Field {
4
- public static override type: string = "decimal";
5
- protected fieldName: string;
6
- protected collectionName: string;
7
-
8
- constructor(fieldName: string, collectionName: string) {
9
- super();
10
- this.fieldName = fieldName;
11
- this.collectionName = collectionName;
12
- }
13
- }
@@ -1,56 +0,0 @@
1
- import type { Field } from "../types/index.ts";
2
- import { StringField } from "../fields/StringField.ts";
3
- import { TextField } from "../fields/TextField.ts";
4
- import { DateField } from "../fields/DateField.ts";
5
- import { LongField } from "../fields/LongField.ts";
6
- import { DecimalField } from "../fields/DecimalField.ts";
7
- import { FloatField } from "../fields/FloatField.ts";
8
- import { BoolField } from "../fields/BoolField.ts";
9
- import { IntegerField } from "../fields/IntegerField.ts";
10
-
11
- import { LobbError } from "../LobbError.ts";
12
- import { TimeField } from "./TimeField.ts";
13
- import { DateTimeField } from "./DateTimeField.ts";
14
- import { Lobb } from "../Lobb.ts";
15
-
16
- export class FieldUtils {
17
- public static fieldClasses: any[] = [
18
- StringField,
19
- TextField,
20
- LongField,
21
- DecimalField,
22
- FloatField,
23
- BoolField,
24
- IntegerField,
25
- DateField,
26
- TimeField,
27
- DateTimeField,
28
- ];
29
-
30
- public static getFieldInstance(
31
- fieldName: string,
32
- collectionName: string,
33
- ): Field {
34
- const fieldConfig = Lobb.instance.configManager.getField(
35
- fieldName,
36
- collectionName,
37
- );
38
-
39
- for (let index = 0; index < this.fieldClasses.length; index++) {
40
- const FieldClass = this.fieldClasses[index];
41
- if (FieldClass.type === fieldConfig.type) {
42
- return new FieldClass(fieldName, collectionName) as Field;
43
- }
44
- }
45
-
46
- throw new LobbError({
47
- code: "INTERNAL_SERVER_ERROR",
48
- message:
49
- `The ${fieldConfig.type} CollectionFieldType specified by the user doesnt exist`,
50
- });
51
- }
52
-
53
- public static getFieldsTypes(): string[] {
54
- return this.fieldClasses.map((Class) => Class.type);
55
- }
56
- }
@@ -1,13 +0,0 @@
1
- import { Field } from "../types/index.ts";
2
-
3
- export class FloatField extends Field {
4
- public static override type: string = "float";
5
- protected fieldName: string;
6
- protected collectionName: string;
7
-
8
- constructor(fieldName: string, collectionName: string) {
9
- super();
10
- this.fieldName = fieldName;
11
- this.collectionName = collectionName;
12
- }
13
- }
@@ -1,13 +0,0 @@
1
- import { Field } from "../types/index.ts";
2
-
3
- export class IntegerField extends Field {
4
- public static override type: string = "integer";
5
- protected fieldName: string;
6
- protected collectionName: string;
7
-
8
- constructor(fieldName: string, collectionName: string) {
9
- super();
10
- this.fieldName = fieldName;
11
- this.collectionName = collectionName;
12
- }
13
- }
@@ -1,13 +0,0 @@
1
- import { Field } from "../types/index.ts";
2
-
3
- export class LongField extends Field {
4
- public static override type: string = "long";
5
- protected fieldName: string;
6
- protected collectionName: string;
7
-
8
- constructor(fieldName: string, collectionName: string) {
9
- super();
10
- this.fieldName = fieldName;
11
- this.collectionName = collectionName;
12
- }
13
- }
@@ -1,15 +0,0 @@
1
- // import { Field, type FieldOperatorsType } from "./Field.ts";
2
- // import _ from "lodash";
3
-
4
- // export class ObjectField extends Field {
5
- // public static override type: string = "object";
6
- // protected fieldName: string;
7
- // protected collectionName: string;
8
- // public fieldOperatorsType: FieldOperatorsType = "string";
9
-
10
- // constructor(fieldName: string, collectionName: string) {
11
- // super();
12
- // this.fieldName = fieldName;
13
- // this.collectionName = collectionName;
14
- // }
15
- // }
@@ -1,13 +0,0 @@
1
- import { Field } from "../types/index.ts";
2
-
3
- export class StringField extends Field {
4
- public static override type: string = "string";
5
- protected fieldName: string;
6
- protected collectionName: string;
7
-
8
- constructor(fieldName: string, collectionName: string) {
9
- super();
10
- this.fieldName = fieldName;
11
- this.collectionName = collectionName;
12
- }
13
- }
@@ -1,13 +0,0 @@
1
- import { Field } from "../types/index.ts";
2
-
3
- export class TextField extends Field {
4
- public static override type: string = "text";
5
- protected fieldName: string;
6
- protected collectionName: string;
7
-
8
- constructor(fieldName: string, collectionName: string) {
9
- super();
10
- this.fieldName = fieldName;
11
- this.collectionName = collectionName;
12
- }
13
- }
@@ -1,13 +0,0 @@
1
- import { Field } from "../types/index.ts";
2
-
3
- export class TimeField extends Field {
4
- public static override type: string = "time";
5
- protected fieldName: string;
6
- protected collectionName: string;
7
-
8
- constructor(fieldName: string, collectionName: string) {
9
- super();
10
- this.fieldName = fieldName;
11
- this.collectionName = collectionName;
12
- }
13
- }
@@ -1,29 +0,0 @@
1
- import { LobbError } from "../LobbError.ts";
2
-
3
- export abstract class Field {
4
- protected abstract fieldName: string;
5
- protected abstract collectionName: string;
6
-
7
- // this forces all child classes to define a static 'type' property
8
- static get type(): string {
9
- throw new Error(
10
- `(${this.name}) class must define a static 'type' property.`,
11
- );
12
- }
13
-
14
- public encodeFieldValue(value: any): any {
15
- return value;
16
- }
17
-
18
- public decodeFieldValue(value: any): any {
19
- return value;
20
- }
21
-
22
- protected throwIncompatibleValueTypeError(): never {
23
- throw new LobbError({
24
- code: "BAD_REQUEST",
25
- message:
26
- `The value provided for the (${this.fieldName}) field is incompatible.`,
27
- });
28
- }
29
- }
@@ -1 +0,0 @@
1
- export type ProcessorType = "pre_processors" | "post_processors";
@@ -1,46 +0,0 @@
1
- import type { Workflow } from "../../WorkflowSystem.ts";
2
- import { processorForDB } from "./processorForDB.ts";
3
- import { processor } from "./processors/processor.ts";
4
-
5
- export const postOperationsWorkflows: Workflow[] = [
6
- {
7
- name: "core_postProcessingForCreate",
8
- eventName: "core.store.createOne",
9
- handler: async (input) => {
10
- input = processorForDB("decode", input);
11
- input = await processor(input, "post_processors", true);
12
-
13
- return input;
14
- },
15
- },
16
- {
17
- name: "core_postProcessingForRead",
18
- eventName: "core.store.findOne",
19
- handler: async (input) => {
20
- input = processorForDB("decode", input);
21
- input = await processor(input, "post_processors", true);
22
-
23
- return input;
24
- },
25
- },
26
- {
27
- name: "core_postProcessingForUpdate",
28
- eventName: "core.store.updateOne",
29
- handler: async (input) => {
30
- input = processorForDB("decode", input);
31
- input = await processor(input, "post_processors", true);
32
-
33
- return input;
34
- },
35
- },
36
- {
37
- name: "core_postProcessingForDelete",
38
- eventName: "core.store.deleteOne",
39
- handler: async (input) => {
40
- input = processorForDB("decode", input);
41
- input = await processor(input, "post_processors", true);
42
-
43
- return input;
44
- },
45
- },
46
- ];
@@ -1,27 +0,0 @@
1
- import type { Workflow } from "../../WorkflowSystem.ts";
2
- import { processorForDB } from "./processorForDB.ts";
3
- import { processor } from "./processors/processor.ts";
4
- import { validator } from "./validator/validator.ts";
5
-
6
- export const preOperationsWorkflows: Workflow[] = [
7
- {
8
- name: "core_preProcessingForPreCreate",
9
- eventName: "core.store.preCreateOne",
10
- handler: async (input) => {
11
- input = await processor(input, "pre_processors", true);
12
- await validator(input, true);
13
- input = processorForDB("encode", input);
14
- return input;
15
- },
16
- },
17
- {
18
- name: "core_preProcessingForPreUpdate",
19
- eventName: "core.store.preUpdateOne",
20
- handler: async (input) => {
21
- input = await processor(input, "pre_processors", false);
22
- await validator(input, false);
23
- input = processorForDB("encode", input);
24
- return input;
25
- },
26
- },
27
- ];
@@ -1,13 +0,0 @@
1
- import { Lobb } from "../../../Lobb.ts";
2
-
3
- export function processorForDB(
4
- type: "encode" | "decode",
5
- input: any,
6
- ) {
7
- input.data = Lobb.instance.utils.processDocumentsForDb(
8
- type,
9
- input.collectionName,
10
- input.data,
11
- );
12
- return input;
13
- }
@@ -1,23 +0,0 @@
1
- import type { ProcessorType } from "../../../../types/index.ts";
2
- import { Lobb } from "../../../../Lobb.ts";
3
-
4
- import { processPayloadWithSchema } from "../utils.ts";
5
-
6
- export async function processor(
7
- input: any,
8
- type: ProcessorType,
9
- processAllFields: boolean,
10
- ) {
11
- const fieldsSchema =
12
- Lobb.instance.configManager.isCollectionVirtual(input.collectionName) ? {} as any : Lobb.instance.configManager.getNormalCollection(input.collectionName).fields;
13
-
14
- input.data = await processPayloadWithSchema(
15
- type,
16
- input.data,
17
- fieldsSchema,
18
- processAllFields,
19
- input.context,
20
- );
21
-
22
- return input;
23
- }
@@ -1,47 +0,0 @@
1
- import { Utils } from "../../../../utils/utils.ts";
2
-
3
- interface ProcessorInput {
4
- value: any;
5
- args?: any;
6
- context?: any;
7
- }
8
-
9
- interface Processor {
10
- handler: (input: ProcessorInput) => Promise<any>;
11
- }
12
-
13
- export const processorsFunctions: Record<string, Processor> = {
14
- default: {
15
- handler: (input) => {
16
- input.args = Utils.renderTemplateDeep(
17
- input.args,
18
- {
19
- value: input.value,
20
- ctx: input.context,
21
- now: (new Date()).toISOString(),
22
- },
23
- );
24
- return typeof input.value === "undefined" || input.value === null
25
- ? input.args
26
- : input.value;
27
- },
28
- },
29
- overwrite: {
30
- handler: (input) => {
31
- input.args = Utils.renderTemplateDeep(
32
- input.args,
33
- {
34
- value: input.value,
35
- ctx: input.context,
36
- now: (new Date()).toISOString(),
37
- },
38
- );
39
- return input.args;
40
- },
41
- },
42
- remove: {
43
- handler: async () => {
44
- return undefined;
45
- },
46
- },
47
- };
@@ -1,103 +0,0 @@
1
- import type { CollectionFieldBase } from "../../../types/index.ts";
2
- import type { ProcessorType } from "../../../types/index.ts";
3
- import { LobbError } from "../../../LobbError.ts";
4
- import { processorsFunctions } from "./processors/processorsFunctions.ts";
5
- import { validatorsFunctions } from "./validator/validatorsFunction.ts";
6
-
7
- export async function processPayloadWithSchema(
8
- type: ProcessorType,
9
- data: any,
10
- fieldsSchema: Record<string, CollectionFieldBase>,
11
- processAllFields: boolean = false,
12
- context: any,
13
- ) {
14
- const entries = Array.isArray(data) ? data : [data];
15
- for (let index = 0; index < entries.length; index++) {
16
- const entry = entries[index];
17
- const fieldsNames = processAllFields
18
- ? Object.keys(fieldsSchema)
19
- : Object.keys(entry);
20
- for (const fieldName of fieldsNames) {
21
- const fieldConfig = fieldsSchema[fieldName];
22
- if (!fieldConfig) {
23
- continue;
24
- }
25
-
26
- const processors = fieldConfig[type];
27
- if (processors) {
28
- for (const processorName in processors) {
29
- const processorArgs: any = processors[processorName];
30
- const result = await processorsFunctions[processorName].handler({
31
- value: entry[fieldName],
32
- args: processorArgs,
33
- context,
34
- });
35
- entry[fieldName] = result;
36
- }
37
- }
38
- }
39
- }
40
-
41
- return data;
42
- }
43
-
44
- export async function validatePayloadWithSchema(
45
- data: any,
46
- fieldsSchema: Record<string, CollectionFieldBase>,
47
- processAllFields: boolean,
48
- context: any,
49
- ) {
50
- const entries = Array.isArray(data) ? data : [data];
51
- const validationErrorsDetails: Record<string, any[]> = {};
52
-
53
- for (let index = 0; index < entries.length; index++) {
54
- const entry = entries[index];
55
- const fieldsNames = processAllFields
56
- ? Object.keys(fieldsSchema)
57
- : Object.keys(entry);
58
- for (const fieldName of fieldsNames) {
59
- const fieldValue = entry[fieldName];
60
- const fieldConfig = fieldsSchema[fieldName];
61
- if (!fieldConfig) {
62
- throw new LobbError({
63
- code: "INTERNAL_SERVER_ERROR",
64
- message:
65
- `The (${fieldName}) fieldName that we will preprocess doesnt exist in the collections`,
66
- });
67
- }
68
-
69
- const validators = fieldConfig.validators;
70
- if (!validators) {
71
- entry[fieldName] = fieldValue;
72
- } else {
73
- for (const validator in validators) {
74
- const validatorArgs: any = validators[validator];
75
- const valid = await validatorsFunctions[validator].handler({
76
- value: fieldValue,
77
- args: validatorArgs,
78
- context,
79
- });
80
- if (!valid) {
81
- if (!validationErrorsDetails[fieldName]) {
82
- validationErrorsDetails[fieldName] = [];
83
- }
84
- validationErrorsDetails[fieldName].push(
85
- `The value provided for (${fieldName}) is not valid according to the (${validator}) validator.`,
86
- );
87
- }
88
- }
89
- }
90
-
91
- }
92
- }
93
-
94
- if (Object.keys(validationErrorsDetails).length) {
95
- throw new LobbError({
96
- code: "BAD_REQUEST",
97
- message: `Validation failed. Please check your inputs and try again.`,
98
- details: validationErrorsDetails,
99
- });
100
- }
101
-
102
- return validationErrorsDetails;
103
- }
@@ -1,19 +0,0 @@
1
- import { Lobb } from "../../../../Lobb.ts";
2
- import { validatePayloadWithSchema } from "../utils.ts";
3
-
4
- export async function validator(
5
- input: any,
6
- processAllFields: boolean = false,
7
- ) {
8
- const fieldsSchema =
9
- Lobb.instance.configManager.isCollectionVirtual(input.collectionName) ? {} as any : Lobb.instance.configManager.getNormalCollection(input.collectionName).fields;
10
-
11
- await validatePayloadWithSchema(
12
- input.data,
13
- fieldsSchema,
14
- processAllFields,
15
- input.context,
16
- );
17
-
18
- return input;
19
- }
@@ -1,52 +0,0 @@
1
- import validator from "validator";
2
-
3
- interface ValidatorInput {
4
- value: any;
5
- args?: any;
6
- context?: any;
7
- }
8
-
9
- interface Validator {
10
- handler: (input: ValidatorInput) => Promise<boolean>;
11
- }
12
-
13
- export const validatorsFunctions: Record<string, Validator> = {
14
- enum: {
15
- handler: async (input) => {
16
- if (typeof input.value === "undefined" || input.value === null) {
17
- return true;
18
- }
19
- return input.args.includes(input.value);
20
- },
21
- },
22
- required: {
23
- handler: async (input) => {
24
- return typeof input.value !== "undefined" && input.value !== null;
25
- },
26
- },
27
- maxLength: {
28
- handler: async (input) => {
29
- return input.value.length <= input.args;
30
- },
31
- },
32
- regex: {
33
- handler: async (input) => {
34
- const regex = new RegExp(input.args);
35
- return regex.test(input.value);
36
- },
37
- },
38
- validator: {
39
- handler: async (input) => {
40
- const validatorFunc = (validator as any)[input.args.name];
41
- if (!validatorFunc) {
42
- return;
43
- }
44
-
45
- try {
46
- return await validatorFunc(input.value, input.args.options);
47
- } catch (_error) {
48
- return false;
49
- }
50
- },
51
- },
52
- };