@johntalton/http-util 5.1.6 → 6.0.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.
Files changed (70) hide show
  1. package/README.md +54 -7
  2. package/package.json +11 -5
  3. package/src/body.js +8 -8
  4. package/src/{response/defs.js → defs.js} +2 -1
  5. package/src/{accept-encoding.js → headers/accept-encoding.js} +2 -2
  6. package/src/{accept-language.js → headers/accept-language.js} +4 -5
  7. package/src/{accept.js → headers/accept.js} +18 -26
  8. package/src/headers/client-hints.js +81 -0
  9. package/src/{conditional.js → headers/conditional.js} +21 -12
  10. package/src/{content-disposition.js → headers/content-disposition.js} +26 -25
  11. package/src/{content-range.js → headers/content-range.js} +1 -1
  12. package/src/headers/content-type.js +101 -0
  13. package/src/{forwarded.js → headers/forwarded.js} +6 -23
  14. package/src/{index.js → headers/index.js} +4 -2
  15. package/src/{multipart.js → headers/multipart.js} +6 -5
  16. package/src/{preference.js → headers/preference.js} +3 -15
  17. package/src/{range.js → headers/range.js} +1 -1
  18. package/src/{server-timing.js → headers/server-timing.js} +2 -2
  19. package/src/headers/strict-transport-security.js +38 -0
  20. package/src/{accept-util.js → headers/util/accept-util.js} +8 -14
  21. package/src/headers/util/index.js +7 -0
  22. package/src/headers/util/kvp.js +79 -0
  23. package/src/headers/util/mime.js +77 -0
  24. package/src/headers/util/whitespace.js +6 -0
  25. package/src/{www-authenticate.js → headers/www-authenticate.js} +1 -1
  26. package/src/response/{accepted.js → 2xx/accepted.js} +2 -2
  27. package/src/response/{bytes.js → 2xx/bytes.js} +5 -5
  28. package/src/response/{created.js → 2xx/created.js} +4 -4
  29. package/src/response/{json.js → 2xx/json.js} +5 -5
  30. package/src/response/{no-content.js → 2xx/no-content.js} +4 -4
  31. package/src/response/{partial-content.js → 2xx/partial-content.js} +9 -9
  32. package/src/response/{preflight.js → 2xx/preflight.js} +4 -4
  33. package/src/response/{sse.js → 2xx/sse.js} +2 -2
  34. package/src/response/{trace.js → 2xx/trace.js} +3 -3
  35. package/src/response/{moved-permanently.js → 3xx/moved-permanently.js} +2 -2
  36. package/src/response/{multiple-choices.js → 3xx/multiple-choices.js} +2 -3
  37. package/src/response/{not-modified.js → 3xx/not-modified.js} +7 -7
  38. package/src/response/{permanent-redirect.js → 3xx/permanent-redirect.js} +2 -2
  39. package/src/response/{see-other.js → 3xx/see-other.js} +2 -2
  40. package/src/response/{temporary-redirect.js → 3xx/temporary-redirect.js} +2 -2
  41. package/src/response/{conflict.js → 4xx/conflict.js} +2 -2
  42. package/src/response/{content-too-large.js → 4xx/content-too-large.js} +2 -2
  43. package/src/response/{forbidden.js → 4xx/forbidden.js} +3 -2
  44. package/src/response/{gone.js → 4xx/gone.js} +2 -2
  45. package/src/response/{im-a-teapot.js → 4xx/im-a-teapot.js} +2 -2
  46. package/src/response/{not-acceptable.js → 4xx/not-acceptable.js} +3 -3
  47. package/src/response/{not-allowed.js → 4xx/not-allowed.js} +2 -2
  48. package/src/response/{not-found.js → 4xx/not-found.js} +3 -3
  49. package/src/response/{precondition-failed.js → 4xx/precondition-failed.js} +2 -2
  50. package/src/response/{range-not-satisfiable.js → 4xx/range-not-satisfiable.js} +4 -4
  51. package/src/response/{timeout.js → 4xx/timeout.js} +2 -2
  52. package/src/response/{too-many-requests.js → 4xx/too-many-requests.js} +5 -5
  53. package/src/response/{unauthorized.js → 4xx/unauthorized.js} +4 -4
  54. package/src/response/{unprocessable.js → 4xx/unprocessable.js} +2 -2
  55. package/src/response/{unsupported-media.js → 4xx/unsupported-media.js} +3 -3
  56. package/src/response/{error.js → 5xx/error.js} +3 -3
  57. package/src/response/{insufficient-storage.js → 5xx/insufficient-storage.js} +2 -2
  58. package/src/response/{not-implemented.js → 5xx/not-implemented.js} +4 -4
  59. package/src/response/{unavailable.js → 5xx/unavailable.js} +4 -4
  60. package/src/response/header-util.js +2 -2
  61. package/src/response/index.js +35 -35
  62. package/src/response/response.js +34 -34
  63. package/src/response/send-util.js +11 -13
  64. package/src/content-type.js +0 -148
  65. /package/src/{cache-control.js → headers/cache-control.js} +0 -0
  66. /package/src/{clear-site-data.js → headers/clear-site-data.js} +0 -0
  67. /package/src/{fetch-metadata.js → headers/fetch-metadata.js} +0 -0
  68. /package/src/{link.js → headers/link.js} +0 -0
  69. /package/src/{rate-limit.js → headers/rate-limit.js} +0 -0
  70. /package/src/{quote.js → headers/util/quote.js} +0 -0
