@xitkov/core 2.5.0 → 2.6.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 +23 -0
- package/lib/services/register-services.d.ts +4 -0
- package/lib/services/register-services.js +40 -13
- package/lib/services/register-services.js.map +1 -1
- package/lib/services/service.d.ts +0 -8
- package/lib/services/service.js.map +1 -1
- package/package.json +1 -1
- package/src/services/register-services.ts +41 -14
- package/src/services/service.ts +0 -9
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
|
|
@@ -12,6 +12,10 @@ type Functions = 'find' | 'get' | 'create' | 'update' | 'patch' | 'remove';
|
|
|
12
12
|
export declare function validate(Validator: any, func: string, value: any): Promise<any>;
|
|
13
13
|
/** Where a method's validated input sits in the service call. */
|
|
14
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;
|
|
15
19
|
export declare function registerServices(app: Application, auth?: IAuth, database?: Config['database']): Promise<{
|
|
16
20
|
[serviceName: string]: any;
|
|
17
21
|
}>;
|
|
@@ -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 = exports.VALIDATED_INDEX = exports.validate = 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"));
|
|
@@ -85,6 +85,32 @@ function getJWTDecoded(accessToken) {
|
|
|
85
85
|
return {};
|
|
86
86
|
}
|
|
87
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;
|
|
88
114
|
async function registerServices(app, auth, database) {
|
|
89
115
|
var _a, _b, _c;
|
|
90
116
|
if (((_a = require.main) === null || _a === void 0 ? void 0 : _a.filename) == undefined) {
|
|
@@ -92,7 +118,8 @@ async function registerServices(app, auth, database) {
|
|
|
92
118
|
}
|
|
93
119
|
const appDir = path_1.default.dirname((_b = require.main) === null || _b === void 0 ? void 0 : _b.filename);
|
|
94
120
|
const serviceDirPath = appDir + '/services';
|
|
95
|
-
|
|
121
|
+
// Deepest first: `/skills/:id` would otherwise match `/skills/versions`.
|
|
122
|
+
const discovered = (await discoverServices(serviceDirPath)).sort((a, b) => b.length - a.length);
|
|
96
123
|
const methods = [
|
|
97
124
|
{
|
|
98
125
|
method: 'get',
|
|
@@ -126,11 +153,14 @@ async function registerServices(app, auth, database) {
|
|
|
126
153
|
},
|
|
127
154
|
];
|
|
128
155
|
const services = {};
|
|
129
|
-
for (const
|
|
130
|
-
|
|
131
|
-
const
|
|
132
|
-
const
|
|
133
|
-
const
|
|
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`;
|
|
134
164
|
let Model = null;
|
|
135
165
|
let Validator = null;
|
|
136
166
|
let Hooks = null;
|
|
@@ -285,12 +315,9 @@ async function registerServices(app, auth, database) {
|
|
|
285
315
|
router[method](url, callback);
|
|
286
316
|
}
|
|
287
317
|
app.set('services', services);
|
|
288
|
-
//
|
|
289
|
-
//
|
|
290
|
-
|
|
291
|
-
? `/${service.parent}/:parentId/${serviceName}`
|
|
292
|
-
: `/${serviceName}`;
|
|
293
|
-
app.use(mountPath, 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);
|
|
294
321
|
}
|
|
295
322
|
return services;
|
|
296
323
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register-services.js","sourceRoot":"","sources":["../../src/services/register-services.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,sEAAsE;AACtE,2BAA4B;AAC5B,gDAAwB;AACxB,qCAA+B;AAC/B,0BAAmE;AAEnE,sCAAqD;AACrD,iCAAqC;AAcrC;;;;;;;GAOG;AACI,KAAK,UAAU,QAAQ,CAAC,SAAc,EAAE,IAAY,EAAE,KAAU;IACrE,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,CAAA;IACpC,IAAI,MAAM,EAAE;QACV,IAAI;YACF,MAAM,WAAW,GAAG,MAAM,MAAM,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;YACxD,OAAO,WAAW,CAAC;SACpB;QAAC,OAAO,GAAQ,EAAE;YACjB,MAAM,IAAI,mBAAU,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;SAClC;KACF;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAXD,4BAWC;AAED,iEAAiE;AACpD,QAAA,eAAe,GAA8B;IACxD,IAAI,EAAE,CAAC;IACP,GAAG,EAAE,CAAC;IACN,MAAM,EAAE,CAAC;IACT,KAAK,EAAE,CAAC;IACR,MAAM,EAAE,CAAC;IACT,MAAM,EAAE,CAAC;CACV,CAAA;AAED,KAAK,UAAU,KAAK,CAAC,KAAU,EAAE,IAAY,EAAE,UAAiB,EAAE,UAAkB;;IAClF,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,CAAA;IAE5B,MAAA,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,OAAO,yDAAG,CAAC,IAAS,EAAE,EAAE;QAChC,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAG,GAAG,UAAU,EAAE,UAAU,CAAC,CAAA;IACnC,CAAC,CAAC,CAAC;IACH,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED,SAAS,aAAa,CAAC,WAAmB;IACxC,IAAI;QACF,MAAM,SAAS,GAAG,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5C,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAA;QAC3D,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;KAC3B;IAAC,OAAM,KAAK,EAAE;QACb,UAAM,CAAC,KAAK,CAAC,eAAe,EAAE,KAAK,CAAC,CAAA;QACpC,OAAO,EAAE,CAAA;KACV;AACH,CAAC;
|
|
1
|
+
{"version":3,"file":"register-services.js","sourceRoot":"","sources":["../../src/services/register-services.ts"],"names":[],"mappings":"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"}
|
|
@@ -24,14 +24,6 @@ export declare class XitkovService<T, U extends Dialect = DefaultDialect> implem
|
|
|
24
24
|
private service;
|
|
25
25
|
private Model;
|
|
26
26
|
pagination?: boolean;
|
|
27
|
-
/**
|
|
28
|
-
* Mount beneath another service, by name.
|
|
29
|
-
*
|
|
30
|
-
* `parent = "agents"` serves this resource at
|
|
31
|
-
* `/agents/:parentId/<service>`, and the handler reads `req.params.parentId`.
|
|
32
|
-
* Unset means a top-level resource.
|
|
33
|
-
*/
|
|
34
|
-
parent?: string;
|
|
35
27
|
static defaultDialect: Dialect;
|
|
36
28
|
static setDefaultDialect(dialect: Dialect): void;
|
|
37
29
|
constructor({ Model, dialect, pagination }: IXitovServiceProps);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/services/service.ts"],"names":[],"mappings":";;;AAIA,2CAAkH;AAkClH,MAAa,aAAa;
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/services/service.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -71,6 +71,33 @@ function getJWTDecoded(accessToken: string) {
|
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
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
|
+
|
|
74
101
|
export async function registerServices(app: Application, auth?: IAuth, database?: Config['database']) {
|
|
75
102
|
if (require.main?.filename == undefined) {
|
|
76
103
|
throw new Error("Please provide service path")
|
|
@@ -78,8 +105,9 @@ export async function registerServices(app: Application, auth?: IAuth, database?
|
|
|
78
105
|
const appDir = path.dirname(require.main?.filename as string);
|
|
79
106
|
const serviceDirPath = appDir + '/services';
|
|
80
107
|
|
|
81
|
-
|
|
82
|
-
|
|
108
|
+
// Deepest first: `/skills/:id` would otherwise match `/skills/versions`.
|
|
109
|
+
const discovered = (await discoverServices(serviceDirPath)).sort(
|
|
110
|
+
(a, b) => b.length - a.length
|
|
83
111
|
);
|
|
84
112
|
|
|
85
113
|
const methods: Methods[] = [
|
|
@@ -117,11 +145,14 @@ export async function registerServices(app: Application, auth?: IAuth, database?
|
|
|
117
145
|
|
|
118
146
|
const services: {[serviceName: string]: any} = {}
|
|
119
147
|
|
|
120
|
-
for (const
|
|
121
|
-
|
|
122
|
-
const
|
|
123
|
-
const
|
|
124
|
-
const
|
|
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`;
|
|
125
156
|
let Model = null
|
|
126
157
|
let Validator: any = null
|
|
127
158
|
let Hooks: any = null
|
|
@@ -286,13 +317,9 @@ export async function registerServices(app: Application, auth?: IAuth, database?
|
|
|
286
317
|
}
|
|
287
318
|
app.set('services', services)
|
|
288
319
|
|
|
289
|
-
//
|
|
290
|
-
//
|
|
291
|
-
|
|
292
|
-
? `/${service.parent}/:parentId/${serviceName}`
|
|
293
|
-
: `/${serviceName}`;
|
|
294
|
-
|
|
295
|
-
app.use(mountPath, 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);
|
|
296
323
|
}
|
|
297
324
|
return services;
|
|
298
325
|
}
|
package/src/services/service.ts
CHANGED
|
@@ -41,15 +41,6 @@ export class XitkovService<T, U extends Dialect = DefaultDialect> implements Int
|
|
|
41
41
|
private Model: ModelType<T, U>
|
|
42
42
|
pagination?: boolean;
|
|
43
43
|
|
|
44
|
-
/**
|
|
45
|
-
* Mount beneath another service, by name.
|
|
46
|
-
*
|
|
47
|
-
* `parent = "agents"` serves this resource at
|
|
48
|
-
* `/agents/:parentId/<service>`, and the handler reads `req.params.parentId`.
|
|
49
|
-
* Unset means a top-level resource.
|
|
50
|
-
*/
|
|
51
|
-
parent?: string;
|
|
52
|
-
|
|
53
44
|
static defaultDialect: Dialect = 'mongodb';
|
|
54
45
|
|
|
55
46
|
static setDefaultDialect(dialect: Dialect) {
|