@lobb-js/core 0.13.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 (86) hide show
  1. package/package.json +48 -0
  2. package/src/Lobb.ts +150 -0
  3. package/src/LobbError.ts +105 -0
  4. package/src/TypesGenerator.ts +11 -0
  5. package/src/api/WebServer.ts +126 -0
  6. package/src/api/collections/CollectionControllers.ts +485 -0
  7. package/src/api/collections/CollectionService.ts +162 -0
  8. package/src/api/collections/collectionRoutes.ts +105 -0
  9. package/src/api/collections/collectionStore.ts +647 -0
  10. package/src/api/collections/transactions.ts +166 -0
  11. package/src/api/collections/utils.ts +73 -0
  12. package/src/api/errorHandler.ts +73 -0
  13. package/src/api/events/index.ts +129 -0
  14. package/src/api/meta/route.ts +66 -0
  15. package/src/api/meta/service.ts +163 -0
  16. package/src/api/middlewares.ts +71 -0
  17. package/src/api/openApiRoute.ts +1017 -0
  18. package/src/api/schema/SchemaService.ts +71 -0
  19. package/src/api/schema/schemaRoutes.ts +13 -0
  20. package/src/config/ConfigManager.ts +252 -0
  21. package/src/config/validations.ts +49 -0
  22. package/src/coreCollections/collectionsCollection.ts +56 -0
  23. package/src/coreCollections/index.ts +14 -0
  24. package/src/coreCollections/migrationsCollection.ts +36 -0
  25. package/src/coreCollections/queryCollection.ts +26 -0
  26. package/src/coreCollections/workflowsCollection.ts +73 -0
  27. package/src/coreDbSetup/index.ts +72 -0
  28. package/src/coreMigrations/index.ts +3 -0
  29. package/src/database/DatabaseService.ts +44 -0
  30. package/src/database/DatabaseSyncManager.ts +173 -0
  31. package/src/database/MigrationsManager.ts +95 -0
  32. package/src/database/drivers/MongoDriver.ts +750 -0
  33. package/src/database/drivers/pgDriver/PGDriver.ts +655 -0
  34. package/src/database/drivers/pgDriver/QueryBuilder.ts +474 -0
  35. package/src/database/drivers/pgDriver/utils.ts +6 -0
  36. package/src/events/EventSystem.ts +191 -0
  37. package/src/events/coreEvents/index.ts +218 -0
  38. package/src/events/studioEvents/index.ts +32 -0
  39. package/src/extension/ExtensionSystem.ts +236 -0
  40. package/src/extension/dashboardRoute.ts +35 -0
  41. package/src/fields/ArrayField.ts +33 -0
  42. package/src/fields/BoolField.ts +34 -0
  43. package/src/fields/DateField.ts +13 -0
  44. package/src/fields/DateTimeField.ts +13 -0
  45. package/src/fields/DecimalField.ts +13 -0
  46. package/src/fields/FieldUtils.ts +56 -0
  47. package/src/fields/FloatField.ts +13 -0
  48. package/src/fields/IntegerField.ts +13 -0
  49. package/src/fields/LongField.ts +13 -0
  50. package/src/fields/ObjectField.ts +15 -0
  51. package/src/fields/StringField.ts +13 -0
  52. package/src/fields/TextField.ts +13 -0
  53. package/src/fields/TimeField.ts +13 -0
  54. package/src/index.ts +53 -0
  55. package/src/studio/Studio.ts +108 -0
  56. package/src/types/CollectionControllers.ts +15 -0
  57. package/src/types/DatabaseDriver.ts +115 -0
  58. package/src/types/Extension.ts +46 -0
  59. package/src/types/Field.ts +29 -0
  60. package/src/types/apiSchema.ts +12 -0
  61. package/src/types/collectionServiceSchema.ts +18 -0
  62. package/src/types/config/collectionFields.ts +85 -0
  63. package/src/types/config/collectionsConfig.ts +50 -0
  64. package/src/types/config/config.ts +66 -0
  65. package/src/types/config/relations.ts +17 -0
  66. package/src/types/filterSchema.ts +88 -0
  67. package/src/types/index.ts +38 -0
  68. package/src/types/migrations.ts +12 -0
  69. package/src/types/websockets.ts +34 -0
  70. package/src/types/workflows/processors.ts +1 -0
  71. package/src/utils/lockCollectionToObject.ts +204 -0
  72. package/src/utils/utils.ts +310 -0
  73. package/src/workflows/WorkflowSystem.ts +182 -0
  74. package/src/workflows/coreWorkflows/collectionsTable/index.ts +118 -0
  75. package/src/workflows/coreWorkflows/index.ts +18 -0
  76. package/src/workflows/coreWorkflows/processors/postOperationsWorkflows.ts +46 -0
  77. package/src/workflows/coreWorkflows/processors/preOperationsWorkflows.ts +27 -0
  78. package/src/workflows/coreWorkflows/processors/processorForDB.ts +13 -0
  79. package/src/workflows/coreWorkflows/processors/processors/processor.ts +23 -0
  80. package/src/workflows/coreWorkflows/processors/processors/processorsFunctions.ts +47 -0
  81. package/src/workflows/coreWorkflows/processors/utils.ts +102 -0
  82. package/src/workflows/coreWorkflows/processors/validator/validator.ts +19 -0
  83. package/src/workflows/coreWorkflows/processors/validator/validatorsFunction.ts +52 -0
  84. package/src/workflows/coreWorkflows/queryCoreWorkflows.ts +31 -0
  85. package/src/workflows/coreWorkflows/utilsCoreWorkflows.ts +40 -0
  86. package/src/workflows/coreWorkflows/workflowsCollection/workflowsCollectionWorkflows.ts +101 -0
