@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,189 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
const componentsPrefix = '#/components/schemas/';
|
|
3
|
+
const definitionsPrefix = '#/$defs/';
|
|
4
|
+
const maxSummaryCharacters = 120;
|
|
5
|
+
const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
6
|
+
/** A schema whose references name `#/$defs/<type>` instead of the OpenAPI components. */
|
|
7
|
+
const withDefinitionRefs = (value) => {
|
|
8
|
+
if (Array.isArray(value))
|
|
9
|
+
return value.map(withDefinitionRefs);
|
|
10
|
+
if (!isRecord(value))
|
|
11
|
+
return value;
|
|
12
|
+
return Object.fromEntries(Object.entries(value).map(([key, child]) => [
|
|
13
|
+
key,
|
|
14
|
+
key === '$ref' && typeof child === 'string' && child.startsWith(componentsPrefix)
|
|
15
|
+
? `${definitionsPrefix}${child.slice(componentsPrefix.length)}`
|
|
16
|
+
: withDefinitionRefs(child),
|
|
17
|
+
]));
|
|
18
|
+
};
|
|
19
|
+
/** The type a `#/$defs/<type>` reference names. */
|
|
20
|
+
const referencedType = (schema) => typeof schema.$ref === 'string' && schema.$ref.startsWith(definitionsPrefix)
|
|
21
|
+
? schema.$ref.slice(definitionsPrefix.length)
|
|
22
|
+
: undefined;
|
|
23
|
+
/**
|
|
24
|
+
* A request schema without the omitted properties: an object loses them, and a reference to a
|
|
25
|
+
* named type that holds one becomes the type's own schema without them.
|
|
26
|
+
*/
|
|
27
|
+
const withoutOmitted = (schema, omit, types) => {
|
|
28
|
+
const target = referencedType(schema);
|
|
29
|
+
if (target !== undefined) {
|
|
30
|
+
const named = types[target];
|
|
31
|
+
return named !== undefined && holdsOmitted(named, omit, types, new Set([target]))
|
|
32
|
+
? withoutOmitted(named, omit, types)
|
|
33
|
+
: schema;
|
|
34
|
+
}
|
|
35
|
+
const stripped = { ...schema };
|
|
36
|
+
if (isRecord(stripped.properties)) {
|
|
37
|
+
stripped.properties = Object.fromEntries(Object.entries(stripped.properties).filter(([key]) => !omit.has(key)));
|
|
38
|
+
}
|
|
39
|
+
if (Array.isArray(stripped.required)) {
|
|
40
|
+
stripped.required = stripped.required.filter((key) => typeof key !== 'string' || !omit.has(key));
|
|
41
|
+
}
|
|
42
|
+
for (const key of ['allOf', 'anyOf', 'oneOf']) {
|
|
43
|
+
const members = stripped[key];
|
|
44
|
+
if (Array.isArray(members)) {
|
|
45
|
+
stripped[key] = members.map((member) => isRecord(member) ? withoutOmitted(member, omit, types) : member);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return stripped;
|
|
49
|
+
};
|
|
50
|
+
/** Whether a schema has an omitted property, itself or through the schemas it composes or refers to. */
|
|
51
|
+
const holdsOmitted = (schema, omit, types, seen) => {
|
|
52
|
+
if (isRecord(schema.properties) && Object.keys(schema.properties).some((key) => omit.has(key)))
|
|
53
|
+
return true;
|
|
54
|
+
const target = referencedType(schema);
|
|
55
|
+
if (target !== undefined) {
|
|
56
|
+
if (seen.has(target) || types[target] === undefined)
|
|
57
|
+
return false;
|
|
58
|
+
seen.add(target);
|
|
59
|
+
return holdsOmitted(types[target], omit, types, seen);
|
|
60
|
+
}
|
|
61
|
+
return ['allOf', 'anyOf', 'oneOf'].some((key) => Array.isArray(schema[key])
|
|
62
|
+
&& schema[key].some((member) => isRecord(member) && holdsOmitted(member, omit, types, seen)));
|
|
63
|
+
};
|
|
64
|
+
/** Whether a call may leave out every parameter: its schema requires none. */
|
|
65
|
+
const requiresNothing = (schema, types) => {
|
|
66
|
+
const target = referencedType(schema);
|
|
67
|
+
if (target !== undefined)
|
|
68
|
+
return types[target] === undefined || requiresNothing(types[target], types);
|
|
69
|
+
if (Array.isArray(schema.required) && schema.required.length > 0)
|
|
70
|
+
return false;
|
|
71
|
+
return !Array.isArray(schema.allOf)
|
|
72
|
+
|| schema.allOf.every((member) => !isRecord(member) || requiresNothing(member, types));
|
|
73
|
+
};
|
|
74
|
+
/** The named types a schema refers to. */
|
|
75
|
+
const referencesIn = (value, found) => {
|
|
76
|
+
if (Array.isArray(value)) {
|
|
77
|
+
for (const entry of value)
|
|
78
|
+
referencesIn(entry, found);
|
|
79
|
+
}
|
|
80
|
+
else if (isRecord(value)) {
|
|
81
|
+
const target = referencedType(value);
|
|
82
|
+
if (target !== undefined)
|
|
83
|
+
found.add(target);
|
|
84
|
+
for (const child of Object.values(value))
|
|
85
|
+
referencesIn(child, found);
|
|
86
|
+
}
|
|
87
|
+
return found;
|
|
88
|
+
};
|
|
89
|
+
/** The first sentence of a contract's JSDoc, its tags left out, within the summary's length. */
|
|
90
|
+
const summaryOf = (documentation, method) => {
|
|
91
|
+
const prose = (documentation ?? '').split(/\n(?=@)/)[0].replace(/\s+/g, ' ').trim();
|
|
92
|
+
const sentence = prose.split(/(?<=[.!?])\s/)[0] ?? '';
|
|
93
|
+
if (!sentence)
|
|
94
|
+
return method;
|
|
95
|
+
return [...sentence].length <= maxSummaryCharacters
|
|
96
|
+
? sentence
|
|
97
|
+
: `${[...sentence].slice(0, maxSummaryCharacters - 1).join('')}…`;
|
|
98
|
+
};
|
|
99
|
+
/** A JSDoc block of the given lines; `*\/` in them cannot end it early. */
|
|
100
|
+
const docBlock = (lines) => {
|
|
101
|
+
const text = lines.join('\n').replace(/\*\//g, '*\\/');
|
|
102
|
+
return `/**\n${text.split('\n').map((line) => (line ? ` * ${line}` : ' *')).join('\n')}\n */\n`;
|
|
103
|
+
};
|
|
104
|
+
/** The area of a contract when the options name none: its file's base name, an `index` file's folder. */
|
|
105
|
+
const defaultArea = (file) => {
|
|
106
|
+
const parsed = plugins.path.posix.parse(file);
|
|
107
|
+
return parsed.name === 'index' ? plugins.path.posix.basename(parsed.dir) || parsed.name : parsed.name;
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* Reads a typedrequest API's contracts from their TypeScript sources: each typed method's path
|
|
111
|
+
* (`<area>.<method>`), its summary and JSDoc, its request and response as JSON Schema for
|
|
112
|
+
* validation, and its TypeScript declaration as the contract writes it, for the model to read; and
|
|
113
|
+
* the named types they reach, each with its schema, its declaration, or both. Needs the optional
|
|
114
|
+
* peer `@api.global/typedopenapi`. Run it at build time and keep the JSON, or at startup where the
|
|
115
|
+
* sources are at hand.
|
|
116
|
+
*/
|
|
117
|
+
export async function describeTypedRequests(options) {
|
|
118
|
+
let typedopenapi;
|
|
119
|
+
try {
|
|
120
|
+
typedopenapi = await plugins.importTypedOpenApi();
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
throw new plugins.FlexHarnessValidationError('describeTypedRequests needs the optional peer @api.global/typedopenapi (0.8.0 or later).', { cause: error });
|
|
124
|
+
}
|
|
125
|
+
const omit = [...new Set(options.omit ?? [])].sort();
|
|
126
|
+
const source = {
|
|
127
|
+
tsconfig: options.tsconfig,
|
|
128
|
+
contracts: options.contracts,
|
|
129
|
+
...(options.contractNamePattern ? { contractNamePattern: options.contractNamePattern } : {}),
|
|
130
|
+
};
|
|
131
|
+
const schemaSet = typedopenapi.generateContractSchemas(source);
|
|
132
|
+
const declarationSet = typedopenapi.generateContractDeclarations({ ...source, omit });
|
|
133
|
+
const schemas = withDefinitionRefs(schemaSet.schemas);
|
|
134
|
+
const declarations = new Map(declarationSet.contracts.map((contract) => [contract.name, contract]));
|
|
135
|
+
const omitted = new Set(omit);
|
|
136
|
+
const paths = new Set();
|
|
137
|
+
const methods = schemaSet.contracts.map((contract) => {
|
|
138
|
+
const declared = declarations.get(contract.name);
|
|
139
|
+
if (!declared) {
|
|
140
|
+
throw new plugins.FlexHarnessValidationError(`@api.global/typedopenapi declared no contract ${contract.name}, whose schemas it generated.`);
|
|
141
|
+
}
|
|
142
|
+
const area = options.area ? options.area({ name: contract.name, method: contract.method, file: declared.file }) : defaultArea(declared.file);
|
|
143
|
+
const name = plugins.FlexToolCatalog.identifierOf(contract.method);
|
|
144
|
+
const base = `${plugins.FlexToolCatalog.identifierOf(area)}.${name}`;
|
|
145
|
+
let path = base;
|
|
146
|
+
for (let suffix = 2; paths.has(path); suffix++)
|
|
147
|
+
path = `${base}_${suffix}`;
|
|
148
|
+
paths.add(path);
|
|
149
|
+
const inputSchema = withoutOmitted(withDefinitionRefs(contract.request), omitted, schemas);
|
|
150
|
+
const docLines = [
|
|
151
|
+
...(declared.documentation ? [declared.documentation] : []),
|
|
152
|
+
...(declared.request.documentation ? [`@param params ${declared.request.documentation}`] : []),
|
|
153
|
+
...(declared.response.documentation ? [`@returns ${declared.response.documentation}`] : []),
|
|
154
|
+
];
|
|
155
|
+
const functionName = path.slice(path.lastIndexOf('.') + 1);
|
|
156
|
+
const optional = requiresNothing(inputSchema, schemas) ? '?' : '';
|
|
157
|
+
return {
|
|
158
|
+
method: contract.method,
|
|
159
|
+
path,
|
|
160
|
+
summary: summaryOf(declared.documentation, contract.method),
|
|
161
|
+
...(declared.documentation ? { description: declared.documentation } : {}),
|
|
162
|
+
...(contract.deprecated ? { deprecated: true } : {}),
|
|
163
|
+
inputSchema,
|
|
164
|
+
outputSchema: withDefinitionRefs(contract.response),
|
|
165
|
+
declaration: `${docLines.length ? docBlock(docLines) : ''}function ${functionName}(params${optional}: ${declared.request.type}): Promise<${declared.response.type}>;`,
|
|
166
|
+
};
|
|
167
|
+
}).sort((left, right) => (left.path < right.path ? -1 : left.path > right.path ? 1 : 0));
|
|
168
|
+
// Only the schemas the methods still reach: an omitted property's types are gone with it.
|
|
169
|
+
const reached = new Set();
|
|
170
|
+
const pending = [...referencesIn(methods.map((method) => [method.inputSchema, method.outputSchema]), new Set())];
|
|
171
|
+
while (pending.length) {
|
|
172
|
+
const name = pending.pop();
|
|
173
|
+
if (reached.has(name) || schemas[name] === undefined)
|
|
174
|
+
continue;
|
|
175
|
+
reached.add(name);
|
|
176
|
+
pending.push(...referencesIn(schemas[name], new Set()));
|
|
177
|
+
}
|
|
178
|
+
const names = [...new Set([...reached, ...Object.keys(declarationSet.types)])].sort();
|
|
179
|
+
return {
|
|
180
|
+
methods,
|
|
181
|
+
types: names.map((name) => ({
|
|
182
|
+
name,
|
|
183
|
+
...(reached.has(name) ? { schema: schemas[name] } : {}),
|
|
184
|
+
...(declarationSet.types[name] !== undefined ? { declaration: declarationSet.types[name] } : {}),
|
|
185
|
+
})),
|
|
186
|
+
omit,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVzY3JpYmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90c190eXBlZHJlcXVlc3QvZGVzY3JpYmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxjQUFjLENBQUM7QUFReEMsTUFBTSxnQkFBZ0IsR0FBRyx1QkFBdUIsQ0FBQztBQUNqRCxNQUFNLGlCQUFpQixHQUFHLFVBQVUsQ0FBQztBQUNyQyxNQUFNLG9CQUFvQixHQUFHLEdBQUcsQ0FBQztBQUVqQyxNQUFNLFFBQVEsR0FBRyxDQUFDLEtBQWMsRUFBb0MsRUFBRSxDQUNwRSxPQUFPLEtBQUssS0FBSyxRQUFRLElBQUksS0FBSyxLQUFLLElBQUksSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUM7QUFFdkUseUZBQXlGO0FBQ3pGLE1BQU0sa0JBQWtCLEdBQUcsQ0FBQyxLQUFjLEVBQVcsRUFBRTtJQUNyRCxJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDO1FBQUUsT0FBTyxLQUFLLENBQUMsR0FBRyxDQUFDLGtCQUFrQixDQUFDLENBQUM7SUFDL0QsSUFBSSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUM7UUFBRSxPQUFPLEtBQUssQ0FBQztJQUNuQyxPQUFPLE1BQU0sQ0FBQyxXQUFXLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsRUFBRSxFQUFFLENBQUM7UUFDcEUsR0FBRztRQUNILEdBQUcsS0FBSyxNQUFNLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUSxJQUFJLEtBQUssQ0FBQyxVQUFVLENBQUMsZ0JBQWdCLENBQUM7WUFDL0UsQ0FBQyxDQUFDLEdBQUcsaUJBQWlCLEdBQUcsS0FBSyxDQUFDLEtBQUssQ0FBQyxnQkFBZ0IsQ0FBQyxNQUFNLENBQUMsRUFBRTtZQUMvRCxDQUFDLENBQUMsa0JB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
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export * from './interfaces.js';
|
|
2
|
+
export { describeTypedRequests } from './describe.js';
|
|
3
|
+
export { FlexTypedRequestError, catalogFromTypedRequests } from './catalog.js';
|
|
4
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90c190eXBlZHJlcXVlc3QvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsY0FBYyxpQkFBaUIsQ0FBQztBQUNoQyxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDdEQsT0FBTyxFQUFFLHFCQUFxQixFQUFFLHdCQUF3QixFQUFFLE1BQU0sY0FBYyxDQUFDIn0=
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import type { IFlexCatalogRunContext, IFlexCatalogType, IFlexToolCatalog, TFlexCatalogCallDecision } from '@modelprofile.com/flexharness';
|
|
2
|
+
/** A JSON Schema of the surface, plain JSON; its `$ref`s name `#/$defs/<type>` of the surface's types. */
|
|
3
|
+
export type TFlexTypedRequestSchema = Record<string, unknown>;
|
|
4
|
+
/** Where to read a typedrequest API's contracts, and what of them the catalog takes. */
|
|
5
|
+
export interface IFlexTypedRequestSurfaceOptions {
|
|
6
|
+
/** The `tsconfig.json` of the project that holds the contracts. */
|
|
7
|
+
tsconfig: string;
|
|
8
|
+
/** The contract files, or a glob of several: `ts_interfaces/requests/*.ts`. */
|
|
9
|
+
contracts: string;
|
|
10
|
+
/** The names of the exports that are contracts (default `/^IReq_/`). */
|
|
11
|
+
contractNamePattern?: RegExp;
|
|
12
|
+
/**
|
|
13
|
+
* Request properties the binding fills from the run instead of the model, such as the caller's
|
|
14
|
+
* identity or a CSRF token: left out of every request's schema and declaration.
|
|
15
|
+
*/
|
|
16
|
+
omit?: readonly string[];
|
|
17
|
+
/**
|
|
18
|
+
* The namespace of a contract's method. Default: the base name of the file that declares it
|
|
19
|
+
* (`vouchers` for `requests/vouchers.ts`, the folder's name for an `index.ts`).
|
|
20
|
+
*/
|
|
21
|
+
area?(contract: {
|
|
22
|
+
name: string;
|
|
23
|
+
method: string;
|
|
24
|
+
file: string;
|
|
25
|
+
}): string;
|
|
26
|
+
}
|
|
27
|
+
/** One typed method of a surface: how the catalog shows it and validates its calls. */
|
|
28
|
+
export interface IFlexTypedRequestMethod {
|
|
29
|
+
/** The typed method the router answers: `getVoucher`. */
|
|
30
|
+
method: string;
|
|
31
|
+
/** The catalog path the model calls: `vouchers.getVoucher`. */
|
|
32
|
+
path: string;
|
|
33
|
+
/** The first sentence of the contract's JSDoc, or the method. */
|
|
34
|
+
summary: string;
|
|
35
|
+
/** The contract's JSDoc, its tags included. */
|
|
36
|
+
description?: string;
|
|
37
|
+
/** Set by a `@deprecated` tag on the contract. */
|
|
38
|
+
deprecated?: boolean;
|
|
39
|
+
/** The request's schema, without the omitted properties. */
|
|
40
|
+
inputSchema: TFlexTypedRequestSchema;
|
|
41
|
+
/** The response's schema. */
|
|
42
|
+
outputSchema: TFlexTypedRequestSchema;
|
|
43
|
+
/** `function getVoucher(params: …): Promise<…>;` as the contract writes it, after its JSDoc. */
|
|
44
|
+
declaration: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* A typedrequest API as the catalog takes it, read from its TypeScript sources by
|
|
48
|
+
* `describeTypedRequests()`. Plain JSON, so a build step can write it and a server read it.
|
|
49
|
+
*/
|
|
50
|
+
export interface IFlexTypedRequestSurface {
|
|
51
|
+
/** The typed methods, by path. */
|
|
52
|
+
methods: IFlexTypedRequestMethod[];
|
|
53
|
+
/**
|
|
54
|
+
* The named types, by name: each with its JSON Schema where requests and responses refer to it,
|
|
55
|
+
* its TypeScript declaration where declarations name it, or both. A generic is declared once
|
|
56
|
+
* (`IPage`) and its instances have schemas (`IPage_IVoucher`).
|
|
57
|
+
*/
|
|
58
|
+
types: IFlexCatalogType[];
|
|
59
|
+
/** The request properties left out of every request, which the binding fills. */
|
|
60
|
+
omit: string[];
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* A router that answers typed requests in process, such as a `TypedRouter` of
|
|
64
|
+
* `@api.global/typedrequest`.
|
|
65
|
+
*/
|
|
66
|
+
export interface IFlexTypedRequestRouter {
|
|
67
|
+
routeAndAddResponse(request: IFlexTypedRequestEnvelope, options?: {
|
|
68
|
+
trustedLocalData?: Record<string, unknown>;
|
|
69
|
+
trustedAbortSignal?: AbortSignal;
|
|
70
|
+
}): Promise<IFlexTypedRequestEnvelope | null>;
|
|
71
|
+
}
|
|
72
|
+
/** A typed request as the router takes it and answers it. */
|
|
73
|
+
export interface IFlexTypedRequestEnvelope {
|
|
74
|
+
method: string;
|
|
75
|
+
request: object;
|
|
76
|
+
response: object;
|
|
77
|
+
correlation?: {
|
|
78
|
+
id: string;
|
|
79
|
+
phase: 'request' | 'response';
|
|
80
|
+
};
|
|
81
|
+
error?: {
|
|
82
|
+
text: string;
|
|
83
|
+
data: any;
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/** How a catalog answers a surface's methods. */
|
|
87
|
+
export interface IFlexTypedRequestCatalogOptions<TScope = unknown> {
|
|
88
|
+
/** The router that answers the methods, in process. */
|
|
89
|
+
router: IFlexTypedRequestRouter;
|
|
90
|
+
/**
|
|
91
|
+
* The request properties the surface leaves out, from the run: `{ identity }` of the user the
|
|
92
|
+
* session acts for. They are merged over the model's parameters.
|
|
93
|
+
*/
|
|
94
|
+
request?(method: IFlexTypedRequestMethod, context: IFlexCatalogRunContext<TScope>): Record<string, unknown> | PromiseLike<Record<string, unknown>>;
|
|
95
|
+
/** Server-owned data the handlers read as `localData`, as a transport would hand it. */
|
|
96
|
+
localData?(method: IFlexTypedRequestMethod, context: IFlexCatalogRunContext<TScope>): Record<string, unknown> | PromiseLike<Record<string, unknown>>;
|
|
97
|
+
/** The methods the model may use. Default: every method that is not deprecated. */
|
|
98
|
+
include?(method: IFlexTypedRequestMethod): boolean;
|
|
99
|
+
/** Other names of methods, by typed method: `{ getVoucher: ['Beleg abrufen'] }`. */
|
|
100
|
+
aliases?: Readonly<Record<string, readonly string[]>>;
|
|
101
|
+
/** The words the API's users say for its own words; see `IFlexToolCatalog.vocabulary`. */
|
|
102
|
+
vocabulary?: IFlexToolCatalog['vocabulary'];
|
|
103
|
+
/** Decides each validated call before it runs; see `IFlexCatalogMethod.onCall`. */
|
|
104
|
+
onCall?(method: IFlexTypedRequestMethod, params: Record<string, unknown>, context: IFlexCatalogRunContext<TScope>): TFlexCatalogCallDecision | PromiseLike<TFlexCatalogCallDecision>;
|
|
105
|
+
/** Receives the calls `onCall` proposed; see `IFlexToolCatalog.onProposals`. */
|
|
106
|
+
onProposals?: IFlexToolCatalog<TScope>['onProposals'];
|
|
107
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import * as crypto from 'node:crypto';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
export { crypto, path };
|
|
4
|
+
import { FlexHarnessValidationError, FlexToolCatalog } from '@modelprofile.com/flexharness';
|
|
5
|
+
export { FlexHarnessValidationError, FlexToolCatalog };
|
|
6
|
+
/** Loads @api.global/typedopenapi, which reads the contracts from their TypeScript sources. */
|
|
7
|
+
export declare const importTypedOpenApi: () => Promise<typeof import("@api.global/typedopenapi")>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// node native scope
|
|
2
|
+
import * as crypto from 'node:crypto';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
export { crypto, path };
|
|
5
|
+
// @modelprofile.com scope
|
|
6
|
+
import { FlexHarnessValidationError, FlexToolCatalog } from '@modelprofile.com/flexharness';
|
|
7
|
+
export { FlexHarnessValidationError, FlexToolCatalog };
|
|
8
|
+
// @api.global scope: @api.global/typedopenapi is an optional peer, loaded only to describe a surface
|
|
9
|
+
/** Loads @api.global/typedopenapi, which reads the contracts from their TypeScript sources. */
|
|
10
|
+
export const importTypedOpenApi = async () => await import('@api.global/typedopenapi');
|
|
11
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzX3R5cGVkcmVxdWVzdC9wbHVnaW5zLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLG9CQUFvQjtBQUNwQixPQUFPLEtBQUssTUFBTSxNQUFNLGFBQWEsQ0FBQztBQUN0QyxPQUFPLEtBQUssSUFBSSxNQUFNLFdBQVcsQ0FBQztBQUVsQyxPQUFPLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBRSxDQUFDO0FBRXhCLDBCQUEwQjtBQUMxQixPQUFPLEVBQUUsMEJBQTBCLEVBQUUsZUFBZSxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFFNUYsT0FBTyxFQUFFLDBCQUEwQixFQUFFLGVBQWUsRUFBRSxDQUFDO0FBRXZELHFHQUFxRztBQUVyRywrRkFBK0Y7QUFDL0YsTUFBTSxDQUFDLE1BQU0sa0JBQWtCLEdBQUcsS0FBSyxJQUFJLEVBQUUsQ0FBQyxNQUFNLE1BQU0sQ0FBQywwQkFBMEIsQ0FBQyxDQUFDIn0=
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"author": "Task Venture Capital GmbH",
|
|
3
3
|
"license": "MIT",
|
|
4
4
|
"name": "@modelprofile.com/flexharness",
|
|
5
|
-
"version": "10.
|
|
5
|
+
"version": "10.17.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"description": "Managed agent sessions with tools, MCP, chat, OCR, documents and browser control through focused subpath imports.",
|
|
8
8
|
"main": "./dist_ts/index.js",
|
|
@@ -71,12 +71,16 @@
|
|
|
71
71
|
"./stores/nosqldb": {
|
|
72
72
|
"types": "./dist_ts_stores_nosqldb/index.d.ts",
|
|
73
73
|
"import": "./dist_ts_stores_nosqldb/index.js"
|
|
74
|
+
},
|
|
75
|
+
"./typedrequest": {
|
|
76
|
+
"types": "./dist_ts_typedrequest/index.d.ts",
|
|
77
|
+
"import": "./dist_ts_typedrequest/index.js"
|
|
74
78
|
}
|
|
75
79
|
},
|
|
76
80
|
"dependencies": {
|
|
77
81
|
"@cfworker/json-schema": "^4.1.1",
|
|
78
|
-
"@modelprofile.com/flexharness-agent": "10.
|
|
79
|
-
"@modelprofile.com/flexharness-models": "10.
|
|
82
|
+
"@modelprofile.com/flexharness-agent": "10.17.0",
|
|
83
|
+
"@modelprofile.com/flexharness-models": "10.17.0",
|
|
80
84
|
"@push.rocks/smartrequest": "^5.5.1",
|
|
81
85
|
"ai": "^7.0.111",
|
|
82
86
|
"zod": "^4.6.5"
|
|
@@ -96,7 +100,8 @@
|
|
|
96
100
|
"@design.estate/dees-catalog": "^17.0.0 || ^19.5.0 || ^20.0.0 || ^21.0.0",
|
|
97
101
|
"quickjs-emscripten-core": "^0.32.0",
|
|
98
102
|
"@jitl/quickjs-ng-wasmfile-release-sync": "^0.32.0",
|
|
99
|
-
"amaro": "^1.2.1"
|
|
103
|
+
"amaro": "^1.2.1",
|
|
104
|
+
"@api.global/typedopenapi": "^0.8.0"
|
|
100
105
|
},
|
|
101
106
|
"peerDependenciesMeta": {
|
|
102
107
|
"@modelcontextprotocol/sdk": {
|
|
@@ -143,6 +148,9 @@
|
|
|
143
148
|
},
|
|
144
149
|
"amaro": {
|
|
145
150
|
"optional": true
|
|
151
|
+
},
|
|
152
|
+
"@api.global/typedopenapi": {
|
|
153
|
+
"optional": true
|
|
146
154
|
}
|
|
147
155
|
},
|
|
148
156
|
"engines": {
|
|
@@ -184,6 +192,8 @@
|
|
|
184
192
|
"dist_ts_chat_harness",
|
|
185
193
|
"ts_stores_nosqldb",
|
|
186
194
|
"dist_ts_stores_nosqldb",
|
|
195
|
+
"ts_typedrequest",
|
|
196
|
+
"dist_ts_typedrequest",
|
|
187
197
|
"readme.md",
|
|
188
198
|
"license.md"
|
|
189
199
|
]
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -497,6 +497,22 @@ interface IRunReadCall {
|
|
|
497
497
|
|
|
498
498
|
const notShared = Symbol('not shared');
|
|
499
499
|
|
|
500
|
+
/** Runs `operation` after the operations queued before it under `key`, whatever their outcome. */
|
|
501
|
+
function queueOperation<TResult>(
|
|
502
|
+
queues: Map<string, Promise<void>>,
|
|
503
|
+
key: string,
|
|
504
|
+
operation: () => Promise<TResult>,
|
|
505
|
+
): Promise<TResult> {
|
|
506
|
+
const previous = queues.get(key) ?? Promise.resolve();
|
|
507
|
+
const result = previous.then(operation);
|
|
508
|
+
const barrier = result.then(() => undefined, () => undefined);
|
|
509
|
+
queues.set(key, barrier);
|
|
510
|
+
void barrier.then(() => {
|
|
511
|
+
if (queues.get(key) === barrier) queues.delete(key);
|
|
512
|
+
});
|
|
513
|
+
return result;
|
|
514
|
+
}
|
|
515
|
+
|
|
500
516
|
/** The steers of a run: the input its generation takes in, and the prompts it accepted for it. */
|
|
501
517
|
interface IRunSteering {
|
|
502
518
|
readonly input: plugins.AgentPendingInputQueue;
|
|
@@ -928,6 +944,8 @@ export class FlexHarness<TScope = unknown> {
|
|
|
928
944
|
private readonly orphanedTombstoneOwners = new Map<string, IOrphanedTombstoneOwner<TScope>>();
|
|
929
945
|
private readonly pendingPromptAdmissionOwners = new Set<IPendingPromptAdmission>();
|
|
930
946
|
private readonly projectManagementQueues = new Map<string, Promise<void>>();
|
|
947
|
+
/** A session's result writes, one at a time: they share the session's index of results. */
|
|
948
|
+
private readonly resultQueues = new Map<string, Promise<void>>();
|
|
931
949
|
private readonly activeSlashCommandExecutions = new Map<string, IActiveSlashCommandExecution>();
|
|
932
950
|
private readonly activeSlashCommandListings = new Set<IActiveSlashCommandListing>();
|
|
933
951
|
private readonly stagedSessionImports = new Map<string, IStagedSessionImport>();
|
|
@@ -5318,17 +5336,7 @@ export class FlexHarness<TScope = unknown> {
|
|
|
5318
5336
|
sessionId: string,
|
|
5319
5337
|
operation: () => Promise<TResult>,
|
|
5320
5338
|
): Promise<TResult> {
|
|
5321
|
-
|
|
5322
|
-
const previous = this.projectManagementQueues.get(key) ?? Promise.resolve();
|
|
5323
|
-
const result = previous.then(operation);
|
|
5324
|
-
const barrier = result.then(() => undefined, () => undefined);
|
|
5325
|
-
this.projectManagementQueues.set(key, barrier);
|
|
5326
|
-
void barrier.then(() => {
|
|
5327
|
-
if (this.projectManagementQueues.get(key) === barrier) {
|
|
5328
|
-
this.projectManagementQueues.delete(key);
|
|
5329
|
-
}
|
|
5330
|
-
});
|
|
5331
|
-
return result;
|
|
5339
|
+
return queueOperation(this.projectManagementQueues, this.projectManagementKey(storageKey, sessionId), operation);
|
|
5332
5340
|
}
|
|
5333
5341
|
|
|
5334
5342
|
private async awaitProjectManagementOperations(
|
|
@@ -6016,10 +6024,22 @@ export class FlexHarness<TScope = unknown> {
|
|
|
6016
6024
|
};
|
|
6017
6025
|
// A budget too small for even the handle keeps the truncated output.
|
|
6018
6026
|
if (jsonBytes(result) > maxBytes) return normalizeJsonValue(output, this.toolOutputLimits);
|
|
6019
|
-
|
|
6027
|
+
const { storageKey } = run.state;
|
|
6028
|
+
await queueOperation(this.resultQueues, this.resultQueueKey(storageKey, run.sessionId), async () => {
|
|
6029
|
+
await results.save(storageKey, run.sessionId, handle, stored);
|
|
6030
|
+
// The session keeps its latest results; older ones are deleted.
|
|
6031
|
+
const handles = await results.list(storageKey, run.sessionId);
|
|
6032
|
+
for (const old of handles.slice(0, Math.max(0, handles.length - FLEX_RESULT_LIMITS.maxResultsPerSession))) {
|
|
6033
|
+
await results.delete(storageKey, run.sessionId, old);
|
|
6034
|
+
}
|
|
6035
|
+
});
|
|
6020
6036
|
return result as unknown as TJsonValue;
|
|
6021
6037
|
}
|
|
6022
6038
|
|
|
6039
|
+
private resultQueueKey(storageKey: string, sessionId: string): string {
|
|
6040
|
+
return JSON.stringify([storageKey, sessionId]);
|
|
6041
|
+
}
|
|
6042
|
+
|
|
6023
6043
|
/** The JSON bytes of one `results.get` page: the output budget, less room for the page's fields. */
|
|
6024
6044
|
private resultPageBytes(): number {
|
|
6025
6045
|
return Math.max(64, this.toolOutputLimits.maxBytes - 512);
|
|
@@ -6033,7 +6053,7 @@ export class FlexHarness<TScope = unknown> {
|
|
|
6033
6053
|
const method = FLEX_RESULT_METHOD;
|
|
6034
6054
|
const stored = await this.stores.results!.load(run.state.storageKey, run.sessionId, params.handle);
|
|
6035
6055
|
if (stored === undefined) {
|
|
6036
|
-
return { ok: false, error: { error: 'unknown_handle', method, handle: params.handle, hint:
|
|
6056
|
+
return { ok: false, error: { error: 'unknown_handle', method, handle: params.handle, hint: `A handle reads one of the latest ${FLEX_RESULT_LIMITS.maxResultsPerSession} results this session stored.` } };
|
|
6037
6057
|
}
|
|
6038
6058
|
const path = params.path ?? '';
|
|
6039
6059
|
const value = selectPath(stored, path);
|
|
@@ -11243,12 +11263,18 @@ export class FlexHarness<TScope = unknown> {
|
|
|
11243
11263
|
tombstone,
|
|
11244
11264
|
);
|
|
11245
11265
|
};
|
|
11266
|
+
const resultStore = this.stores.results;
|
|
11267
|
+
const resultCleanup = async (): Promise<void> => {
|
|
11268
|
+
// A result being saved for the session is written before its results are deleted.
|
|
11269
|
+
await this.resultQueues.get(this.resultQueueKey(storageKey, sessionId));
|
|
11270
|
+
await resultStore!.deleteSession(storageKey, sessionId);
|
|
11271
|
+
};
|
|
11246
11272
|
const results = await Promise.allSettled([
|
|
11247
11273
|
this.stores.agentEvents.deleteSession(storageKey, sessionId),
|
|
11248
11274
|
this.stores.jobs.deleteSession(storageKey, sessionId),
|
|
11249
11275
|
this.stores.projections.deleteSession(storageKey, sessionId),
|
|
11250
11276
|
this.stores.permissions.deleteSession(storageKey, sessionId),
|
|
11251
|
-
...(
|
|
11277
|
+
...(resultStore ? [resultCleanup()] : []),
|
|
11252
11278
|
// An imported session refuses every project-management write, so it has no state to fence.
|
|
11253
11279
|
...(tombstone.imported ? [] : [projectManagementCleanup()]),
|
|
11254
11280
|
]);
|
package/ts/classes.stores.ts
CHANGED
|
@@ -52,6 +52,7 @@ import {
|
|
|
52
52
|
providerKey,
|
|
53
53
|
validateExpectedRevision,
|
|
54
54
|
validateIdentifier,
|
|
55
|
+
validateResultIndex,
|
|
55
56
|
validateProjectManagementWriteContext,
|
|
56
57
|
validateSnapshotSave,
|
|
57
58
|
} from './utils.storerules.js';
|
|
@@ -355,6 +356,14 @@ class InMemoryResultStore implements IFlexResultStore {
|
|
|
355
356
|
return value === undefined ? undefined : cloneSerializable(value);
|
|
356
357
|
}
|
|
357
358
|
|
|
359
|
+
public async list(storageKey: string, sessionId: string): Promise<string[]> {
|
|
360
|
+
return [...this.sessions.get(providerKey(storageKey, sessionId))?.keys() ?? []];
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
public async delete(storageKey: string, sessionId: string, handle: string): Promise<void> {
|
|
364
|
+
this.sessions.get(providerKey(storageKey, sessionId))?.delete(handle);
|
|
365
|
+
}
|
|
366
|
+
|
|
358
367
|
public async deleteSession(storageKey: string, sessionId: string): Promise<void> {
|
|
359
368
|
this.sessions.delete(providerKey(storageKey, sessionId));
|
|
360
369
|
}
|
|
@@ -464,6 +473,10 @@ class JsonFileRoot {
|
|
|
464
473
|
return plugins.path.join(this.resultSessionPath(storageKey, sessionId), `${this.digest(handle)}.json`);
|
|
465
474
|
}
|
|
466
475
|
|
|
476
|
+
public resultIndexPath(storageKey: string, sessionId: string): string {
|
|
477
|
+
return plugins.path.join(this.resultSessionPath(storageKey, sessionId), 'index.json');
|
|
478
|
+
}
|
|
479
|
+
|
|
467
480
|
public resultSessionPath(storageKey: string, sessionId: string): string {
|
|
468
481
|
validateIdentifier(storageKey, 'storageKey');
|
|
469
482
|
validateIdentifier(sessionId, 'sessionId');
|
|
@@ -888,30 +901,60 @@ class JsonPermissionStore implements IFlexPermissionStore {
|
|
|
888
901
|
}
|
|
889
902
|
}
|
|
890
903
|
|
|
904
|
+
/**
|
|
905
|
+
* One file per result and an index of the session's handles, oldest first. Every operation of a
|
|
906
|
+
* session runs in the session's queue, so the index follows the files.
|
|
907
|
+
*/
|
|
891
908
|
class JsonResultStore implements IFlexResultStore {
|
|
892
909
|
constructor(private readonly root: JsonFileRoot) {}
|
|
893
910
|
|
|
894
|
-
|
|
911
|
+
private inSessionQueue<T>(storageKey: string, sessionId: string, operation: () => Promise<T>): Promise<T> {
|
|
912
|
+
return JsonFileRoot.inQueue(`results:${this.root.resultSessionPath(storageKey, sessionId)}`, operation);
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
private readIndex(storageKey: string, sessionId: string): Promise<string[]> {
|
|
916
|
+
return this.root.read(this.root.resultIndexPath(storageKey, sessionId), validateResultIndex)
|
|
917
|
+
.then((index) => index?.handles ?? []);
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
public save(storageKey: string, sessionId: string, handle: string, value: TJsonValue): Promise<void> {
|
|
895
921
|
const filePath = this.root.resultPath(storageKey, sessionId, handle);
|
|
896
|
-
|
|
922
|
+
return this.inSessionQueue(storageKey, sessionId, async () => {
|
|
897
923
|
if (await this.root.read(filePath, (stored) => stored) !== undefined) {
|
|
898
924
|
throw new FlexHarnessStoreConflictError(`${providerKey(storageKey, sessionId)}:${handle}`, 0, 1);
|
|
899
925
|
}
|
|
900
926
|
await this.root.write(filePath, value);
|
|
927
|
+
const handles = await this.readIndex(storageKey, sessionId);
|
|
928
|
+
await this.root.write(this.root.resultIndexPath(storageKey, sessionId), { handles: [...handles, handle] });
|
|
901
929
|
});
|
|
902
930
|
}
|
|
903
931
|
|
|
904
|
-
public
|
|
932
|
+
public load(storageKey: string, sessionId: string, handle: string): Promise<TJsonValue | undefined> {
|
|
905
933
|
const filePath = this.root.resultPath(storageKey, sessionId, handle);
|
|
906
|
-
return
|
|
934
|
+
return this.inSessionQueue(storageKey, sessionId, () => this.root.read(filePath, (value) => {
|
|
907
935
|
assertJsonSerializable(value);
|
|
908
936
|
return value as TJsonValue;
|
|
909
937
|
}));
|
|
910
938
|
}
|
|
911
939
|
|
|
912
|
-
public
|
|
940
|
+
public list(storageKey: string, sessionId: string): Promise<string[]> {
|
|
941
|
+
return this.inSessionQueue(storageKey, sessionId, () => this.readIndex(storageKey, sessionId));
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
public delete(storageKey: string, sessionId: string, handle: string): Promise<void> {
|
|
945
|
+
return this.inSessionQueue(storageKey, sessionId, async () => {
|
|
946
|
+
const handles = await this.readIndex(storageKey, sessionId);
|
|
947
|
+
await this.root.write(
|
|
948
|
+
this.root.resultIndexPath(storageKey, sessionId),
|
|
949
|
+
{ handles: handles.filter((entry) => entry !== handle) },
|
|
950
|
+
);
|
|
951
|
+
await this.root.deleteFile(this.root.resultPath(storageKey, sessionId, handle));
|
|
952
|
+
});
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
public deleteSession(storageKey: string, sessionId: string): Promise<void> {
|
|
913
956
|
const directory = this.root.resultSessionPath(storageKey, sessionId);
|
|
914
|
-
|
|
957
|
+
return this.inSessionQueue(storageKey, sessionId, () => this.root.deleteDirectory(directory));
|
|
915
958
|
}
|
|
916
959
|
}
|
|
917
960
|
|