@@ -1,3 +1,5 @@
1
+ import { KVP } from './util/kvp.js'
2
+
1
3
  export const FORWARDED_KEY_BY = 'by'
2
4
  export const FORWARDED_KEY_FOR = 'for'
3
5
  export const FORWARDED_KEY_HOST = 'host'
@@ -13,11 +15,8 @@ export const KNOWN_FORWARDED_KEYS = [
13
15
  export const SKIP_ANY = '*'
14
16
 
15
17
  export const FORWARDED_SEPARATOR = {
16
- ITEM: ',',
17
- ELEMENT: ';',
18
- KVP: '='
18
+ ITEM: ','
19
19
  }
20
-
21
20
  export class Forwarded {
22
21
  /**
23
22
  * @param {string|undefined} header
@@ -30,26 +29,10 @@ export class Forwarded {
30
29
  return header
31
30
  .trim()
32
31
  .split(FORWARDED_SEPARATOR.ITEM)
33
- .map(single => new Map(single
34
- .trim()
35
- .split(FORWARDED_SEPARATOR.ELEMENT)
36
- .map(kvp => {
37
- const [ rawKey, rawValue ] = kvp.trim().split(FORWARDED_SEPARATOR.KVP)
38
-
39
- const key = rawKey?.trim()?.toLowerCase()
40
- if (key === undefined || !acceptedKeys.includes(key)) { return undefined }
41
-
42
- const value = rawValue?.trim()
43
- if(value === undefined) { return undefined }
44
- if(value.length <= 0) { return undefined }
45
-
46
- /** @type {[string, string]} */
47
- const result = [ key, value ]
48
- return result
49
- })
50
- .filter(item => item !== undefined))
51
- )
32
+ .map(single => KVP.parseParameters(single, acceptedKeys)?.parameters)
33
+ .filter(m => m !== undefined)
52
34
  .filter(m => m.size > 0)
35
+ .filter(m => m.get(FORWARDED_KEY_FOR) !== undefined)
53
36
  }
54
37
 
55
38
  /**
@@ -3,18 +3,20 @@
3
3
  export * from './accept.js'
4
4
  export * from './accept-encoding.js'
5
5
  export * from './accept-language.js'
6
- export * from './accept-util.js'
7
6
  export * from './cache-control.js'
8
7
  export * from './clear-site-data.js'
8
+ export * from './client-hints.js'
9
9
  export * from './conditional.js'
10
10
  export * from './content-disposition.js'
11
11
  export * from './content-range.js'
12
12
  export * from './content-type.js'
13
- export * from './forwarded.js'
14
13
  export * from './fetch-metadata.js'
14
+ export * from './forwarded.js'
15
+ export * from './link.js'
15
16
  export * from './multipart.js'
16
17
  export * from './preference.js'
17
18
  export * from './range.js'
18
19
  export * from './rate-limit.js'
19
20
  export * from './server-timing.js'
21
+ export * from './strict-transport-security.js'
20
22
  export * from './www-authenticate.js'
@@ -1,11 +1,11 @@
1
1
  import { ReadableStream } from 'node:stream/web'
2
2
 
3
- import { parseContentDisposition } from './content-disposition.js'
3
+ import { ContentDisposition } from './content-disposition.js'
4
4
  import { ContentRange } from './content-range.js'
5
- import { parseContentType } from './content-type.js'
5
+ import { ContentType } from './content-type.js'
6
6
 
7
7
  /** @import { ContentRangeDirective } from './content-range.js' */
