@xitkov/core 2.4.10 → 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/README.md CHANGED
@@ -25,6 +25,29 @@ src/
25
25
  └─ billing.ts # optional external service client
26
26
  ```
27
27
 
28
+ ### Nested services
29
+
30
+ A service folder inside another is served beneath it. Position is the only
31
+ declaration needed:
32
+
33
+ ```
34
+ services/
35
+ ├─ skills/
36
+ │ ├─ service.ts # /skills
37
+ │ └─ versions/
38
+ │ └─ service.ts # /skills/:parentId/versions
39
+ └─ conversations/
40
+ ├─ service.ts # /conversations
41
+ └─ messages/
42
+ └─ service.ts # /conversations/:parentId/messages
43
+ ```
44
+
45
+ Read the parent from `req.params.parentId`, and address the service by its
46
+ path: `Services.get('skills.versions')`.
47
+
48
+ A folder with no `service` module is a grouping folder, not a service; the
49
+ services inside it are still found.
50
+
28
51
  `src/index.ts`:
29
52
 
30
53
  ```ts
@@ -1,5 +1,22 @@
1
1
  import type { Application } from 'express';
2
2
  import { Config, IAuth } from '..';
3
+ type Functions = 'find' | 'get' | 'create' | 'update' | 'patch' | 'remove';
4
+ /**
5
+ * Validate one method's input and return the coerced result.
6
+ *
7
+ * Joi converts as well as checks: `"25"` becomes `25`, and a declared default
8
+ * fills a missing field. The caller must therefore use the returned value —
9
+ * discarding it would validate the request and then hand the service the raw
10
+ * one, so defaults would silently never arrive.
11
+ */
12
+ export declare function validate(Validator: any, func: string, value: any): Promise<any>;
13
+ /** Where a method's validated input sits in the service call. */
14
+ export declare const VALIDATED_INDEX: Record<Functions, number>;
15
+ /** Every service folder under `services`, nested ones included. */
16
+ export declare function discoverServices(root: string, segments?: string[]): Promise<string[][]>;
17
+ /** `['skills','versions']` -> `/skills/:parentId/versions`. */
18
+ export declare function mountPath(segments: string[]): string;
3
19
  export declare function registerServices(app: Application, auth?: IAuth, database?: Config['database']): Promise<{
4
20
  [serviceName: string]: any;
5
21
  }>;
22
+ export {};
@@ -27,7 +27,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
27
27
  };
28
28
  var _a, _b, _c, _d, _e;
29
29
  Object.defineProperty(exports, "__esModule", { value: true });
30
- exports.registerServices = void 0;
30
+ exports.registerServices = exports.mountPath = exports.discoverServices = exports.VALIDATED_INDEX = exports.validate = void 0;
31
31
  // eslint-disable-next-line node/no-unsupported-features/node-builtins
32
32
  const fs_1 = require("fs");
33
33
  const path_1 = __importDefault(require("path"));
@@ -35,6 +35,14 @@ const express_1 = require("express");
35
35
  const __1 = require("..");
36
36
  const errors_1 = require("../errors");
37
37
  const auth_1 = require("./auth");
