@modelprofile.com/flexharness 10.16.0 → 10.17.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_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.flexharness.d.ts +3 -0
- package/dist_ts/classes.flexharness.js +36 -15
- package/dist_ts/classes.stores.js +40 -8
- package/dist_ts/classes.toolcatalog.d.ts +9 -0
- package/dist_ts/classes.toolcatalog.js +98 -8
- package/dist_ts/interfaces.d.ts +9 -1
- package/dist_ts/interfaces.js +4 -2
- package/dist_ts/utils.storerules.d.ts +5 -0
- package/dist_ts/utils.storerules.js +10 -1
- package/dist_ts_stores_nosqldb/classes.nosqldbstores.js +27 -2
- package/dist_ts_stores_nosqldb/classes.recordstore.d.ts +2 -0
- package/dist_ts_stores_nosqldb/classes.recordstore.js +7 -1
- package/dist_ts_typedrequest/catalog.d.ts +28 -0
- package/dist_ts_typedrequest/catalog.js +79 -0
- package/dist_ts_typedrequest/describe.d.ts +10 -0
- package/dist_ts_typedrequest/describe.js +189 -0
- package/dist_ts_typedrequest/index.d.ts +3 -0
- package/dist_ts_typedrequest/index.js +4 -0
- package/dist_ts_typedrequest/interfaces.d.ts +107 -0
- package/dist_ts_typedrequest/interfaces.js +2 -0
- package/dist_ts_typedrequest/plugins.d.ts +7 -0
- package/dist_ts_typedrequest/plugins.js +11 -0
- package/package.json +14 -4
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.flexharness.ts +40 -14
- package/ts/classes.stores.ts +49 -6
- package/ts/classes.toolcatalog.ts +90 -7
- package/ts/interfaces.ts +10 -2
- package/ts/utils.storerules.ts +15 -0
- package/ts_stores_nosqldb/classes.nosqldbstores.ts +29 -0
- package/ts_stores_nosqldb/classes.recordstore.ts +8 -0
- package/ts_typedrequest/catalog.ts +91 -0
- package/ts_typedrequest/describe.ts +211 -0
- package/ts_typedrequest/index.ts +3 -0
- package/ts_typedrequest/interfaces.ts +124 -0
- package/ts_typedrequest/plugins.ts +15 -0
- package/ts_typedrequest/readme.md +66 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// The public types come from the package itself, not from plugins.ts, so the declarations of this
|
|
2
|
+
// subpath never reach the optional peer @api.global/typedopenapi.
|
|
3
|
+
import type {
|
|
4
|
+
IFlexCatalogRunContext,
|
|
5
|
+
IFlexCatalogType,
|
|
6
|
+
IFlexToolCatalog,
|
|
7
|
+
TFlexCatalogCallDecision,
|
|
8
|
+
} from '@modelprofile.com/flexharness';
|
|
9
|
+
|
|
10
|
+
/** A JSON Schema of the surface, plain JSON; its `$ref`s name `#/$defs/<type>` of the surface's types. */
|
|
11
|
+
export type TFlexTypedRequestSchema = Record<string, unknown>;
|
|
12
|
+
|
|
13
|
+
/** Where to read a typedrequest API's contracts, and what of them the catalog takes. */
|
|
14
|
+
export interface IFlexTypedRequestSurfaceOptions {
|
|
15
|
+
/** The `tsconfig.json` of the project that holds the contracts. */
|
|
16
|
+
tsconfig: string;
|
|
17
|
+
/** The contract files, or a glob of several: `ts_interfaces/requests/*.ts`. */
|
|
18
|
+
contracts: string;
|
|
19
|
+
/** The names of the exports that are contracts (default `/^IReq_/`). */
|
|
20
|
+
contractNamePattern?: RegExp;
|
|
21
|
+
/**
|
|
22
|
+
* Request properties the binding fills from the run instead of the model, such as the caller's
|
|
23
|
+
* identity or a CSRF token: left out of every request's schema and declaration.
|
|
24
|
+
*/
|
|
25
|
+
omit?: readonly string[];
|
|
26
|
+
/**
|
|
27
|
+
* The namespace of a contract's method. Default: the base name of the file that declares it
|
|
28
|
+
* (`vouchers` for `requests/vouchers.ts`, the folder's name for an `index.ts`).
|
|
29
|
+
*/
|
|
30
|
+
area?(contract: { name: string; method: string; file: string }): string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** One typed method of a surface: how the catalog shows it and validates its calls. */
|
|
34
|
+
export interface IFlexTypedRequestMethod {
|
|
35
|
+
/** The typed method the router answers: `getVoucher`. */
|
|
36
|
+
method: string;
|
|
37
|
+
/** The catalog path the model calls: `vouchers.getVoucher`. */
|
|
38
|
+
path: string;
|
|
39
|
+
/** The first sentence of the contract's JSDoc, or the method. */
|
|
40
|
+
summary: string;
|
|
41
|
+
/** The contract's JSDoc, its tags included. */
|
|
42
|
+
description?: string;
|
|
43
|
+
/** Set by a `@deprecated` tag on the contract. */
|
|
44
|
+
deprecated?: boolean;
|
|
45
|
+
/** The request's schema, without the omitted properties. */
|
|
46
|
+
inputSchema: TFlexTypedRequestSchema;
|
|
47
|
+
/** The response's schema. */
|
|
48
|
+
outputSchema: TFlexTypedRequestSchema;
|
|
49
|
+
/** `function getVoucher(params: …): Promise<…>;` as the contract writes it, after its JSDoc. */
|
|
50
|
+
declaration: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* A typedrequest API as the catalog takes it, read from its TypeScript sources by
|
|
55
|
+
* `describeTypedRequests()`. Plain JSON, so a build step can write it and a server read it.
|
|
56
|
+
*/
|
|
57
|
+
export interface IFlexTypedRequestSurface {
|
|
58
|
+
/** The typed methods, by path. */
|
|
59
|
+
methods: IFlexTypedRequestMethod[];
|
|
60
|
+
/**
|
|
61
|
+
* The named types, by name: each with its JSON Schema where requests and responses refer to it,
|
|
62
|
+
* its TypeScript declaration where declarations name it, or both. A generic is declared once
|
|
63
|
+
* (`IPage`) and its instances have schemas (`IPage_IVoucher`).
|
|
64
|
+
*/
|
|
65
|
+
types: IFlexCatalogType[];
|
|
66
|
+
/** The request properties left out of every request, which the binding fills. */
|
|
67
|
+
omit: string[];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* A router that answers typed requests in process, such as a `TypedRouter` of
|
|
72
|
+
* `@api.global/typedrequest`.
|
|
73
|
+
*/
|
|
74
|
+
export interface IFlexTypedRequestRouter {
|
|
75
|
+
routeAndAddResponse(
|
|
76
|
+
request: IFlexTypedRequestEnvelope,
|
|
77
|
+
options?: {
|
|
78
|
+
trustedLocalData?: Record<string, unknown>;
|
|
79
|
+
trustedAbortSignal?: AbortSignal;
|
|
80
|
+
},
|
|
81
|
+
): Promise<IFlexTypedRequestEnvelope | null>;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** A typed request as the router takes it and answers it. */
|
|
85
|
+
export interface IFlexTypedRequestEnvelope {
|
|
86
|
+
method: string;
|
|
87
|
+
request: object;
|
|
88
|
+
response: object;
|
|
89
|
+
correlation?: { id: string; phase: 'request' | 'response' };
|
|
90
|
+
error?: { text: string; data: any };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** How a catalog answers a surface's methods. */
|
|
94
|
+
export interface IFlexTypedRequestCatalogOptions<TScope = unknown> {
|
|
95
|
+
/** The router that answers the methods, in process. */
|
|
96
|
+
router: IFlexTypedRequestRouter;
|
|
97
|
+
/**
|
|
98
|
+
* The request properties the surface leaves out, from the run: `{ identity }` of the user the
|
|
99
|
+
* session acts for. They are merged over the model's parameters.
|
|
100
|
+
*/
|
|
101
|
+
request?(
|
|
102
|
+
method: IFlexTypedRequestMethod,
|
|
103
|
+
context: IFlexCatalogRunContext<TScope>,
|
|
104
|
+
): Record<string, unknown> | PromiseLike<Record<string, unknown>>;
|
|
105
|
+
/** Server-owned data the handlers read as `localData`, as a transport would hand it. */
|
|
106
|
+
localData?(
|
|
107
|
+
method: IFlexTypedRequestMethod,
|
|
108
|
+
context: IFlexCatalogRunContext<TScope>,
|
|
109
|
+
): Record<string, unknown> | PromiseLike<Record<string, unknown>>;
|
|
110
|
+
/** The methods the model may use. Default: every method that is not deprecated. */
|
|
111
|
+
include?(method: IFlexTypedRequestMethod): boolean;
|
|
112
|
+
/** Other names of methods, by typed method: `{ getVoucher: ['Beleg abrufen'] }`. */
|
|
113
|
+
aliases?: Readonly<Record<string, readonly string[]>>;
|
|
114
|
+
/** The words the API's users say for its own words; see `IFlexToolCatalog.vocabulary`. */
|
|
115
|
+
vocabulary?: IFlexToolCatalog['vocabulary'];
|
|
116
|
+
/** Decides each validated call before it runs; see `IFlexCatalogMethod.onCall`. */
|
|
117
|
+
onCall?(
|
|
118
|
+
method: IFlexTypedRequestMethod,
|
|
119
|
+
params: Record<string, unknown>,
|
|
120
|
+
context: IFlexCatalogRunContext<TScope>,
|
|
121
|
+
): TFlexCatalogCallDecision | PromiseLike<TFlexCatalogCallDecision>;
|
|
122
|
+
/** Receives the calls `onCall` proposed; see `IFlexToolCatalog.onProposals`. */
|
|
123
|
+
onProposals?: IFlexToolCatalog<TScope>['onProposals'];
|
|
124
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// node native scope
|
|
2
|
+
import * as crypto from 'node:crypto';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
|
|
5
|
+
export { crypto, path };
|
|
6
|
+
|
|
7
|
+
// @modelprofile.com scope
|
|
8
|
+
import { FlexHarnessValidationError, FlexToolCatalog } from '@modelprofile.com/flexharness';
|
|
9
|
+
|
|
10
|
+
export { FlexHarnessValidationError, FlexToolCatalog };
|
|
11
|
+
|
|
12
|
+
// @api.global scope: @api.global/typedopenapi is an optional peer, loaded only to describe a surface
|
|
13
|
+
|
|
14
|
+
/** Loads @api.global/typedopenapi, which reads the contracts from their TypeScript sources. */
|
|
15
|
+
export const importTypedOpenApi = async () => await import('@api.global/typedopenapi');
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
A FlexHarness tool catalog from a typedrequest API: the model reads the API's contracts as their authors wrote them, and its calls run through the API's own router in process.
|
|
2
|
+
|
|
3
|
+
```sh
|
|
4
|
+
pnpm add @modelprofile.com/flexharness @api.global/typedrequest
|
|
5
|
+
pnpm add -D @api.global/typedopenapi # where the surface is described
|
|
6
|
+
```
|
|
7
|
+
|
|
8
|
+
```typescript
|
|
9
|
+
import { catalogFromTypedRequests, describeTypedRequests } from '@modelprofile.com/flexharness/typedrequest';
|
|
10
|
+
|
|
11
|
+
// At build time, or at startup where the sources are at hand: plain JSON.
|
|
12
|
+
const surface = await describeTypedRequests({
|
|
13
|
+
tsconfig: './tsconfig.json',
|
|
14
|
+
contracts: './ts_interfaces/requests/*.ts',
|
|
15
|
+
omit: ['identity'],
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// At run time: the catalog of a tool provider.
|
|
19
|
+
const catalog = catalogFromTypedRequests<IAppScope>(surface, {
|
|
20
|
+
router: server.typedrouter,
|
|
21
|
+
request: (method, context) => ({ identity: identityOf(context.provider.scope) }),
|
|
22
|
+
onCall: (method) => (method.method.startsWith('get') || method.method.startsWith('list') ? 'run' : 'propose'),
|
|
23
|
+
});
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`describeTypedRequests()` reads the contracts with `@api.global/typedopenapi`, an optional peer it loads only when it runs:
|
|
27
|
+
|
|
28
|
+
- Each typed method becomes a method of the path `<area>.<method>`; the area is the base name of the file that declares the contract (`vouchers` for `requests/vouchers.ts`, the folder's name for an `index.ts`), or what `area` returns. A name that is no identifier is mapped as `FlexToolCatalog.identifierOf()` maps it (`export` becomes `export_`).
|
|
29
|
+
- Its declaration is the contract as written: `function getVoucher(params: { voucherId: string }): Promise<{ voucher: IVoucher }>;` after the contract's JSDoc with its tags, the request property's JSDoc as `@param params`, the response property's as `@returns`. `params` is optional where the request requires nothing.
|
|
30
|
+
- Its `inputSchema` and `outputSchema` are the request's and response's JSON Schema; `execute` validates every call against the request's.
|
|
31
|
+
- `omit` names request properties the binding fills from the run, such as the caller's identity or a CSRF token: they leave every request's schema and declaration, so the model can neither see nor send them.
|
|
32
|
+
- The named types the requests and responses reach become the catalog's types, each with its JSON Schema, its declaration from the sources, or both: a generic is declared once (`IPage<T>`) and its instances have schemas (`IPage_IVoucher`). A method's result types are declared only as deep as `toolCatalog.outputTypeDepth` asks, as for any declaration.
|
|
33
|
+
|
|
34
|
+
The surface is plain JSON, so a build step can write it next to the server and the server read it at startup without TypeScript or `@api.global/typedopenapi`. A 330-method API is described in about 8 seconds.
|
|
35
|
+
|
|
36
|
+
`catalogFromTypedRequests(surface, options)` returns an `IFlexToolCatalog`:
|
|
37
|
+
|
|
38
|
+
- `router` answers the calls: anything with `routeAndAddResponse()`, such as a `TypedRouter` of `@api.global/typedrequest`. A call is routed as a typed request with the model's parameters and what `request` returns for the omitted properties, `localData` as the trusted route data a transport would hand the handlers, and the run's abort signal.
|
|
39
|
+
- A refusal rejects with `FlexTypedRequestError`: the typed method, the error's `text` and `data`, as a client of the API would receive them. A handler's own failure arrives as the router's internal error. A host's `externalErrorProjector` decides what of it the model sees.
|
|
40
|
+
- `include` picks the methods the model may use, by default every method that is not deprecated; `aliases` gives methods other names by typed method; `vocabulary`, `onCall` and `onProposals` are the catalog's own (`onCall` receives the typed method first).
|
|
41
|
+
- A surface that omits properties needs `request`; an alias for a method the surface lacks is refused.
|
|
42
|
+
|
|
43
|
+
## Issue Reporting and Security
|
|
44
|
+
|
|
45
|
+
For reporting bugs, issues, or security vulnerabilities, please visit [community.foss.global/](https://community.foss.global/). This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a [code.foss.global/](https://code.foss.global/) account to submit Pull Requests directly.
|
|
46
|
+
|
|
47
|
+
## License and Legal Information
|
|
48
|
+
|
|
49
|
+
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the repository license file.
|
|
50
|
+
|
|
51
|
+
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
|
|
52
|
+
|
|
53
|
+
### Trademarks
|
|
54
|
+
|
|
55
|
+
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.
|
|
56
|
+
|
|
57
|
+
Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.
|
|
58
|
+
|
|
59
|
+
### Company Information
|
|
60
|
+
|
|
61
|
+
Task Venture Capital GmbH<br>
|
|
62
|
+
Registered at District Court Bremen HRB 35230 HB, Germany
|
|
63
|
+
|
|
64
|
+
For any legal inquiries or further information, please contact us via email at hello@task.vc.
|
|
65
|
+
|
|
66
|
+
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
|