8
- /** @import { SendBody } from './response/send-util.js' */
8
+ /** @import { SendBody } from '../defs.js' */
9
9
 
10
10
  /**
11
11
  * @typedef {Object} MultipartBytePart
@@ -94,11 +94,11 @@ export class Multipart {
94
94
  const name = rawName?.toLowerCase()
95
95
  // console.log('header', name, value)
96
96
  if(name === MULTIPART_HEADER.CONTENT_TYPE) {
97
- const _contentType = parseContentType(value)
97
+ const _contentType = ContentType.parse(value)
98
98
  // console.log({ contentType })
99
99
  }
100
100
  else if(name === MULTIPART_HEADER.CONTENT_DISPOSITION) {
101
- const disposition = parseContentDisposition(value)
101
+ const disposition = ContentDisposition.parse(value)
102
102
  if(disposition?.disposition !== DISPOSITION_FORM_DATA) {
103
103
  throw new Error('disposition not form-data')
104
104
  }
@@ -161,6 +161,7 @@ export class Multipart {
161
161
  // controller.enqueue(encoder.encode(MULTIPART_SEPARATOR))
162
162
 
163
163
  if(part.obj instanceof ReadableStream) {
164
+ // biome-ignore lint/performance/noAwaitInLoops: readable
164
165
  for await (const chunk of part.obj) {
165
166
  if(chunk instanceof ArrayBuffer || ArrayBuffer.isView(chunk)) {
166
167
  controller.enqueue(chunk)
@@ -1,7 +1,8 @@
1
1
  // https://datatracker.ietf.org/doc/html/rfc7240
2
2
  // https://www.rfc-editor.org/rfc/rfc7240#section-3
3
3
 
4
- import { isQuoted, stripQuotes } from './quote.js'
4
+ import { KVP } from './util/kvp.js'
5
+ import { isQuoted, stripQuotes } from './util/quote.js'
5
6
 
6
7
  export const PREFERENCE_SEPARATOR = {
7
8
  PREFERENCE: ',',
@@ -53,26 +54,13 @@ export class Preferences {
53
54
 
54
55
  const preferences = new Map(header.split(PREFERENCE_SEPARATOR.PREFERENCE)
55
56
  .map(pref => {
56
- const [ kvp, ...params ] = pref.trim().split(PREFERENCE_SEPARATOR.PARAMS)
57
+ const { name: kvp, parameters } = KVP.parse(pref) ?? { parameters: new Map() }
57
58
  const [ key, rawValue ] = kvp?.split(PREFERENCE_SEPARATOR.KVP) ?? []
58
59
 
59
60
  if(key === undefined) { return {} }
60
61
  const valueOrEmpty = isQuoted(rawValue) ? stripQuotes(rawValue) : rawValue
61
62
  const value = (valueOrEmpty !== '') ? valueOrEmpty : undefined
62
63
 
63
- const parameters = new Map(params
64
- .map(param => {
65
- const [ pKey, rawPValue ] = param.split(PREFERENCE_SEPARATOR.PARAM_KVP)
66
- if(pKey === undefined) { return {} }
67
- const trimmedRawPValue = rawPValue?.trim()
68
- const pValueOrEmpty = isQuoted(trimmedRawPValue) ? stripQuotes(trimmedRawPValue) : trimmedRawPValue
69
- const pValue = (pValueOrEmpty !== '') ? pValueOrEmpty : undefined
70
- return { key: pKey.trim(), value: pValue }
71
- })
72
- .filter(item => item.key !== undefined)
73
- .map(item => ([ item.key, item.value ]))
74
- )
75
-
76
64
  return { key, value, parameters }
77
65
  })
78
66
  .filter(item => item.key !== undefined)
@@ -1,4 +1,4 @@
1
- import { RANGE_UNITS_BYTES } from "./response/defs.js"
1
+ import { RANGE_UNITS_BYTES } from '../defs.js'
2
2
 
3
3
  export const RANGE_EQUAL = '='
4
4
  export const RANGE_SEPARATOR = '-'
@@ -28,8 +28,8 @@ export class ServerTiming {
28
28
  return timings
29
29
  .map(({ name, duration, description }) => [
30
30
  `${name}`,
31
- description !== undefined ? `${SERVER_TIMING_KEY_DESCRIPTION}${SERVER_TIMING_SEPARATOR.KVP}"${description}"` : undefined,
32
- duration !== undefined ? `${SERVER_TIMING_KEY_DURATION}${SERVER_TIMING_SEPARATOR.KVP}${Math.trunc(duration * 10) / 10}` : undefined
31
+ description === undefined ? undefined : `${SERVER_TIMING_KEY_DESCRIPTION}${SERVER_TIMING_SEPARATOR.KVP}"${description}"`,
32
+ duration === undefined ? undefined : `${SERVER_TIMING_KEY_DURATION}${SERVER_TIMING_SEPARATOR.KVP}${Math.trunc(duration * 10) / 10}`
33
33
  ]
34
34
  .filter(item => item !== undefined)
35
35
  .join(SERVER_TIMING_SEPARATOR.PARAMETER))
@@ -0,0 +1,38 @@
1
+
2
+
3
+ export const STS_MAX_AGE = 'max-age'
4
+ export const STS_INCLUDE_SUBDOMAIN = 'includeSubDomains'
5
+ export const STS_PRELOAD = 'preload'
6
+
7
+ export const STS_MIN_AGE_FOR_PRELOAD_SECS = 60 * 60 * 24 * 365 // 31536000
8
+
9
+
10
+ /**
11
+ * @typedef {Object} StrictTransportSecurityOptions
12
+ * @property {number} maxAge
13
+ * @property {boolean|undefined} [includeSubDomains]
14
+ * @property {boolean|undefined} [preload]
15
+ */
16
+
17
+ export class StrictTransportSecurity {
18
+ /**
19
+ * @param {StrictTransportSecurityOptions} sts
20
+ */
21
+ static *#encode(sts) {
22
+ if(!Number.isFinite(sts.maxAge)) {
23
+ throw new Error('invalid max-age')
24
+ }
25
+
26
+ const maxAge = sts.preload ? Math.max(STS_MIN_AGE_FOR_PRELOAD_SECS, sts.maxAge) : sts.maxAge
27
+ yield `${STS_MAX_AGE}=${maxAge}`
28
+ if(sts.includeSubDomains) { yield STS_INCLUDE_SUBDOMAIN }
29
+ if(sts.preload) { yield STS_PRELOAD }
30
+ }
31
+
32
+ /**
33
+ * @param {StrictTransportSecurityOptions} sts
34
+ */
35
+ static encode(sts) {
36
+ return [ ...StrictTransportSecurity.#encode(sts) ].join('; ')
37
+ }
38
+ }
@@ -1,8 +1,8 @@
1
+ import { KVP } from './kvp.js'
2
+
1
3
  export const QUALITY = 'q'
