@theia/ovsx-client 1.47.1 → 1.48.1
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 +61 -61
- package/lib/index.d.ts +6 -6
- package/lib/index.js +30 -30
- package/lib/ovsx-api-filter.d.ts +30 -30
- package/lib/ovsx-api-filter.js +73 -73
- package/lib/ovsx-http-client.d.ts +17 -17
- package/lib/ovsx-http-client.js +82 -82
- package/lib/ovsx-mock-client.d.ts +43 -43
- package/lib/ovsx-mock-client.js +168 -168
- package/lib/ovsx-router-client.d.ts +68 -68
- package/lib/ovsx-router-client.js +169 -169
- package/lib/ovsx-router-client.spec-data.d.ts +10 -10
- package/lib/ovsx-router-client.spec-data.js +66 -66
- package/lib/ovsx-router-client.spec.d.ts +1 -1
- package/lib/ovsx-router-client.spec.js +107 -107
- package/lib/ovsx-router-filters/abstract-reg-exp-filter.d.ts +5 -5
- package/lib/ovsx-router-filters/abstract-reg-exp-filter.js +27 -27
- package/lib/ovsx-router-filters/extension-id-matches-filter.d.ts +7 -7
- package/lib/ovsx-router-filters/extension-id-matches-filter.js +33 -33
- package/lib/ovsx-router-filters/index.d.ts +2 -2
- package/lib/ovsx-router-filters/index.js +22 -22
- package/lib/ovsx-router-filters/request-contains-filter.d.ts +8 -8
- package/lib/ovsx-router-filters/request-contains-filter.js +35 -35
- package/lib/ovsx-types.d.ts +212 -212
- package/lib/ovsx-types.js +74 -74
- package/lib/types.d.ts +1 -1
- package/lib/types.js +17 -17
- package/package.json +3 -3
- package/src/index.ts +22 -22
- package/src/ovsx-api-filter.ts +91 -91
- package/src/ovsx-http-client.ts +85 -85
- package/src/ovsx-mock-client.ts +182 -182
- package/src/ovsx-router-client.spec-data.ts +68 -68
- package/src/ovsx-router-client.spec.ts +126 -126
- package/src/ovsx-router-client.ts +248 -248
- package/src/ovsx-router-filters/abstract-reg-exp-filter.ts +26 -26
- package/src/ovsx-router-filters/extension-id-matches-filter.ts +32 -32
- package/src/ovsx-router-filters/index.ts +18 -18
- package/src/ovsx-router-filters/request-contains-filter.ts +35 -35
- package/src/ovsx-types.ts +268 -268
- package/src/types.ts +17 -17
|
@@ -1,248 +1,248 @@
|
|
|
1
|
-
// *****************************************************************************
|
|
2
|
-
// Copyright (C) 2023 Ericsson and others.
|
|
3
|
-
//
|
|
4
|
-
// This program and the accompanying materials are made available under the
|
|
5
|
-
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
-
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
-
//
|
|
8
|
-
// This Source Code may also be made available under the following Secondary
|
|
9
|
-
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
-
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
-
// with the GNU Classpath Exception which is available at
|
|
12
|
-
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
-
//
|
|
14
|
-
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
-
// *****************************************************************************
|
|
16
|
-
|
|
17
|
-
import { ExtensionLike, OVSXClient, OVSXClientProvider, VSXExtensionRaw, VSXQueryOptions, VSXQueryResult, VSXSearchEntry, VSXSearchOptions, VSXSearchResult } from './ovsx-types';
|
|
18
|
-
import type { MaybePromise } from './types';
|
|
19
|
-
|
|
20
|
-
export interface OVSXRouterFilter {
|
|
21
|
-
filterSearchOptions?(searchOptions?: VSXSearchOptions): MaybePromise<unknown>;
|
|
22
|
-
filterQueryOptions?(queryOptions?: VSXQueryOptions): MaybePromise<unknown>;
|
|
23
|
-
filterExtension?(extension: ExtensionLike): MaybePromise<unknown>;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* @param conditions key/value mapping of condition statements that rules may process
|
|
28
|
-
* @param remainingKeys keys left to be processed, remove items from it when you handled them
|
|
29
|
-
*/
|
|
30
|
-
export type OVSXRouterFilterFactory = (conditions: Readonly<Record<string, unknown>>, remainingKeys: Set<string>) => MaybePromise<OVSXRouterFilter | undefined>;
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Helper function to create factories that handle a single condition key.
|
|
34
|
-
*/
|
|
35
|
-
export function createFilterFactory(conditionKey: string, factory: (conditionValue: unknown) => OVSXRouterFilter | undefined): OVSXRouterFilterFactory {
|
|
36
|
-
return (conditions, remainingKeys) => {
|
|
37
|
-
if (conditionKey in conditions) {
|
|
38
|
-
const filter = factory(conditions[conditionKey]);
|
|
39
|
-
if (filter) {
|
|
40
|
-
remainingKeys.delete(conditionKey);
|
|
41
|
-
return filter;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export interface OVSXRouterConfig {
|
|
48
|
-
/**
|
|
49
|
-
* Registry aliases that will be used for routing.
|
|
50
|
-
*/
|
|
51
|
-
registries?: {
|
|
52
|
-
[alias: string]: string
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* The registry/ies to use by default.
|
|
56
|
-
*/
|
|
57
|
-
use: string | string[]
|
|
58
|
-
/**
|
|
59
|
-
* Filters for the different phases of interfacing with a registry.
|
|
60
|
-
*/
|
|
61
|
-
rules?: OVSXRouterRule[]
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export interface OVSXRouterRule {
|
|
65
|
-
[condition: string]: unknown
|
|
66
|
-
use?: string | string[] | null
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* @internal
|
|
71
|
-
*/
|
|
72
|
-
export interface OVSXRouterParsedRule {
|
|
73
|
-
filters: OVSXRouterFilter[]
|
|
74
|
-
use: string[]
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Route and agglomerate queries according to {@link routerConfig}.
|
|
79
|
-
* {@link ruleFactories} is the actual logic used to evaluate the config.
|
|
80
|
-
* Each rule implementation will be ran sequentially over each configured rule.
|
|
81
|
-
*/
|
|
82
|
-
export class OVSXRouterClient implements OVSXClient {
|
|
83
|
-
|
|
84
|
-
static async FromConfig(routerConfig: OVSXRouterConfig, clientProvider: OVSXClientProvider, filterFactories: OVSXRouterFilterFactory[]): Promise<OVSXRouterClient> {
|
|
85
|
-
const rules = routerConfig.rules ? await this.ParseRules(routerConfig.rules, filterFactories, routerConfig.registries) : [];
|
|
86
|
-
return new this(
|
|
87
|
-
this.ParseUse(routerConfig.use, routerConfig.registries),
|
|
88
|
-
clientProvider,
|
|
89
|
-
rules
|
|
90
|
-
);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
protected static async ParseRules(rules: OVSXRouterRule[], filterFactories: OVSXRouterFilterFactory[], aliases?: Record<string, string>): Promise<OVSXRouterParsedRule[]> {
|
|
94
|
-
return Promise.all(rules.map(async ({ use, ...conditions }) => {
|
|
95
|
-
const remainingKeys = new Set(Object.keys(conditions));
|
|
96
|
-
const filters = removeNullValues(await Promise.all(filterFactories.map(filterFactory => filterFactory(conditions, remainingKeys))));
|
|
97
|
-
if (remainingKeys.size > 0) {
|
|
98
|
-
throw new Error(`unknown conditions: ${Array.from(remainingKeys).join(', ')}`);
|
|
99
|
-
}
|
|
100
|
-
return {
|
|
101
|
-
filters,
|
|
102
|
-
use: this.ParseUse(use, aliases)
|
|
103
|
-
};
|
|
104
|
-
}));
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
protected static ParseUse(use: string | string[] | null | undefined, aliases?: Record<string, string>): string[] {
|
|
108
|
-
if (typeof use === 'string') {
|
|
109
|
-
return [alias(use)];
|
|
110
|
-
} else if (Array.isArray(use)) {
|
|
111
|
-
return use.map(alias);
|
|
112
|
-
} else {
|
|
113
|
-
return [];
|
|
114
|
-
}
|
|
115
|
-
function alias(aliasOrUri: string): string {
|
|
116
|
-
return aliases?.[aliasOrUri] ?? aliasOrUri;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
constructor(
|
|
121
|
-
protected readonly useDefault: string[],
|
|
122
|
-
protected readonly clientProvider: OVSXClientProvider,
|
|
123
|
-
protected readonly rules: OVSXRouterParsedRule[],
|
|
124
|
-
) { }
|
|
125
|
-
|
|
126
|
-
async search(searchOptions?: VSXSearchOptions): Promise<VSXSearchResult> {
|
|
127
|
-
return this.runRules(
|
|
128
|
-
filter => filter.filterSearchOptions?.(searchOptions),
|
|
129
|
-
rule => rule.use.length > 0
|
|
130
|
-
? this.mergedSearch(rule.use, searchOptions)
|
|
131
|
-
: this.emptySearchResult(searchOptions),
|
|
132
|
-
() => this.mergedSearch(this.useDefault, searchOptions)
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
async query(queryOptions: VSXQueryOptions = {}): Promise<VSXQueryResult> {
|
|
137
|
-
return this.runRules(
|
|
138
|
-
filter => filter.filterQueryOptions?.(queryOptions),
|
|
139
|
-
rule => rule.use.length > 0
|
|
140
|
-
? this.mergedQuery(rule.use, queryOptions)
|
|
141
|
-
: this.emptyQueryResult(queryOptions),
|
|
142
|
-
() => this.mergedQuery(this.useDefault, queryOptions)
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
protected emptySearchResult(searchOptions?: VSXSearchOptions): VSXSearchResult {
|
|
147
|
-
return {
|
|
148
|
-
extensions: [],
|
|
149
|
-
offset: searchOptions?.offset ?? 0
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
protected emptyQueryResult(queryOptions?: VSXQueryOptions): VSXQueryResult {
|
|
154
|
-
return {
|
|
155
|
-
extensions: []
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
protected async mergedQuery(registries: string[], queryOptions?: VSXQueryOptions): Promise<VSXQueryResult> {
|
|
160
|
-
return this.mergeQueryResults(await createMapping(registries, async registry => (await this.clientProvider(registry)).query(queryOptions)));
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
protected async mergedSearch(registries: string[], searchOptions?: VSXSearchOptions): Promise<VSXSearchResult> {
|
|
164
|
-
return this.mergeSearchResults(await createMapping(registries, async registry => (await this.clientProvider(registry)).search(searchOptions)));
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
protected async mergeSearchResults(results: Map<string, VSXSearchResult>): Promise<VSXSearchResult> {
|
|
168
|
-
const filtering = [] as Promise<VSXSearchEntry[]>[];
|
|
169
|
-
results.forEach((result, sourceUri) => {
|
|
170
|
-
filtering.push(Promise
|
|
171
|
-
.all(result.extensions.map(extension => this.filterExtension(sourceUri, extension)))
|
|
172
|
-
.then(removeNullValues)
|
|
173
|
-
);
|
|
174
|
-
});
|
|
175
|
-
return {
|
|
176
|
-
extensions: interleave(await Promise.all(filtering)),
|
|
177
|
-
offset: Math.min(...Array.from(results.values(), result => result.offset))
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
protected async mergeQueryResults(results: Map<string, VSXQueryResult>): Promise<VSXQueryResult> {
|
|
182
|
-
const filtering = [] as Promise<VSXExtensionRaw | undefined>[];
|
|
183
|
-
results.forEach((result, sourceUri) => {
|
|
184
|
-
result.extensions.forEach(extension => filtering.push(this.filterExtension(sourceUri, extension)));
|
|
185
|
-
});
|
|
186
|
-
return {
|
|
187
|
-
extensions: removeNullValues(await Promise.all(filtering))
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
protected async filterExtension<T extends ExtensionLike>(sourceUri: string, extension: T): Promise<T | undefined> {
|
|
192
|
-
return this.runRules(
|
|
193
|
-
filter => filter.filterExtension?.(extension),
|
|
194
|
-
rule => rule.use.includes(sourceUri) ? extension : undefined,
|
|
195
|
-
() => extension
|
|
196
|
-
);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
protected runRules<T>(runFilter: (filter: OVSXRouterFilter) => unknown, onRuleMatched: (rule: OVSXRouterParsedRule) => T): Promise<T | undefined>;
|
|
200
|
-
protected runRules<T, U>(runFilter: (filter: OVSXRouterFilter) => unknown, onRuleMatched: (rule: OVSXRouterParsedRule) => T, onNoRuleMatched: () => U): Promise<T | U>;
|
|
201
|
-
protected async runRules<T, U>(
|
|
202
|
-
runFilter: (filter: OVSXRouterFilter) => unknown,
|
|
203
|
-
onRuleMatched: (rule: OVSXRouterParsedRule) => T,
|
|
204
|
-
onNoRuleMatched?: () => U
|
|
205
|
-
): Promise<T | U | undefined> {
|
|
206
|
-
for (const rule of this.rules) {
|
|
207
|
-
const results = removeNullValues(await Promise.all(rule.filters.map(filter => runFilter(filter))));
|
|
208
|
-
if (results.length > 0 && results.every(value => value)) {
|
|
209
|
-
return onRuleMatched(rule);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
return onNoRuleMatched?.();
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
function nonNullable<T>(value: T | null | undefined): value is T {
|
|
217
|
-
// eslint-disable-next-line no-null/no-null
|
|
218
|
-
return typeof value !== 'undefined' && value !== null;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
function removeNullValues<T>(values: (T | null | undefined)[]): T[] {
|
|
222
|
-
return values.filter(nonNullable);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* Create a map where the keys are each element from {@link values} and the
|
|
227
|
-
* values are the result of a mapping function applied on the key.
|
|
228
|
-
*/
|
|
229
|
-
async function createMapping<T, U>(values: T[], map: (value: T, index: number) => MaybePromise<U>, thisArg?: unknown): Promise<Map<T, U>> {
|
|
230
|
-
return new Map(await Promise.all(values.map(async (value, index) => [value, await map.call(thisArg, value, index)] as [T, U])));
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* @example
|
|
235
|
-
* interleave([[1, 2, 3], [4, 5], [6, 7, 8]]) === [1, 4, 6, 2, 5, 7, 3, 8]
|
|
236
|
-
*/
|
|
237
|
-
function interleave<T>(arrays: T[][]): T[] {
|
|
238
|
-
const interleaved: T[] = [];
|
|
239
|
-
const length = Math.max(...arrays.map(array => array.length));
|
|
240
|
-
for (let i = 0; i < length; i++) {
|
|
241
|
-
for (const array of arrays) {
|
|
242
|
-
if (i < array.length) {
|
|
243
|
-
interleaved.push(array[i]);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
return interleaved;
|
|
248
|
-
}
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2023 Ericsson and others.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { ExtensionLike, OVSXClient, OVSXClientProvider, VSXExtensionRaw, VSXQueryOptions, VSXQueryResult, VSXSearchEntry, VSXSearchOptions, VSXSearchResult } from './ovsx-types';
|
|
18
|
+
import type { MaybePromise } from './types';
|
|
19
|
+
|
|
20
|
+
export interface OVSXRouterFilter {
|
|
21
|
+
filterSearchOptions?(searchOptions?: VSXSearchOptions): MaybePromise<unknown>;
|
|
22
|
+
filterQueryOptions?(queryOptions?: VSXQueryOptions): MaybePromise<unknown>;
|
|
23
|
+
filterExtension?(extension: ExtensionLike): MaybePromise<unknown>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param conditions key/value mapping of condition statements that rules may process
|
|
28
|
+
* @param remainingKeys keys left to be processed, remove items from it when you handled them
|
|
29
|
+
*/
|
|
30
|
+
export type OVSXRouterFilterFactory = (conditions: Readonly<Record<string, unknown>>, remainingKeys: Set<string>) => MaybePromise<OVSXRouterFilter | undefined>;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Helper function to create factories that handle a single condition key.
|
|
34
|
+
*/
|
|
35
|
+
export function createFilterFactory(conditionKey: string, factory: (conditionValue: unknown) => OVSXRouterFilter | undefined): OVSXRouterFilterFactory {
|
|
36
|
+
return (conditions, remainingKeys) => {
|
|
37
|
+
if (conditionKey in conditions) {
|
|
38
|
+
const filter = factory(conditions[conditionKey]);
|
|
39
|
+
if (filter) {
|
|
40
|
+
remainingKeys.delete(conditionKey);
|
|
41
|
+
return filter;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface OVSXRouterConfig {
|
|
48
|
+
/**
|
|
49
|
+
* Registry aliases that will be used for routing.
|
|
50
|
+
*/
|
|
51
|
+
registries?: {
|
|
52
|
+
[alias: string]: string
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The registry/ies to use by default.
|
|
56
|
+
*/
|
|
57
|
+
use: string | string[]
|
|
58
|
+
/**
|
|
59
|
+
* Filters for the different phases of interfacing with a registry.
|
|
60
|
+
*/
|
|
61
|
+
rules?: OVSXRouterRule[]
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface OVSXRouterRule {
|
|
65
|
+
[condition: string]: unknown
|
|
66
|
+
use?: string | string[] | null
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @internal
|
|
71
|
+
*/
|
|
72
|
+
export interface OVSXRouterParsedRule {
|
|
73
|
+
filters: OVSXRouterFilter[]
|
|
74
|
+
use: string[]
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Route and agglomerate queries according to {@link routerConfig}.
|
|
79
|
+
* {@link ruleFactories} is the actual logic used to evaluate the config.
|
|
80
|
+
* Each rule implementation will be ran sequentially over each configured rule.
|
|
81
|
+
*/
|
|
82
|
+
export class OVSXRouterClient implements OVSXClient {
|
|
83
|
+
|
|
84
|
+
static async FromConfig(routerConfig: OVSXRouterConfig, clientProvider: OVSXClientProvider, filterFactories: OVSXRouterFilterFactory[]): Promise<OVSXRouterClient> {
|
|
85
|
+
const rules = routerConfig.rules ? await this.ParseRules(routerConfig.rules, filterFactories, routerConfig.registries) : [];
|
|
86
|
+
return new this(
|
|
87
|
+
this.ParseUse(routerConfig.use, routerConfig.registries),
|
|
88
|
+
clientProvider,
|
|
89
|
+
rules
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
protected static async ParseRules(rules: OVSXRouterRule[], filterFactories: OVSXRouterFilterFactory[], aliases?: Record<string, string>): Promise<OVSXRouterParsedRule[]> {
|
|
94
|
+
return Promise.all(rules.map(async ({ use, ...conditions }) => {
|
|
95
|
+
const remainingKeys = new Set(Object.keys(conditions));
|
|
96
|
+
const filters = removeNullValues(await Promise.all(filterFactories.map(filterFactory => filterFactory(conditions, remainingKeys))));
|
|
97
|
+
if (remainingKeys.size > 0) {
|
|
98
|
+
throw new Error(`unknown conditions: ${Array.from(remainingKeys).join(', ')}`);
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
filters,
|
|
102
|
+
use: this.ParseUse(use, aliases)
|
|
103
|
+
};
|
|
104
|
+
}));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
protected static ParseUse(use: string | string[] | null | undefined, aliases?: Record<string, string>): string[] {
|
|
108
|
+
if (typeof use === 'string') {
|
|
109
|
+
return [alias(use)];
|
|
110
|
+
} else if (Array.isArray(use)) {
|
|
111
|
+
return use.map(alias);
|
|
112
|
+
} else {
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
function alias(aliasOrUri: string): string {
|
|
116
|
+
return aliases?.[aliasOrUri] ?? aliasOrUri;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
constructor(
|
|
121
|
+
protected readonly useDefault: string[],
|
|
122
|
+
protected readonly clientProvider: OVSXClientProvider,
|
|
123
|
+
protected readonly rules: OVSXRouterParsedRule[],
|
|
124
|
+
) { }
|
|
125
|
+
|
|
126
|
+
async search(searchOptions?: VSXSearchOptions): Promise<VSXSearchResult> {
|
|
127
|
+
return this.runRules(
|
|
128
|
+
filter => filter.filterSearchOptions?.(searchOptions),
|
|
129
|
+
rule => rule.use.length > 0
|
|
130
|
+
? this.mergedSearch(rule.use, searchOptions)
|
|
131
|
+
: this.emptySearchResult(searchOptions),
|
|
132
|
+
() => this.mergedSearch(this.useDefault, searchOptions)
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async query(queryOptions: VSXQueryOptions = {}): Promise<VSXQueryResult> {
|
|
137
|
+
return this.runRules(
|
|
138
|
+
filter => filter.filterQueryOptions?.(queryOptions),
|
|
139
|
+
rule => rule.use.length > 0
|
|
140
|
+
? this.mergedQuery(rule.use, queryOptions)
|
|
141
|
+
: this.emptyQueryResult(queryOptions),
|
|
142
|
+
() => this.mergedQuery(this.useDefault, queryOptions)
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
protected emptySearchResult(searchOptions?: VSXSearchOptions): VSXSearchResult {
|
|
147
|
+
return {
|
|
148
|
+
extensions: [],
|
|
149
|
+
offset: searchOptions?.offset ?? 0
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
protected emptyQueryResult(queryOptions?: VSXQueryOptions): VSXQueryResult {
|
|
154
|
+
return {
|
|
155
|
+
extensions: []
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
protected async mergedQuery(registries: string[], queryOptions?: VSXQueryOptions): Promise<VSXQueryResult> {
|
|
160
|
+
return this.mergeQueryResults(await createMapping(registries, async registry => (await this.clientProvider(registry)).query(queryOptions)));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
protected async mergedSearch(registries: string[], searchOptions?: VSXSearchOptions): Promise<VSXSearchResult> {
|
|
164
|
+
return this.mergeSearchResults(await createMapping(registries, async registry => (await this.clientProvider(registry)).search(searchOptions)));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
protected async mergeSearchResults(results: Map<string, VSXSearchResult>): Promise<VSXSearchResult> {
|
|
168
|
+
const filtering = [] as Promise<VSXSearchEntry[]>[];
|
|
169
|
+
results.forEach((result, sourceUri) => {
|
|
170
|
+
filtering.push(Promise
|
|
171
|
+
.all(result.extensions.map(extension => this.filterExtension(sourceUri, extension)))
|
|
172
|
+
.then(removeNullValues)
|
|
173
|
+
);
|
|
174
|
+
});
|
|
175
|
+
return {
|
|
176
|
+
extensions: interleave(await Promise.all(filtering)),
|
|
177
|
+
offset: Math.min(...Array.from(results.values(), result => result.offset))
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
protected async mergeQueryResults(results: Map<string, VSXQueryResult>): Promise<VSXQueryResult> {
|
|
182
|
+
const filtering = [] as Promise<VSXExtensionRaw | undefined>[];
|
|
183
|
+
results.forEach((result, sourceUri) => {
|
|
184
|
+
result.extensions.forEach(extension => filtering.push(this.filterExtension(sourceUri, extension)));
|
|
185
|
+
});
|
|
186
|
+
return {
|
|
187
|
+
extensions: removeNullValues(await Promise.all(filtering))
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
protected async filterExtension<T extends ExtensionLike>(sourceUri: string, extension: T): Promise<T | undefined> {
|
|
192
|
+
return this.runRules(
|
|
193
|
+
filter => filter.filterExtension?.(extension),
|
|
194
|
+
rule => rule.use.includes(sourceUri) ? extension : undefined,
|
|
195
|
+
() => extension
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
protected runRules<T>(runFilter: (filter: OVSXRouterFilter) => unknown, onRuleMatched: (rule: OVSXRouterParsedRule) => T): Promise<T | undefined>;
|
|
200
|
+
protected runRules<T, U>(runFilter: (filter: OVSXRouterFilter) => unknown, onRuleMatched: (rule: OVSXRouterParsedRule) => T, onNoRuleMatched: () => U): Promise<T | U>;
|
|
201
|
+
protected async runRules<T, U>(
|
|
202
|
+
runFilter: (filter: OVSXRouterFilter) => unknown,
|
|
203
|
+
onRuleMatched: (rule: OVSXRouterParsedRule) => T,
|
|
204
|
+
onNoRuleMatched?: () => U
|
|
205
|
+
): Promise<T | U | undefined> {
|
|
206
|
+
for (const rule of this.rules) {
|
|
207
|
+
const results = removeNullValues(await Promise.all(rule.filters.map(filter => runFilter(filter))));
|
|
208
|
+
if (results.length > 0 && results.every(value => value)) {
|
|
209
|
+
return onRuleMatched(rule);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return onNoRuleMatched?.();
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function nonNullable<T>(value: T | null | undefined): value is T {
|
|
217
|
+
// eslint-disable-next-line no-null/no-null
|
|
218
|
+
return typeof value !== 'undefined' && value !== null;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function removeNullValues<T>(values: (T | null | undefined)[]): T[] {
|
|
222
|
+
return values.filter(nonNullable);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Create a map where the keys are each element from {@link values} and the
|
|
227
|
+
* values are the result of a mapping function applied on the key.
|
|
228
|
+
*/
|
|
229
|
+
async function createMapping<T, U>(values: T[], map: (value: T, index: number) => MaybePromise<U>, thisArg?: unknown): Promise<Map<T, U>> {
|
|
230
|
+
return new Map(await Promise.all(values.map(async (value, index) => [value, await map.call(thisArg, value, index)] as [T, U])));
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* @example
|
|
235
|
+
* interleave([[1, 2, 3], [4, 5], [6, 7, 8]]) === [1, 4, 6, 2, 5, 7, 3, 8]
|
|
236
|
+
*/
|
|
237
|
+
function interleave<T>(arrays: T[][]): T[] {
|
|
238
|
+
const interleaved: T[] = [];
|
|
239
|
+
const length = Math.max(...arrays.map(array => array.length));
|
|
240
|
+
for (let i = 0; i < length; i++) {
|
|
241
|
+
for (const array of arrays) {
|
|
242
|
+
if (i < array.length) {
|
|
243
|
+
interleaved.push(array[i]);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return interleaved;
|
|
248
|
+
}
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
// *****************************************************************************
|
|
2
|
-
// Copyright (C) 2023 Ericsson and others.
|
|
3
|
-
//
|
|
4
|
-
// This program and the accompanying materials are made available under the
|
|
5
|
-
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
-
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
-
//
|
|
8
|
-
// This Source Code may also be made available under the following Secondary
|
|
9
|
-
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
-
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
-
// with the GNU Classpath Exception which is available at
|
|
12
|
-
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
-
//
|
|
14
|
-
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
-
// *****************************************************************************
|
|
16
|
-
|
|
17
|
-
export abstract class AbstractRegExpFilter {
|
|
18
|
-
|
|
19
|
-
constructor(
|
|
20
|
-
protected regExp: RegExp
|
|
21
|
-
) { }
|
|
22
|
-
|
|
23
|
-
protected test(value: unknown): boolean {
|
|
24
|
-
return typeof value === 'string' && this.regExp.test(value);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2023 Ericsson and others.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
export abstract class AbstractRegExpFilter {
|
|
18
|
+
|
|
19
|
+
constructor(
|
|
20
|
+
protected regExp: RegExp
|
|
21
|
+
) { }
|
|
22
|
+
|
|
23
|
+
protected test(value: unknown): boolean {
|
|
24
|
+
return typeof value === 'string' && this.regExp.test(value);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
// *****************************************************************************
|
|
2
|
-
// Copyright (C) 2023 Ericsson and others.
|
|
3
|
-
//
|
|
4
|
-
// This program and the accompanying materials are made available under the
|
|
5
|
-
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
-
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
-
//
|
|
8
|
-
// This Source Code may also be made available under the following Secondary
|
|
9
|
-
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
-
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
-
// with the GNU Classpath Exception which is available at
|
|
12
|
-
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
-
//
|
|
14
|
-
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
-
// *****************************************************************************
|
|
16
|
-
|
|
17
|
-
import { createFilterFactory, OVSXRouterFilter } from '../ovsx-router-client';
|
|
18
|
-
import { ExtensionLike } from '../ovsx-types';
|
|
19
|
-
import { AbstractRegExpFilter } from './abstract-reg-exp-filter';
|
|
20
|
-
|
|
21
|
-
export const ExtensionIdMatchesFilterFactory = createFilterFactory('ifExtensionIdMatches', ifExtensionIdMatches => {
|
|
22
|
-
if (typeof ifExtensionIdMatches !== 'string') {
|
|
23
|
-
throw new TypeError(`expected a string, got: ${typeof ifExtensionIdMatches}`);
|
|
24
|
-
}
|
|
25
|
-
return new ExtensionIdMatchesFilter(new RegExp(ifExtensionIdMatches, 'i'));
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
export class ExtensionIdMatchesFilter extends AbstractRegExpFilter implements OVSXRouterFilter {
|
|
29
|
-
filterExtension(extension: ExtensionLike): boolean {
|
|
30
|
-
return this.test(ExtensionLike.id(extension));
|
|
31
|
-
}
|
|
32
|
-
}
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2023 Ericsson and others.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { createFilterFactory, OVSXRouterFilter } from '../ovsx-router-client';
|
|
18
|
+
import { ExtensionLike } from '../ovsx-types';
|
|
19
|
+
import { AbstractRegExpFilter } from './abstract-reg-exp-filter';
|
|
20
|
+
|
|
21
|
+
export const ExtensionIdMatchesFilterFactory = createFilterFactory('ifExtensionIdMatches', ifExtensionIdMatches => {
|
|
22
|
+
if (typeof ifExtensionIdMatches !== 'string') {
|
|
23
|
+
throw new TypeError(`expected a string, got: ${typeof ifExtensionIdMatches}`);
|
|
24
|
+
}
|
|
25
|
+
return new ExtensionIdMatchesFilter(new RegExp(ifExtensionIdMatches, 'i'));
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
export class ExtensionIdMatchesFilter extends AbstractRegExpFilter implements OVSXRouterFilter {
|
|
29
|
+
filterExtension(extension: ExtensionLike): boolean {
|
|
30
|
+
return this.test(ExtensionLike.id(extension));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
// *****************************************************************************
|
|
2
|
-
// Copyright (C) 2023 Ericsson and others.
|
|
3
|
-
//
|
|
4
|
-
// This program and the accompanying materials are made available under the
|
|
5
|
-
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
-
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
-
//
|
|
8
|
-
// This Source Code may also be made available under the following Secondary
|
|
9
|
-
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
-
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
-
// with the GNU Classpath Exception which is available at
|
|
12
|
-
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
-
//
|
|
14
|
-
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
-
// *****************************************************************************
|
|
16
|
-
|
|
17
|
-
export { ExtensionIdMatchesFilterFactory } from './extension-id-matches-filter';
|
|
18
|
-
export { RequestContainsFilterFactory } from './request-contains-filter';
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2023 Ericsson and others.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
export { ExtensionIdMatchesFilterFactory } from './extension-id-matches-filter';
|
|
18
|
+
export { RequestContainsFilterFactory } from './request-contains-filter';
|