source: node_modules/undici/lib/fetch/webidl.js

main
Last change on this file was d24f17c, checked in by Aleksandar Panovski <apano77@…>, 15 months ago

Initial commit

  • Property mode set to 100644
File size: 18.1 KB
Line 
1'use strict'
2
3const { types } = require('util')
4const { hasOwn, toUSVString } = require('./util')
5
6/** @type {import('../../types/webidl').Webidl} */
7const webidl = {}
8webidl.converters = {}
9webidl.util = {}
10webidl.errors = {}
11
12webidl.errors.exception = function (message) {
13 return new TypeError(`${message.header}: ${message.message}`)
14}
15
16webidl.errors.conversionFailed = function (context) {
17 const plural = context.types.length === 1 ? '' : ' one of'
18 const message =
19 `${context.argument} could not be converted to` +
20 `${plural}: ${context.types.join(', ')}.`
21
22 return webidl.errors.exception({
23 header: context.prefix,
24 message
25 })
26}
27
28webidl.errors.invalidArgument = function (context) {
29 return webidl.errors.exception({
30 header: context.prefix,
31 message: `"${context.value}" is an invalid ${context.type}.`
32 })
33}
34
35// https://webidl.spec.whatwg.org/#implements
36webidl.brandCheck = function (V, I, opts = undefined) {
37 if (opts?.strict !== false && !(V instanceof I)) {
38 throw new TypeError('Illegal invocation')
39 } else {
40 return V?.[Symbol.toStringTag] === I.prototype[Symbol.toStringTag]
41 }
42}
43
44webidl.argumentLengthCheck = function ({ length }, min, ctx) {
45 if (length < min) {
46 throw webidl.errors.exception({
47 message: `${min} argument${min !== 1 ? 's' : ''} required, ` +
48 `but${length ? ' only' : ''} ${length} found.`,
49 ...ctx
50 })
51 }
52}
53
54webidl.illegalConstructor = function () {
55 throw webidl.errors.exception({
56 header: 'TypeError',
57 message: 'Illegal constructor'
58 })
59}
60
61// https://tc39.es/ecma262/#sec-ecmascript-data-types-and-values
62webidl.util.Type = function (V) {
63 switch (typeof V) {
64 case 'undefined': return 'Undefined'
65 case 'boolean': return 'Boolean'
66 case 'string': return 'String'
67 case 'symbol': return 'Symbol'
68 case 'number': return 'Number'
69 case 'bigint': return 'BigInt'
70 case 'function':
71 case 'object': {
72 if (V === null) {
73 return 'Null'
74 }
75
76 return 'Object'
77 }
78 }
79}
80
81// https://webidl.spec.whatwg.org/#abstract-opdef-converttoint
82webidl.util.ConvertToInt = function (V, bitLength, signedness, opts = {}) {
83 let upperBound
84 let lowerBound
85
86 // 1. If bitLength is 64, then:
87 if (bitLength === 64) {
88 // 1. Let upperBound be 2^53 − 1.
89 upperBound = Math.pow(2, 53) - 1
90
91 // 2. If signedness is "unsigned", then let lowerBound be 0.
92 if (signedness === 'unsigned') {
93 lowerBound = 0
94 } else {
95 // 3. Otherwise let lowerBound be −2^53 + 1.
96 lowerBound = Math.pow(-2, 53) + 1
97 }
98 } else if (signedness === 'unsigned') {
99 // 2. Otherwise, if signedness is "unsigned", then:
100
101 // 1. Let lowerBound be 0.
102 lowerBound = 0
103
104 // 2. Let upperBound be 2^bitLength − 1.
105 upperBound = Math.pow(2, bitLength) - 1
106 } else {
107 // 3. Otherwise:
108
109 // 1. Let lowerBound be -2^bitLength − 1.
110 lowerBound = Math.pow(-2, bitLength) - 1
111
112 // 2. Let upperBound be 2^bitLength − 1 − 1.
113 upperBound = Math.pow(2, bitLength - 1) - 1
114 }
115
116 // 4. Let x be ? ToNumber(V).
117 let x = Number(V)
118
119 // 5. If x is −0, then set x to +0.
120 if (x === 0) {
121 x = 0
122 }
123
124 // 6. If the conversion is to an IDL type associated
125 // with the [EnforceRange] extended attribute, then:
126 if (opts.enforceRange === true) {
127 // 1. If x is NaN, +∞, or −∞, then throw a TypeError.
128 if (
129 Number.isNaN(x) ||
130 x === Number.POSITIVE_INFINITY ||
131 x === Number.NEGATIVE_INFINITY
132 ) {
133 throw webidl.errors.exception({
134 header: 'Integer conversion',
135 message: `Could not convert ${V} to an integer.`
136 })
137 }
138
139 // 2. Set x to IntegerPart(x).
140 x = webidl.util.IntegerPart(x)
141
142 // 3. If x < lowerBound or x > upperBound, then
143 // throw a TypeError.
144 if (x < lowerBound || x > upperBound) {
145 throw webidl.errors.exception({
146 header: 'Integer conversion',
147 message: `Value must be between ${lowerBound}-${upperBound}, got ${x}.`
148 })
149 }
150
151 // 4. Return x.
152 return x
153 }
154
155 // 7. If x is not NaN and the conversion is to an IDL
156 // type associated with the [Clamp] extended
157 // attribute, then:
158 if (!Number.isNaN(x) && opts.clamp === true) {
159 // 1. Set x to min(max(x, lowerBound), upperBound).
160 x = Math.min(Math.max(x, lowerBound), upperBound)
161
162 // 2. Round x to the nearest integer, choosing the
163 // even integer if it lies halfway between two,
164 // and choosing +0 rather than −0.
165 if (Math.floor(x) % 2 === 0) {
166 x = Math.floor(x)
167 } else {
168 x = Math.ceil(x)
169 }
170
171 // 3. Return x.
172 return x
173 }
174
175 // 8. If x is NaN, +0, +∞, or −∞, then return +0.
176 if (
177 Number.isNaN(x) ||
178 (x === 0 && Object.is(0, x)) ||
179 x === Number.POSITIVE_INFINITY ||
180 x === Number.NEGATIVE_INFINITY
181 ) {
182 return 0
183 }
184
185 // 9. Set x to IntegerPart(x).
186 x = webidl.util.IntegerPart(x)
187
188 // 10. Set x to x modulo 2^bitLength.
189 x = x % Math.pow(2, bitLength)
190
191 // 11. If signedness is "signed" and x ≥ 2^bitLength − 1,
192 // then return x − 2^bitLength.
193 if (signedness === 'signed' && x >= Math.pow(2, bitLength) - 1) {
194 return x - Math.pow(2, bitLength)
195 }
196
197 // 12. Otherwise, return x.
198 return x
199}
200
201// https://webidl.spec.whatwg.org/#abstract-opdef-integerpart
202webidl.util.IntegerPart = function (n) {
203 // 1. Let r be floor(abs(n)).
204 const r = Math.floor(Math.abs(n))
205
206 // 2. If n < 0, then return -1 × r.
207 if (n < 0) {
208 return -1 * r
209 }
210
211 // 3. Otherwise, return r.
212 return r
213}
214
215// https://webidl.spec.whatwg.org/#es-sequence
216webidl.sequenceConverter = function (converter) {
217 return (V) => {
218 // 1. If Type(V) is not Object, throw a TypeError.
219 if (webidl.util.Type(V) !== 'Object') {
220 throw webidl.errors.exception({
221 header: 'Sequence',
222 message: `Value of type ${webidl.util.Type(V)} is not an Object.`
223 })
224 }
225
226 // 2. Let method be ? GetMethod(V, @@iterator).
227 /** @type {Generator} */
228 const method = V?.[Symbol.iterator]?.()
229 const seq = []
230
231 // 3. If method is undefined, throw a TypeError.
232 if (
233 method === undefined ||
234 typeof method.next !== 'function'
235 ) {
236 throw webidl.errors.exception({
237 header: 'Sequence',
238 message: 'Object is not an iterator.'
239 })
240 }
241
242 // https://webidl.spec.whatwg.org/#create-sequence-from-iterable
243 while (true) {
244 const { done, value } = method.next()
245
246 if (done) {
247 break
248 }
249
250 seq.push(converter(value))
251 }
252
253 return seq
254 }
255}
256
257// https://webidl.spec.whatwg.org/#es-to-record
258webidl.recordConverter = function (keyConverter, valueConverter) {
259 return (O) => {
260 // 1. If Type(O) is not Object, throw a TypeError.
261 if (webidl.util.Type(O) !== 'Object') {
262 throw webidl.errors.exception({
263 header: 'Record',
264 message: `Value of type ${webidl.util.Type(O)} is not an Object.`
265 })
266 }
267
268 // 2. Let result be a new empty instance of record<K, V>.
269 const result = {}
270
271 if (!types.isProxy(O)) {
272 // Object.keys only returns enumerable properties
273 const keys = Object.keys(O)
274
275 for (const key of keys) {
276 // 1. Let typedKey be key converted to an IDL value of type K.
277 const typedKey = keyConverter(key)
278
279 // 2. Let value be ? Get(O, key).
280 // 3. Let typedValue be value converted to an IDL value of type V.
281 const typedValue = valueConverter(O[key])
282
283 // 4. Set result[typedKey] to typedValue.
284 result[typedKey] = typedValue
285 }
286
287 // 5. Return result.
288 return result
289 }
290
291 // 3. Let keys be ? O.[[OwnPropertyKeys]]().
292 const keys = Reflect.ownKeys(O)
293
294 // 4. For each key of keys.
295 for (const key of keys) {
296 // 1. Let desc be ? O.[[GetOwnProperty]](key).
297 const desc = Reflect.getOwnPropertyDescriptor(O, key)
298
299 // 2. If desc is not undefined and desc.[[Enumerable]] is true:
300 if (desc?.enumerable) {
301 // 1. Let typedKey be key converted to an IDL value of type K.
302 const typedKey = keyConverter(key)
303
304 // 2. Let value be ? Get(O, key).
305 // 3. Let typedValue be value converted to an IDL value of type V.
306 const typedValue = valueConverter(O[key])
307
308 // 4. Set result[typedKey] to typedValue.
309 result[typedKey] = typedValue
310 }
311 }
312
313 // 5. Return result.
314 return result
315 }
316}
317
318webidl.interfaceConverter = function (i) {
319 return (V, opts = {}) => {
320 if (opts.strict !== false && !(V instanceof i)) {
321 throw webidl.errors.exception({
322 header: i.name,
323 message: `Expected ${V} to be an instance of ${i.name}.`
324 })
325 }
326
327 return V
328 }
329}
330
331webidl.dictionaryConverter = function (converters) {
332 return (dictionary) => {
333 const type = webidl.util.Type(dictionary)
334 const dict = {}
335
336 if (type === 'Null' || type === 'Undefined') {
337 return dict
338 } else if (type !== 'Object') {
339 throw webidl.errors.exception({
340 header: 'Dictionary',
341 message: `Expected ${dictionary} to be one of: Null, Undefined, Object.`
342 })
343 }
344
345 for (const options of converters) {
346 const { key, defaultValue, required, converter } = options
347
348 if (required === true) {
349 if (!hasOwn(dictionary, key)) {
350 throw webidl.errors.exception({
351 header: 'Dictionary',
352 message: `Missing required key "${key}".`
353 })
354 }
355 }
356
357 let value = dictionary[key]
358 const hasDefault = hasOwn(options, 'defaultValue')
359
360 // Only use defaultValue if value is undefined and
361 // a defaultValue options was provided.
362 if (hasDefault && value !== null) {
363 value = value ?? defaultValue
364 }
365
366 // A key can be optional and have no default value.
367 // When this happens, do not perform a conversion,
368 // and do not assign the key a value.
369 if (required || hasDefault || value !== undefined) {
370 value = converter(value)
371
372 if (
373 options.allowedValues &&
374 !options.allowedValues.includes(value)
375 ) {
376 throw webidl.errors.exception({
377 header: 'Dictionary',
378 message: `${value} is not an accepted type. Expected one of ${options.allowedValues.join(', ')}.`
379 })
380 }
381
382 dict[key] = value
383 }
384 }
385
386 return dict
387 }
388}
389
390webidl.nullableConverter = function (converter) {
391 return (V) => {
392 if (V === null) {
393 return V
394 }
395
396 return converter(V)
397 }
398}
399
400// https://webidl.spec.whatwg.org/#es-DOMString
401webidl.converters.DOMString = function (V, opts = {}) {
402 // 1. If V is null and the conversion is to an IDL type
403 // associated with the [LegacyNullToEmptyString]
404 // extended attribute, then return the DOMString value
405 // that represents the empty string.
406 if (V === null && opts.legacyNullToEmptyString) {
407 return ''
408 }
409
410 // 2. Let x be ? ToString(V).
411 if (typeof V === 'symbol') {
412 throw new TypeError('Could not convert argument of type symbol to string.')
413 }
414
415 // 3. Return the IDL DOMString value that represents the
416 // same sequence of code units as the one the
417 // ECMAScript String value x represents.
418 return String(V)
419}
420
421// https://webidl.spec.whatwg.org/#es-ByteString
422webidl.converters.ByteString = function (V) {
423 // 1. Let x be ? ToString(V).
424 // Note: DOMString converter perform ? ToString(V)
425 const x = webidl.converters.DOMString(V)
426
427 // 2. If the value of any element of x is greater than
428 // 255, then throw a TypeError.
429 for (let index = 0; index < x.length; index++) {
430 if (x.charCodeAt(index) > 255) {
431 throw new TypeError(
432 'Cannot convert argument to a ByteString because the character at ' +
433 `index ${index} has a value of ${x.charCodeAt(index)} which is greater than 255.`
434 )
435 }
436 }
437
438 // 3. Return an IDL ByteString value whose length is the
439 // length of x, and where the value of each element is
440 // the value of the corresponding element of x.
441 return x
442}
443
444// https://webidl.spec.whatwg.org/#es-USVString
445webidl.converters.USVString = toUSVString
446
447// https://webidl.spec.whatwg.org/#es-boolean
448webidl.converters.boolean = function (V) {
449 // 1. Let x be the result of computing ToBoolean(V).
450 const x = Boolean(V)
451
452 // 2. Return the IDL boolean value that is the one that represents
453 // the same truth value as the ECMAScript Boolean value x.
454 return x
455}
456
457// https://webidl.spec.whatwg.org/#es-any
458webidl.converters.any = function (V) {
459 return V
460}
461
462// https://webidl.spec.whatwg.org/#es-long-long
463webidl.converters['long long'] = function (V) {
464 // 1. Let x be ? ConvertToInt(V, 64, "signed").
465 const x = webidl.util.ConvertToInt(V, 64, 'signed')
466
467 // 2. Return the IDL long long value that represents
468 // the same numeric value as x.
469 return x
470}
471
472// https://webidl.spec.whatwg.org/#es-unsigned-long-long
473webidl.converters['unsigned long long'] = function (V) {
474 // 1. Let x be ? ConvertToInt(V, 64, "unsigned").
475 const x = webidl.util.ConvertToInt(V, 64, 'unsigned')
476
477 // 2. Return the IDL unsigned long long value that
478 // represents the same numeric value as x.
479 return x
480}
481
482// https://webidl.spec.whatwg.org/#es-unsigned-long
483webidl.converters['unsigned long'] = function (V) {
484 // 1. Let x be ? ConvertToInt(V, 32, "unsigned").
485 const x = webidl.util.ConvertToInt(V, 32, 'unsigned')
486
487 // 2. Return the IDL unsigned long value that
488 // represents the same numeric value as x.
489 return x
490}
491
492// https://webidl.spec.whatwg.org/#es-unsigned-short
493webidl.converters['unsigned short'] = function (V, opts) {
494 // 1. Let x be ? ConvertToInt(V, 16, "unsigned").
495 const x = webidl.util.ConvertToInt(V, 16, 'unsigned', opts)
496
497 // 2. Return the IDL unsigned short value that represents
498 // the same numeric value as x.
499 return x
500}
501
502// https://webidl.spec.whatwg.org/#idl-ArrayBuffer
503webidl.converters.ArrayBuffer = function (V, opts = {}) {
504 // 1. If Type(V) is not Object, or V does not have an
505 // [[ArrayBufferData]] internal slot, then throw a
506 // TypeError.
507 // see: https://tc39.es/ecma262/#sec-properties-of-the-arraybuffer-instances
508 // see: https://tc39.es/ecma262/#sec-properties-of-the-sharedarraybuffer-instances
509 if (
510 webidl.util.Type(V) !== 'Object' ||
511 !types.isAnyArrayBuffer(V)
512 ) {
513 throw webidl.errors.conversionFailed({
514 prefix: `${V}`,
515 argument: `${V}`,
516 types: ['ArrayBuffer']
517 })
518 }
519
520 // 2. If the conversion is not to an IDL type associated
521 // with the [AllowShared] extended attribute, and
522 // IsSharedArrayBuffer(V) is true, then throw a
523 // TypeError.
524 if (opts.allowShared === false && types.isSharedArrayBuffer(V)) {
525 throw webidl.errors.exception({
526 header: 'ArrayBuffer',
527 message: 'SharedArrayBuffer is not allowed.'
528 })
529 }
530
531 // 3. If the conversion is not to an IDL type associated
532 // with the [AllowResizable] extended attribute, and
533 // IsResizableArrayBuffer(V) is true, then throw a
534 // TypeError.
535 // Note: resizable ArrayBuffers are currently a proposal.
536
537 // 4. Return the IDL ArrayBuffer value that is a
538 // reference to the same object as V.
539 return V
540}
541
542webidl.converters.TypedArray = function (V, T, opts = {}) {
543 // 1. Let T be the IDL type V is being converted to.
544
545 // 2. If Type(V) is not Object, or V does not have a
546 // [[TypedArrayName]] internal slot with a value
547 // equal to T’s name, then throw a TypeError.
548 if (
549 webidl.util.Type(V) !== 'Object' ||
550 !types.isTypedArray(V) ||
551 V.constructor.name !== T.name
552 ) {
553 throw webidl.errors.conversionFailed({
554 prefix: `${T.name}`,
555 argument: `${V}`,
556 types: [T.name]
557 })
558 }
559
560 // 3. If the conversion is not to an IDL type associated
561 // with the [AllowShared] extended attribute, and
562 // IsSharedArrayBuffer(V.[[ViewedArrayBuffer]]) is
563 // true, then throw a TypeError.
564 if (opts.allowShared === false && types.isSharedArrayBuffer(V.buffer)) {
565 throw webidl.errors.exception({
566 header: 'ArrayBuffer',
567 message: 'SharedArrayBuffer is not allowed.'
568 })
569 }
570
571 // 4. If the conversion is not to an IDL type associated
572 // with the [AllowResizable] extended attribute, and
573 // IsResizableArrayBuffer(V.[[ViewedArrayBuffer]]) is
574 // true, then throw a TypeError.
575 // Note: resizable array buffers are currently a proposal
576
577 // 5. Return the IDL value of type T that is a reference
578 // to the same object as V.
579 return V
580}
581
582webidl.converters.DataView = function (V, opts = {}) {
583 // 1. If Type(V) is not Object, or V does not have a
584 // [[DataView]] internal slot, then throw a TypeError.
585 if (webidl.util.Type(V) !== 'Object' || !types.isDataView(V)) {
586 throw webidl.errors.exception({
587 header: 'DataView',
588 message: 'Object is not a DataView.'
589 })
590 }
591
592 // 2. If the conversion is not to an IDL type associated
593 // with the [AllowShared] extended attribute, and
594 // IsSharedArrayBuffer(V.[[ViewedArrayBuffer]]) is true,
595 // then throw a TypeError.
596 if (opts.allowShared === false && types.isSharedArrayBuffer(V.buffer)) {
597 throw webidl.errors.exception({
598 header: 'ArrayBuffer',
599 message: 'SharedArrayBuffer is not allowed.'
600 })
601 }
602
603 // 3. If the conversion is not to an IDL type associated
604 // with the [AllowResizable] extended attribute, and
605 // IsResizableArrayBuffer(V.[[ViewedArrayBuffer]]) is
606 // true, then throw a TypeError.
607 // Note: resizable ArrayBuffers are currently a proposal
608
609 // 4. Return the IDL DataView value that is a reference
610 // to the same object as V.
611 return V
612}
613
614// https://webidl.spec.whatwg.org/#BufferSource
615webidl.converters.BufferSource = function (V, opts = {}) {
616 if (types.isAnyArrayBuffer(V)) {
617 return webidl.converters.ArrayBuffer(V, opts)
618 }
619
620 if (types.isTypedArray(V)) {
621 return webidl.converters.TypedArray(V, V.constructor)
622 }
623
624 if (types.isDataView(V)) {
625 return webidl.converters.DataView(V, opts)
626 }
627
628 throw new TypeError(`Could not convert ${V} to a BufferSource.`)
629}
630
631webidl.converters['sequence<ByteString>'] = webidl.sequenceConverter(
632 webidl.converters.ByteString
633)
634
635webidl.converters['sequence<sequence<ByteString>>'] = webidl.sequenceConverter(
636 webidl.converters['sequence<ByteString>']
637)
638
639webidl.converters['record<ByteString, ByteString>'] = webidl.recordConverter(
640 webidl.converters.ByteString,
641 webidl.converters.ByteString
642)
643
644module.exports = {
645 webidl
646}
Note: See TracBrowser for help on using the repository browser.