@modelprofile.com/flexharness 13.0.0 → 13.1.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.
@@ -0,0 +1,68 @@
1
+ import * as plugins from './plugins.js';
2
+ import type { IFlexCatalogType } from '@modelprofile.com/flexharness';
3
+ import type { IFlexTypedRequestMethod, TFlexTypedRequestSchema } from './interfaces.js';
4
+
5
+ export const definitionsPrefix = '#/$defs/';
6
+
7
+ export const isRecord = (value: unknown): value is Record<string, unknown> =>
8
+ typeof value === 'object' && value !== null && !Array.isArray(value);
9
+
10
+ /** The type a `#/$defs/<type>` reference names. */
11
+ export const referencedType = (schema: TFlexTypedRequestSchema): string | undefined =>
12
+ typeof schema.$ref === 'string' && schema.$ref.startsWith(definitionsPrefix)
13
+ ? schema.$ref.slice(definitionsPrefix.length)
14
+ : undefined;
15
+
16
+ /** The named types a schema refers to. */
17
+ export const referencesIn = (value: unknown, found: Set<string>): Set<string> => {
18
+ if (Array.isArray(value)) {
19
+ for (const entry of value) referencesIn(entry, found);
20
+ } else if (isRecord(value)) {
21
+ const target = referencedType(value);
22
+ if (target !== undefined) found.add(target);
23
+ for (const child of Object.values(value)) referencesIn(child, found);
24
+ }
25
+ return found;
26
+ };
27
+
28
+ /**
29
+ * The types of `types` that `methods` reach, in their order: the ones the methods' schemas refer
30
+ * to and their declarations' code names, and the ones those types reach the same way. A type only
31
+ * other methods use is left out. A declaration names a type by the identifier the catalog of all of
32
+ * `types` declares it under (`FlexToolCatalog.identifiersOf()`); as that depends on the other
33
+ * types, a reached type keeps the ones that took the lower identifiers of its name along, so it is
34
+ * declared under the same identifier.
35
+ */
36
+ export const typesReachedBy = (
37
+ methods: readonly IFlexTypedRequestMethod[],
38
+ types: readonly IFlexCatalogType[],
39
+ ): IFlexCatalogType[] => {
40
+ const identifiers = plugins.FlexToolCatalog.identifiersOf(types.map((type) => type.name));
41
+ const byName = new Map(types.map((type) => [type.name, type]));
42
+ const byIdentifier = new Map(types.map((type) => [identifiers.get(type.name)!, type]));
43
+ const pending: IFlexCatalogType[] = [];
44
+ const reach = (schemas: unknown, declaration: string | undefined) => {
45
+ for (const name of referencesIn(schemas, new Set())) {
46
+ const type = byName.get(name);
47
+ if (type !== undefined) pending.push(type);
48
+ }
49
+ for (const identifier of declaration === undefined ? [] : plugins.FlexToolCatalog.identifiersIn(declaration)) {
50
+ const type = byIdentifier.get(identifier);
51
+ if (type !== undefined) pending.push(type);
52
+ }
53
+ };
54
+ for (const method of methods) reach([method.inputSchema, method.outputSchema], method.declaration);
55
+ const reached = new Set<IFlexCatalogType>();
56
+ while (pending.length) {
57
+ const type = pending.pop()!;
58
+ if (reached.has(type)) continue;
59
+ reached.add(type);
60
+ reach(type.schema, type.declaration);
61
+ const identifier = identifiers.get(type.name)!;
62
+ const base = plugins.FlexToolCatalog.identifierOf(type.name);
63
+ for (let suffix = 1, lower = base; lower !== identifier; lower = `${base}_${++suffix}`) {
64
+ pending.push(byIdentifier.get(lower)!);
65
+ }
66
+ }
67
+ return types.filter((type) => reached.has(type));
68
+ };