@@ -0,0 +1,66 @@
1
+ import type { Context } from "hono";
2
+ import type { CollectionsConfig } from "./collectionsConfig.ts";
3
+ import type { Extension } from "../Extension.ts";
4
+ import type { Migrations } from "../migrations.ts";
5
+ import type { RelationsConfig } from "./relations.ts";
6
+ import type { Workflow } from "../../workflows/WorkflowSystem.ts";
7
+
8
+ // project schema
9
+ type Server = {
10
+ url: string;
11
+ description?: string;
12
+ };
13
+
14
+ export type ProjectConfig = {
15
+ context?: string;
16
+ force_sync?: boolean;
17
+ info?: {
18
+ title?: string;
19
+ version?: string;
20
+ description?: string;
21
+ };
22
+ servers?: Server[];
23
+ [key: string]: unknown;
24
+ };
25
+
26
+ // server database connection
27
+ export type DatabaseConfig = {
28
+ type?: "pg" | "mongo";
29
+ host: string;
30
+ port: number;
31
+ database: string;
32
+ username: string;
33
+ password: string;
34
+ pool_connections?: number;
35
+ };
36
+
37
+ // webserver
38
+ type CORSOptions = {
39
+ origin:
40
+ | string
41
+ | string[]
42
+ | ((origin: string, c: Context) => string | undefined | null);
43
+ allowMethods?: string[];
44
+ allowHeaders?: string[];
45
+ maxAge?: number;
46
+ credentials?: boolean;
47
+ exposeHeaders?: string[];
48
+ };
49
+
50
+ export type WebConfig = {
51
+ host?: string;
52
+ port?: number;
53
+ cors?: CORSOptions;
54
+ };
55
+
56
+ // configuration
57
+ export interface Config {
58
+ project: ProjectConfig;
59
+ database: DatabaseConfig;
60
+ web_server: WebConfig;
61
+ migrations?: Migrations;
62
+ workflows?: Workflow[];
63
+ extensions?: Extension[];
64
+ relations?: RelationsConfig;
65
+ collections: CollectionsConfig;
66
+ }
@@ -0,0 +1,17 @@
1
+ import { z } from "zod";
2
+
3
+ // Base RelationCollectionField schema
4
+ export const RelationCollectionFieldSchema = z.object({
5
+ collection: z.string(),
6
+ field: z.string(),
7
+ });
8
+
9
+ // Regular relation schema
10
+ const RelationSchema = z.object({
11
+ from: RelationCollectionFieldSchema,
12
+ to: RelationCollectionFieldSchema,
13
+ });
14
+
15
+ // Union of all relation types
16
+ export const RelationsConfigSchema = z.array(RelationSchema);
17
+ export type RelationsConfig = z.infer<typeof RelationsConfigSchema>;
@@ -0,0 +1,88 @@
1
+ import { z } from "zod";
2
+ import { zodToJsonSchema } from "zod-to-json-schema";
3
+
4
+ // Define primitive types
5
+ const primitiveTypes = z.union([
6
+ z.string(),
7
+ z.number(),
8
+ z.boolean(),
9
+ z.null(),
10
+ z.date(),
11
+ ]);
12
+
13
+ const ArrayOfPrimitives = z.array(primitiveTypes);
14
+
15
+ // Define range types
16
+ const rangeTypes = z.union([
17
+ z.number(),
18
+ z.date(),
19
+ z.string(),
20
+ ]);
21
+
22
+ // Basic filter operators without recursion
23
+ export const basicFilterOperatorsSchema = z.object({
24
+ $eq: primitiveTypes.optional(),
25
+ $ne: primitiveTypes.optional(),
26
+ $in: ArrayOfPrimitives.optional(),
27
+ $nin: ArrayOfPrimitives.optional(),
28
+
29
+ // String matching
30
+ $contains: z.string().optional(),
31
+ $icontains: z.string().optional(),
32
+ $ncontains: z.string().optional(),
33
+ $incontains: z.string().optional(),
34
+
35
+ $starts_with: z.string().optional(),
36
+ $istarts_with: z.string().optional(),
37
+ $nstarts_with: z.string().optional(),
38
+ $instarts_with: z.string().optional(),
39
+
40
+ $ends_with: z.string().optional(),
41
+ $iends_with: z.string().optional(),
42
+ $nends_with: z.string().optional(),
43
+ $inends_with: z.string().optional(),
44
+
45
+ $regex: z.string().optional(),
46
+
47
+ // Comparison
48
+ $lt: rangeTypes.optional(),
49
+ $lte: rangeTypes.optional(),
50
+ $gt: rangeTypes.optional(),
51
+ $gte: rangeTypes.optional(),
52
+ $between: z.tuple([rangeTypes, rangeTypes]).optional(),
53
+ $nbetween: z.tuple([rangeTypes, rangeTypes]).optional(),
54
+ }).strict();
55
+
56
+ // Recursive filter operators schema with $and and $or
57
+ const recursiveFilterOperatorsSchema: any = z.lazy(() =>
58
+ basicFilterOperatorsSchema.extend({
59
+ $and: z.array(recursiveFilterOperatorsSchema).optional(),
60
+ $or: z.array(recursiveFilterOperatorsSchema).optional(),
61
+ }).strict()
62
+ );
63
+
64
+ // Final filter schema which can accept both primitive types and operators
65
+ export const filterSchema = z.record(
66
+ z.union([recursiveFilterOperatorsSchema, primitiveTypes]),
67
+ );
68
+
69
+ type FilterFieldPrimitiveValue =
70
+ | string
71
+ | number
72
+ | boolean
73
+ | null;
74
+
75
+ type FilterFieldValue<C = any> =
76
+ | FilterFieldPrimitiveValue
77
+ | Array<Filter<C>>
78
+ | Record<string, Filter<C>>
79
+ | ((ctx: C) => FilterFieldPrimitiveValue);
80
+
81
+ export type Filter<C = any> = {
82
+ [key: string]: FilterFieldValue<C> | Filter<C>;
83
+ };
84
+
85
+ // this is needed so that the AI in lobb studio knows how to generate a filter object
86
+ export const filterJsonSchema = zodToJsonSchema(
87
+ filterSchema as any,
88
+ );
@@ -0,0 +1,38 @@
1
+ export { DatabaseDriver } from "./DatabaseDriver.ts";
2
+ export type { FindAllResult, MassReturn } from "./DatabaseDriver.ts";
3
+ export type { ApiCollectionAction } from "./apiSchema.ts";
4
+ export type { Config, WebConfig } from "./config/config.ts";
5
+ export type { RelationsConfig } from "./config/relations.ts";
6
+ export type {
7
+ CollectionConfig,
8
+ CollectionIndex,
9
+ CollectionIndexes,
10
+ CollectionsConfig,
11
+ } from "./config/collectionsConfig.ts";
12
+ export type { CollectionFieldsWithoutId } from "./config/collectionsConfig.ts";
13
+ export type {
14
+ CollectionField,
15
+ CollectionFieldBase,
16
+ } from "./config/collectionFields.ts";
17
+ export type { Dashboard, Extension } from "./Extension.ts";
18
+ export { Field } from "./Field.ts";
19
+ export type { Migration, MigrationProps, Migrations } from "./migrations.ts";
20
+ export type { Filter } from "./filterSchema.ts";
21
+ export {
22
+ basicFilterOperatorsSchema,
23
+ filterJsonSchema,
24
+ filterSchema,
25
+ } from "./filterSchema.ts";
26
+ export type {
27
+ FindAllParamsInput,
28
+ FindAllParamsOutput,
29
+ } from "./collectionServiceSchema.ts";
30
+ export { findAllParamsSchema } from "./collectionServiceSchema.ts";
31
+ export { CollectionConfigSchema } from "./config/collectionsConfig.ts";
32
+ export type { CollectionControllers } from "./CollectionControllers.ts";
33
+ export type {
34
+ HeartBeatMessage,
35
+ RequestMessage,
36
+ SubscribeMessage,
37
+ } from "./websockets.ts";
38
+ export type { ProcessorType } from "./workflows/processors.ts";
@@ -0,0 +1,12 @@
1
+ import type { DatabaseDriver } from "./DatabaseDriver.ts";
2
+
3
+ export interface MigrationProps {
4
+ driver: DatabaseDriver;
5
+ }
6
+
7
+ export interface Migration {
8
+ up: (props: MigrationProps) => Promise<void>;
9
+ down?: (props: MigrationProps) => Promise<void>;
10
+ }
11
+
12
+ export type Migrations = Record<string, Migration>;
@@ -0,0 +1,34 @@
1
+ export interface HeartBeatMessage {
2
+ type: "heartbeat";
3
+ message: "ping" | "pong";
4
+ }
5
+
6
+ export interface SubscribeMessage {
7
+ type: "subscribe";
8
+ eventName: string;
9
+ filter?: any;
10
+ }
11
+
12
+ export interface EventMessage {
13
+ type: "event";
14
+ event: string;
15
+ data: any;
16
+ }
17
+
18
+ export interface RequestMessage {
19
+ type: "request";
20
+ url: string;
21
+ requestInit: RequestInit;
22
+ fireAndForget?: boolean;
23
+ }
24
+
25
+ export interface ResponseMessage {
26
+ type: "response";
27
+ body: string;
28
+ }
29
+
30
+ export type Message =
31
+ | HeartBeatMessage
32
+ | SubscribeMessage
33
+ | RequestMessage
34
+ | ResponseMessage;
@@ -0,0 +1 @@
1
+ export type ProcessorType = "pre_processors" | "post_processors";
@@ -0,0 +1,204 @@
1
+ import type { CollectionConfig } from "../types/index.ts";
2
+ import { Lobb } from "../Lobb.ts";
3
+ import { LobbError } from "../LobbError.ts";
4
+
5
+ export interface LockCollectionToObjectProps {
6
+ collectionName: string;
7
+ object: Record<string, any>;
8
+ }
9
+
10
+ let preUpdateEntry: Record<string, any>;
11
+
12
+ export function innerLockCollectionToObject(
13
+ props: LockCollectionToObjectProps,
14
+ ): CollectionConfig {
15
+ setWorkflowsForLockedCollections(props.collectionName, props.object);
16
+ return generateSchemasForLockedCollections(props.collectionName);
17
+ }
18
+
19
+ function generateSchemasForLockedCollections(
20
+ collectionName: string,
21
+ ): CollectionConfig {
22
+ return {
23
+ "indexes": {
24
+ [`${collectionName}_unique_index`]: {
25
+ "unique": true,
26
+ "fields": {
27
+ "key": {
28
+ "order": "asc",
29
+ },
30
+ },
31
+ },
32
+ },
33
+ "fields": {
34
+ "id": {
35
+ "type": "integer",
36
+ },
37
+ "key": {
38
+ "type": "string",
39
+ "length": 255,
40
+ "validators": {
41
+ "required": true,
42
+ },
43
+ },
44
+ "value": {
45
+ "type": "text",
46
+ "validators": {
47
+ "required": true,
48
+ },
49
+ "pre_processors": {
50
+ "default": "{}",
51
+ },
52
+ "ui": {
53
+ "input": {
54
+ "type": "code",
55
+ "args": {
56
+ "type": "json",
57
+ "height": 200,
58
+ // "types": "type Value = any",
59
+ },
60
+ },
61
+ },
62
+ },
63
+ "static": {
64
+ "type": "bool",
65
+ "pre_processors": {
66
+ "overwrite": false,
67
+ },
68
+ "validators": {
69
+ "required": true,
70
+ },
71
+ },
72
+ },
73
+ };
74
+ }
75
+
76
+ async function getCollectionElementById(
77
+ id: string,
78
+ collectionName: string,
79
+ ) {
80
+ const result = await Lobb.instance.collectionService.findOne({
81
+ collectionName,
82
+ id,
83
+ });
84
+ return result.data;
85
+ }
86
+
87
+ function setWorkflowsForLockedCollections(
88
+ collectionName: string,
89
+ object: Record<string, any>,
90
+ ) {
91
+ Lobb.instance.workflowSystem.add({
92
+ name: `${collectionName}CollectionLockInitWorkflow`,
93
+ eventName: "core.init",
94
+ handler: async () => {
95
+ const entries = (await Lobb.instance.collectionService.findAll({
96
+ collectionName,
97
+ })).data;
98
+ for (let index = 0; index < entries.length; index++) {
99
+ const entry = entries[index];
100
+ if (object[entry.key]) {
101
+ throw new LobbError({
102
+ code: "BAD_REQUEST",
103
+ message:
104
+ `The (${entry.key}) key you are trying to add already exists in the memory`,
105
+ });
106
+ }
107
+ object[entry.key] = JSON.parse(entry.value);
108
+ }
109
+ },
110
+ });
111
+
112
+ Lobb.instance.workflowSystem.add({
113
+ name: `${collectionName}CollectionLockCreateWorkflow`,
114
+ eventName: `core.store.createOne`,
115
+ handler: async (input) => {
116
+ if (input.collectionName === collectionName) {
117
+ const entry = input.data;
118
+ if (object[entry.key]) {
119
+ throw new LobbError({
120
+ code: "BAD_REQUEST",
121
+ message:
122
+ `The (${entry.key}) key you are trying to add already exists in the memory`,
123
+ });
124
+ }
125
+ object[entry.key] = JSON.parse(entry.value);
126
+ }
127
+ return input;
128
+ },
129
+ });
130
+
131
+ Lobb.instance.workflowSystem.add({
132
+ name: `${collectionName}CollectionLockPreUpdateWorkflow`,
133
+ eventName: `core.store.preUpdateOne`,
134
+ handler: async (input) => {
135
+ if (input.collectionName === collectionName) {
136
+ preUpdateEntry = await getCollectionElementById(
137
+ input.id,
138
+ collectionName,
139
+ );
140
+ }
141
+ return input;
142
+ },
143
+ });
144
+
145
+ Lobb.instance.workflowSystem.add({
146
+ name: `${collectionName}CollectionLockUpdateWorkflow`,
147
+ eventName: `core.store.updateOne`,
148
+ handler: async (input) => {
149
+ if (input.collectionName === collectionName) {
150
+ const entry = input.data;
151
+
152
+ // TODO: I cant update the same non static key if I try. it shows me the bellow error
153
+
154
+ if (object[entry.key]) {
155
+ throw new LobbError({
156
+ code: "BAD_REQUEST",
157
+ message:
158
+ `The (${entry.key}) key you are trying to add already exists in the memory`,
159
+ });
160
+ }
161
+
162
+ delete object[preUpdateEntry.key];
163
+ object[entry.key] = JSON.parse(entry.value);
164
+ }
165
+ return input;
166
+ },
167
+ });
168
+
169
+ Lobb.instance.workflowSystem.add({
170
+ name: `${collectionName}CollectionLockDeleteWorkflow`,
171
+ eventName: `core.store.deleteOne`,
172
+ handler: async (input) => {
173
+ if (input.collectionName === collectionName) {
174
+ const entry = input.data;
175
+ delete object[entry.key];
176
+ }
177
+ return input;
178
+ },
179
+ });
180
+
181
+ Lobb.instance.workflowSystem.add({
182
+ name: `${collectionName}CollectionLockFindAllWorkflow`,
183
+ eventName: `core.controllers.findAll`,
184
+ handler: async (input) => {
185
+ if (input.collectionName === collectionName) {
186
+ for (const [key, value] of Object.entries(object)) {
187
+ const entryExists = input.response.data.find((entry: any) =>
188
+ entry.key === key
189
+ );
190
+ if (!entryExists) {
191
+ input.response.data.push({
192
+ id: null,
193
+ key: key,
194
+ value: JSON.stringify(value),
195
+ static: true,
196
+ __disabled: true,
197
+ });
198
+ input.response.meta.totalCount = input.response.meta.totalCount + 1;
199
+ }
200
+ }
201
+ }
202
+ },
203
+ });
204
+ }