2
4
  export const SEPARATOR = {
3
- MEDIA_RANGE: ',',
4
- PARAMETER: ';',
5
- KVP: '='
5
+ MEDIA_RANGE: ','
6
6
  }
7
7
 
8
8
  export const DEFAULT_QUALITY_STRING = '1'
@@ -29,16 +29,10 @@ export function parseAcceptStyleHeader(header, wellKnown) {
29
29
  .trim()
30
30
  .split(SEPARATOR.MEDIA_RANGE)
31
31
  .map(mediaRange => {
32
- const [ name, ...parametersSet ] = mediaRange
33
- .trim()
34
- .split(SEPARATOR.PARAMETER)
35
-
36
- const parameters = new Map(parametersSet.map(parameter => {
37
- const [ key, value ] = parameter.split(SEPARATOR.KVP).map(p => p.trim())
38
- return [ key, value ]
39
- }))
32
+ const { name, parameters } = KVP.parse(mediaRange) ?? { parameters: new Map() }
33
+ if(name === undefined) { return undefined }
34
+ if(name === '') { return undefined }
40
35
 
41
- if(!parameters.has(QUALITY)) { parameters.set(QUALITY, DEFAULT_QUALITY_STRING) }
42
36
  const quality = Number.parseFloat(parameters.get(QUALITY) ?? DEFAULT_QUALITY_STRING)
43
37
 
44
38
  return {
@@ -47,9 +41,9 @@ export function parseAcceptStyleHeader(header, wellKnown) {
47
41
  parameters
48
42
  }
49
43
  })
50
- .filter(entry => entry.name !== undefined && entry.name !== '')
44
+ .filter(entry => entry !== undefined)
51
45
  .sort((entryA, entryB) => {
52
46
  // B - A descending order
53
47
  return entryB.quality - entryA.quality
54
48
  })
55
- }
49
+ }
@@ -0,0 +1,7 @@
1
+ /** biome-ignore-all lint/performance/noBarrelFile: entry point */
2
+ /** biome-ignore-all lint/performance/noReExportAll: entry point */
3
+ export * from './accept-util.js'
4
+ export * from './kvp.js'
5
+ export * from './mime.js'
6
+ export * from './quote.js'
7
+ export * from './whitespace.js'
@@ -0,0 +1,79 @@
1
+ import { hasSpecialChar } from './mime.js'
2
+ import { isQuoted, stripQuotes } from './quote.js'
3
+
4
+ export const DEFAULT_DELIMITER = ';'
5
+ export const KVP_DELIMITER = '='
6
+ export const KVP_EMPTY = ''
7
+
8
+
9
+ export class KVP {
10
+ /**
11
+ * @param {Array<string>|undefined} params
12
+ * @param {Array<string>|undefined} [acceptableKeys=undefined]
13
+ */
14
+ static #parse(params, acceptableKeys = undefined) {
15
+ const parameters = new Map()
16
+
17
+ if(params === undefined) { return parameters }
18
+
19
+ for(const kvp of params) {
20
+ const [ rawKey, rawValue ] = kvp
21
+ .split(KVP_DELIMITER)
22
+ .map(p => p.trim())
23
+
24
+ if(rawKey === undefined) { continue }
25
+ if(rawKey === KVP_EMPTY) { continue }
26
+ const key = rawKey.toLowerCase()
27
+ if(hasSpecialChar(key)) { continue }
28
+
29
+ if(acceptableKeys !== undefined && !acceptableKeys.includes(key)) { continue }
30
+
31
+ const unquotedValue = isQuoted(rawValue) ? stripQuotes(rawValue) : rawValue
32
+ const value = unquotedValue === KVP_EMPTY ? undefined : unquotedValue
33
+
34
+ if(!parameters.has(key)) {
35
+ parameters.set(key, value)
36
+ }
37
+ }
38
+
39
+ return parameters
40
+ }
41
+
42
+ /**
43
+ * @param {string|undefined} str
44
+ * @param {Array<string>|undefined} [acceptableKeys=undefined]
45
+ */
46
+ static parse(str, acceptableKeys = undefined, delimiter = DEFAULT_DELIMITER) {
47
+ if(str === undefined) { return undefined }
48
+ if(str === KVP_EMPTY) { return undefined }
49
+
50
+ const [ name, ...params ] = str
51
+ .trim()
52
+ .split(delimiter)
53
+ .map(p => p.trim())
54
+
55
+ const parameters = KVP.#parse(params, acceptableKeys)
56
+
57
+ return { name, parameters }
58
+ }
59
+
60
+ /**
61
+ * @param {string|undefined} str
62
+ * @param {Array<string>|undefined} [acceptableKeys=undefined]
63
+ */
64
+ static parseParameters(str, acceptableKeys = undefined, delimiter = DEFAULT_DELIMITER) {
65
+ if(str === undefined) { return undefined }
66
+ if(str === KVP_EMPTY) { return undefined }
67
+
68
+ const params = str
69
+ .trim()
70
+ .split(delimiter)
71
+ .map(p => p.trim())
72
+
73
+ const parameters = KVP.#parse(params, acceptableKeys)
74
+
75
+ return { parameters }
76
+ }
77
+ }
78
+
79
+
@@ -0,0 +1,77 @@
1
+
2
+ /**
3
+ * @typedef {Object} MimeItem
4
+ * @property {string} mimetype
5
+ * @property {string} type
6
+ * @property {string} subtype
7
+ */
8
+
9
+ export const MIME_SEPARATOR = { SUBTYPE: '/' }
10
+
11
+ export const MIME_ANY = '*'
12
+
13
+ //
14
+ export const SPECIAL_CHARS = [
15
+ // special
16
+ '(', ')',
17
+ '<', '>',
18
+ '[', ']',
19
+ '{', '}',
20
+ '@', ',', ';', ':',
21
+ '\\', '"', '/', '?', '=', // '.',
22
+ // '%', // '!', '$', '&', // # ^ * | ~ `
23
+ // space
24
+ ' ', '\u000B', '\u000C',
25
+ // control
26
+ '\n', '\r', '\t'
27
+ ]
28
+
29
+ /**
30
+ * @param {string|undefined} value
31
+ */
32
+ export function hasSpecialChar(value) {
33
+ if(value === undefined) { return false }
34
+ for(const special of SPECIAL_CHARS) {
35
+ if(value.includes(special)) { return true }
36
+ }
37
+
38
+ return false
39
+ }
40
+
41
+ export class Mime {
42
+ /**
43
+ * @param {string|undefined} name
44
+ * @returns {MimeItem|undefined}
45
+ */
46
+ static parse(name) {
47
+ // console.log('mime::parse', name)
48
+ if(name === undefined) { return undefined }
49
+ if(name === '') { return undefined }
50
+
51
+ const parts = name
52
+ .trim() // leading space of type and trailing of subtype
53
+ .split(MIME_SEPARATOR.SUBTYPE)
54
+ .map(t => t.toLowerCase()) // all type/subtypes should be lower
55
+
56
+ // protect against multiple slashes
57
+ if(parts.length > 2) { return undefined }
58
+
59
+ const [ type, candidateSubtype ] = parts
60
+
61
+ if(type === undefined) { return undefined }
62
+ if(type === '') { return undefined }
63
+ if(hasSpecialChar(type)) { return undefined }
64
+
65
+ // if(candidateSubtype === undefined) { return undefined }
66
+ // if(candidateSubtype === '') { return undefined }
67
+ if(hasSpecialChar(candidateSubtype)) { return undefined }
68
+
69
+ const subtype = (candidateSubtype === '') ? MIME_ANY : candidateSubtype ?? MIME_ANY
70
+
71
+ return {
72
+ mimetype: `${type}${MIME_SEPARATOR.SUBTYPE}${subtype ?? MIME_ANY}`,
73
+ type,
74
+ subtype
75
+ }
76
+ }
77
+ }
@@ -0,0 +1,6 @@
1
+ export const WHITESPACE_REGEX = /\s/
2
+
3
+ /**
4
+ * @param {string} c
5
+ */
6
+ export function isWhitespace(c){ return WHITESPACE_REGEX.test(c) }
@@ -99,7 +99,7 @@ export class Challenge {
99
99
  return `${key}=${value}`
100
100
  })
