bradb 2.2.0 → 3.0.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/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/src/builder.d.ts +17 -0
- package/dist/src/builder.js +182 -0
- package/dist/src/commands.d.ts +2 -20
- package/dist/src/commands.js +37 -114
- package/dist/src/config.d.ts +16 -0
- package/dist/src/config.js +43 -0
- package/dist/src/filters.d.ts +3 -0
- package/dist/src/filters.js +8 -0
- package/dist/src/generators.d.ts +7 -0
- package/dist/src/generators.js +164 -0
- package/dist/src/pg.d.ts +3 -0
- package/dist/src/pg.js +31 -0
- package/dist/src/service.d.ts +10 -10
- package/dist/src/service.js +18 -36
- package/package.json +15 -5
- package/dist/bin/brad.d.ts +0 -2
- package/dist/bin/brad.js +0 -52
- package/dist/src/relational.d.ts +0 -8
- package/dist/src/relational.js +0 -53
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -17,6 +17,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
17
17
|
__exportStar(require("./src/types"), exports);
|
|
18
18
|
__exportStar(require("./src/controller"), exports);
|
|
19
19
|
__exportStar(require("./src/service"), exports);
|
|
20
|
-
__exportStar(require("./src/relational"), exports);
|
|
21
20
|
__exportStar(require("./src/filters"), exports);
|
|
22
21
|
__exportStar(require("./src/errors"), exports);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { BradConfig } from './config';
|
|
2
|
+
export declare const nodeTypes: readonly ["validator", "controller", "service", "schema", "router", "filter", "db"];
|
|
3
|
+
export type NodeType = typeof nodeTypes[number];
|
|
4
|
+
type Node = {
|
|
5
|
+
name: string;
|
|
6
|
+
type: NodeType;
|
|
7
|
+
path: string;
|
|
8
|
+
adj: Node[];
|
|
9
|
+
exports: string[];
|
|
10
|
+
};
|
|
11
|
+
type Export = Record<string, any>;
|
|
12
|
+
export declare let mods: Export;
|
|
13
|
+
export declare function buildGraph(cfg: BradConfig, name: string, type: NodeType): Node;
|
|
14
|
+
export declare function addNodeTo(cfg: BradConfig, name: string, type: NodeType, to: Node): Node;
|
|
15
|
+
export declare function build(root: Node): void;
|
|
16
|
+
export declare function destroy(root: Node): void;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.mods = exports.nodeTypes = void 0;
|
|
7
|
+
exports.buildGraph = buildGraph;
|
|
8
|
+
exports.addNodeTo = addNodeTo;
|
|
9
|
+
exports.build = build;
|
|
10
|
+
exports.destroy = destroy;
|
|
11
|
+
const path_1 = __importDefault(require("path"));
|
|
12
|
+
const fs_1 = __importDefault(require("fs"));
|
|
13
|
+
const generators_1 = require("./generators");
|
|
14
|
+
exports.nodeTypes = [
|
|
15
|
+
"validator", "controller", "service", "schema", "router", "filter", "db"
|
|
16
|
+
];
|
|
17
|
+
// Default and base dependecies for each node type
|
|
18
|
+
const dependOn = {
|
|
19
|
+
router: ["controller"],
|
|
20
|
+
controller: ["service", "validator"],
|
|
21
|
+
service: ["schema", "filter", "db"],
|
|
22
|
+
validator: ["schema"],
|
|
23
|
+
filter: ["schema", "validator"],
|
|
24
|
+
schema: [],
|
|
25
|
+
db: []
|
|
26
|
+
};
|
|
27
|
+
// Default and base exports for each node type
|
|
28
|
+
const requiredExports = {
|
|
29
|
+
validator: (name) => [`${name}Validator`],
|
|
30
|
+
service: (name) => [`${name}Service`],
|
|
31
|
+
controller: (name) => [`${name}Controller`],
|
|
32
|
+
schema: (name) => [`${name}Table`],
|
|
33
|
+
router: (name) => [`${name}Router`],
|
|
34
|
+
filter: (name) => [`${name}FilterMap`],
|
|
35
|
+
db: (name) => ["db"]
|
|
36
|
+
};
|
|
37
|
+
const generators = {
|
|
38
|
+
router: generators_1.generateRouter,
|
|
39
|
+
controller: generators_1.generateController,
|
|
40
|
+
validator: generators_1.generateValidator,
|
|
41
|
+
service: generators_1.generateService,
|
|
42
|
+
filter: generators_1.generateFilter,
|
|
43
|
+
schema: null,
|
|
44
|
+
db: null,
|
|
45
|
+
};
|
|
46
|
+
const visited = {};
|
|
47
|
+
const checked = {};
|
|
48
|
+
const valid = {};
|
|
49
|
+
exports.mods = {};
|
|
50
|
+
// Build the graph dependecies for a given name
|
|
51
|
+
function buildGraph(cfg, name, type) {
|
|
52
|
+
const n = {
|
|
53
|
+
// TODO: refactor this
|
|
54
|
+
path: type == "db"
|
|
55
|
+
? path_1.default.resolve(cfg.cwd, cfg.dbImportPath)
|
|
56
|
+
: pathTo(cfg, name, type),
|
|
57
|
+
name: name,
|
|
58
|
+
type: type,
|
|
59
|
+
adj: [],
|
|
60
|
+
exports: requiredExports[type](name)
|
|
61
|
+
};
|
|
62
|
+
if (!checked[id(n)]) {
|
|
63
|
+
checked[id(n)] = true;
|
|
64
|
+
try {
|
|
65
|
+
const mod = checkNode(n);
|
|
66
|
+
exports.mods = { ...exports.mods, ...mod };
|
|
67
|
+
valid[id(n)] = true;
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
// If config is set to override = true we not check the modules and re-write anywise
|
|
71
|
+
if (!cfg.override) {
|
|
72
|
+
valid[id(n)] = true;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
console.error(err.message);
|
|
76
|
+
// it's not necessary to set it to false
|
|
77
|
+
valid[id(n)] = false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
for (const depType of dependOn[type]) {
|
|
82
|
+
const adj = buildGraph(cfg, n.name, depType);
|
|
83
|
+
n.adj.push(adj);
|
|
84
|
+
}
|
|
85
|
+
return n;
|
|
86
|
+
}
|
|
87
|
+
// Creates a new node directed to <to> node
|
|
88
|
+
function addNodeTo(cfg, name, type, to) {
|
|
89
|
+
const n = {
|
|
90
|
+
path: type == "db"
|
|
91
|
+
? path_1.default.resolve(cfg.cwd, cfg.dbImportPath)
|
|
92
|
+
: pathTo(cfg, name, type),
|
|
93
|
+
name: name,
|
|
94
|
+
type: type,
|
|
95
|
+
adj: [to],
|
|
96
|
+
exports: requiredExports[to.type](name)
|
|
97
|
+
};
|
|
98
|
+
return n;
|
|
99
|
+
}
|
|
100
|
+
function build(root) {
|
|
101
|
+
if (visited[id(root)]) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
visited[id(root)] = true;
|
|
105
|
+
const importLines = [];
|
|
106
|
+
for (const dep of root.adj) {
|
|
107
|
+
importLines.push(buildImportLine(root, dep));
|
|
108
|
+
build(dep);
|
|
109
|
+
}
|
|
110
|
+
if (!valid[id(root)]) {
|
|
111
|
+
const gen = generators[root.type];
|
|
112
|
+
if (!gen) {
|
|
113
|
+
throw Error(`Missing generator for ${root.type}`);
|
|
114
|
+
}
|
|
115
|
+
console.log(`Generating ${root.name}.${root.type} ...`);
|
|
116
|
+
const table = exports.mods[`${root.name}Table`];
|
|
117
|
+
const code = gen(root.name, table, importLines);
|
|
118
|
+
fs_1.default.writeFileSync(root.path, code);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function destroy(root) {
|
|
122
|
+
for (const n of root.adj) {
|
|
123
|
+
destroy(n);
|
|
124
|
+
}
|
|
125
|
+
// We only delete the nodes with dependencies
|
|
126
|
+
// we are not going to delete schemas or db
|
|
127
|
+
if (root.adj.length > 0) {
|
|
128
|
+
const path = root.path;
|
|
129
|
+
try {
|
|
130
|
+
console.log(`Deleting ${path}...`);
|
|
131
|
+
fs_1.default.rmSync(path);
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
console.error(path, "does not exists");
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/*
|
|
139
|
+
* Check if a Node exists and has it's exports
|
|
140
|
+
* @throws Error
|
|
141
|
+
*/
|
|
142
|
+
function checkNode(root) {
|
|
143
|
+
console.log(`Checking ${root.name}.${root.type}...`);
|
|
144
|
+
let module = {};
|
|
145
|
+
try {
|
|
146
|
+
module = require(root.path);
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
throw new Error(`[Missing file]: ${root.path}`);
|
|
150
|
+
}
|
|
151
|
+
for (const exp of root.exports) {
|
|
152
|
+
if (!module[exp]) {
|
|
153
|
+
throw new Error(`[Missing export]: Cannot import ${exp} from ${path_1.default}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return module;
|
|
157
|
+
}
|
|
158
|
+
function buildImportLine(from, to, maxColumns = 80) {
|
|
159
|
+
const relativePath = buildRelativeImport(from.path, to.path);
|
|
160
|
+
let sep = ' ';
|
|
161
|
+
let importLine = '';
|
|
162
|
+
let imports = to.exports.join(`,${sep}`);
|
|
163
|
+
importLine = `import {${sep}${imports}${sep}} from "${relativePath}"`;
|
|
164
|
+
if (importLine.length > maxColumns) {
|
|
165
|
+
sep = '\n\t';
|
|
166
|
+
imports = to.exports.join(`,${sep}`);
|
|
167
|
+
importLine = `import {${sep}${imports}${sep}} from "${relativePath}"`;
|
|
168
|
+
}
|
|
169
|
+
return importLine;
|
|
170
|
+
}
|
|
171
|
+
function pathTo(cfg, name, type) {
|
|
172
|
+
return path_1.default.resolve(cfg.cwd, dir(cfg, type), `${name}.${type}.ts`);
|
|
173
|
+
}
|
|
174
|
+
function removeExt(p) {
|
|
175
|
+
const parsed = path_1.default.parse(p);
|
|
176
|
+
return path_1.default.join(parsed.dir, parsed.name);
|
|
177
|
+
}
|
|
178
|
+
function buildRelativeImport(from, to) {
|
|
179
|
+
return path_1.default.relative(path_1.default.dirname(from), removeExt(to));
|
|
180
|
+
}
|
|
181
|
+
const dir = (cfg, type) => cfg[`${type}Dir`];
|
|
182
|
+
const id = (n) => `${n.name}.${n.type}`;
|
package/dist/src/commands.d.ts
CHANGED
|
@@ -1,20 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
schemas?: string;
|
|
4
|
-
routesDir?: string;
|
|
5
|
-
controllerDir?: string;
|
|
6
|
-
serviceDir?: string;
|
|
7
|
-
};
|
|
8
|
-
declare class Commander {
|
|
9
|
-
config: Required<Omit<BradConfig, 'dbConnection' | 'schemas'>>;
|
|
10
|
-
constructor(config?: BradConfig);
|
|
11
|
-
private capitalize;
|
|
12
|
-
private toCamelCase;
|
|
13
|
-
private formatImportPath;
|
|
14
|
-
createEntity(entityName: string, basePath?: string, config?: BradConfig): Promise<void>;
|
|
15
|
-
private createServiceFile;
|
|
16
|
-
private createControllerFile;
|
|
17
|
-
private createRouterFile;
|
|
18
|
-
}
|
|
19
|
-
declare const _default: Commander;
|
|
20
|
-
export default _default;
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
export {};
|
package/dist/src/commands.js
CHANGED
|
@@ -1,116 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
1
2
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
console.log(`- ${path_1.default.join(controllerDir, `${entityName}.controller.ts`)}`);
|
|
43
|
-
console.log(`- ${path_1.default.join(routerDir, `${entityName}.router.ts`)}`);
|
|
44
|
-
}
|
|
45
|
-
catch (error) {
|
|
46
|
-
console.error("Error creating entity:", error);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
async createServiceFile(dir, name, camelCaseName, config) {
|
|
50
|
-
let dbImport = `import { db } from '../db'; // fallback`;
|
|
51
|
-
let schemaImport = `import { ${camelCaseName}Table } from '../schema'; // fallback`;
|
|
52
|
-
if (config.dbConnection) {
|
|
53
|
-
const dbPath = path_1.default.relative(dir, path_1.default.resolve(config.dbConnection));
|
|
54
|
-
dbImport = `import { db } from '${this.formatImportPath(dbPath)}';`;
|
|
55
|
-
}
|
|
56
|
-
if (config.schemas) {
|
|
57
|
-
const schemaPath = path_1.default.relative(dir, path_1.default.resolve(config.schemas));
|
|
58
|
-
schemaImport = `import { ${camelCaseName}Table } from '${this.formatImportPath(schemaPath)}';`;
|
|
59
|
-
}
|
|
60
|
-
const serviceTemplate = `import { ServiceBuilder } from 'bradb';
|
|
61
|
-
${dbImport}
|
|
62
|
-
${schemaImport}
|
|
63
|
-
|
|
64
|
-
// TODO: Define your filter map
|
|
65
|
-
// import { ${camelCaseName}FilterMap } from '../filter';
|
|
66
|
-
|
|
67
|
-
const builder = new ServiceBuilder(db, ${camelCaseName}Table, {});
|
|
68
|
-
|
|
69
|
-
export const ${camelCaseName}Service = {
|
|
70
|
-
create: builder.create(),
|
|
71
|
-
update: builder.update(),
|
|
72
|
-
delete: builder.softDelete(),
|
|
73
|
-
findOne: builder.findOne(
|
|
74
|
-
db.select().from(${camelCaseName}Table).$dynamic()
|
|
75
|
-
),
|
|
76
|
-
findAll: builder.findAll(
|
|
77
|
-
db.select().from(${camelCaseName}Table).$dynamic()
|
|
78
|
-
),
|
|
79
|
-
count: builder.count()
|
|
80
|
-
};
|
|
81
|
-
`;
|
|
82
|
-
await fs_1.promises.writeFile(path_1.default.join(dir, `${name}.service.ts`), serviceTemplate);
|
|
83
|
-
}
|
|
84
|
-
async createControllerFile(dir, name, camelCaseName) {
|
|
85
|
-
const controllerTemplate = `import { createBuilder, findAllBuilder, findOneBuilder, updateBuilder, deleteBuilder } from 'bradb';
|
|
86
|
-
import { ${camelCaseName}Service } from '../services/${name}.service';
|
|
87
|
-
// import { ${camelCaseName}CreateSchema, ${camelCaseName}UpdateSchema } from '../validators'; // Adjust this import
|
|
88
|
-
|
|
89
|
-
export const ${camelCaseName}Controller = {
|
|
90
|
-
create: createBuilder(${camelCaseName}Service), // Add schema validator as second argument
|
|
91
|
-
findAll: findAllBuilder(${camelCaseName}Service),
|
|
92
|
-
findOne: findOneBuilder(${camelCaseName}Service),
|
|
93
|
-
update: updateBuilder(${camelCaseName}Service), // Add schema validator as second argument
|
|
94
|
-
delete: deleteBuilder(${camelCaseName}Service)
|
|
95
|
-
};
|
|
96
|
-
`;
|
|
97
|
-
await fs_1.promises.writeFile(path_1.default.join(dir, `${name}.controller.ts`), controllerTemplate);
|
|
98
|
-
}
|
|
99
|
-
async createRouterFile(dir, name, camelCaseName) {
|
|
100
|
-
const routerTemplate = `import { Router } from 'express';
|
|
101
|
-
import { ${camelCaseName}Controller } from '../controllers/${name}.controller';
|
|
102
|
-
|
|
103
|
-
const router = Router();
|
|
104
|
-
|
|
105
|
-
router.get('/', ${camelCaseName}Controller.findAll);
|
|
106
|
-
router.get('/:id', ${camelCaseName}Controller.findOne);
|
|
107
|
-
router.post('/', ${camelCaseName}Controller.create);
|
|
108
|
-
router.put('/:id', ${camelCaseName}Controller.update);
|
|
109
|
-
router.delete('/:id', ${camelCaseName}Controller.delete);
|
|
110
|
-
|
|
111
|
-
export default router;
|
|
112
|
-
`;
|
|
113
|
-
await fs_1.promises.writeFile(path_1.default.join(dir, `${name}.router.ts`), routerTemplate);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
exports.default = new Commander();
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const builder_1 = require("./builder");
|
|
6
|
+
const config_1 = require("./config");
|
|
7
|
+
const program = new commander_1.Command();
|
|
8
|
+
program.name("brad").description("BRAD - Generator");
|
|
9
|
+
program
|
|
10
|
+
.command("generate")
|
|
11
|
+
.argument('<name>', 'name (singular) of entity')
|
|
12
|
+
.addArgument(new commander_1.Argument('<node-type>', 'Type of node to generate')
|
|
13
|
+
.choices(builder_1.nodeTypes)
|
|
14
|
+
.default("router"))
|
|
15
|
+
.option('-o, --override', 'Generator will override the current code', false)
|
|
16
|
+
.description("generate entity recursively")
|
|
17
|
+
.action(async (name, type, options) => {
|
|
18
|
+
const cfg = (0, config_1.loadConfig)();
|
|
19
|
+
cfg.override = options.override || false;
|
|
20
|
+
require('ts-node/register');
|
|
21
|
+
const root = (0, builder_1.buildGraph)(cfg, name, type);
|
|
22
|
+
// Aca tendríamos que ir desde (name, type) hasta la raiz (name, "router")
|
|
23
|
+
// y desde ahi, ver si tenemos dependencias de otra entidad
|
|
24
|
+
console.log(JSON.stringify(root, null, 4));
|
|
25
|
+
(0, builder_1.build)(root);
|
|
26
|
+
});
|
|
27
|
+
program
|
|
28
|
+
.command("destroy")
|
|
29
|
+
.argument("<name>", "name of entity")
|
|
30
|
+
.addArgument(new commander_1.Argument('<node-type>', 'Type of Node to generate')
|
|
31
|
+
.choices(builder_1.nodeTypes)
|
|
32
|
+
.default("router"))
|
|
33
|
+
.description("destroy entity recursively")
|
|
34
|
+
.action(async (name, type) => {
|
|
35
|
+
const cfg = (0, config_1.loadConfig)();
|
|
36
|
+
const root = (0, builder_1.buildGraph)(cfg, name, type);
|
|
37
|
+
(0, builder_1.destroy)(root);
|
|
38
|
+
});
|
|
39
|
+
program.parseAsync(process.argv);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
declare const ConfigSchema: z.ZodObject<{
|
|
3
|
+
dbImportPath: z.ZodDefault<z.ZodString>;
|
|
4
|
+
validatorDir: z.ZodDefault<z.ZodString>;
|
|
5
|
+
schemaDir: z.ZodDefault<z.ZodString>;
|
|
6
|
+
serviceDir: z.ZodDefault<z.ZodString>;
|
|
7
|
+
controllerDir: z.ZodDefault<z.ZodString>;
|
|
8
|
+
routerDir: z.ZodDefault<z.ZodString>;
|
|
9
|
+
filterDir: z.ZodDefault<z.ZodString>;
|
|
10
|
+
cwd: z.ZodDefault<z.ZodString>;
|
|
11
|
+
maxColumns: z.ZodDefault<z.ZodNumber>;
|
|
12
|
+
override: z.ZodDefault<z.ZodCoercedBoolean<unknown>>;
|
|
13
|
+
}, z.core.$strip>;
|
|
14
|
+
export type BradConfig = z.infer<typeof ConfigSchema>;
|
|
15
|
+
export declare function loadConfig(customPath?: string): BradConfig;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.loadConfig = loadConfig;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const zod_1 = require("zod");
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const ConfigSchema = zod_1.z.object({
|
|
11
|
+
dbImportPath: zod_1.z.string().default("src/db.ts"),
|
|
12
|
+
validatorDir: zod_1.z.string().default("src/validators"),
|
|
13
|
+
schemaDir: zod_1.z.string().default("src/schemas"),
|
|
14
|
+
serviceDir: zod_1.z.string().default("src/services"),
|
|
15
|
+
controllerDir: zod_1.z.string().default("src/controllers"),
|
|
16
|
+
routerDir: zod_1.z.string().default("src/routes"),
|
|
17
|
+
filterDir: zod_1.z.string().default("src/filters"),
|
|
18
|
+
cwd: zod_1.z.string().default(process.cwd()),
|
|
19
|
+
maxColumns: zod_1.z.number().default(80),
|
|
20
|
+
override: zod_1.z.coerce.boolean().default(false)
|
|
21
|
+
});
|
|
22
|
+
function loadConfig(customPath) {
|
|
23
|
+
const configName = "brad.config.json";
|
|
24
|
+
const filePath = path_1.default.resolve(process.cwd(), configName);
|
|
25
|
+
try {
|
|
26
|
+
const file = fs_1.default.readFileSync(filePath, 'utf8');
|
|
27
|
+
return ConfigSchema.parse(JSON.parse(file));
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
if (err instanceof zod_1.ZodError) {
|
|
31
|
+
error(err.message);
|
|
32
|
+
}
|
|
33
|
+
else if (err instanceof SyntaxError) {
|
|
34
|
+
error("File is not a valid JSON");
|
|
35
|
+
}
|
|
36
|
+
error('No config found. Create brad.config.json');
|
|
37
|
+
process.exit(2);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function error(msg) {
|
|
41
|
+
console.error(`\x1b[41m${msg}\x1b[0m`);
|
|
42
|
+
process.exit(2);
|
|
43
|
+
}
|
package/dist/src/filters.d.ts
CHANGED
|
@@ -2,5 +2,8 @@ import { SQL } from "drizzle-orm";
|
|
|
2
2
|
import { Filter, FilterMap, PrimaryKeyData } from "./types";
|
|
3
3
|
import { ZodObject } from "zod";
|
|
4
4
|
import { AnyPgTable, PgColumn } from "drizzle-orm/pg-core";
|
|
5
|
+
export declare const createFilterSchema: import("drizzle-zod").CreateInsertSchema<{
|
|
6
|
+
number: true;
|
|
7
|
+
}>;
|
|
5
8
|
export declare function buildFilters<FSchema extends ZodObject>(map: FilterMap<FSchema>, filters?: Filter<FSchema>): SQL<unknown> | undefined;
|
|
6
9
|
export declare function buildPKFilters<T extends AnyPgTable>(pks: PgColumn[], values: PrimaryKeyData<T>): SQL<unknown> | undefined;
|
package/dist/src/filters.js
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createFilterSchema = void 0;
|
|
3
4
|
exports.buildFilters = buildFilters;
|
|
4
5
|
exports.buildPKFilters = buildPKFilters;
|
|
5
6
|
const drizzle_orm_1 = require("drizzle-orm");
|
|
7
|
+
const drizzle_zod_1 = require("drizzle-zod");
|
|
8
|
+
const { createInsertSchema } = (0, drizzle_zod_1.createSchemaFactory)({
|
|
9
|
+
coerce: {
|
|
10
|
+
number: true,
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
exports.createFilterSchema = createInsertSchema;
|
|
6
14
|
function buildFilters(map, filters) {
|
|
7
15
|
const conditions = [];
|
|
8
16
|
if (!filters)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { PgTable } from "drizzle-orm/pg-core";
|
|
2
|
+
export declare function generateRouter(name: string, table: PgTable, imports: string[]): string;
|
|
3
|
+
export declare function generateValidator(name: string, table: PgTable, imports: string[]): string;
|
|
4
|
+
export declare function generateController(name: string, table: PgTable, imports: string[]): string;
|
|
5
|
+
export declare function generateService(name: string, table: PgTable, imports: string[]): string;
|
|
6
|
+
export declare function generateFilter(name: string, table: PgTable, imports: string[]): string;
|
|
7
|
+
export declare function getRelationName(fkName: string): string;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateRouter = generateRouter;
|
|
4
|
+
exports.generateValidator = generateValidator;
|
|
5
|
+
exports.generateController = generateController;
|
|
6
|
+
exports.generateService = generateService;
|
|
7
|
+
exports.generateFilter = generateFilter;
|
|
8
|
+
exports.getRelationName = getRelationName;
|
|
9
|
+
const pg_core_1 = require("drizzle-orm/pg-core");
|
|
10
|
+
const pg_1 = require("./pg");
|
|
11
|
+
function generateRouter(name, table, imports) {
|
|
12
|
+
const pkColumns = (0, pg_1.getPKs)(table);
|
|
13
|
+
const route = pkColumns.map(pk => `${getRelationName(pk.name)}/:${pk.name}`).join("/");
|
|
14
|
+
return `import { Router } from "express";
|
|
15
|
+
${imports.join('\n')};
|
|
16
|
+
export const ${name}Router = Router();
|
|
17
|
+
|
|
18
|
+
// Get All
|
|
19
|
+
${name}Router.get("/", ${name}Controller.getAll);
|
|
20
|
+
|
|
21
|
+
// Create
|
|
22
|
+
${name}Router.post("/", ${name}Controller.create);
|
|
23
|
+
|
|
24
|
+
// Get one
|
|
25
|
+
${name}Router.get("${route}", ${name}Controller.getOne);
|
|
26
|
+
|
|
27
|
+
// Update
|
|
28
|
+
${name}Router.put("${route}", ${name}Controller.update);
|
|
29
|
+
|
|
30
|
+
// Delete
|
|
31
|
+
${name}Router.delete("${route}", ${name}Controller.remove);`;
|
|
32
|
+
}
|
|
33
|
+
function generateValidator(name, table, imports) {
|
|
34
|
+
const pkColumns = (0, pg_1.getPKs)(table);
|
|
35
|
+
const zodPkPicker = buildZodPkPicker(pkColumns);
|
|
36
|
+
return `import { createInsertSchema, createSelectSchema } from "drizzle-zod";
|
|
37
|
+
import { createFilterSchema } from "bradb";
|
|
38
|
+
import { z } from "zod";
|
|
39
|
+
${imports.join('\n')};
|
|
40
|
+
|
|
41
|
+
const select = createSelectSchema(${name}Table);
|
|
42
|
+
const insert = createInsertSchema(${name}Table);
|
|
43
|
+
const update = insert.partial();
|
|
44
|
+
const filter = createFilterSchema(${name}Table).partial();
|
|
45
|
+
const pk = select.pick({
|
|
46
|
+
${zodPkPicker}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
type ${capitalize(name)} = z.infer<typeof select>;
|
|
50
|
+
type ${capitalize(name)}Insert = z.infer<typeof insert>;
|
|
51
|
+
type ${capitalize(name)}Update = z.infer<typeof update>;
|
|
52
|
+
type ${capitalize(name)}Filter = z.infer<typeof filter>;
|
|
53
|
+
type ${capitalize(name)}Pk = z.infer<typeof pk>;
|
|
54
|
+
|
|
55
|
+
export const ${name}Validator = {
|
|
56
|
+
select,
|
|
57
|
+
insert,
|
|
58
|
+
update,
|
|
59
|
+
filter,
|
|
60
|
+
pk
|
|
61
|
+
};`;
|
|
62
|
+
}
|
|
63
|
+
function generateController(name, table, imports) {
|
|
64
|
+
return `import { Request, Response } from "express";
|
|
65
|
+
${imports.join('\n')};
|
|
66
|
+
|
|
67
|
+
async function getAll(req: Request, res: Response) {
|
|
68
|
+
const filters = ${name}Validator.filter.parse(req.query);
|
|
69
|
+
const items = await ${name}Service.findAll(filters);
|
|
70
|
+
const total = await ${name}Service.count(filters);
|
|
71
|
+
|
|
72
|
+
res.json({
|
|
73
|
+
items,
|
|
74
|
+
total
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function getOne(req: Request, res: Response) {
|
|
79
|
+
const pk = ${name}Validator.pk.parse(req.params);
|
|
80
|
+
const item = await ${name}Service.findOne(pk);
|
|
81
|
+
res.json(item);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function create(req: Request, res: Response) {
|
|
85
|
+
const data = ${name}Validator.insert.parse(req.body);
|
|
86
|
+
const item = await ${name}Service.create(data);
|
|
87
|
+
res.status(201).json(item);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function update(req: Request, res: Response) {
|
|
91
|
+
const pk = ${name}Validator.pk.parse(req.params);
|
|
92
|
+
const data = ${name}Validator.update.parse(req.body);
|
|
93
|
+
const item = await ${name}Service.update(pk, data);
|
|
94
|
+
res.status(200).json(item);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function remove(req: Request, res: Response) {
|
|
98
|
+
const pk = ${name}Validator.pk.parse(req.params);
|
|
99
|
+
await ${name}Service.delete(pk);
|
|
100
|
+
res.status(204).send();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export const ${name}Controller = {
|
|
104
|
+
getAll,
|
|
105
|
+
getOne,
|
|
106
|
+
create,
|
|
107
|
+
update,
|
|
108
|
+
remove
|
|
109
|
+
};`;
|
|
110
|
+
}
|
|
111
|
+
function generateService(name, table, imports) {
|
|
112
|
+
return `import { ServiceBuilder } from "bradb";
|
|
113
|
+
${imports.join('\n')};
|
|
114
|
+
|
|
115
|
+
const builder = new ServiceBuilder(db, ${name}Table, ${name}FilterMap);
|
|
116
|
+
|
|
117
|
+
const selection = db
|
|
118
|
+
.select()
|
|
119
|
+
.from(${name}Table)
|
|
120
|
+
.$dynamic();
|
|
121
|
+
|
|
122
|
+
export const ${name}Service = {
|
|
123
|
+
create: builder.create(),
|
|
124
|
+
update: builder.update(),
|
|
125
|
+
count: builder.count(),
|
|
126
|
+
delete: builder.delete(),
|
|
127
|
+
findAll: builder.findAll(selection),
|
|
128
|
+
findOne: builder.findOne(selection)
|
|
129
|
+
};`;
|
|
130
|
+
}
|
|
131
|
+
function generateFilter(name, table, imports) {
|
|
132
|
+
const { columns } = (0, pg_core_1.getTableConfig)(table);
|
|
133
|
+
const filters = columns.map(c => `${c.name}: (val) => eq(${name}Table.${c.name}, val)`).join(',\n\t');
|
|
134
|
+
return `import { FilterMap } from "bradb";
|
|
135
|
+
import { eq } from "drizzle-orm";
|
|
136
|
+
${imports.join('\n')};
|
|
137
|
+
|
|
138
|
+
export const ${name}FilterMap: FilterMap<typeof ${name}Validator.filter> = {
|
|
139
|
+
${filters}
|
|
140
|
+
}`;
|
|
141
|
+
}
|
|
142
|
+
function getRelationName(fkName) {
|
|
143
|
+
const parts = fkName.split('id');
|
|
144
|
+
if (parts.length < 2) {
|
|
145
|
+
throw new Error("fkName must have name like 'id{Entity}'");
|
|
146
|
+
}
|
|
147
|
+
return parts[1].toLowerCase();
|
|
148
|
+
}
|
|
149
|
+
function buildArgs(columns) {
|
|
150
|
+
let args = [];
|
|
151
|
+
for (const col of columns) {
|
|
152
|
+
args.push(`${col.name}: ${col.dataType}`);
|
|
153
|
+
}
|
|
154
|
+
return args.join(', ');
|
|
155
|
+
}
|
|
156
|
+
function buildZodPkPicker(pks) {
|
|
157
|
+
return pks.map(pk => `${pk.name}: true`).join(',\n\t');
|
|
158
|
+
}
|
|
159
|
+
function capitalize(s) {
|
|
160
|
+
if (typeof s !== 'string' || !s || s.length == 0) {
|
|
161
|
+
return '';
|
|
162
|
+
}
|
|
163
|
+
return s[0].toUpperCase() + s.slice(1);
|
|
164
|
+
}
|
package/dist/src/pg.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { PgTable } from 'drizzle-orm/pg-core';
|
|
2
|
+
export declare function getFkNames(table: PgTable): import("drizzle-orm/pg-core").ForeignKey[];
|
|
3
|
+
export declare function getPKs(table: PgTable): import("drizzle-orm/pg-core").PgColumn<import("drizzle-orm").ColumnBaseConfig<import("drizzle-orm").ColumnDataType, string>, {}, {}>[];
|
package/dist/src/pg.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getFkNames = getFkNames;
|
|
4
|
+
exports.getPKs = getPKs;
|
|
5
|
+
const pg_core_1 = require("drizzle-orm/pg-core");
|
|
6
|
+
function getFkNames(table) {
|
|
7
|
+
const { foreignKeys } = (0, pg_core_1.getTableConfig)(table);
|
|
8
|
+
return foreignKeys.map(fk => fk);
|
|
9
|
+
}
|
|
10
|
+
function getPKs(table) {
|
|
11
|
+
const { columns, primaryKeys } = (0, pg_core_1.getTableConfig)(table);
|
|
12
|
+
if (primaryKeys.length > 0) {
|
|
13
|
+
// TODO: for now we pick the first pk
|
|
14
|
+
return primaryKeys[0].columns;
|
|
15
|
+
}
|
|
16
|
+
let pks = [];
|
|
17
|
+
for (const col of columns) {
|
|
18
|
+
if (col.primary) {
|
|
19
|
+
pks.push(col);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
if (pks.length <= 0) {
|
|
23
|
+
throw new Error("Service builder needs at least one primary key field");
|
|
24
|
+
}
|
|
25
|
+
return pks;
|
|
26
|
+
}
|
|
27
|
+
function isDrizzleTable(obj) {
|
|
28
|
+
return obj &&
|
|
29
|
+
typeof obj === 'object' &&
|
|
30
|
+
obj instanceof pg_core_1.PgTable;
|
|
31
|
+
}
|
package/dist/src/service.d.ts
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import { Filter, FilterMap,
|
|
2
|
-
import { PgSelect, PgTransaction } from "drizzle-orm/pg-core";
|
|
1
|
+
import { Filter, FilterMap, PrimaryKeyData } from "./types";
|
|
2
|
+
import { AnyPgTable, PgColumn, PgSelect, PgTransaction } from "drizzle-orm/pg-core";
|
|
3
3
|
import { NodePgDatabase } from "drizzle-orm/node-postgres";
|
|
4
4
|
import { InferInsertModel, SQL } from "drizzle-orm";
|
|
5
5
|
import { ZodObject } from "zod";
|
|
6
|
-
export declare class ServiceBuilder<T extends
|
|
6
|
+
export declare class ServiceBuilder<T extends AnyPgTable, TSchema extends Record<string, unknown>, FSchema extends ZodObject, PKType extends object = PrimaryKeyData<T>> {
|
|
7
7
|
private readonly db;
|
|
8
8
|
private readonly table;
|
|
9
9
|
private readonly map;
|
|
10
|
-
|
|
10
|
+
readonly pks: PgColumn[];
|
|
11
11
|
readonly haveSoftDelete: boolean;
|
|
12
12
|
readonly findAllConditions: (f?: Filter<FSchema>) => SQL | undefined;
|
|
13
|
-
readonly findOneConditions: (
|
|
13
|
+
readonly findOneConditions: (pkFilter: PKType) => SQL | undefined;
|
|
14
14
|
readonly tableName: string;
|
|
15
15
|
constructor(db: NodePgDatabase<TSchema>, table: T, map: FilterMap<FSchema>);
|
|
16
|
-
findOne<S extends PgSelect>(select: S): (
|
|
16
|
+
findOne<S extends PgSelect>(select: S): (pkValues: PKType) => Promise<Awaited<ReturnType<S["execute"]>>[number]>;
|
|
17
17
|
findAll<S extends PgSelect>(select: S, paginated?: boolean): (filters?: Filter<FSchema>, page?: number, pageSize?: number) => Promise<Awaited<ReturnType<S["execute"]>>[number][]>;
|
|
18
18
|
count(): (filters?: Filter<FSchema>) => Promise<number>;
|
|
19
19
|
create<S extends any>(hook?: (data: S extends Object ? S : InferInsertModel<T>, tx?: PgTransaction<any, TSchema, any>) => Promise<InferInsertModel<T>>): (data: S extends Object ? S : InferInsertModel<T>, tx?: PgTransaction<any, TSchema, any>) => Promise<{ [Key in keyof T["_"]["columns"] & string as Key]: T["_"]["columns"][Key]["_"]["notNull"] extends true ? T["_"]["columns"][Key]["_"]["data"] : T["_"]["columns"][Key]["_"]["data"] | null; } extends infer T_1 ? { [K in keyof T_1]: T_1[K]; } : never>;
|
|
20
|
-
update<S extends any>(pre?: (data: S extends Object ? S : InferInsertModel<T
|
|
21
|
-
delete(): (
|
|
22
|
-
softDelete(): (
|
|
23
|
-
hardDelete(): (
|
|
20
|
+
update<S extends any>(pre?: (data: S extends Object ? S : Partial<InferInsertModel<T>>, tx?: PgTransaction<any, TSchema, any>) => Promise<InferInsertModel<T>>): (pks: PKType, data: S extends Object ? S : Partial<InferInsertModel<T>>, tx?: PgTransaction<any, TSchema, any>) => Promise<{ [Key in keyof T["_"]["columns"] & string as Key]: T["_"]["columns"][Key]["_"]["notNull"] extends true ? T["_"]["columns"][Key]["_"]["data"] : T["_"]["columns"][Key]["_"]["data"] | null; } extends infer T_1 ? { [K in keyof T_1]: T_1[K]; } : never>;
|
|
21
|
+
delete(): (pks: PKType, tx?: PgTransaction<any, TSchema, any>) => Promise<void>;
|
|
22
|
+
softDelete(): (pks: PKType, tx?: PgTransaction<any, TSchema, any>) => Promise<void>;
|
|
23
|
+
hardDelete(): (pks: PKType, tx?: PgTransaction<any, TSchema, any>) => Promise<void>;
|
|
24
24
|
}
|
package/dist/src/service.js
CHANGED
|
@@ -5,30 +5,29 @@ const pg_core_1 = require("drizzle-orm/pg-core");
|
|
|
5
5
|
const drizzle_orm_1 = require("drizzle-orm");
|
|
6
6
|
const filters_1 = require("./filters");
|
|
7
7
|
const errors_1 = require("./errors");
|
|
8
|
+
const pg_1 = require("./pg");
|
|
8
9
|
class ServiceBuilder {
|
|
9
10
|
constructor(db, table, map) {
|
|
10
11
|
this.db = db;
|
|
11
12
|
this.table = table;
|
|
12
13
|
this.tableName = (0, drizzle_orm_1.getTableName)(table);
|
|
13
|
-
this.pks = getPKs(table);
|
|
14
|
+
this.pks = (0, pg_1.getPKs)(table);
|
|
14
15
|
this.map = map;
|
|
15
16
|
this.haveSoftDelete = haveSoftDelete(table);
|
|
16
17
|
if (this.haveSoftDelete) {
|
|
17
18
|
this.findAllConditions = (f) => (0, drizzle_orm_1.and)((0, drizzle_orm_1.isNull)(this.table.deletedAt), (0, filters_1.buildFilters)(this.map, f));
|
|
18
|
-
this.findOneConditions = (
|
|
19
|
-
// buildPKFilters(this.pks, pkFilter)
|
|
20
|
-
(0, drizzle_orm_1.eq)(this.table.id, id));
|
|
19
|
+
this.findOneConditions = (pkFilter) => (0, drizzle_orm_1.and)((0, drizzle_orm_1.isNull)(this.table.deletedAt), (0, filters_1.buildPKFilters)(this.pks, pkFilter));
|
|
21
20
|
}
|
|
22
21
|
else {
|
|
23
22
|
this.findAllConditions = (f) => (0, filters_1.buildFilters)(this.map, f);
|
|
24
|
-
this.findOneConditions = (
|
|
23
|
+
this.findOneConditions = (pkFilter) => (0, filters_1.buildPKFilters)(this.pks, pkFilter);
|
|
25
24
|
}
|
|
26
25
|
}
|
|
27
26
|
findOne(select) {
|
|
28
|
-
return async (
|
|
29
|
-
const result = await select.where(this.findOneConditions(
|
|
27
|
+
return async (pkValues) => {
|
|
28
|
+
const result = await select.where(this.findOneConditions(pkValues));
|
|
30
29
|
if (result.length == 0)
|
|
31
|
-
throw notFoundWithId(this.tableName,
|
|
30
|
+
throw notFoundWithId(this.tableName, pkValues);
|
|
32
31
|
return result[0];
|
|
33
32
|
};
|
|
34
33
|
}
|
|
@@ -74,7 +73,7 @@ class ServiceBuilder {
|
|
|
74
73
|
};
|
|
75
74
|
}
|
|
76
75
|
update(pre) {
|
|
77
|
-
return async (
|
|
76
|
+
return async (pks, data, tx) => {
|
|
78
77
|
let values = data;
|
|
79
78
|
if (pre) {
|
|
80
79
|
values = await pre(data);
|
|
@@ -86,11 +85,11 @@ class ServiceBuilder {
|
|
|
86
85
|
const [result] = await executor
|
|
87
86
|
.update(this.table)
|
|
88
87
|
.set(values)
|
|
89
|
-
.where(this.findOneConditions(
|
|
88
|
+
.where(this.findOneConditions(pks))
|
|
90
89
|
.returning()
|
|
91
90
|
.catch(errors_1.handleSqlError);
|
|
92
91
|
if (!result) {
|
|
93
|
-
throw notFoundWithId(this.tableName,
|
|
92
|
+
throw notFoundWithId(this.tableName, pks);
|
|
94
93
|
}
|
|
95
94
|
return result;
|
|
96
95
|
};
|
|
@@ -104,27 +103,25 @@ class ServiceBuilder {
|
|
|
104
103
|
}
|
|
105
104
|
}
|
|
106
105
|
softDelete() {
|
|
107
|
-
return async (
|
|
106
|
+
return async (pks, tx) => {
|
|
108
107
|
const executor = tx || this.db;
|
|
109
108
|
const { rowCount } = await executor
|
|
110
109
|
.update(this.table)
|
|
111
110
|
.set({ deletedAt: new Date() })
|
|
112
|
-
.where(this.findOneConditions(
|
|
111
|
+
.where(this.findOneConditions(pks));
|
|
113
112
|
if (rowCount == 0) {
|
|
114
|
-
throw notFoundWithId(this.tableName,
|
|
113
|
+
throw notFoundWithId(this.tableName, pks);
|
|
115
114
|
}
|
|
116
115
|
};
|
|
117
116
|
}
|
|
118
117
|
hardDelete() {
|
|
119
|
-
return async (
|
|
120
|
-
// id: PrimaryKeyData<T>,
|
|
121
|
-
id, tx) => {
|
|
118
|
+
return async (pks, tx) => {
|
|
122
119
|
const executor = tx || this.db;
|
|
123
120
|
const { rowCount } = await executor
|
|
124
121
|
.delete(this.table)
|
|
125
|
-
.where(this.findOneConditions(
|
|
122
|
+
.where(this.findOneConditions(pks));
|
|
126
123
|
if (rowCount == 0) {
|
|
127
|
-
throw notFoundWithId(this.tableName,
|
|
124
|
+
throw notFoundWithId(this.tableName, pks);
|
|
128
125
|
}
|
|
129
126
|
};
|
|
130
127
|
}
|
|
@@ -139,22 +136,7 @@ function haveSoftDelete(table, columnName = "deleted_at") {
|
|
|
139
136
|
}
|
|
140
137
|
return false;
|
|
141
138
|
}
|
|
142
|
-
function getPKs(table) {
|
|
143
|
-
const pks = [];
|
|
144
|
-
const { columns } = (0, pg_core_1.getTableConfig)(table);
|
|
145
|
-
for (const col of columns) {
|
|
146
|
-
if (col.primary) {
|
|
147
|
-
pks.push(col);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
if (pks.length <= 0)
|
|
151
|
-
throw new Error("Service builder needs at least one primary key field");
|
|
152
|
-
return pks;
|
|
153
|
-
}
|
|
154
139
|
function notFoundWithId(tableName, pkFilter) {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
// }
|
|
158
|
-
// TODO: proper message
|
|
159
|
-
return new errors_1.NotFound(`${tableName} not found`);
|
|
140
|
+
const message = Object.entries(pkFilter).map(([k, v]) => `${k}=${v}`).join(' and ');
|
|
141
|
+
return new errors_1.NotFound(`${tableName} with ${message} not found`);
|
|
160
142
|
}
|
package/package.json
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bradb",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"bin": {
|
|
8
|
-
"brad": "dist/
|
|
8
|
+
"brad": "dist/src/commands.js"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"dist"
|
|
12
12
|
],
|
|
13
13
|
"scripts": {
|
|
14
14
|
"build": "rm -rf dist/ && tsc",
|
|
15
|
-
"test": "
|
|
15
|
+
"test": "vitest",
|
|
16
|
+
"yalc:publish": "npm run build && npx yalc publish",
|
|
17
|
+
"yalc:push": "npm run build && chmod +x dist/src/commands.js && npx yalc push"
|
|
16
18
|
},
|
|
17
19
|
"keywords": [
|
|
18
20
|
"api",
|
|
@@ -24,13 +26,21 @@
|
|
|
24
26
|
"devDependencies": {
|
|
25
27
|
"@types/express": "^5.0.3",
|
|
26
28
|
"@types/pg": "^8.15.5",
|
|
27
|
-
"
|
|
29
|
+
"drizzle-kit": "^0.31.7",
|
|
30
|
+
"ts-node": "^10.9.2",
|
|
31
|
+
"tsd": "^0.33.0",
|
|
32
|
+
"typescript": "^5.9.2",
|
|
33
|
+
"vitest": "^3.2.4"
|
|
28
34
|
},
|
|
29
35
|
"peerDependencies": {
|
|
30
36
|
"@types/pg": "^8.15.5",
|
|
31
|
-
"drizzle-orm": "^0.44.
|
|
37
|
+
"drizzle-orm": "^0.44.4",
|
|
38
|
+
"drizzle-zod": "^0.8.3",
|
|
32
39
|
"express": "^5.1.0",
|
|
33
40
|
"pg": "^8.16.3",
|
|
34
41
|
"zod": "^4.1.5"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"commander": "^14.0.3"
|
|
35
45
|
}
|
|
36
46
|
}
|
package/dist/bin/brad.d.ts
DELETED
package/dist/bin/brad.js
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
-
};
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
const commands_1 = __importDefault(require("../src/commands"));
|
|
8
|
-
const path_1 = __importDefault(require("path"));
|
|
9
|
-
const fs_1 = __importDefault(require("fs"));
|
|
10
|
-
const args = process.argv.slice(2);
|
|
11
|
-
const main = async () => {
|
|
12
|
-
// Load config file
|
|
13
|
-
const configPath = path_1.default.resolve(process.cwd(), 'brad.config.mjs');
|
|
14
|
-
let config = {};
|
|
15
|
-
if (fs_1.default.existsSync(configPath)) {
|
|
16
|
-
try {
|
|
17
|
-
const loadedConfig = require(configPath);
|
|
18
|
-
config = loadedConfig.default || loadedConfig;
|
|
19
|
-
console.log(config);
|
|
20
|
-
console.log("Loaded brad.config.js");
|
|
21
|
-
}
|
|
22
|
-
catch (e) {
|
|
23
|
-
console.error("Error loading brad.config.js:", e);
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
else {
|
|
28
|
-
console.warn("Warning: brad.config.js not found. Using default settings.");
|
|
29
|
-
}
|
|
30
|
-
if (args.length === 0) {
|
|
31
|
-
console.log("Brad CLI - Available commands: make:entity");
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
const command = args[0];
|
|
35
|
-
if (command === 'make:entity') {
|
|
36
|
-
const entityName = args[1];
|
|
37
|
-
if (!entityName) {
|
|
38
|
-
console.error("Error: Entity name is required. Usage: brad make:entity <EntityName>");
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
let basePath = 'src'; // Default path
|
|
42
|
-
const pathArg = args.find(arg => arg.startsWith('--path='));
|
|
43
|
-
if (pathArg) {
|
|
44
|
-
basePath = pathArg.split('=')[1];
|
|
45
|
-
}
|
|
46
|
-
await commands_1.default.createEntity(entityName, basePath, config);
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
console.error(`Error: Unknown command '${command}'`);
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
main();
|
package/dist/src/relational.d.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { Filter, FilterMap } from "../src/types";
|
|
2
|
-
import { BuildQueryResult, DBQueryConfig, KnownKeysOnly, TableRelationalConfig, TablesRelationalConfig } from "drizzle-orm";
|
|
3
|
-
import { RelationalQueryBuilder } from "drizzle-orm/pg-core/query-builders/query";
|
|
4
|
-
import { ZodObject } from "zod";
|
|
5
|
-
export declare function RelationalBuilder<TSchema extends TablesRelationalConfig, TFields extends TableRelationalConfig, FSchema extends ZodObject>(q: RelationalQueryBuilder<TSchema, TFields>, filterMap: FilterMap<FSchema>): {
|
|
6
|
-
findAll<TConfig extends DBQueryConfig<"many", true, TSchema, TFields>>(config?: TConfig): (filters?: Filter<FSchema>, page?: number, pageSize?: number) => Promise<BuildQueryResult<TSchema, TFields, TConfig>[]>;
|
|
7
|
-
findOne<TSelection extends Omit<DBQueryConfig<"many", true, TSchema, TFields>, "limit">>(config?: KnownKeysOnly<TSelection, Omit<DBQueryConfig<"many", true, TSchema, TFields>, "limit">>): (id: number) => Promise<BuildQueryResult<TSchema, TFields, TSelection>>;
|
|
8
|
-
};
|
package/dist/src/relational.js
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RelationalBuilder = RelationalBuilder;
|
|
4
|
-
const drizzle_orm_1 = require("drizzle-orm");
|
|
5
|
-
const filters_1 = require("./filters");
|
|
6
|
-
const errors_1 = require("./errors");
|
|
7
|
-
const pg_core_1 = require("drizzle-orm/pg-core");
|
|
8
|
-
function RelationalBuilder(q, filterMap) {
|
|
9
|
-
const baseWhere = (table) => (0, drizzle_orm_1.isNull)(table.deletedAt);
|
|
10
|
-
return {
|
|
11
|
-
findAll(config) {
|
|
12
|
-
return async (filters, page = 1, pageSize = 10) => {
|
|
13
|
-
const offset = (page - 1) * pageSize;
|
|
14
|
-
const items = q.findMany({
|
|
15
|
-
...config,
|
|
16
|
-
where: (table) => (0, drizzle_orm_1.and)(baseWhere(table), (0, filters_1.buildFilters)(filterMap, filters)),
|
|
17
|
-
limit: pageSize,
|
|
18
|
-
offset
|
|
19
|
-
});
|
|
20
|
-
return items;
|
|
21
|
-
};
|
|
22
|
-
},
|
|
23
|
-
findOne(config) {
|
|
24
|
-
return async (id) => {
|
|
25
|
-
const result = await q.findFirst({
|
|
26
|
-
...config,
|
|
27
|
-
// TODO: Validate table contains 'id' field
|
|
28
|
-
where: (table, { eq }) => (0, drizzle_orm_1.and)(baseWhere(table), eq(table.id, id))
|
|
29
|
-
});
|
|
30
|
-
if (!result) {
|
|
31
|
-
// TODO: proper name
|
|
32
|
-
const t = "record";
|
|
33
|
-
throw new errors_1.NotFound(`${t} with id ${id} not found`);
|
|
34
|
-
}
|
|
35
|
-
return result;
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
function findByPkConditions(table, data) {
|
|
41
|
-
const { primaryKeys } = (0, pg_core_1.getTableConfig)(table);
|
|
42
|
-
const conditions = [];
|
|
43
|
-
for (const pk of primaryKeys) {
|
|
44
|
-
for (const column of pk.columns) {
|
|
45
|
-
const name = column.name;
|
|
46
|
-
// if (data[name] === undefined || data[name] === null) {
|
|
47
|
-
// throw new Error(`Missing value for pk column: ${column.name}`);
|
|
48
|
-
// }
|
|
49
|
-
conditions.push((0, drizzle_orm_1.eq)(table[name], data[name]));
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return conditions;
|
|
53
|
-
}
|