@xitkov/core 2.5.0 → 2.7.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 +17 -0
- package/lib/services/register-services.js +63 -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 +66 -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,23 @@ 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
|
+
/**
|
|
18
|
+
* Where a service is served, from where its folder sits.
|
|
19
|
+
*
|
|
20
|
+
* `['skills','versions']` -> `/skills/:parentId/versions`: a version belongs to
|
|
21
|
+
* one skill, so the path has to say which.
|
|
22
|
+
*
|
|
23
|
+
* A parent that is not itself a service is only grouping, and contributes a
|
|
24
|
+
* plain segment: with `mcp/tools` and `mcp/servers` but no `mcp` service,
|
|
25
|
+
* `/mcp/tools`. There is no MCP record to identify, so a parameter there would
|
|
26
|
+
* ask for an id nobody could supply.
|
|
27
|
+
*
|
|
28
|
+
* `services` is what discovery returned. Omit it and every parent is treated
|
|
29
|
+
* as a service, which is what this did before.
|
|
30
|
+
*/
|
|
31
|
+
export declare function mountPath(segments: string[], services?: string[][]): string;
|
|
15
32
|
export declare function registerServices(app: Application, auth?: IAuth, database?: Config['database']): Promise<{
|
|
16
33
|
[serviceName: string]: any;
|
|
17
34
|
}>;
|
|
@@ -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,55 @@ 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
|
+
/**
|
|
106
|
+
* Where a service is served, from where its folder sits.
|
|
107
|
+
*
|
|
108
|
+
* `['skills','versions']` -> `/skills/:parentId/versions`: a version belongs to
|
|
109
|
+
* one skill, so the path has to say which.
|
|
110
|
+
*
|
|
111
|
+
* A parent that is not itself a service is only grouping, and contributes a
|
|
112
|
+
* plain segment: with `mcp/tools` and `mcp/servers` but no `mcp` service,
|
|
113
|
+
* `/mcp/tools`. There is no MCP record to identify, so a parameter there would
|
|
114
|
+
* ask for an id nobody could supply.
|
|
115
|
+
*
|
|
116
|
+
* `services` is what discovery returned. Omit it and every parent is treated
|
|
117
|
+
* as a service, which is what this did before.
|
|
118
|
+
*/
|
|
119
|
+
function mountPath(segments, services = []) {
|
|
120
|
+
const isService = new Set(services.map(s => s.join('/')));
|
|
121
|
+
const parents = segments.slice(0, -1);
|
|
122
|
+
// A parent with no service of its own takes no id. Omitting `services`
|
|
123
|
+
// leaves the set empty, so every parent takes one, as before.
|
|
124
|
+
const takesId = (i) => isService.size === 0 || isService.has(parents.slice(0, i + 1).join('/'));
|
|
125
|
+
// Numbered from the closest outwards, counting only the parents that take
|
|
126
|
+
// one: Express rejects two parameters sharing a name.
|
|
127
|
+
let remaining = parents.filter((_name, i) => takesId(i)).length;
|
|
128
|
+
const prefix = parents.map((name, i) => {
|
|
129
|
+
if (!takesId(i))
|
|
130
|
+
return `/${name}`;
|
|
131
|
+
remaining -= 1;
|
|
132
|
+
return `/${name}/:parentId${remaining === 0 ? '' : remaining}`;
|
|
133
|
+
});
|
|
134
|
+
return `${prefix.join('')}/${segments[segments.length - 1]}`;
|
|
135
|
+
}
|
|
136
|
+
exports.mountPath = mountPath;
|
|
88
137
|
async function registerServices(app, auth, database) {
|
|
89
138
|
var _a, _b, _c;
|
|
90
139
|
if (((_a = require.main) === null || _a === void 0 ? void 0 : _a.filename) == undefined) {
|
|
@@ -92,7 +141,8 @@ async function registerServices(app, auth, database) {
|
|
|
92
141
|
}
|
|
93
142
|
const appDir = path_1.default.dirname((_b = require.main) === null || _b === void 0 ? void 0 : _b.filename);
|
|
94
143
|
const serviceDirPath = appDir + '/services';
|
|
95
|
-
|
|
144
|
+
// Deepest first: `/skills/:id` would otherwise match `/skills/versions`.
|
|
145
|
+
const discovered = (await discoverServices(serviceDirPath)).sort((a, b) => b.length - a.length);
|
|
96
146
|
const methods = [
|
|
97
147
|
{
|
|
98
148
|
method: 'get',
|
|
@@ -126,11 +176,14 @@ async function registerServices(app, auth, database) {
|
|
|
126
176
|
},
|
|
127
177
|
];
|
|
128
178
|
const services = {};
|
|
129
|
-
for (const
|
|
130
|
-
|
|
131
|
-
const
|
|
132
|
-
const
|
|
133
|
-
const
|
|
179
|
+
for (const segments of discovered) {
|
|
180
|
+
// `skills/versions` is keyed `skills.versions` and mounted beneath skills.
|
|
181
|
+
const serviceName = segments.join('.');
|
|
182
|
+
const directory = path_1.default.join(serviceDirPath, ...segments);
|
|
183
|
+
const servicePath = `${directory}/service`;
|
|
184
|
+
const modelPath = `${directory}/model`;
|
|
185
|
+
const validatorPath = `${directory}/validator`;
|
|
186
|
+
const hooksPath = `${directory}/hooks`;
|
|
134
187
|
let Model = null;
|
|
135
188
|
let Validator = null;
|
|
136
189
|
let Hooks = null;
|
|
@@ -285,12 +338,9 @@ async function registerServices(app, auth, database) {
|
|
|
285
338
|
router[method](url, callback);
|
|
286
339
|
}
|
|
287
340
|
app.set('services', services);
|
|
288
|
-
//
|
|
289
|
-
//
|
|
290
|
-
|
|
291
|
-
? `/${service.parent}/:parentId/${serviceName}`
|
|
292
|
-
: `/${serviceName}`;
|
|
293
|
-
app.use(mountPath, router);
|
|
341
|
+
// The folder's position is the mount path, so a nested service is only
|
|
342
|
+
// reachable through its parent without naming that parent in code.
|
|
343
|
+
app.use(mountPath(segments, discovered), router);
|
|
294
344
|
}
|
|
295
345
|
return services;
|
|
296
346
|
}
|
|
@@ -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,58 @@ 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
|
+
/**
|
|
91
|
+
* Where a service is served, from where its folder sits.
|
|
92
|
+
*
|
|
93
|
+
* `['skills','versions']` -> `/skills/:parentId/versions`: a version belongs to
|
|
94
|
+
* one skill, so the path has to say which.
|
|
95
|
+
*
|
|
96
|
+
* A parent that is not itself a service is only grouping, and contributes a
|
|
97
|
+
* plain segment: with `mcp/tools` and `mcp/servers` but no `mcp` service,
|
|
98
|
+
* `/mcp/tools`. There is no MCP record to identify, so a parameter there would
|
|
99
|
+
* ask for an id nobody could supply.
|
|
100
|
+
*
|
|
101
|
+
* `services` is what discovery returned. Omit it and every parent is treated
|
|
102
|
+
* as a service, which is what this did before.
|
|
103
|
+
*/
|
|
104
|
+
export function mountPath(segments: string[], services: string[][] = []) {
|
|
105
|
+
const isService = new Set(services.map(s => s.join('/')));
|
|
106
|
+
const parents = segments.slice(0, -1);
|
|
107
|
+
// A parent with no service of its own takes no id. Omitting `services`
|
|
108
|
+
// leaves the set empty, so every parent takes one, as before.
|
|
109
|
+
const takesId = (i: number) =>
|
|
110
|
+
isService.size === 0 || isService.has(parents.slice(0, i + 1).join('/'));
|
|
111
|
+
|
|
112
|
+
// Numbered from the closest outwards, counting only the parents that take
|
|
113
|
+
// one: Express rejects two parameters sharing a name.
|
|
114
|
+
let remaining = parents.filter((_name, i) => takesId(i)).length;
|
|
115
|
+
|
|
116
|
+
const prefix = parents.map((name, i) => {
|
|
117
|
+
if (!takesId(i)) return `/${name}`;
|
|
118
|
+
remaining -= 1;
|
|
119
|
+
return `/${name}/:parentId${remaining === 0 ? '' : remaining}`;
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
return `${prefix.join('')}/${segments[segments.length - 1]}`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
|
|
74
126
|
export async function registerServices(app: Application, auth?: IAuth, database?: Config['database']) {
|
|
75
127
|
if (require.main?.filename == undefined) {
|
|
76
128
|
throw new Error("Please provide service path")
|
|
@@ -78,8 +130,9 @@ export async function registerServices(app: Application, auth?: IAuth, database?
|
|
|
78
130
|
const appDir = path.dirname(require.main?.filename as string);
|
|
79
131
|
const serviceDirPath = appDir + '/services';
|
|
80
132
|
|
|
81
|
-
|
|
82
|
-
|
|
133
|
+
// Deepest first: `/skills/:id` would otherwise match `/skills/versions`.
|
|
134
|
+
const discovered = (await discoverServices(serviceDirPath)).sort(
|
|
135
|
+
(a, b) => b.length - a.length
|
|
83
136
|
);
|
|
84
137
|
|
|
85
138
|
const methods: Methods[] = [
|
|
@@ -117,11 +170,14 @@ export async function registerServices(app: Application, auth?: IAuth, database?
|
|
|
117
170
|
|
|
118
171
|
const services: {[serviceName: string]: any} = {}
|
|
119
172
|
|
|
120
|
-
for (const
|
|
121
|
-
|
|
122
|
-
const
|
|
123
|
-
const
|
|
124
|
-
const
|
|
173
|
+
for (const segments of discovered) {
|
|
174
|
+
// `skills/versions` is keyed `skills.versions` and mounted beneath skills.
|
|
175
|
+
const serviceName = segments.join('.');
|
|
176
|
+
const directory = path.join(serviceDirPath, ...segments);
|
|
177
|
+
const servicePath = `${directory}/service`;
|
|
178
|
+
const modelPath = `${directory}/model`;
|
|
179
|
+
const validatorPath = `${directory}/validator`;
|
|
180
|
+
const hooksPath = `${directory}/hooks`;
|
|
125
181
|
let Model = null
|
|
126
182
|
let Validator: any = null
|
|
127
183
|
let Hooks: any = null
|
|
@@ -286,13 +342,9 @@ export async function registerServices(app: Application, auth?: IAuth, database?
|
|
|
286
342
|
}
|
|
287
343
|
app.set('services', services)
|
|
288
344
|
|
|
289
|
-
//
|
|
290
|
-
//
|
|
291
|
-
|
|
292
|
-
? `/${service.parent}/:parentId/${serviceName}`
|
|
293
|
-
: `/${serviceName}`;
|
|
294
|
-
|
|
295
|
-
app.use(mountPath, router);
|
|
345
|
+
// The folder's position is the mount path, so a nested service is only
|
|
346
|
+
// reachable through its parent without naming that parent in code.
|
|
347
|
+
app.use(mountPath(segments, discovered), router);
|
|
296
348
|
}
|
|
297
349
|
return services;
|
|
298
350
|
}
|
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) {
|