101
101
 
102
- const params = parameters !== undefined ? [ ...parameters ].join(',') : ''
102
+ const params = parameters === undefined ? '' : [ ...parameters ].join(',')
103
103
 
104
104
  return `${challenge.scheme} ${params}`
105
105
  }
@@ -1,9 +1,9 @@
1
1
  import http2 from 'node:http2'
2
2
 
3
- import { send } from './send-util.js'
3
+ import { send } from '../send-util.js'
4
4
 
5
5
  /** @import { ServerHttp2Stream } from 'node:http2' */
6
- /** @import { Metadata } from './defs.js' */
6
+ /** @import { Metadata } from '../../defs.js' */
7
7
 
8
8
  const { HTTP_STATUS_ACCEPTED } = http2.constants
9
9
 
@@ -1,12 +1,12 @@
1
1
  import http2 from 'node:http2'
2
2
 
3
- import { send_bytes } from './send-util.js'
3
+ import { send_bytes } from '../send-util.js'
4
4
 
5
5
  /** @import { ServerHttp2Stream } from 'node:http2' */
6
- /** @import { Metadata } from './defs.js' */
7
- /** @import { EtagItem } from '../conditional.js' */
8
- /** @import { CacheControlOptions } from '../cache-control.js' */
9
- /** @import { SendBody } from './send-util.js' */
6
+ /** @import { Metadata } from '../../defs.js' */
7
+ /** @import { EtagItem } from '../../headers/conditional.js' */
8
+ /** @import { CacheControlOptions } from '../../headers/cache-control.js' */
9
+ /** @import { SendBody } from '../send-util.js' */
10
10
 
