@api-client/core 0.5.15 → 0.5.18
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/build/browser.d.ts +1 -0
- package/build/browser.js +1 -0
- package/build/browser.js.map +1 -1
- package/build/index.d.ts +1 -0
- package/build/index.js +1 -0
- package/build/index.js.map +1 -1
- package/build/src/lib/parsers/UriTemplate.d.ts +18 -3
- package/build/src/lib/parsers/UriTemplate.js +27 -6
- package/build/src/lib/parsers/UriTemplate.js.map +1 -1
- package/build/src/lib/parsers/UrlProcessor.d.ts +255 -0
- package/build/src/lib/parsers/UrlProcessor.js +687 -0
- package/build/src/lib/parsers/UrlProcessor.js.map +1 -0
- package/build/src/models/HttpProject.d.ts +85 -32
- package/build/src/models/HttpProject.js +181 -80
- package/build/src/models/HttpProject.js.map +1 -1
- package/build/src/models/ProjectFolder.d.ts +43 -6
- package/build/src/models/ProjectFolder.js +37 -12
- package/build/src/models/ProjectFolder.js.map +1 -1
- package/build/src/models/ProjectItem.d.ts +13 -6
- package/build/src/models/ProjectItem.js +24 -4
- package/build/src/models/ProjectItem.js.map +1 -1
- package/build/src/models/ProjectParent.d.ts +1 -42
- package/build/src/models/ProjectParent.js +1 -84
- package/build/src/models/ProjectParent.js.map +1 -1
- package/build/src/models/transformers/PostmanBackupTransformer.js +0 -1
- package/build/src/models/transformers/PostmanBackupTransformer.js.map +1 -1
- package/build/src/models/transformers/PostmanV21Transformer.js +0 -1
- package/build/src/models/transformers/PostmanV21Transformer.js.map +1 -1
- package/build/src/models/transformers/PostmanV2Transformer.js +0 -1
- package/build/src/models/transformers/PostmanV2Transformer.js.map +1 -1
- package/build/src/runtime/node/ProjectRequestRunner.js +1 -1
- package/build/src/runtime/node/ProjectRequestRunner.js.map +1 -1
- package/package.json +1 -1
- package/src/lib/parsers/UriTemplate.ts +34 -11
- package/src/lib/parsers/UrlProcessor.ts +799 -0
- package/src/models/HttpProject.ts +236 -99
- package/src/models/ProjectFolder.ts +68 -17
- package/src/models/ProjectItem.ts +33 -11
- package/src/models/ProjectParent.ts +1 -110
- package/src/models/transformers/PostmanBackupTransformer.ts +0 -1
- package/src/models/transformers/PostmanV21Transformer.ts +0 -1
- package/src/models/transformers/PostmanV2Transformer.ts +0 -1
- package/src/runtime/node/ProjectRequestRunner.ts +1 -1
|
@@ -0,0 +1,687 @@
|
|
|
1
|
+
/* eslint-disable class-methods-use-this */
|
|
2
|
+
/* eslint-disable max-classes-per-file */
|
|
3
|
+
import { UrlEncoder } from './UrlEncoder.js';
|
|
4
|
+
class Tokenizer {
|
|
5
|
+
tokens;
|
|
6
|
+
/**
|
|
7
|
+
* This is set to the next token to be read.
|
|
8
|
+
*/
|
|
9
|
+
index = 0;
|
|
10
|
+
size;
|
|
11
|
+
get ended() {
|
|
12
|
+
return this.index === this.size - 1;
|
|
13
|
+
}
|
|
14
|
+
constructor(tokens) {
|
|
15
|
+
this.tokens = tokens;
|
|
16
|
+
this.size = tokens.length;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Reads characters until one of the passed characters (not included).
|
|
20
|
+
* @param chars The list of characters to search for.
|
|
21
|
+
* @returns The string between the current index and the found character or the end of tokens.
|
|
22
|
+
*/
|
|
23
|
+
untilChar(...chars) {
|
|
24
|
+
const { tokens, index, size } = this;
|
|
25
|
+
let result = '';
|
|
26
|
+
for (let i = index; i < size; i++) {
|
|
27
|
+
const token = tokens[i];
|
|
28
|
+
this.index = i;
|
|
29
|
+
if (chars.includes(token)) {
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
result += token;
|
|
33
|
+
}
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Reads the next token and changes the state.
|
|
38
|
+
* @returns The next token.
|
|
39
|
+
*/
|
|
40
|
+
next() {
|
|
41
|
+
const { ended, tokens } = this;
|
|
42
|
+
if (ended) {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
const next = tokens[this.index];
|
|
46
|
+
this.index += 1;
|
|
47
|
+
return next;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Reads the next character without changing the state.
|
|
51
|
+
*/
|
|
52
|
+
getNext() {
|
|
53
|
+
const { ended, tokens, index } = this;
|
|
54
|
+
if (ended) {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
return tokens[index + 1];
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
var PartType;
|
|
61
|
+
(function (PartType) {
|
|
62
|
+
/**
|
|
63
|
+
* Literal string, we do not process these
|
|
64
|
+
*/
|
|
65
|
+
PartType[PartType["literal"] = 0] = "literal";
|
|
66
|
+
/**
|
|
67
|
+
* URI template expression
|
|
68
|
+
*/
|
|
69
|
+
PartType[PartType["template"] = 1] = "template";
|
|
70
|
+
/**
|
|
71
|
+
* query parameter
|
|
72
|
+
*/
|
|
73
|
+
PartType[PartType["param"] = 2] = "param";
|
|
74
|
+
})(PartType || (PartType = {}));
|
|
75
|
+
var State;
|
|
76
|
+
(function (State) {
|
|
77
|
+
/**
|
|
78
|
+
* Processing a literal
|
|
79
|
+
*/
|
|
80
|
+
State[State["literal"] = 0] = "literal";
|
|
81
|
+
/**
|
|
82
|
+
* Processing a query parameter
|
|
83
|
+
*/
|
|
84
|
+
State[State["param"] = 1] = "param";
|
|
85
|
+
/**
|
|
86
|
+
* Processing a template expression
|
|
87
|
+
*/
|
|
88
|
+
State[State["expression"] = 2] = "expression";
|
|
89
|
+
})(State || (State = {}));
|
|
90
|
+
var DataType;
|
|
91
|
+
(function (DataType) {
|
|
92
|
+
// undefined/null
|
|
93
|
+
DataType[DataType["nil"] = 0] = "nil";
|
|
94
|
+
// string
|
|
95
|
+
DataType[DataType["string"] = 1] = "string";
|
|
96
|
+
// object
|
|
97
|
+
DataType[DataType["object"] = 2] = "object";
|
|
98
|
+
// array
|
|
99
|
+
DataType[DataType["array"] = 3] = "array";
|
|
100
|
+
})(DataType || (DataType = {}));
|
|
101
|
+
class SearchParams {
|
|
102
|
+
/**
|
|
103
|
+
* A reference to the URL processor's parts.
|
|
104
|
+
*/
|
|
105
|
+
parts = [];
|
|
106
|
+
constructor(parts) {
|
|
107
|
+
this.parts = parts;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Iterates over each query parameter, in order.
|
|
111
|
+
*/
|
|
112
|
+
*[Symbol.iterator]() {
|
|
113
|
+
for (const part of this.parts) {
|
|
114
|
+
if (part.type === PartType.param) {
|
|
115
|
+
yield part;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Reads parts, in order, that are query parameters.
|
|
121
|
+
*/
|
|
122
|
+
list() {
|
|
123
|
+
return this.parts.filter(i => i.type === PartType.param);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Adds a new query parameter to the list.
|
|
127
|
+
*
|
|
128
|
+
* @param name The name of the parameter.
|
|
129
|
+
* @param value The value of the parameter.
|
|
130
|
+
*/
|
|
131
|
+
append(name, value = '') {
|
|
132
|
+
const part = {
|
|
133
|
+
type: PartType.param,
|
|
134
|
+
expression: `${name}=${value}`,
|
|
135
|
+
name,
|
|
136
|
+
value,
|
|
137
|
+
enabled: true,
|
|
138
|
+
};
|
|
139
|
+
this.parts.push(part);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Replaces the param part in the parts list.
|
|
143
|
+
*
|
|
144
|
+
* @param index The index of the parameter as returned by the `getParameters()` method.
|
|
145
|
+
* @param param The param to set.
|
|
146
|
+
*/
|
|
147
|
+
update(index, param) {
|
|
148
|
+
if (param.type !== PartType.param) {
|
|
149
|
+
throw new Error(`Invalid query parameter definition`);
|
|
150
|
+
}
|
|
151
|
+
// eslint-disable-next-line no-param-reassign
|
|
152
|
+
param.expression = `${param.name}=${param.value || ''}`;
|
|
153
|
+
let current = 0;
|
|
154
|
+
for (let i = 0, len = this.parts.length; i < len; i++) {
|
|
155
|
+
const part = this.parts[i];
|
|
156
|
+
if (part.type === PartType.param) {
|
|
157
|
+
if (current === index) {
|
|
158
|
+
this.parts[i] = param;
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
current += 1;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
throw new Error(`Missing query parameter at position ${index}.`);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Replaces all parameters with the name with the passed value.
|
|
168
|
+
* It insets the param at the first occurrence of the current param in the parts list.
|
|
169
|
+
* If not exists, it adds it as a last.
|
|
170
|
+
*
|
|
171
|
+
* @param name The name of the parameter
|
|
172
|
+
* @param value the value of the parameter
|
|
173
|
+
*/
|
|
174
|
+
set(name, value) {
|
|
175
|
+
let firstIndex = -1;
|
|
176
|
+
for (let i = this.parts.length - 1; i >= 0; i--) {
|
|
177
|
+
const part = this.parts[i];
|
|
178
|
+
if (part.type === PartType.param) {
|
|
179
|
+
const typed = part;
|
|
180
|
+
if (typed.name === name) {
|
|
181
|
+
this.parts.splice(i, 1);
|
|
182
|
+
firstIndex = i;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
const part = {
|
|
187
|
+
type: PartType.param,
|
|
188
|
+
expression: `${name}=${value}`,
|
|
189
|
+
name,
|
|
190
|
+
value,
|
|
191
|
+
enabled: true,
|
|
192
|
+
};
|
|
193
|
+
if (firstIndex === -1) {
|
|
194
|
+
this.parts.push(part);
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
this.parts.splice(firstIndex, 0, part);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Removes a query parameter from the parameters list.
|
|
202
|
+
*
|
|
203
|
+
* @param index The index of the parameter as returned by the `getParameters()` method.
|
|
204
|
+
*/
|
|
205
|
+
delete(index) {
|
|
206
|
+
let current = 0;
|
|
207
|
+
for (let i = 0, len = this.parts.length; i < len; i++) {
|
|
208
|
+
const part = this.parts[i];
|
|
209
|
+
if (part.type === PartType.param) {
|
|
210
|
+
if (current === index) {
|
|
211
|
+
this.parts.splice(i, 1);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
current += 1;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
throw new Error(`Missing query parameter at position ${index}.`);
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Toggles the enabled state of the parameter.
|
|
221
|
+
*
|
|
222
|
+
* @param index The index of the parameter as returned by the `getParameters()` method.
|
|
223
|
+
* @param enabled The enabled state of the parameter.
|
|
224
|
+
*/
|
|
225
|
+
toggle(index, enabled) {
|
|
226
|
+
const params = this.list();
|
|
227
|
+
const param = params[index];
|
|
228
|
+
if (!param) {
|
|
229
|
+
throw new Error(`Missing query parameter at position ${index}.`);
|
|
230
|
+
}
|
|
231
|
+
param.enabled = enabled;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* A class that parses a string a treats it as a value that may include
|
|
236
|
+
* URI templates and request parameters.
|
|
237
|
+
*
|
|
238
|
+
* It can be used by the UI libraries to manipulate the URL templates and query parameters.
|
|
239
|
+
*/
|
|
240
|
+
export class UrlProcessor {
|
|
241
|
+
/**
|
|
242
|
+
* The source expression.
|
|
243
|
+
* It is immutable for the entire manipulation process of the URI.
|
|
244
|
+
* Any modification is on the parts level and the `expand()` and `toString()`
|
|
245
|
+
* function only rely on the `parts`.
|
|
246
|
+
*/
|
|
247
|
+
expression;
|
|
248
|
+
/**
|
|
249
|
+
* The tokenizer object
|
|
250
|
+
*/
|
|
251
|
+
tokens;
|
|
252
|
+
/**
|
|
253
|
+
* An ordered list of parts of the expression.
|
|
254
|
+
*/
|
|
255
|
+
parts = [];
|
|
256
|
+
/**
|
|
257
|
+
* A helper class to manipulate query parameters on the parser.
|
|
258
|
+
*/
|
|
259
|
+
search;
|
|
260
|
+
constructor(expression) {
|
|
261
|
+
this.expression = expression;
|
|
262
|
+
this.tokens = new Tokenizer(expression);
|
|
263
|
+
this.search = new SearchParams(this.parts);
|
|
264
|
+
this._parse();
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Creates an URI leaving the template expressions are they are (without expanding them).
|
|
268
|
+
*/
|
|
269
|
+
toString() {
|
|
270
|
+
let result = '';
|
|
271
|
+
this.parts.forEach((part) => {
|
|
272
|
+
if (part.type === PartType.literal) {
|
|
273
|
+
result += part.expression;
|
|
274
|
+
}
|
|
275
|
+
else if (part.type === PartType.template) {
|
|
276
|
+
result += `{${part.expression}}`;
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
const typed = part;
|
|
280
|
+
if (!typed.enabled) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
if (result.includes('?')) {
|
|
284
|
+
result += '&';
|
|
285
|
+
}
|
|
286
|
+
else {
|
|
287
|
+
result += '?';
|
|
288
|
+
}
|
|
289
|
+
result += part.expression;
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
return result;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Creates a URI with expanded template values.
|
|
296
|
+
*
|
|
297
|
+
* @param map The variables to evaluate.
|
|
298
|
+
* @param opts Processing options.
|
|
299
|
+
*/
|
|
300
|
+
expand(map, opts = {}) {
|
|
301
|
+
let result = '';
|
|
302
|
+
for (const part of this.parts) {
|
|
303
|
+
if (part.type === PartType.literal) {
|
|
304
|
+
result += part.expression;
|
|
305
|
+
}
|
|
306
|
+
else if (part.type === PartType.param) {
|
|
307
|
+
const typed = part;
|
|
308
|
+
if (!typed.enabled) {
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
if (result.includes('?')) {
|
|
312
|
+
result += '&';
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
result += '?';
|
|
316
|
+
}
|
|
317
|
+
result += part.expression;
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
result += this._expandExpression(part, map, opts);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return result;
|
|
324
|
+
}
|
|
325
|
+
_expandExpression(part, map, opts) {
|
|
326
|
+
const { operator, variables } = part;
|
|
327
|
+
const buffer = [];
|
|
328
|
+
for (const variable of variables) {
|
|
329
|
+
const data = this._getData(map, variable.name);
|
|
330
|
+
if (data.type === DataType.nil && opts.strict) {
|
|
331
|
+
throw new Error(`Missing expansion value for variable "${variable.name}"`);
|
|
332
|
+
}
|
|
333
|
+
if (data.type === DataType.nil && opts.ignoreMissing) {
|
|
334
|
+
buffer.push(part.expression);
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
if (!data.values.length) {
|
|
338
|
+
if (data.type !== DataType.nil) {
|
|
339
|
+
// Empty object / array. We still append the separator.
|
|
340
|
+
buffer.push('');
|
|
341
|
+
}
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
if (data.type > DataType.string && variable.maxLength) {
|
|
345
|
+
if (opts.strict) {
|
|
346
|
+
throw new Error(`Invalid expression: Prefix modifier not applicable to variable "${variable.name}"`);
|
|
347
|
+
}
|
|
348
|
+
// we ignore invalid values.
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
351
|
+
if (operator.named) {
|
|
352
|
+
buffer.push(this._expandNamedExpression(data, operator, variable));
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
buffer.push(this._expandUnNamedExpression(data, operator, variable));
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
if (buffer.length) {
|
|
359
|
+
return (operator.prefix || '') + buffer.join(operator.separator);
|
|
360
|
+
}
|
|
361
|
+
// prefix is not prepended for empty expressions
|
|
362
|
+
return '';
|
|
363
|
+
}
|
|
364
|
+
_expandNamedExpression(data, operator, variable) {
|
|
365
|
+
let result = '';
|
|
366
|
+
const separator = variable.explode && operator.separator || ',';
|
|
367
|
+
const encodeFunction = operator.reserved ? UrlEncoder.encodeReserved : UrlEncoder.strictEncode;
|
|
368
|
+
let name = '';
|
|
369
|
+
if (data.type !== DataType.object && variable.name) {
|
|
370
|
+
name = encodeFunction(variable.name);
|
|
371
|
+
}
|
|
372
|
+
const hasLength = typeof variable.maxLength === 'number';
|
|
373
|
+
data.values.forEach((item, index) => {
|
|
374
|
+
let value;
|
|
375
|
+
if (hasLength) {
|
|
376
|
+
value = encodeFunction(item.value.substring(0, variable.maxLength));
|
|
377
|
+
if (data.type === DataType.object) {
|
|
378
|
+
// apply maxLength to keys of objects as well
|
|
379
|
+
name = encodeFunction(item.name.substring(0, variable.maxLength));
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
else {
|
|
383
|
+
// encode value
|
|
384
|
+
value = encodeFunction(item.value);
|
|
385
|
+
if (data.type === DataType.object) {
|
|
386
|
+
// encode name and cache encoded value
|
|
387
|
+
name = encodeFunction(item.name);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
if (result) {
|
|
391
|
+
result += separator;
|
|
392
|
+
}
|
|
393
|
+
if (!variable.explode) {
|
|
394
|
+
if (!index) {
|
|
395
|
+
result += encodeFunction(variable.name) + (operator.emptyNameSeparator || value ? '=' : '');
|
|
396
|
+
}
|
|
397
|
+
if (data.type === DataType.object) {
|
|
398
|
+
result += `${name},`;
|
|
399
|
+
}
|
|
400
|
+
result += value;
|
|
401
|
+
}
|
|
402
|
+
else {
|
|
403
|
+
result += name + (operator.emptyNameSeparator || value ? '=' : '') + value;
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
return result;
|
|
407
|
+
}
|
|
408
|
+
_expandUnNamedExpression(data, operator, variable) {
|
|
409
|
+
let result = '';
|
|
410
|
+
const separator = variable.explode && operator.separator || ',';
|
|
411
|
+
const encodeFunction = operator.reserved ? UrlEncoder.encodeReserved : UrlEncoder.strictEncode;
|
|
412
|
+
const hasLength = typeof variable.maxLength === 'number';
|
|
413
|
+
data.values.forEach((item) => {
|
|
414
|
+
let value;
|
|
415
|
+
if (hasLength) {
|
|
416
|
+
value = encodeFunction(item.value.substring(0, variable.maxLength));
|
|
417
|
+
}
|
|
418
|
+
else {
|
|
419
|
+
value = encodeFunction(item.value);
|
|
420
|
+
}
|
|
421
|
+
if (result) {
|
|
422
|
+
result += separator;
|
|
423
|
+
}
|
|
424
|
+
if (data.type === DataType.object) {
|
|
425
|
+
let _name;
|
|
426
|
+
if (hasLength) {
|
|
427
|
+
_name = encodeFunction(item.name.substring(0, variable.maxLength));
|
|
428
|
+
}
|
|
429
|
+
else {
|
|
430
|
+
_name = encodeFunction(item.name || '');
|
|
431
|
+
}
|
|
432
|
+
result += _name;
|
|
433
|
+
if (variable.explode) {
|
|
434
|
+
result += (operator.emptyNameSeparator || value ? '=' : '');
|
|
435
|
+
}
|
|
436
|
+
else {
|
|
437
|
+
result += ',';
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
result += value;
|
|
441
|
+
});
|
|
442
|
+
return result;
|
|
443
|
+
}
|
|
444
|
+
_getData(map, name) {
|
|
445
|
+
const result = {
|
|
446
|
+
type: DataType.nil,
|
|
447
|
+
values: [],
|
|
448
|
+
};
|
|
449
|
+
const value = map[name];
|
|
450
|
+
if (value === undefined || value === null) {
|
|
451
|
+
// undefined and null values are to be ignored completely
|
|
452
|
+
return result;
|
|
453
|
+
}
|
|
454
|
+
if (Array.isArray(value)) {
|
|
455
|
+
for (const v of value) {
|
|
456
|
+
// we only allow primitives in the values
|
|
457
|
+
if (this._validExpansionValue(v)) {
|
|
458
|
+
result.values.push({ value: String(v) });
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
else if (String(Object.prototype.toString.call(value)) === '[object Object]') {
|
|
463
|
+
Object.keys(value).forEach((k) => {
|
|
464
|
+
const v = value[k];
|
|
465
|
+
if (this._validExpansionValue(v)) {
|
|
466
|
+
result.values.push({ name: k, value: v });
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
if (result.values.length) {
|
|
470
|
+
result.type = DataType.object;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
else {
|
|
474
|
+
result.type = DataType.string;
|
|
475
|
+
result.values.push({ value: String(value) });
|
|
476
|
+
}
|
|
477
|
+
return result;
|
|
478
|
+
}
|
|
479
|
+
_validExpansionValue(value) {
|
|
480
|
+
if (value === undefined || value === null) {
|
|
481
|
+
// these are ignored completely
|
|
482
|
+
return false;
|
|
483
|
+
}
|
|
484
|
+
// we only allow primitives in the values
|
|
485
|
+
return ['string', 'number', 'boolean'].includes(typeof value);
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Creates an ordered list of parts that describe the passed expression.
|
|
489
|
+
*
|
|
490
|
+
* FIXME: Handle error states like unclosed brackets.
|
|
491
|
+
*/
|
|
492
|
+
_parse() {
|
|
493
|
+
const { tokens } = this;
|
|
494
|
+
let state = State.literal;
|
|
495
|
+
// eslint-disable-next-line no-constant-condition
|
|
496
|
+
while (true) {
|
|
497
|
+
let value;
|
|
498
|
+
if (state === State.literal) {
|
|
499
|
+
value = tokens.untilChar('?', '&', '{', '}');
|
|
500
|
+
}
|
|
501
|
+
else if (state === State.param) {
|
|
502
|
+
value = tokens.untilChar('&', '{');
|
|
503
|
+
}
|
|
504
|
+
else {
|
|
505
|
+
value = tokens.untilChar('}');
|
|
506
|
+
}
|
|
507
|
+
const next = tokens.next();
|
|
508
|
+
if (state === State.literal) {
|
|
509
|
+
// when the value is an empty string that means that we entered the expression string
|
|
510
|
+
// and we have a variable or a query parameter as the first character.
|
|
511
|
+
// We don't need to pass a literal in this case.
|
|
512
|
+
if (value !== '') {
|
|
513
|
+
this._addLiteral(value);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
else if (state === State.param) {
|
|
517
|
+
this._addParam(value);
|
|
518
|
+
}
|
|
519
|
+
else {
|
|
520
|
+
this._addTemplate(value);
|
|
521
|
+
}
|
|
522
|
+
if (next === '?' || next === '&') {
|
|
523
|
+
state = State.param;
|
|
524
|
+
}
|
|
525
|
+
else if (next === '{') {
|
|
526
|
+
state = State.expression;
|
|
527
|
+
}
|
|
528
|
+
else if (next === undefined) {
|
|
529
|
+
break;
|
|
530
|
+
}
|
|
531
|
+
else {
|
|
532
|
+
state = State.literal;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Adds a literal part.
|
|
538
|
+
* Literal parts are not processed in any way. They do not contain query parameters
|
|
539
|
+
* or template expressions.
|
|
540
|
+
*
|
|
541
|
+
* @param expression The literal expression.
|
|
542
|
+
*/
|
|
543
|
+
_addLiteral(expression) {
|
|
544
|
+
this.parts.push({
|
|
545
|
+
type: PartType.literal,
|
|
546
|
+
expression,
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Adds a part that describes a query parameter
|
|
551
|
+
* @param expression The query param as `name=value` or `name` or `name=`
|
|
552
|
+
*/
|
|
553
|
+
_addParam(expression) {
|
|
554
|
+
const parts = expression.split('=');
|
|
555
|
+
const part = {
|
|
556
|
+
type: PartType.param,
|
|
557
|
+
expression,
|
|
558
|
+
name: parts[0],
|
|
559
|
+
value: parts[1] || '',
|
|
560
|
+
enabled: true,
|
|
561
|
+
};
|
|
562
|
+
this.parts.push(part);
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Adds a part that is a template expression.
|
|
566
|
+
*
|
|
567
|
+
* @param expression The template expression as defined in RFC 6570
|
|
568
|
+
*/
|
|
569
|
+
_addTemplate(expression) {
|
|
570
|
+
const ch = expression[0];
|
|
571
|
+
let operator;
|
|
572
|
+
if (ch === '+') {
|
|
573
|
+
// Reserved character strings (no encoding)
|
|
574
|
+
operator = {
|
|
575
|
+
operator: '+',
|
|
576
|
+
separator: ',',
|
|
577
|
+
reserved: true,
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
else if (ch === '#') {
|
|
581
|
+
// Fragment identifiers prefixed by '#'
|
|
582
|
+
operator = {
|
|
583
|
+
operator: '#',
|
|
584
|
+
prefix: '#',
|
|
585
|
+
separator: ',',
|
|
586
|
+
reserved: true,
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
else if (ch === '.') {
|
|
590
|
+
// Name labels or extensions prefixed by '.'
|
|
591
|
+
operator = {
|
|
592
|
+
operator: '.',
|
|
593
|
+
prefix: '.',
|
|
594
|
+
separator: '.',
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
else if (ch === '/') {
|
|
598
|
+
// Path segments prefixed by '/'
|
|
599
|
+
operator = {
|
|
600
|
+
operator: '/',
|
|
601
|
+
prefix: '/',
|
|
602
|
+
separator: '/',
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
else if (ch === ';') {
|
|
606
|
+
// Path parameter name or name=value pairs prefixed by ';'
|
|
607
|
+
operator = {
|
|
608
|
+
operator: ';',
|
|
609
|
+
prefix: ';',
|
|
610
|
+
separator: ';',
|
|
611
|
+
named: true,
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
else if (ch === '?') {
|
|
615
|
+
// Query component beginning with '?' and consisting
|
|
616
|
+
// of name=value pairs separated by '&'
|
|
617
|
+
operator = {
|
|
618
|
+
operator: '?',
|
|
619
|
+
prefix: '?',
|
|
620
|
+
separator: '&',
|
|
621
|
+
named: true,
|
|
622
|
+
emptyNameSeparator: true,
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
else if (ch === '&') {
|
|
626
|
+
// Continuation of query-style &name=value pairs
|
|
627
|
+
// within a literal query component.
|
|
628
|
+
operator = {
|
|
629
|
+
operator: '&',
|
|
630
|
+
prefix: '&',
|
|
631
|
+
separator: '&',
|
|
632
|
+
named: true,
|
|
633
|
+
emptyNameSeparator: true,
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
else {
|
|
637
|
+
// The operator characters equals ("="), comma (","), exclamation ("!"),
|
|
638
|
+
// at sign ("@"), and pipe ("|") are reserved for future extensions.
|
|
639
|
+
// this is level1 simple expression
|
|
640
|
+
operator = {
|
|
641
|
+
separator: ',',
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
const part = {
|
|
645
|
+
type: PartType.template,
|
|
646
|
+
expression,
|
|
647
|
+
operator,
|
|
648
|
+
variables: this._readVariables(expression, operator.operator),
|
|
649
|
+
};
|
|
650
|
+
this.parts.push(part);
|
|
651
|
+
}
|
|
652
|
+
_readVariables(expression, operator) {
|
|
653
|
+
let name = expression;
|
|
654
|
+
if (operator) {
|
|
655
|
+
name = name.substring(1);
|
|
656
|
+
}
|
|
657
|
+
return name.split(',').map((item) => {
|
|
658
|
+
let maxLength;
|
|
659
|
+
let varName = item;
|
|
660
|
+
let explode = false;
|
|
661
|
+
if (varName.endsWith('*')) {
|
|
662
|
+
explode = true;
|
|
663
|
+
varName = varName.substring(0, varName.length - 1);
|
|
664
|
+
}
|
|
665
|
+
const lengthIndex = varName.indexOf(':');
|
|
666
|
+
if (lengthIndex >= 0) {
|
|
667
|
+
const len = varName.substring(lengthIndex + 1);
|
|
668
|
+
varName = varName.substring(0, lengthIndex);
|
|
669
|
+
if (len) {
|
|
670
|
+
const parsed = Number(len);
|
|
671
|
+
if (Number.isInteger(parsed)) {
|
|
672
|
+
maxLength = parsed;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
const result = {
|
|
677
|
+
name: varName,
|
|
678
|
+
explode,
|
|
679
|
+
};
|
|
680
|
+
if (typeof maxLength === 'number') {
|
|
681
|
+
result.maxLength = maxLength;
|
|
682
|
+
}
|
|
683
|
+
return result;
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
//# sourceMappingURL=UrlProcessor.js.map
|