@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.
- package/package.json +48 -0
- package/src/Lobb.ts +150 -0
- package/src/LobbError.ts +105 -0
- package/src/TypesGenerator.ts +11 -0
- package/src/api/WebServer.ts +126 -0
- package/src/api/collections/CollectionControllers.ts +485 -0
- package/src/api/collections/CollectionService.ts +162 -0
- package/src/api/collections/collectionRoutes.ts +105 -0
- package/src/api/collections/collectionStore.ts +647 -0
- package/src/api/collections/transactions.ts +166 -0
- package/src/api/collections/utils.ts +73 -0
- package/src/api/errorHandler.ts +73 -0
- package/src/api/events/index.ts +129 -0
- package/src/api/meta/route.ts +66 -0
- package/src/api/meta/service.ts +163 -0
- package/src/api/middlewares.ts +71 -0
- package/src/api/openApiRoute.ts +1017 -0
- package/src/api/schema/SchemaService.ts +71 -0
- package/src/api/schema/schemaRoutes.ts +13 -0
- package/src/config/ConfigManager.ts +252 -0
- package/src/config/validations.ts +49 -0
- package/src/coreCollections/collectionsCollection.ts +56 -0
- package/src/coreCollections/index.ts +14 -0
- package/src/coreCollections/migrationsCollection.ts +36 -0
- package/src/coreCollections/queryCollection.ts +26 -0
- package/src/coreCollections/workflowsCollection.ts +73 -0
- package/src/coreDbSetup/index.ts +72 -0
- package/src/coreMigrations/index.ts +3 -0
- package/src/database/DatabaseService.ts +44 -0
- package/src/database/DatabaseSyncManager.ts +173 -0
- package/src/database/MigrationsManager.ts +95 -0
- package/src/database/drivers/MongoDriver.ts +750 -0
- package/src/database/drivers/pgDriver/PGDriver.ts +655 -0
- package/src/database/drivers/pgDriver/QueryBuilder.ts +474 -0
- package/src/database/drivers/pgDriver/utils.ts +6 -0
- package/src/events/EventSystem.ts +191 -0
- package/src/events/coreEvents/index.ts +218 -0
- package/src/events/studioEvents/index.ts +32 -0
- package/src/extension/ExtensionSystem.ts +236 -0
- package/src/extension/dashboardRoute.ts +35 -0
- package/src/fields/ArrayField.ts +33 -0
- package/src/fields/BoolField.ts +34 -0
- package/src/fields/DateField.ts +13 -0
- package/src/fields/DateTimeField.ts +13 -0
- package/src/fields/DecimalField.ts +13 -0
- package/src/fields/FieldUtils.ts +56 -0
- package/src/fields/FloatField.ts +13 -0
- package/src/fields/IntegerField.ts +13 -0
- package/src/fields/LongField.ts +13 -0
- package/src/fields/ObjectField.ts +15 -0
- package/src/fields/StringField.ts +13 -0
- package/src/fields/TextField.ts +13 -0
- package/src/fields/TimeField.ts +13 -0
- package/src/index.ts +53 -0
- package/src/studio/Studio.ts +108 -0
- package/src/types/CollectionControllers.ts +15 -0
- package/src/types/DatabaseDriver.ts +115 -0
- package/src/types/Extension.ts +46 -0
- package/src/types/Field.ts +29 -0
- package/src/types/apiSchema.ts +12 -0
- package/src/types/collectionServiceSchema.ts +18 -0
- package/src/types/config/collectionFields.ts +85 -0
- package/src/types/config/collectionsConfig.ts +50 -0
- package/src/types/config/config.ts +66 -0
- package/src/types/config/relations.ts +17 -0
- package/src/types/filterSchema.ts +88 -0
- package/src/types/index.ts +38 -0
- package/src/types/migrations.ts +12 -0
- package/src/types/websockets.ts +34 -0
- package/src/types/workflows/processors.ts +1 -0
- package/src/utils/lockCollectionToObject.ts +204 -0
- package/src/utils/utils.ts +310 -0
- package/src/workflows/WorkflowSystem.ts +182 -0
- package/src/workflows/coreWorkflows/collectionsTable/index.ts +118 -0
- package/src/workflows/coreWorkflows/index.ts +18 -0
- package/src/workflows/coreWorkflows/processors/postOperationsWorkflows.ts +46 -0
- package/src/workflows/coreWorkflows/processors/preOperationsWorkflows.ts +27 -0
- package/src/workflows/coreWorkflows/processors/processorForDB.ts +13 -0
- package/src/workflows/coreWorkflows/processors/processors/processor.ts +23 -0
- package/src/workflows/coreWorkflows/processors/processors/processorsFunctions.ts +47 -0
- package/src/workflows/coreWorkflows/processors/utils.ts +102 -0
- package/src/workflows/coreWorkflows/processors/validator/validator.ts +19 -0
- package/src/workflows/coreWorkflows/processors/validator/validatorsFunction.ts +52 -0
- package/src/workflows/coreWorkflows/queryCoreWorkflows.ts +31 -0
- package/src/workflows/coreWorkflows/utilsCoreWorkflows.ts +40 -0
- package/src/workflows/coreWorkflows/workflowsCollection/workflowsCollectionWorkflows.ts +101 -0
|
@@ -0,0 +1,13 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
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
|
+
// }
|
|
@@ -0,0 +1,13 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
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
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// export lobb version
|
|
2
|
+
import packageJson from "../package.json" with { type: "json" };
|
|
3
|
+
export const version = packageJson.version;
|
|
4
|
+
|
|
5
|
+
// export Lobb core
|
|
6
|
+
export { run } from "./Lobb.ts";
|
|
7
|
+
export { Lobb } from "./Lobb.ts";
|
|
8
|
+
export { LobbError } from "./LobbError.ts";
|
|
9
|
+
|
|
10
|
+
// export core types
|
|
11
|
+
export type { Workflow } from "./workflows/WorkflowSystem.ts";
|
|
12
|
+
export type { CollectionService, ExposedServiceOutput, ExposedManyServiceOutput, ServiceOp } from "./api/collections/CollectionService.ts";
|
|
13
|
+
export type { EventContext } from "./events/EventSystem.ts";
|
|
14
|
+
|
|
15
|
+
// export all types from types directory
|
|
16
|
+
export { DatabaseDriver } from "./types/DatabaseDriver.ts";
|
|
17
|
+
export type { FindAllResult, MassReturn } from "./types/DatabaseDriver.ts";
|
|
18
|
+
export type { ApiCollectionAction } from "./types/apiSchema.ts";
|
|
19
|
+
export type { Config, WebConfig } from "./types/config/config.ts";
|
|
20
|
+
export type { RelationsConfig } from "./types/config/relations.ts";
|
|
21
|
+
export type {
|
|
22
|
+
CollectionConfig,
|
|
23
|
+
CollectionIndex,
|
|
24
|
+
CollectionIndexes,
|
|
25
|
+
CollectionsConfig,
|
|
26
|
+
} from "./types/config/collectionsConfig.ts";
|
|
27
|
+
export type { CollectionFieldsWithoutId } from "./types/config/collectionsConfig.ts";
|
|
28
|
+
export type {
|
|
29
|
+
CollectionField,
|
|
30
|
+
CollectionFieldBase,
|
|
31
|
+
} from "./types/config/collectionFields.ts";
|
|
32
|
+
export type { Dashboard, Extension } from "./types/Extension.ts";
|
|
33
|
+
export { Field } from "./types/Field.ts";
|
|
34
|
+
export type { Migration, MigrationProps, Migrations } from "./types/migrations.ts";
|
|
35
|
+
export type { Filter } from "./types/filterSchema.ts";
|
|
36
|
+
export {
|
|
37
|
+
basicFilterOperatorsSchema,
|
|
38
|
+
filterJsonSchema,
|
|
39
|
+
filterSchema,
|
|
40
|
+
} from "./types/filterSchema.ts";
|
|
41
|
+
export type {
|
|
42
|
+
FindAllParamsInput,
|
|
43
|
+
FindAllParamsOutput,
|
|
44
|
+
} from "./types/collectionServiceSchema.ts";
|
|
45
|
+
export { findAllParamsSchema } from "./types/collectionServiceSchema.ts";
|
|
46
|
+
export { CollectionConfigSchema } from "./types/config/collectionsConfig.ts";
|
|
47
|
+
export type { CollectionControllers } from "./types/CollectionControllers.ts";
|
|
48
|
+
export type {
|
|
49
|
+
HeartBeatMessage,
|
|
50
|
+
RequestMessage,
|
|
51
|
+
SubscribeMessage,
|
|
52
|
+
} from "./types/websockets.ts";
|
|
53
|
+
export type { ProcessorType } from "./types/workflows/processors.ts";
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { stat } from "node:fs/promises";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { createServer, createConnection } from "node:net";
|
|
5
|
+
import type { AddressInfo } from "node:net";
|
|
6
|
+
|
|
7
|
+
export class Studio {
|
|
8
|
+
private studioDir: string;
|
|
9
|
+
private process: ReturnType<typeof Bun.spawn> | null = null;
|
|
10
|
+
|
|
11
|
+
constructor(projectDir: string) {
|
|
12
|
+
// Support flat structure (vite.config.ts at project root) and nested (studio/ subdir).
|
|
13
|
+
this.studioDir = existsSync(join(projectDir, "vite.config.ts"))
|
|
14
|
+
? projectDir
|
|
15
|
+
: join(projectDir, "studio");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
public stop(): void {
|
|
19
|
+
this.process?.kill();
|
|
20
|
+
this.process = null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public async start(webPort: number): Promise<number | null> {
|
|
24
|
+
if (!await this.exists()) return null;
|
|
25
|
+
|
|
26
|
+
const mode = process.env.LOBB_MODE;
|
|
27
|
+
|
|
28
|
+
if (mode === "prod") {
|
|
29
|
+
const buildDir = `${this.studioDir}/build`;
|
|
30
|
+
try {
|
|
31
|
+
const s = await stat(buildDir);
|
|
32
|
+
if (!s.isDirectory()) throw new Error();
|
|
33
|
+
} catch {
|
|
34
|
+
throw new Error(
|
|
35
|
+
"Studio build not found. Run 'bun run build:studio' before starting in production mode."
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
return await this.serveBuild(webPort);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return await this.runDevServer(webPort);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
private async exists(): Promise<boolean> {
|
|
45
|
+
try {
|
|
46
|
+
const s = await stat(this.studioDir);
|
|
47
|
+
return s.isDirectory();
|
|
48
|
+
} catch {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private async serveBuild(webPort: number): Promise<number> {
|
|
54
|
+
console.log("Studio: running production build.");
|
|
55
|
+
const port = await Studio.getFreePort();
|
|
56
|
+
this.process = Bun.spawn(["bun", "run", "build/index.js"], {
|
|
57
|
+
cwd: this.studioDir,
|
|
58
|
+
env: { ...process.env, PORT: String(port) },
|
|
59
|
+
stdin: "pipe",
|
|
60
|
+
stdout: "inherit",
|
|
61
|
+
stderr: "inherit",
|
|
62
|
+
});
|
|
63
|
+
await Studio.waitForPort(port);
|
|
64
|
+
console.log(`Studio: http://localhost:${webPort}/studio`);
|
|
65
|
+
return port;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private async runDevServer(webPort: number): Promise<number> {
|
|
69
|
+
const port = await Studio.getFreePort();
|
|
70
|
+
console.log("Studio: running dev server.");
|
|
71
|
+
this.process = Bun.spawn(["bun", "run", "dev:studio", "--port", String(port)], {
|
|
72
|
+
cwd: this.studioDir,
|
|
73
|
+
env: { ...process.env },
|
|
74
|
+
stdin: "pipe",
|
|
75
|
+
stdout: "pipe",
|
|
76
|
+
stderr: "inherit",
|
|
77
|
+
});
|
|
78
|
+
await Studio.waitForPort(port);
|
|
79
|
+
console.log(`Studio: http://localhost:${webPort}/studio`);
|
|
80
|
+
return port;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private static getFreePort(): Promise<number> {
|
|
84
|
+
return new Promise((resolve, reject) => {
|
|
85
|
+
const server = createServer();
|
|
86
|
+
server.listen(0, "127.0.0.1", () => {
|
|
87
|
+
const port = (server.address() as AddressInfo).port;
|
|
88
|
+
server.close((err) => {
|
|
89
|
+
if (err) reject(err);
|
|
90
|
+
else resolve(port);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
private static async waitForPort(port: number, maxAttempts = 50): Promise<void> {
|
|
97
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
98
|
+
const connected = await new Promise<boolean>((resolve) => {
|
|
99
|
+
const sock = createConnection({ port, host: "127.0.0.1" });
|
|
100
|
+
sock.once("connect", () => { sock.destroy(); resolve(true); });
|
|
101
|
+
sock.once("error", () => { sock.destroy(); resolve(false); });
|
|
102
|
+
});
|
|
103
|
+
if (connected) return;
|
|
104
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
105
|
+
}
|
|
106
|
+
throw new Error(`Studio server did not start on port ${port}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Context } from "hono";
|
|
2
|
+
|
|
3
|
+
export interface CollectionControllers {
|
|
4
|
+
findAll(c: Context): Promise<Response>;
|
|
5
|
+
findOne(c: Context): Promise<Response>;
|
|
6
|
+
createOne(c: Context): Promise<Response>;
|
|
7
|
+
updateOne(c: Context): Promise<Response>;
|
|
8
|
+
deleteOne(c: Context): Promise<Response>;
|
|
9
|
+
deleteMany(c: Context): Promise<Response>;
|
|
10
|
+
updateMany(c: Context): Promise<Response>;
|
|
11
|
+
createMany(c: Context): Promise<Response>;
|
|
12
|
+
readSingleton(c: Context): Promise<Response>;
|
|
13
|
+
updateSingleton(c: Context): Promise<Response>;
|
|
14
|
+
transactions(c: Context): Promise<Response>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { FindAllParamsOutput } from "./collectionServiceSchema.ts";
|
|
2
|
+
import type { PoolClient } from "pg";
|
|
3
|
+
import type {
|
|
4
|
+
CollectionConfig,
|
|
5
|
+
CollectionIndex,
|
|
6
|
+
CollectionIndexes,
|
|
7
|
+
CollectionsConfig,
|
|
8
|
+
} from "./config/collectionsConfig.ts";
|
|
9
|
+
import type { DatabaseConfig } from "./config/config.ts";
|
|
10
|
+
import type { CollectionField } from "./config/collectionFields.ts";
|
|
11
|
+
|
|
12
|
+
export type Entry = {
|
|
13
|
+
id: string;
|
|
14
|
+
[key: string]: any;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export interface Meta {
|
|
18
|
+
totalCount: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface FindAllResult {
|
|
22
|
+
data: Entry[];
|
|
23
|
+
meta: Meta;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface MassReturn {
|
|
27
|
+
affectedCount: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export abstract class DatabaseDriver {
|
|
31
|
+
// Subclasses should set this in their constructor by accessing Lobb.instance
|
|
32
|
+
protected databaseConfig!: DatabaseConfig;
|
|
33
|
+
|
|
34
|
+
public abstract createConnection(): Promise<void>;
|
|
35
|
+
public abstract getConnection(): { connect(): Promise<any> };
|
|
36
|
+
public abstract close(): Promise<void>;
|
|
37
|
+
public abstract query<T>(sql: string): Promise<T[]>;
|
|
38
|
+
public abstract dropDatabase(): Promise<void>;
|
|
39
|
+
public abstract createCollection(
|
|
40
|
+
collectionName: string,
|
|
41
|
+
collectionConfig: CollectionConfig,
|
|
42
|
+
): Promise<void>;
|
|
43
|
+
public abstract dropCollection(collectionName: string): Promise<void>;
|
|
44
|
+
public abstract addField(
|
|
45
|
+
collectionName: string,
|
|
46
|
+
fieldName: string,
|
|
47
|
+
field: CollectionField,
|
|
48
|
+
): Promise<void>;
|
|
49
|
+
public abstract removeField(
|
|
50
|
+
collectionName: string,
|
|
51
|
+
fieldName: string,
|
|
52
|
+
): Promise<void>;
|
|
53
|
+
public abstract getDbSchema(): Promise<CollectionsConfig>;
|
|
54
|
+
public abstract getIdsByFilter(
|
|
55
|
+
collectionName: string,
|
|
56
|
+
filter?: any,
|
|
57
|
+
client?: PoolClient,
|
|
58
|
+
): Promise<string[]>;
|
|
59
|
+
|
|
60
|
+
/////////////////////////
|
|
61
|
+
// Indexes Operations //
|
|
62
|
+
////////////////////////
|
|
63
|
+
public abstract createIndex(
|
|
64
|
+
collectionName: string,
|
|
65
|
+
indexName: string,
|
|
66
|
+
index: CollectionIndex,
|
|
67
|
+
): Promise<void>;
|
|
68
|
+
public abstract dropIndex(
|
|
69
|
+
collectionName: string,
|
|
70
|
+
indexName: string,
|
|
71
|
+
): Promise<any>;
|
|
72
|
+
public abstract getIndexes(
|
|
73
|
+
collectionName: string,
|
|
74
|
+
): Promise<CollectionIndexes>;
|
|
75
|
+
|
|
76
|
+
/////////////////////
|
|
77
|
+
// CRUD Operations //
|
|
78
|
+
/////////////////////
|
|
79
|
+
public abstract findAll(
|
|
80
|
+
collectionName: string,
|
|
81
|
+
params: FindAllParamsOutput,
|
|
82
|
+
client?: PoolClient,
|
|
83
|
+
): Promise<FindAllResult>;
|
|
84
|
+
|
|
85
|
+
public abstract createOne(
|
|
86
|
+
collectionName: string,
|
|
87
|
+
data: any,
|
|
88
|
+
client?: PoolClient,
|
|
89
|
+
): Promise<Entry>;
|
|
90
|
+
|
|
91
|
+
public abstract updateOne(
|
|
92
|
+
collectionName: string,
|
|
93
|
+
id: string,
|
|
94
|
+
data: any,
|
|
95
|
+
client?: PoolClient,
|
|
96
|
+
): Promise<Entry | null>;
|
|
97
|
+
|
|
98
|
+
public abstract deleteOne(
|
|
99
|
+
collectionName: string,
|
|
100
|
+
id: string,
|
|
101
|
+
client?: PoolClient,
|
|
102
|
+
): Promise<Entry | null>;
|
|
103
|
+
|
|
104
|
+
///////////////////////////////////////////////////////////
|
|
105
|
+
// parse some types of data to be compatible with the db //
|
|
106
|
+
// used mainly for mongodb //
|
|
107
|
+
///////////////////////////////////////////////////////////
|
|
108
|
+
public encodeFieldValue(fieldType: string, value: any) {
|
|
109
|
+
return value;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
public decodeFieldValue(fieldType: string, value: any) {
|
|
113
|
+
return value;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Hono, MiddlewareHandler } from "hono";
|
|
2
|
+
import type { CollectionConfig } from "./config/collectionsConfig.ts";
|
|
3
|
+
import type { Migrations } from "./migrations.ts";
|
|
4
|
+
import type { OpenAPIV3_1 } from "openapi-types";
|
|
5
|
+
import type { RelationsConfig } from "./config/relations.ts";
|
|
6
|
+
import type { Lobb } from "../Lobb.ts";
|
|
7
|
+
import type { Workflow } from "../workflows/WorkflowSystem.ts";
|
|
8
|
+
|
|
9
|
+
interface OpenApiProperty {
|
|
10
|
+
paths: OpenAPIV3_1.Document["paths"];
|
|
11
|
+
components?: Record<string, any>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface Dashboard {
|
|
15
|
+
extension?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface ExtensionBase {
|
|
19
|
+
name: string;
|
|
20
|
+
workflows?: Workflow[];
|
|
21
|
+
migrations?: Migrations;
|
|
22
|
+
init?: (lobb: Lobb) => Promise<void>;
|
|
23
|
+
close?: (lobb: Lobb) => Promise<void>;
|
|
24
|
+
collectionRoutes?: (routes: Hono) => void;
|
|
25
|
+
collectionMiddlewares?: Array<MiddlewareHandler>;
|
|
26
|
+
routes?: () => Hono;
|
|
27
|
+
collections?: (lobb: Lobb) => Record<string, CollectionConfig>;
|
|
28
|
+
relations?: (lobb: Lobb) => RelationsConfig;
|
|
29
|
+
meta?: (lobb: Lobb) => Promise<any>;
|
|
30
|
+
openapi?: OpenApiProperty;
|
|
31
|
+
services?: (lobb: Lobb) => object;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Has dashboard → version is required
|
|
35
|
+
interface ExtensionWithDashboard extends ExtensionBase {
|
|
36
|
+
dashboard: Dashboard;
|
|
37
|
+
version?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// No dashboard → version is optional
|
|
41
|
+
interface ExtensionWithoutDashboard extends ExtensionBase {
|
|
42
|
+
dashboard?: undefined;
|
|
43
|
+
version?: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type Extension = ExtensionWithDashboard | ExtensionWithoutDashboard;
|
|
@@ -0,0 +1,29 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export const findAllParamsSchema = z
|
|
4
|
+
.object({
|
|
5
|
+
sort: z.string().optional(),
|
|
6
|
+
limit: z.coerce.number().default(100),
|
|
7
|
+
offset: z.coerce.number().default(0),
|
|
8
|
+
page: z.coerce.number().optional(),
|
|
9
|
+
fields: z.string().default("*"),
|
|
10
|
+
filter: z.any(),
|
|
11
|
+
}).strict()
|
|
12
|
+
.default({
|
|
13
|
+
fields: "*",
|
|
14
|
+
limit: 100,
|
|
15
|
+
offset: 0,
|
|
16
|
+
});
|
|
17
|
+
export type FindAllParamsInput = z.input<typeof findAllParamsSchema>;
|
|
18
|
+
export type FindAllParamsOutput = z.output<typeof findAllParamsSchema>;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { RelationCollectionFieldSchema } from "./relations.ts";
|
|
2
|
+
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
// Define the UiInput schema
|
|
6
|
+
const UiInputSchema = z.object({
|
|
7
|
+
type: z.string(),
|
|
8
|
+
args: z.any().optional(),
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
// Defining the ui property schema
|
|
12
|
+
const UiSchema = z.object({
|
|
13
|
+
disabled: z.boolean().optional(),
|
|
14
|
+
placeholder: z.string().optional(),
|
|
15
|
+
input: UiInputSchema.optional(),
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// Base field schema
|
|
19
|
+
export const CollectionFieldBaseSchema = z.object({
|
|
20
|
+
pre_processors: z.record(z.any()).optional(),
|
|
21
|
+
post_processors: z.record(z.any()).optional(),
|
|
22
|
+
validators: z.record(z.any()).optional(),
|
|
23
|
+
ui: UiSchema.optional(),
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
export type CollectionFieldBase = z.infer<typeof CollectionFieldBaseSchema>;
|
|
27
|
+
|
|
28
|
+
// Specialized field schemas
|
|
29
|
+
export const CollectionBoolFieldSchema = CollectionFieldBaseSchema.extend({
|
|
30
|
+
type: z.literal("bool"),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
export const CollectionDateFieldSchema = CollectionFieldBaseSchema.extend({
|
|
34
|
+
type: z.literal("date"),
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
export const CollectionDateTimeFieldSchema = CollectionFieldBaseSchema.extend({
|
|
38
|
+
type: z.literal("datetime"),
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
export const CollectionDecimalFieldSchema = CollectionFieldBaseSchema.extend({
|
|
42
|
+
type: z.literal("decimal"),
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
export const CollectionFloatFieldSchema = CollectionFieldBaseSchema.extend({
|
|
46
|
+
type: z.literal("float"),
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
export const CollectionIntegerFieldSchema = CollectionFieldBaseSchema.extend({
|
|
50
|
+
type: z.literal("integer"),
|
|
51
|
+
references: RelationCollectionFieldSchema.optional(),
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
export const CollectionLongFieldSchema = CollectionFieldBaseSchema.extend({
|
|
55
|
+
type: z.literal("long"),
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
export const CollectionStringFieldSchema = CollectionFieldBaseSchema.extend({
|
|
59
|
+
type: z.literal("string"),
|
|
60
|
+
length: z.number(),
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
export const CollectionTextFieldSchema = CollectionFieldBaseSchema.extend({
|
|
64
|
+
type: z.literal("text"),
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
export const CollectionTimeFieldSchema = CollectionFieldBaseSchema.extend({
|
|
68
|
+
type: z.literal("time"),
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// Union type if you want a single schema for all
|
|
72
|
+
export const CollectionFieldSchema = z.union([
|
|
73
|
+
CollectionBoolFieldSchema,
|
|
74
|
+
CollectionDateFieldSchema,
|
|
75
|
+
CollectionDateTimeFieldSchema,
|
|
76
|
+
CollectionDecimalFieldSchema,
|
|
77
|
+
CollectionFloatFieldSchema,
|
|
78
|
+
CollectionIntegerFieldSchema,
|
|
79
|
+
CollectionLongFieldSchema,
|
|
80
|
+
CollectionStringFieldSchema,
|
|
81
|
+
CollectionTextFieldSchema,
|
|
82
|
+
CollectionTimeFieldSchema,
|
|
83
|
+
]);
|
|
84
|
+
|
|
85
|
+
export type CollectionField = z.infer<typeof CollectionFieldSchema>;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import {
|
|
3
|
+
CollectionFieldSchema,
|
|
4
|
+
CollectionIntegerFieldSchema,
|
|
5
|
+
} from "./collectionFields.ts";
|
|
6
|
+
|
|
7
|
+
// CollectionIndexField
|
|
8
|
+
export const CollectionIndexFieldSchema = z.object({
|
|
9
|
+
order: z.enum(["asc", "desc"]),
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
// CollectionIndexFields (Record<string, CollectionIndexField>)
|
|
13
|
+
export const CollectionIndexFieldsSchema = z.record(CollectionIndexFieldSchema);
|
|
14
|
+
|
|
15
|
+
// CollectionIndex
|
|
16
|
+
export const CollectionIndexSchema = z.object({
|
|
17
|
+
unique: z.boolean(),
|
|
18
|
+
fields: CollectionIndexFieldsSchema,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// CollectionIndexes (Record<string, CollectionIndex>)
|
|
22
|
+
export const CollectionIndexesSchema = z.record(CollectionIndexSchema);
|
|
23
|
+
|
|
24
|
+
// CollectionFields (Record<string, CollectionField>)
|
|
25
|
+
export const CollectionFieldsSchema = z.intersection(
|
|
26
|
+
z.object({
|
|
27
|
+
id: CollectionIntegerFieldSchema,
|
|
28
|
+
}),
|
|
29
|
+
z.record(CollectionFieldSchema),
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
// CollectionConfig
|
|
33
|
+
export const CollectionConfigSchema = z.object({
|
|
34
|
+
category: z.string().optional(),
|
|
35
|
+
singleton: z.boolean().optional(),
|
|
36
|
+
indexes: CollectionIndexesSchema,
|
|
37
|
+
fields: CollectionFieldsSchema,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
export const CollectionsConfigSchema = z.record(CollectionConfigSchema);
|
|
41
|
+
|
|
42
|
+
// Inferred Types
|
|
43
|
+
export type CollectionIndexField = z.infer<typeof CollectionIndexFieldSchema>;
|
|
44
|
+
export type CollectionIndexFields = z.infer<typeof CollectionIndexFieldsSchema>;
|
|
45
|
+
export type CollectionIndex = z.infer<typeof CollectionIndexSchema>;
|
|
46
|
+
export type CollectionIndexes = z.infer<typeof CollectionIndexesSchema>;
|
|
47
|
+
export type CollectionFields = z.infer<typeof CollectionFieldsSchema>;
|
|
48
|
+
export type CollectionFieldsWithoutId = Omit<CollectionFields, "id">;
|
|
49
|
+
export type CollectionConfig = z.infer<typeof CollectionConfigSchema>;
|
|
50
|
+
export type CollectionsConfig = z.infer<typeof CollectionsConfigSchema>;
|