@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,166 @@
1
+ import type { CollectionService, TriggeredBy } from "./CollectionService.ts";
2
+ import type { CollectionStore } from "./collectionStore.ts";
3
+ import type { Context as HonoContext } from "hono";
4
+ import type { PoolClient } from "pg";
5
+ import { Lobb } from "../../Lobb.ts";
6
+ import { LobbReturnError } from "../errorHandler.ts";
7
+
8
+ // Helper: add Symbol.dispose to a pg PoolClient so `using` syntax works
9
+ function asDisposable<T extends { release(): void }>(client: T): T & Disposable {
10
+ (client as any)[Symbol.dispose] = () => client.release();
11
+ return client as T & Disposable;
12
+ }
13
+
14
+ export type TransactionBody = {
15
+ [
16
+ K in keyof CollectionService as CollectionService[K] extends
17
+ (...args: any[]) => any ? K
18
+ : never
19
+ ]: {
20
+ method: K;
21
+ props: K extends keyof CollectionStore
22
+ ? Parameters<CollectionStore[K & keyof CollectionStore]>[0]
23
+ : Parameters<CollectionService[K]>[0];
24
+ };
25
+ }[keyof CollectionService];
26
+
27
+ export async function beginTransaction<T>(
28
+ func: (client: PoolClient) => Promise<T>,
29
+ localClient?: PoolClient,
30
+ rollback: boolean = false,
31
+ ): Promise<T> {
32
+ if (localClient) {
33
+ return await func(localClient);
34
+ }
35
+
36
+ const dbDriver = Lobb.instance.databaseService.getDriver();
37
+ const pool = dbDriver.getConnection();
38
+ const client = asDisposable(await pool.connect());
39
+ using _ = client;
40
+ await client.query("BEGIN");
41
+ let returnedValue;
42
+ try {
43
+ returnedValue = await func(client);
44
+ } catch (error) {
45
+ await client.query("ROLLBACK");
46
+ throw error;
47
+ }
48
+
49
+ if (rollback) {
50
+ await client.query("ROLLBACK");
51
+ }
52
+
53
+ await client.query("COMMIT");
54
+
55
+ return returnedValue;
56
+ }
57
+
58
+ export async function transactions({
59
+ body,
60
+ rollback = false,
61
+ triggeredBy,
62
+ context,
63
+ }: {
64
+ body: TransactionBody[];
65
+ rollback?: boolean;
66
+ triggeredBy?: TriggeredBy;
67
+ context?: HonoContext;
68
+ }): Promise<any> {
69
+ // starting the transactions
70
+ const responses = await beginTransaction(
71
+ async (client) => {
72
+ const responses = [];
73
+ for (let index = 0; index < body.length; index++) {
74
+ const transaction = body[index];
75
+ transaction.props = Lobb.instance.utils.renderTemplateDeep(
76
+ transaction.props,
77
+ {
78
+ responses: responses,
79
+ },
80
+ );
81
+
82
+ if (transaction.method === "findAll") {
83
+ const res = await Lobb.instance.collectionService.findAll({
84
+ ...transaction.props,
85
+ context,
86
+ client,
87
+ triggeredBy,
88
+ });
89
+ responses.push(res);
90
+ } else if (transaction.method === "findOne") {
91
+ const res = await Lobb.instance.collectionService.findOne({
92
+ ...transaction.props,
93
+ context,
94
+ client,
95
+ triggeredBy,
96
+ });
97
+ responses.push(res);
98
+ } else if (transaction.method === "createOne") {
99
+ const res = await Lobb.instance.collectionService.createOne({
100
+ ...transaction.props,
101
+ context,
102
+ client,
103
+ triggeredBy,
104
+ });
105
+ responses.push(res);
106
+ } else if (transaction.method === "updateOne") {
107
+ const res = await Lobb.instance.collectionService.updateOne({
108
+ ...transaction.props,
109
+ context,
110
+ client,
111
+ triggeredBy,
112
+ });
113
+ responses.push(res);
114
+ } else if (transaction.method === "deleteOne") {
115
+ const res = await Lobb.instance.collectionService.deleteOne({
116
+ ...transaction.props,
117
+ context,
118
+ client,
119
+ triggeredBy,
120
+ });
121
+ responses.push(res);
122
+ } else if (transaction.method === "createMany") {
123
+ const res = await Lobb.instance.collectionService.createMany({
124
+ ...transaction.props,
125
+ context,
126
+ client,
127
+ triggeredBy,
128
+ });
129
+ responses.push(res);
130
+ } else if (transaction.method === "updateMany") {
131
+ const res = await Lobb.instance.collectionService.updateMany({
132
+ ...transaction.props,
133
+ context,
134
+ client,
135
+ triggeredBy,
136
+ });
137
+ responses.push(res);
138
+ } else if (transaction.method === "deleteMany") {
139
+ const res = await Lobb.instance.collectionService.deleteMany({
140
+ ...transaction.props,
141
+ context,
142
+ client,
143
+ triggeredBy,
144
+ });
145
+ responses.push(res);
146
+ }
147
+ }
148
+
149
+ return responses;
150
+ },
151
+ undefined,
152
+ rollback,
153
+ );
154
+
155
+ await Lobb.instance.eventSystem.emit(
156
+ `core.collection.transaction`,
157
+ {
158
+ body: body,
159
+ responses: responses,
160
+ rollback: rollback,
161
+ triggeredBy: triggeredBy,
162
+ },
163
+ );
164
+
165
+ return responses;
166
+ }
@@ -0,0 +1,73 @@
1
+ import { Lobb } from "../../Lobb.ts";
2
+ import { z } from "zod";
3
+
4
+ export function getCollectionDocumentSchema(
5
+ collectionName?: string,
6
+ ) {
7
+ if (collectionName) {
8
+ const collection = Lobb.instance.configManager.getCollection(
9
+ collectionName,
10
+ );
11
+ const fieldNames = Object.keys(collection.fields);
12
+
13
+ const properties: Record<string, any> = {};
14
+ for (let index = 0; index < fieldNames.length; index++) {
15
+ const fieldName = fieldNames[index];
16
+ properties[fieldName] = getColFieldPropSchema(
17
+ collectionName,
18
+ fieldName,
19
+ );
20
+ }
21
+
22
+ return z.object(properties);
23
+ }
24
+
25
+ const schema = z.object({
26
+ id: z.number().int().optional(),
27
+ }).passthrough();
28
+
29
+ return schema;
30
+ }
31
+
32
+ export function getColFieldPropSchema(
33
+ collectionName: string,
34
+ fieldName: string,
35
+ ) {
36
+ const field = Lobb.instance.configManager.getField(
37
+ fieldName,
38
+ collectionName,
39
+ );
40
+ let fieldSchema;
41
+ if (field.type === "bool") {
42
+ fieldSchema = z.boolean();
43
+ } else if (field.type === "date") {
44
+ fieldSchema = z.date();
45
+ } else if (field.type === "time") {
46
+ fieldSchema = z.date();
47
+ } else if (field.type === "datetime") {
48
+ fieldSchema = z.date();
49
+ } else if (field.type === "decimal") {
50
+ fieldSchema = z.string();
51
+ } else if (field.type === "float") {
52
+ fieldSchema = z.number();
53
+ } else if (field.type === "integer") {
54
+ fieldSchema = z.number().int();
55
+ } else if (field.type === "long") {
56
+ fieldSchema = z.bigint();
57
+ } else if (field.type === "string") {
58
+ fieldSchema = z.string().max(field.type.length);
59
+ } else if (field.type === "text") {
60
+ fieldSchema = z.string();
61
+ } else {
62
+ throw new Error(
63
+ `The (${collectionName}.${fieldName}) field has a type thats not implemented in the genColPropsJsonSchema`,
64
+ );
65
+ }
66
+
67
+ const requiredField = Boolean(field.validators?.required);
68
+ if (!requiredField) {
69
+ fieldSchema = fieldSchema.optional();
70
+ }
71
+
72
+ return fieldSchema;
73
+ }
@@ -0,0 +1,73 @@
1
+ import type { Context } from "hono";
2
+ import { LobbError } from "../LobbError.ts";
3
+
4
+ // pg errors have a 5-char SQLSTATE code and a detail property
5
+ function isPgDatabaseError(error: any): error is { code: string; detail?: string; message: string } {
6
+ return (
7
+ error != null &&
8
+ typeof error.code === "string" &&
9
+ /^[0-9A-Z]{5}$/.test(error.code)
10
+ );
11
+ }
12
+
13
+ export class LobbReturnError extends Error {
14
+ public any: any;
15
+
16
+ constructor(any: any) {
17
+ super();
18
+ this.any = any;
19
+ }
20
+ }
21
+
22
+ export function errorHandler(error: any, c: Context) {
23
+ if (error instanceof LobbError) {
24
+ return c.json(
25
+ {
26
+ status: error.status,
27
+ code: error.code,
28
+ message: error.message,
29
+ details: error.details,
30
+ },
31
+ error.status as any,
32
+ );
33
+ } else if (isPgDatabaseError(error)) {
34
+ if (error.code === "23505") {
35
+ const match = error.detail?.match(/\((\w+)\)=\(/);
36
+ if (match) {
37
+ const uniqeFieldName = match[1];
38
+ throw new LobbError({
39
+ code: "CONFLICT",
40
+ message: `Violation of the uniqueness constraint.`,
41
+ details: {
42
+ [uniqeFieldName]: [
43
+ `Duplicate value found, this field must be unique.`,
44
+ ],
45
+ },
46
+ });
47
+ }
48
+ throw new LobbError({
49
+ code: "CONFLICT",
50
+ message: `One or more fields violate the uniqueness constraint.`,
51
+ details: error.detail,
52
+ });
53
+ } else if (error.code === "22P02") {
54
+ throw new LobbError({
55
+ code: "BAD_REQUEST",
56
+ message: error.message,
57
+ });
58
+ }
59
+ } else if (error instanceof LobbReturnError) {
60
+ return error.any;
61
+ }
62
+
63
+ // TODO: replace this with a better logging system
64
+ console.error(error);
65
+ return c.json(
66
+ {
67
+ status: 500,
68
+ code: "INTERNAL_ERROR",
69
+ message: "An unexpected error occurred. Please contact support.",
70
+ },
71
+ 500,
72
+ );
73
+ }
@@ -0,0 +1,129 @@
1
+ import { type Context, Hono } from "hono";
2
+ import { stream } from "hono/streaming";
3
+ import { Lobb } from "../../Lobb.ts";
4
+ import { z } from "zod";
5
+ import { Query } from "mingo";
6
+
7
+ const eventSubscriptionSchema = z.object({
8
+ event: z.string(),
9
+ filter: z.record(z.unknown()).optional(),
10
+ });
11
+
12
+ const eventsRequestSchema = z.object({
13
+ subscriptions: z.array(eventSubscriptionSchema).min(1, "At least one subscription is required"),
14
+ });
15
+
16
+ interface EventPayload {
17
+ eventName: string;
18
+ payload: Record<string, unknown>;
19
+ timestamp: string;
20
+ }
21
+
22
+ export function getEventsRoute() {
23
+ const rest = new Hono();
24
+
25
+ rest.post("/", async (c: Context) => {
26
+ // Parse and validate request body
27
+ const body = await c.req.json();
28
+ const parseResult = eventsRequestSchema.safeParse(body);
29
+
30
+ if (!parseResult.success) {
31
+ return c.json({ error: parseResult.error.errors }, 400);
32
+ }
33
+
34
+ const { subscriptions } = parseResult.data;
35
+ const lobb = Lobb.instance;
36
+
37
+ // Validate that all requested events exist
38
+ for (const subscription of subscriptions) {
39
+ if (!lobb.eventSystem.eventExists(subscription.event)) {
40
+ return c.json({
41
+ error: `Event '${subscription.event}' does not exist`
42
+ }, 400);
43
+ }
44
+ }
45
+
46
+ return stream(c, async (stream) => {
47
+ const signal = c.req.raw.signal;
48
+ const subscriptionIds: string[] = [];
49
+ const eventQueue: EventPayload[] = [];
50
+ let isProcessing = false;
51
+
52
+ // Handler that queues events with optional filtering
53
+ const createHandler = (eventName: string, filter?: Record<string, unknown>) => async (input: Record<string, unknown>) => {
54
+ // Apply filter if provided
55
+ if (filter) {
56
+ const query = new Query(filter);
57
+ if (!query.test(input)) {
58
+ return input; // Filter didn't match, don't queue
59
+ }
60
+ }
61
+
62
+ eventQueue.push({
63
+ eventName,
64
+ payload: input,
65
+ timestamp: new Date().toISOString()
66
+ });
67
+
68
+ // Process queue if not already processing
69
+ if (!isProcessing && !signal.aborted) {
70
+ await processQueue();
71
+ }
72
+
73
+ return input;
74
+ };
75
+
76
+ // Process queued events
77
+ const processQueue = async () => {
78
+ if (isProcessing) return;
79
+ isProcessing = true;
80
+
81
+ while (eventQueue.length > 0 && !signal.aborted) {
82
+ const event = eventQueue.shift();
83
+ try {
84
+ await stream.write(JSON.stringify(event) + "\n");
85
+ } catch (_error) {
86
+ // Stream closed, stop processing
87
+ break;
88
+ }
89
+ }
90
+
91
+ isProcessing = false;
92
+ };
93
+
94
+ try {
95
+ // Send an initial empty object so Bun resolves the client's fetch() promise
96
+ // immediately (Bun waits for the first chunk before resolving a streaming fetch)
97
+ await stream.write("\n");
98
+
99
+ // Subscribe to all requested events with filters
100
+ for (const subscription of subscriptions) {
101
+ const subscriptionId = `stream_${crypto.randomUUID()}`;
102
+ subscriptionIds.push(subscriptionId);
103
+
104
+ lobb.eventSystem.subscribe({
105
+ name: subscriptionId,
106
+ eventName: subscription.event,
107
+ handler: createHandler(subscription.event, subscription.filter),
108
+ });
109
+ }
110
+
111
+ // Keep connection alive and wait for abort signal
112
+ await new Promise((resolve) => {
113
+ signal.addEventListener('abort', () => {
114
+ resolve(null);
115
+ }, { once: true });
116
+ });
117
+ } catch (_error) {
118
+ // Client disconnected - clean exit
119
+ } finally {
120
+ // Clean up all subscriptions
121
+ for (const subscriptionId of subscriptionIds) {
122
+ await lobb.eventSystem.unsubscribe(subscriptionId);
123
+ }
124
+ }
125
+ });
126
+ });
127
+
128
+ return rest;
129
+ }
@@ -0,0 +1,66 @@
1
+ import type { Context } from "hono";
2
+ import { Hono } from "hono";
3
+ import { MetaService } from "./service.ts";
4
+
5
+ export const metaRoutePathDescription: any = {
6
+ "/api/meta": {
7
+ get: {
8
+ summary: `Get Meta`,
9
+ description: "Get meta data of the API",
10
+ tags: [
11
+ "meta",
12
+ ],
13
+ responses: {
14
+ 200: {
15
+ description: "Successful response",
16
+ content: {
17
+ "application/json": {
18
+ schema: {
19
+ type: "object",
20
+ properties: {
21
+ collections: {
22
+ type: "object",
23
+ properties: {},
24
+ },
25
+ filter: {
26
+ type: "object",
27
+ properties: {},
28
+ },
29
+ extensions: {
30
+ type: "object",
31
+ properties: {},
32
+ },
33
+ dashboard_navs: {
34
+ type: "object",
35
+ properties: {},
36
+ },
37
+ },
38
+ },
39
+ },
40
+ },
41
+ },
42
+ },
43
+ },
44
+ },
45
+ };
46
+
47
+ export function getMetaRoute() {
48
+ const rest = new Hono();
49
+
50
+ rest.get("/", async (c: Context) => {
51
+ const returnedValue: any = {};
52
+
53
+ returnedValue["version"] = MetaService.getVersion();
54
+ returnedValue["relations"] = MetaService.getRelations();
55
+ returnedValue["collections"] = MetaService.getCollections();
56
+ returnedValue["extensions"] = await MetaService.getExtensions();
57
+ returnedValue["filter"] = MetaService.getFilter();
58
+ returnedValue["studio_workflows"] = MetaService.getStudioWorkflows();
59
+ returnedValue["events"] = MetaService.getEvents();
60
+ returnedValue["event_context_type"] = MetaService.getEventContextType();
61
+
62
+ return c.json(returnedValue);
63
+ });
64
+
65
+ return rest;
66
+ }
@@ -0,0 +1,163 @@
1
+ import {
2
+ basicFilterOperatorsSchema,
3
+ filterJsonSchema,
4
+ filterSchema,
5
+ } from "../../types/index.ts";
6
+ import { Lobb } from "../../Lobb.ts";
7
+ import { convertSchemaToTypeString } from "../../utils/utils.ts";
8
+ import { z, type ZodFunction } from "zod";
9
+ import { LobbErrorSchema } from "../../LobbError.ts";
10
+ import packageJson from "../../../package.json" with { type: "json" };
11
+
12
+ export class MetaService {
13
+ public static getConfig() {
14
+ const { collections } = Lobb.instance.configManager.config;
15
+ const returnedData = { collections };
16
+ return returnedData;
17
+ }
18
+
19
+ public static getVersion() {
20
+ return packageJson.version;
21
+ }
22
+
23
+ public static getRelations() {
24
+ const relations = Lobb.instance.configManager.config.relations;
25
+ return relations;
26
+ }
27
+
28
+ public static getCollections() {
29
+ const collections: any = {};
30
+
31
+ const collectionsNames = Lobb.instance.configManager.getCollectionsNames();
32
+ for (let index = 0; index < collectionsNames.length; index++) {
33
+ const collectionName = collectionsNames[index];
34
+ const collection = Lobb.instance.configManager.getCollection(
35
+ collectionName,
36
+ );
37
+ collections[collectionName] = {};
38
+
39
+ // is collection singleton
40
+ collections[collectionName].singleton = Lobb.instance.configManager
41
+ .isCollectionSingleton(collectionName);
42
+
43
+ // filling the extension property
44
+ collections[collectionName].category = collection.category;
45
+ collections[collectionName].owner = Lobb.instance.utils
46
+ .getCollectionOwner(
47
+ collectionName,
48
+ );
49
+
50
+ // filling the fields
51
+ collections[collectionName].fields = {};
52
+ const fieldNames = Lobb.instance.configManager.getFieldNames(
53
+ collectionName,
54
+ );
55
+ for (let index = 0; index < fieldNames.length; index++) {
56
+ const fieldName = fieldNames[index];
57
+ const fieldConfig = Lobb.instance.configManager.getField(
58
+ fieldName,
59
+ collectionName,
60
+ );
61
+
62
+ const field: any = {};
63
+ field.type = fieldConfig.type;
64
+ field.label = fieldName;
65
+ field.key = fieldName;
66
+ field.validators = fieldConfig.validators;
67
+ field.pre_processors = fieldConfig.pre_processors;
68
+ field.ui = fieldConfig.ui;
69
+
70
+ collections[collectionName].fields[fieldName] = field;
71
+ }
72
+ }
73
+
74
+ return collections;
75
+ }
76
+
77
+ public static async getExtensions() {
78
+ const extensions: Record<string, any> = {};
79
+ if (Lobb.instance.configManager.config.extensions) {
80
+ const extensionNames = Lobb.instance.configManager.getExtensionNames();
81
+ for (let index = 0; index < extensionNames.length; index++) {
82
+ const extensionName = extensionNames[index];
83
+ const lobb = Lobb.instance;
84
+ const extensionMeta = await lobb.extensionSystem.getExtensionMeta(
85
+ extensionName,
86
+ );
87
+ extensions[extensionName] = extensionMeta;
88
+ }
89
+ }
90
+
91
+ return extensions;
92
+ }
93
+
94
+ public static getFilter() {
95
+ const filter: Record<string, any> = {};
96
+
97
+ filter.operators = Object.keys(basicFilterOperatorsSchema.shape);
98
+ // this is needed so that the AI in lobb studio knows how to generate a filter object
99
+ filter.filter_schema = filterJsonSchema;
100
+
101
+ return filter;
102
+ }
103
+
104
+ public static getStudioWorkflows() {
105
+ const workflows = Lobb.instance.workflowSystem.workflows;
106
+ const studio_workflows = workflows.filter((workflow) => {
107
+ return workflow.eventName?.startsWith("studio.");
108
+ });
109
+ return studio_workflows.map((workflow) => {
110
+ return {
111
+ ...workflow,
112
+ handler: workflow.handler.toString(),
113
+ };
114
+ });
115
+ }
116
+
117
+ public static getEvents() {
118
+ const events = Lobb.instance.eventSystem.events;
119
+ return events.map((event) => {
120
+ return {
121
+ ...event,
122
+ inputSchema: convertSchemaToTypeString(
123
+ "Input",
124
+ event.inputSchema,
125
+ ),
126
+ outputSchema: convertSchemaToTypeString(
127
+ "Output",
128
+ event.outputSchema,
129
+ ),
130
+ };
131
+ });
132
+ }
133
+
134
+ public static getEventContextType() {
135
+ const workflows = Lobb.instance.workflowSystem.workflows;
136
+
137
+ const workflowsObjectSchema: Record<string, ZodFunction<any, any>> = {};
138
+ for (let index = 0; index < workflows.length; index++) {
139
+ const workflow = workflows[index];
140
+ const inputSchema = workflow.inputSchema
141
+ ? workflow.inputSchema(z)
142
+ : z.any();
143
+ const outputSchema = workflow.outputSchema
144
+ ? workflow.outputSchema(z)
145
+ : z.any();
146
+ workflowsObjectSchema[workflow.name] = z.function().args(inputSchema)
147
+ .returns(z.promise(outputSchema));
148
+ }
149
+
150
+ const schema = z.object({
151
+ eventName: z.string(),
152
+ workflows: z.object(workflowsObjectSchema),
153
+ LobbError: z.function().args(LobbErrorSchema).returns(z.void()),
154
+ });
155
+
156
+ const typeString = convertSchemaToTypeString(
157
+ "Context",
158
+ schema,
159
+ );
160
+
161
+ return typeString;
162
+ }
163
+ }