38
+ /**
39
+ * Validate one method's input and return the coerced result.
40
+ *
41
+ * Joi converts as well as checks: `"25"` becomes `25`, and a declared default
42
+ * fills a missing field. The caller must therefore use the returned value —
43
+ * discarding it would validate the request and then hand the service the raw
44
+ * one, so defaults would silently never arrive.
45
+ */
38
46
  async function validate(Validator, func, value) {
39
47
  const schema = await Validator[func];
40
48
  if (schema) {
@@ -48,6 +56,16 @@ async function validate(Validator, func, value) {
48
56
  }
49
57
  return value;
50
58
  }
59
+ exports.validate = validate;
60
+ /** Where a method's validated input sits in the service call. */
61
+ exports.VALIDATED_INDEX = {
62
+ find: 0,
63
+ get: 1,
64
+ create: 0,
65
+ patch: 1,
66
+ update: 1,
67
+ remove: 1,
68
+ };
51
69
  async function hooks(Hooks, func, parameters, methodName) {
52
70
  var _a;
53
71
  const allHooks = Hooks[func];
@@ -67,6 +85,32 @@ function getJWTDecoded(accessToken) {
67
85
  return {};
68
86
  }
69
87
  }
88
+ /** Every service folder under `services`, nested ones included. */
89
+ async function discoverServices(root, segments = []) {
90
+ const entries = await fs_1.promises.readdir(path_1.default.join(root, ...segments), { withFileTypes: true });
91
+ const found = [];
92
+ for (const entry of entries) {
93
+ if (!entry.isDirectory() || entry.name === 'hooks')
94
+ continue;
95
+ const child = [...segments, entry.name];
96
+ const files = await fs_1.promises.readdir(path_1.default.join(root, ...child));
97
+ if (files.some(file => /^service\.(ts|js)$/.test(file)))
98
+ found.push(child);
99
+ // Recurse either way: a grouping folder holds no service of its own.
100
+ found.push(...(await discoverServices(root, child)));
101
+ }
102
+ return found;
103
+ }
104
+ exports.discoverServices = discoverServices;
105
+ /** `['skills','versions']` -> `/skills/:parentId/versions`. */
106
+ function mountPath(segments) {
107
+ const parents = segments.slice(0, -1).map((name, i) =>
108
+ // Express rejects two parameters with the same name, so only the closest
109
+ // parent gets the plain one.
110
+ `/${name}/:parentId${i === segments.length - 2 ? '' : segments.length - 2 - i}`);
111
+ return `${parents.join('')}/${segments[segments.length - 1]}`;
112
+ }
113
+ exports.mountPath = mountPath;
70
114
  async function registerServices(app, auth, database) {
71
115
  var _a, _b, _c;
72
116
  if (((_a = require.main) === null || _a === void 0 ? void 0 : _a.filename) == undefined) {
@@ -74,7 +118,8 @@ async function registerServices(app, auth, database) {
74
118
  }
75
119
  const appDir = path_1.default.dirname((_b = require.main) === null || _b === void 0 ? void 0 : _b.filename);
76
120
  const serviceDirPath = appDir + '/services';
77
- const servicesNames = (await fs_1.promises.readdir(serviceDirPath)).filter(file => !/^index\.(ts|js|d\.ts)$/.test(file) && !/\.map$/.test(file));
121
+ // Deepest first: `/skills/:id` would otherwise match `/skills/versions`.
122
+ const discovered = (await discoverServices(serviceDirPath)).sort((a, b) => b.length - a.length);
78
123
  const methods = [
79
124
  {
80
125
  method: 'get',
@@ -108,11 +153,14 @@ async function registerServices(app, auth, database) {
108
153
  },
109
154
  ];
110
155
  const services = {};
111
- for (const serviceName of servicesNames) {
112
- const servicePath = `${serviceDirPath}/${serviceName}/service`;
113
- const modelPath = `${serviceDirPath}/${serviceName}/model`;
114
- const validatorPath = `${serviceDirPath}/${serviceName}/validator`;
115
- const hooksPath = `${serviceDirPath}/${serviceName}/hooks`;
156
+ for (const segments of discovered) {
157
+ // `skills/versions` is keyed `skills.versions` and mounted beneath skills.
158
+ const serviceName = segments.join('.');
159
+ const directory = path_1.default.join(serviceDirPath, ...segments);
160
+ const servicePath = `${directory}/service`;
161
+ const modelPath = `${directory}/model`;
162
+ const validatorPath = `${directory}/validator`;
163
+ const hooksPath = `${directory}/hooks`;
116
164
  let Model = null;
117
165
  let Validator = null;
118
166
  let Hooks = null;
@@ -146,7 +194,9 @@ async function registerServices(app, auth, database) {
146
194
  serviceConfig.dialect = (_c = database === null || database === void 0 ? void 0 : database.dialect) !== null && _c !== void 0 ? _c : 'mongodb';
147
195
  const service = new (await (_e = servicePath, Promise.resolve().then(() => __importStar(require(_e))))).default(serviceConfig);
148
196
  services[serviceName] = service;
149
- const router = (0, express_1.Router)();
197
+ // `mergeParams` lets a nested handler read the parent id from the mount
198
+ // path rather than only its own parameters.
199
+ const router = (0, express_1.Router)({ mergeParams: true });
150
200
  for (const { method, url, func } of methods) {
151
201
  const callback = async (req, res) => {
152
202
  var _a, _b;
@@ -185,47 +235,48 @@ async function registerServices(app, auth, database) {
185
235
  const user = req.user;
186
236
  const { id } = params;
187
237
  let parameters = [];
188
- let validatorValue;
189
238
  switch (func) {
190
239
  case 'find':
191
240
  parameters = [query, user, req];
192
- validatorValue = query;
193
241
  break;
194
242
  case 'get':
195
243
  parameters = [id, query, user, req];
196
- validatorValue = query;
197
244
  break;
198
245
  case 'create':
199
246
  parameters = [body, query, user, req];
200
- validatorValue = body;
201
247
  break;
202
248
  case 'patch':
203
249
  parameters = [id, body, query, user, req];
204
- validatorValue = body;
205
250
  break;
206
251
  case 'update':
207
252
  parameters = [id, body, query, user, req];
208
- validatorValue = body;
209
253
  break;
210
254
  case 'remove':
211
255
  parameters = [id, query, user, req];
212
- validatorValue = query;
213
256
  break;
214
257
  }
215
- if (Hooks) {
216
- await hooks(Hooks, 'all', parameters, func);
217
- await hooks(Hooks, func, parameters, func);
218
- }
219
- if (service.filtering !== false && func === 'find') {
220
- parameters[0] = (0, __1.addQuerySupport)(...parameters, func);
221
- }
222
258
  if (service[func] == undefined) {
223
259
  return res.status(404).send({
224
260
  message: "Not found"
225
261
  });
226
262
  }
263
+ // Validation runs before the hooks so a hook inspects the same
264
+ // coerced value the service receives, and writing the result back
265
+ // into `parameters` is what carries Joi's defaults and type
266
+ // conversions through to the service.
227
267
  if (Validator) {
228
- await validate(Validator, func, validatorValue);
268
+ const index = exports.VALIDATED_INDEX[func];
269
+ parameters[index] = await validate(Validator, func, parameters[index]);
270
+ }
271
+ if (Hooks) {
272
+ await hooks(Hooks, 'all', parameters, func);
273
+ await hooks(Hooks, func, parameters, func);
274
+ }
275
+ // Filter translation comes last: it rewrites the query in place, so
276
+ // running it before validation would validate a value the service
277
+ // never sees.
278
+ if (service.filtering !== false && func === 'find') {
279
+ parameters[0] = (0, __1.addQuerySupport)(...parameters, func);
229
280
  }
230
281
  const response = await service[func](...parameters);
231
282
  let data = response;
@@ -264,7 +315,9 @@ async function registerServices(app, auth, database) {
264
315
  router[method](url, callback);
265
316
  }
266
317
  app.set('services', services);
267
- app.use(`/${serviceName}`, router);
318
+ // The folder's position is the mount path, so a nested service is only
319
+ // reachable through its parent without naming that parent in code.
320
+ app.use(mountPath(segments), router);
268
321
  }
269
322
  return services;
270
323
  }
@@ -1 +1 @@
1
- {"version":3,"file":"register-services.js","sourceRoot":"","sources":["../../src/services/register-services.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"register-services.js","sourceRoot":"","sources":["../../src/services/register-services.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xitkov/core",
3
- "version": "2.4.10",
3
+ "version": "3.0.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -31,6 +31,7 @@
31
31
  "@types/node": "^14.11.2",
32
32
  "gts": "^3.1.0",
33
33
  "jest": "^30.4.2",
34
+ "joi": "^18.2.9",
34
35
  "sqlite3": "^6.0.1",
35
36
  "ts-jest": "^29.4.11",
36
37
  "typescript": "^4.0.3"
@@ -20,7 +20,15 @@ interface Methods {
20
20
  func: Functions;
21
21
  }
22
22
 
23
- async function validate(Validator: any, func: string, value: any) {
23
+ /**
24
+ * Validate one method's input and return the coerced result.
25
+ *
26
+ * Joi converts as well as checks: `"25"` becomes `25`, and a declared default
27
+ * fills a missing field. The caller must therefore use the returned value —
28
+ * discarding it would validate the request and then hand the service the raw
29
+ * one, so defaults would silently never arrive.
30
+ */
31
+ export async function validate(Validator: any, func: string, value: any) {
24
32
  const schema = await Validator[func]
25
33
  if (schema) {
26
34
  try {
@@ -33,6 +41,16 @@ async function validate(Validator: any, func: string, value: any) {
33
41
  return value
34
42
  }
35
43
 
44
+ /** Where a method's validated input sits in the service call. */
45
+ export const VALIDATED_INDEX: Record<Functions, number> = {
46
+ find: 0,
47
+ get: 1,
48
+ create: 0,
49
+ patch: 1,
50
+ update: 1,
51
+ remove: 1,
52
+ }
53
+
36
54
  async function hooks(Hooks: any, func: string, parameters: any[], methodName: string) {
37
55
  const allHooks = Hooks[func]
38
56
 
@@ -53,6 +71,33 @@ function getJWTDecoded(accessToken: string) {
53
71
  }
54
72
  }
55
73
 
74
+ /** Every service folder under `services`, nested ones included. */
75
+ export async function discoverServices(root: string, segments: string[] = []) {
76
+ const entries = await promises.readdir(path.join(root, ...segments), {withFileTypes: true});
77
+ const found: string[][] = [];
78
+
79
+ for (const entry of entries) {
80
+ if (!entry.isDirectory() || entry.name === 'hooks') continue;
81
+ const child = [...segments, entry.name];
82
+ const files = await promises.readdir(path.join(root, ...child));
83
+ if (files.some(file => /^service\.(ts|js)$/.test(file))) found.push(child);
84
+ // Recurse either way: a grouping folder holds no service of its own.
85
+ found.push(...(await discoverServices(root, child)));
86
+ }
87
+ return found;
88
+ }
89
+
90
+ /** `['skills','versions']` -> `/skills/:parentId/versions`. */
91
+ export function mountPath(segments: string[]) {
92
+ const parents = segments.slice(0, -1).map((name, i) =>
93
+ // Express rejects two parameters with the same name, so only the closest
94
+ // parent gets the plain one.
95
+ `/${name}/:parentId${i === segments.length - 2 ? '' : segments.length - 2 - i}`
96
+ );
97
+ return `${parents.join('')}/${segments[segments.length - 1]}`;
98
+ }
99
+
100
+
56
101
  export async function registerServices(app: Application, auth?: IAuth, database?: Config['database']) {
57
102
  if (require.main?.filename == undefined) {
58
103
  throw new Error("Please provide service path")
@@ -60,8 +105,9 @@ export async function registerServices(app: Application, auth?: IAuth, database?
60
105
  const appDir = path.dirname(require.main?.filename as string);
61
106
  const serviceDirPath = appDir + '/services';
62
107
 
63
- const servicesNames = (await promises.readdir(serviceDirPath)).filter(
64
- file => !/^index\.(ts|js|d\.ts)$/.test(file) && !/\.map$/.test(file)
108
+ // Deepest first: `/skills/:id` would otherwise match `/skills/versions`.
109
+ const discovered = (await discoverServices(serviceDirPath)).sort(
110
+ (a, b) => b.length - a.length
65
111
  );
66
112
 
67
113
  const methods: Methods[] = [
@@ -99,11 +145,14 @@ export async function registerServices(app: Application, auth?: IAuth, database?
99
145
 
100
146
  const services: {[serviceName: string]: any} = {}
101
147
 
102
- for (const serviceName of servicesNames) {
103
- const servicePath = `${serviceDirPath}/${serviceName}/service`;
104
- const modelPath = `${serviceDirPath}/${serviceName}/model`;
105
- const validatorPath = `${serviceDirPath}/${serviceName}/validator`;
106
- const hooksPath = `${serviceDirPath}/${serviceName}/hooks`;
148
+ for (const segments of discovered) {
149
+ // `skills/versions` is keyed `skills.versions` and mounted beneath skills.
150
+ const serviceName = segments.join('.');
151
+ const directory = path.join(serviceDirPath, ...segments);
152
+ const servicePath = `${directory}/service`;
153
+ const modelPath = `${directory}/model`;
154
+ const validatorPath = `${directory}/validator`;
155
+ const hooksPath = `${directory}/hooks`;
107
156
  let Model = null
108
157
  let Validator: any = null
109
158
  let Hooks: any = null
@@ -137,7 +186,9 @@ export async function registerServices(app: Application, auth?: IAuth, database?
137
186
 
138
187
  const service = new (await import(servicePath)).default(serviceConfig);
139
188
  services[serviceName] = service
140
- const router = Router();
189
+ // `mergeParams` lets a nested handler read the parent id from the mount
190
+ // path rather than only its own parameters.
191
+ const router = Router({mergeParams: true});
141
192
  for (const {method, url, func} of methods) {
142
193
  const callback = async (req: Request, res: Response) => {
143
194
  const { required, baseURL } = auth ?? {}
@@ -180,48 +231,52 @@ export async function registerServices(app: Application, auth?: IAuth, database?
180
231
  const user = (req as any).user
181
232
  const {id} = params;
182
233
  let parameters: any[] = [];
183
- let validatorValue
184
234
  switch (func) {
185
235
  case 'find':
186
236
  parameters = [query, user, req];
187
- validatorValue = query
188
237
  break;
189
238
  case 'get':
190
239
  parameters = [id, query, user, req];
191
- validatorValue = query
192
240
  break;
193
241
  case 'create':
194
242
  parameters = [body, query, user, req];
195
- validatorValue = body
196
243
  break;
197
244
  case 'patch':
198
245
  parameters = [id, body, query, user, req];
199
- validatorValue = body
200
246
  break;
201
247
  case 'update':
202
248
  parameters = [id, body, query, user, req];
203
- validatorValue = body
204
249
  break;
205
250
  case 'remove':
206
251
  parameters = [id, query, user, req];
207
- validatorValue = query
208
252
  break;
209
253
  }
210
- if (Hooks) {
211
- await hooks(Hooks, 'all', parameters, func)
212
- await hooks(Hooks, func, parameters, func)
213
- }
214
- if (service.filtering !== false && func === 'find') {
215
- parameters[0] = addQuerySupport(...parameters, func)
216
- }
217
254
 
218
255
  if(service[func] == undefined) {
219
256
  return res.status(404).send({
220
257
  message: "Not found"
221
258
  });
222
259
  }
260
+
261
+ // Validation runs before the hooks so a hook inspects the same
262
+ // coerced value the service receives, and writing the result back
263
+ // into `parameters` is what carries Joi's defaults and type
264
+ // conversions through to the service.
223
265
  if (Validator) {
224
- await validate(Validator, func, validatorValue)
266
+ const index = VALIDATED_INDEX[func]
267
+ parameters[index] = await validate(Validator, func, parameters[index])
268
+ }
269
+
270
+ if (Hooks) {
271
+ await hooks(Hooks, 'all', parameters, func)
272
+ await hooks(Hooks, func, parameters, func)
273
+ }
274
+
275
+ // Filter translation comes last: it rewrites the query in place, so
276
+ // running it before validation would validate a value the service
277
+ // never sees.
278
+ if (service.filtering !== false && func === 'find') {
279
+ parameters[0] = addQuerySupport(...parameters, func)
225
280
  }
226
281
 
227
282
  const response = await service[func](...parameters)
@@ -262,7 +317,9 @@ export async function registerServices(app: Application, auth?: IAuth, database?
262
317
  }
263
318
  app.set('services', services)
264
319
 
265
- app.use(`/${serviceName}`, router);
320
+ // The folder's position is the mount path, so a nested service is only
321
+ // reachable through its parent without naming that parent in code.
322
+ app.use(mountPath(segments), router);
266
323
  }
267
324
  return services;
268
325
  }