11
11
  const { HTTP_STATUS_OK } = http2.constants
12
12
 
@@ -1,11 +1,11 @@
1
1
  import http2 from 'node:http2'
2
2
 
3
- import { Conditional } from '../conditional.js'
4
- import { send } from './send-util.js'
3
+ import { Conditional } from '../../headers/conditional.js'
4
+ import { send } from '../send-util.js'
5
5
 
6
6
  /** @import { ServerHttp2Stream } from 'node:http2' */
7
- /** @import { Metadata } from './defs.js' */
8
- /** @import { EtagItem } from '../conditional.js' */
7
+ /** @import { Metadata } from '../../defs.js' */
8
+ /** @import { EtagItem } from '../../headers/conditional.js' */
9
9
 
10
10
  const {
11
11
  HTTP2_HEADER_LOCATION,
@@ -1,12 +1,12 @@
1
1
  import http2 from 'node:http2'
2
2
 
3
- import { CONTENT_TYPE_JSON } from '../content-type.js'
4
- import { send_encoded } from './send-util.js'
3
+ import { CONTENT_TYPE_JSON } from '../../headers/content-type.js'
4
+ import { send_encoded } from '../send-util.js'
5
5
 
6
6
  /** @import { ServerHttp2Stream } from 'node:http2' */
7
- /** @import { Metadata } from './defs.js' */
8
- /** @import { EtagItem } from '../conditional.js' */
9
- /** @import { CacheControlOptions } from '../cache-control.js' */
7
+ /** @import { Metadata } from '../../defs.js' */
8
+ /** @import { EtagItem } from '../../headers/conditional.js' */
9
+ /** @import { CacheControlOptions } from '../../headers/cache-control.js' */
10
10
 
11
11
  const { HTTP_STATUS_OK } = http2.constants
12
12
 
@@ -1,11 +1,11 @@
1
1
  import http2 from 'node:http2'
2
2
 
3
- import { Conditional } from '../conditional.js'
4
- import { send } from './send-util.js'
3
+ import { Conditional } from '../../headers/conditional.js'
4
+ import { send } from '../send-util.js'
5
5
 
6
6
  /** @import { ServerHttp2Stream } from 'node:http2' */
7
- /** @import { Metadata } from './defs.js' */
8
- /** @import { EtagItem } from '../conditional.js' */
7
+ /** @import { Metadata } from '../../defs.js' */
8
+ /** @import { EtagItem } from '../../headers/conditional.js' */
9
9
 
10
10
  const {
11
11
  HTTP2_HEADER_ETAG
@@ -1,16 +1,16 @@
1
1
  import http2 from 'node:http2'
2
2
 
3
- import { MIME_TYPE_MULTIPART_RANGE } from '../content-type.js'
4
- import { Multipart } from '../multipart.js'
5
- import { RANGE_UNITS_BYTES } from "./defs.js"
6
- import { send_bytes } from './send-util.js'
3
+ import { RANGE_UNITS_BYTES } from '../../defs.js'
4
+ import { MIME_TYPE_MULTIPART_RANGE } from '../../headers/content-type.js'
5
+ import { Multipart } from '../../headers/multipart.js'
6
+ import { send_bytes } from '../send-util.js'
7
7
 
8
8
  /** @import { ServerHttp2Stream } from 'node:http2' */
9
- /** @import { Metadata } from './defs.js' */
10
- /** @import { EtagItem } from '../conditional.js' */
11
- /** @import { CacheControlOptions } from '../cache-control.js' */
12
- /** @import { ContentRangeDirective } from '../content-range.js' */
13
- /** @import { SendBody } from './send-util.js' */
9
+ /** @import { Metadata } from '../../defs.js' */
10
+ /** @import { EtagItem } from '../../headers/conditional.js' */
11
+ /** @import { CacheControlOptions } from '../../headers/cache-control.js' */
12
+ /** @import { ContentRangeDirective } from '../../headers/content-range.js' */
13
+ /** @import { SendBody } from '../send-util.js' */
14
14
 
15
15
  const { HTTP_STATUS_PARTIAL_CONTENT } = http2.constants
16
16
 
@@ -5,11 +5,11 @@ import {
5
5
  HTTP_METHOD_QUERY,
6
6
  HTTP2_HEADER_ACCESS_CONTROL_MAX_AGE,
7
7
  PREFLIGHT_AGE_SECONDS
8
- } from './defs.js'
9
- import { send } from './send-util.js'
8
+ } from '../../defs.js'
9
+ import { send } from '../send-util.js'
10
10
 
11
11
  /** @import { ServerHttp2Stream } from 'node:http2' */
12
- /** @import { Metadata } from './defs.js' */
12
+ /** @import { Metadata } from '../../defs.js' */
13
13
 
14
14
  const {
15
15
  HTTP2_HEADER_CONTENT_TYPE,
@@ -35,7 +35,7 @@ const { HTTP_STATUS_OK } = http2.constants
35
35
  export function sendPreflight(stream, methods, supportedQueryTypes, acceptRanges, meta) {
36
36
  const supportsQuery = methods.includes(HTTP_METHOD_QUERY) && supportedQueryTypes !== undefined && supportedQueryTypes.length > 0
37
37
  const exposedHeadersAcceptQuery = supportsQuery ? [ HTTP_HEADER_ACCEPT_QUERY ] : []
38
- const exposedHeaders = acceptRanges !== undefined ? [ HTTP2_HEADER_ACCEPT_RANGES, ...exposedHeadersAcceptQuery ] : exposedHeadersAcceptQuery
38
+ const exposedHeaders = acceptRanges === undefined ? exposedHeadersAcceptQuery : [ HTTP2_HEADER_ACCEPT_RANGES, ...exposedHeadersAcceptQuery ]
39
39
 
40
40
  send(stream, HTTP_STATUS_OK, {
41
41
  [HTTP2_HEADER_ACCESS_CONTROL_ALLOW_METHODS]: methods.join(','),
@@ -6,10 +6,10 @@ import {
6
6
  SSE_INACTIVE_STATUS_CODE,
7
7
  SSE_MIME,
8
8
  } from '@johntalton/sse-util'
9
- import { coreHeaders, performanceHeaders } from './header-util.js'
9
+ import { coreHeaders, performanceHeaders } from '../header-util.js'
10
10
 
11
11
  /** @import { ServerHttp2Stream } from 'node:http2' */
12
- /** @import { Metadata, SSEOptions } from './defs.js' */
12
+ /** @import { Metadata, SSEOptions } from '../../defs.js' */
13
13
 
14
14
  const { HTTP_STATUS_OK } = http2.constants
15
15
 
@@ -1,11 +1,11 @@
1
1
  import http2 from 'node:http2'
2
2
 
3
- import { CONTENT_TYPE_MESSAGE_HTTP } from '../content-type.js'
4
- import { send } from './send-util.js'
3
+ import { CONTENT_TYPE_MESSAGE_HTTP } from '../../headers/content-type.js'
4
+ import { send } from '../send-util.js'
5
5
 
6
6
  /** @import { ServerHttp2Stream } from 'node:http2' */
7
7
  /** @import { IncomingHttpHeaders } from 'node:http2' */
8
- /** @import { Metadata } from './defs.js' */
8
+ /** @import { Metadata } from '../../defs.js' */
9
9
 
10
10
  const { HTTP_STATUS_OK } = http2.constants
11
11
 
@@ -1,9 +1,9 @@
1
1
  import http2 from 'node:http2'
2
2
 
3
- import { send } from './send-util.js'
3
+ import { send } from '../send-util.js'
4
4
 
5
5
  /** @import { ServerHttp2Stream } from 'node:http2' */
6
- /** @import { Metadata } from './defs.js' */
6
+ /** @import { Metadata } from '../../defs.js' */
7
7
 
8
8
  const {
9
9
  HTTP2_HEADER_LOCATION
@@ -1,9 +1,9 @@
1
1
  import http2 from 'node:http2'
2
2
 
3
- import { send } from './send-util.js'
3
+ import { send } from '../send-util.js'
4
4
 
5
5
  /** @import { ServerHttp2Stream } from 'node:http2' */
6
- /** @import { Metadata } from './defs.js' */
6
+ /** @import { Metadata } from '../../defs.js' */
7
7
 
8
8
  const { HTTP_STATUS_MULTIPLE_CHOICES } = http2.constants
9
9
 
@@ -12,7 +12,6 @@ const { HTTP_STATUS_MULTIPLE_CHOICES } = http2.constants
12
12
  * @param {Metadata} meta
13
13
  */
14
14
  export function sendMultipleChoices(stream, meta) {
15
- throw new Error('unsupported')
16
15
  send(stream, HTTP_STATUS_MULTIPLE_CHOICES, {
17
16
  // Alternates:
18
17
  // TCN: list