-
-
Notifications
You must be signed in to change notification settings - Fork 98
/
resolver.ts
86 lines (77 loc) · 2.92 KB
/
resolver.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import { Resolver as SpectralResolver } from '@stoplight/spectral-ref-resolver';
import { Cache } from '@stoplight/json-ref-resolver/cache';
import { resolveFile, resolveHttp } from '@stoplight/json-ref-readers';
import type Uri from 'urijs';
export interface Resolver {
schema: 'file' | 'http' | 'https' | string;
order?: number;
canRead?: boolean | ((uri: Uri, ctx?: any) => boolean);
read: (uri: Uri, ctx?: any) => string | undefined | Promise<string | undefined>;
}
export interface ResolverOptions {
cache?: boolean;
resolvers?: Array<Resolver>;
}
export function createResolver(options: ResolverOptions = {}): SpectralResolver {
const availableResolvers: Array<Resolver> = [
...createDefaultResolvers(),
...(options.resolvers || [])
].map(r => ({
...r,
order: r.order || Number.MAX_SAFE_INTEGER,
canRead: typeof r.canRead === 'undefined' ? true: r.canRead,
}));
const availableSchemas = [...new Set(availableResolvers.map(r => r.schema))];
const resolvers = availableSchemas.reduce((acc, schema) => {
acc[schema] = { resolve: createSchemaResolver(schema, availableResolvers) };
return acc;
}, {} as Record<string, { resolve: (uri: Uri, ctx?: any) => string | Promise<string> }>);
// if cache is enabled, use default Cache instance in SpectralResolver, otherwise use custom one with ttl set to 1ms
const cache = options.cache !== false;
return new SpectralResolver({
uriCache: cache ? undefined : new Cache({ stdTTL: 1 }),
resolvers: resolvers as any,
});
}
function createDefaultResolvers(): Array<Resolver> {
return [
{
schema: 'file',
read: resolveFile as (input: Uri, ctx?: any) => string | Promise<string>,
},
{
schema: 'https',
read: resolveHttp as (input: Uri, ctx?: any) => string | Promise<string>,
},
{
schema: 'http',
read: resolveHttp as (input: Uri, ctx?: any) => string | Promise<string>,
},
];
}
function createSchemaResolver(schema: string, allResolvers: Array<Resolver>): (uri: Uri, ctx?: any) => string | Promise<string> {
const resolvers = allResolvers.filter(r => r.schema === schema).sort((a, b) => { return (a.order as number) - (b.order as number); });
return async (uri, ctx) => {
let result: string | undefined = undefined;
let lastError: Error | undefined;
for (const resolver of resolvers) {
try {
if (!canRead(resolver, uri, ctx)) continue;
result = await resolver.read(uri, ctx);
if (typeof result === 'string') {
break;
}
} catch (e: any) {
lastError = e;
continue;
}
}
if (typeof result !== 'string') {
throw lastError || new Error(`None of the available resolvers for "${schema}" can resolve the given reference.`);
}
return result;
};
}
function canRead(resolver: Resolver, uri: Uri, ctx?: any) {
return typeof resolver.canRead === 'function' ? resolver.canRead(uri, ctx) : resolver.canRead;
}