@johntalton/http-core 1.1.0 → 1.1.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@johntalton/http-core",
3
3
  "type": "module",
4
- "version": "1.1.0",
4
+ "version": "1.1.1",
5
5
  "license": "MIT",
6
6
  "exports": {
7
7
  ".": "./src/index.js"
@@ -16,7 +16,7 @@
16
16
  "lint": "biome check"
17
17
  },
18
18
  "dependencies": {
19
- "@johntalton/http-util": "^7.0.1",
19
+ "@johntalton/http-util": "^7.0.2",
20
20
  "@johntalton/sse-util": "^1.0.0"
21
21
  },
22
22
  "devDependencies": {
package/src/epilogue.js CHANGED
@@ -1,4 +1,3 @@
1
- import { MIME_TYPE_JSON } from '@johntalton/http-util/headers'
2
1
  import { Response } from '@johntalton/http-util/response/object'
3
2
  import { ServerSentEvents } from '@johntalton/sse-util'
4
3
 
@@ -18,11 +17,11 @@ function addSSEPortHandler(stream, port, streamId, shutdownSignal) {
18
17
  stream.end()
19
18
  }
20
19
 
21
- stream.once('close', (() => {
20
+ stream.once('close', () => {
22
21
  console.log('stream close in sse handler', streamId)
23
22
  shutdownSignal.removeEventListener('abort', signalHandler)
24
23
  port.close()
25
- }))
24
+ })
26
25
 
27
26
  shutdownSignal.addEventListener('abort', signalHandler, { once: true })
28
27
 
@@ -97,15 +96,8 @@ export function epilogue(state) {
97
96
  if(active) { addSSEPortHandler(stream, port, state.streamId, state.shutdownSignal) }
98
97
  } break
99
98
  case 'json': {
100
- const { obj, accept, etag, lastModified, age, supportedQueryTypes } = state
101
-
102
- if(accept.type === MIME_TYPE_JSON) {
103
- Response.json(stream, obj, { encoding: accept.encoding, etag, lastModified, age, cacheControl: state.cacheControl ?? {} }, { supportedQueryTypes }, meta)
104
- }
105
- else {
106
- // todo: but we did process the request - is that ok?
107
- Response.notAcceptable(stream, { supportedTypes: [ MIME_TYPE_JSON ] }, meta)
108
- }
99
+ const { obj, encoding, etag, lastModified, age, supportedQueryTypes } = state
100
+ Response.json(stream, obj, { encoding, etag, lastModified, age, cacheControl: state.cacheControl ?? {} }, { supportedQueryTypes }, meta)
109
101
  } break
110
102
  case 'partial-bytes': {
111
103
  const { contentType, contentLength, etag, lastModified, age } = state
@@ -120,12 +112,12 @@ export function epilogue(state) {
120
112
  cacheControl: state.cacheControl ?? {} }, meta)
121
113
  } break
122
114
  case 'bytes': {
123
- const { contentType, contentLength, etag, lastModified, age, acceptRanges } = state
115
+ const { contentType, contentLength, encoding, etag, lastModified, age, acceptRanges } = state
124
116
 
125
117
  Response.bytes(stream, state.obj, {
126
118
  contentType,
127
119
  contentLength,
128
- encoding: 'identity',
120
+ encoding: encoding ?? 'identity',
129
121
  etag,
130
122
  lastModified,
131
123
  age,
package/src/index.js CHANGED
@@ -43,6 +43,7 @@ export const KNOWN_METHODS = [
43
43
  /** @import { Metadata } from '@johntalton/http-util/response' */
44
44
  /** @import { BodyFuture } from '@johntalton/http-util/body' */
45
45
  /** @import {
46
+ AcceptItem,
46
47
  EtagItem,
47
48
  IMFFixDate,
48
49
  IMFFixDateInput,
@@ -52,6 +53,7 @@ export const KNOWN_METHODS = [
52
53
  ChallengeItem,
53
54
  CacheControlOptions
54
55
  } from '@johntalton/http-util/headers' */
56
+ /** @import { AcceptStyleItem } from '@johntalton/http-util/util' */
55
57
  /** @import { SendBody } from '@johntalton/http-util/response' */
56
58
  /** @import { SecFetchSite, SecFetchMode, SecFetchDest } from '@johntalton/http-util/headers' */
57
59
 
@@ -119,8 +121,24 @@ export const KNOWN_METHODS = [
119
121
  * @property {AbortSignal} shutdownSignal
120
122
  */
121
123
 
124
+ /**
125
+ * @typedef {Object} RouteRequestAcceptParsed
126
+ * @property {Array<AcceptItem> | undefined} type
127
+ * @property {Array<AcceptStyleItem> | undefined} encoding
128
+ * @property {Array<AcceptStyleItem>} language
129
+ */
130
+
131
+ /**
132
+ * @typedef {Object} RouteRequestAcceptFn
133
+ * @property {(a: Array<string>|undefined) => AcceptItem|undefined} type
134
+ * @property {(a: Array<string>|undefined) => AcceptStyleItem|undefined} encoding
135
+ * @property {(a: Array<string>|undefined) => AcceptStyleItem|undefined} language
136
+ */
137
+
122
138
  /**
123
139
  * @typedef {Object} RouteRequestAccept
140
+ * @property {RouteRequestAcceptParsed} parsed
141
+ * @property {RouteRequestAcceptFn} select
124
142
  * @property {string|undefined} type
125
143
  * @property {string|undefined} encoding
126
144
  * @property {string|undefined} language
@@ -405,8 +423,9 @@ export const KNOWN_METHODS = [
405
423
  * @typedef {Object} RouteBytesBase
406
424
  * @property {'bytes'} type
407
425
  * @property {string} contentType
408
- * @property {number|undefined} [contentLength]
409
426
  * @property {SendBody|undefined} obj
427
+ * @property {number|undefined} [contentLength]
428
+ * @property {string | undefined} [encoding]
410
429
  * @property {IMFFixDateInput|string|undefined} [lastModified]
411
430
  * @property {EtagItem|undefined} [etag]
412
431
  * @property {number|undefined} [age]
@@ -415,8 +434,6 @@ export const KNOWN_METHODS = [
415
434
  */
416
435
  /** @typedef {RouteBase & RouteBytesBase} RouteBytes */
417
436
 
418
-
419
-
420
437
  /**
421
438
  * @typedef {Object} RoutePartialBytesBase
422
439
  * @property {'partial-bytes'} type
@@ -433,8 +450,8 @@ export const KNOWN_METHODS = [
433
450
  /**
434
451
  * @typedef {Object} RouteJSONBase
435
452
  * @property {'json'} type
436
- * @property {RouteRequestAccept} accept
437
453
  * @property {Record<any, any>} obj
454
+ * @property {string | undefined} [encoding]
438
455
  * @property {IMFFixDateInput|string|undefined} [lastModified]
439
456
  * @property {EtagItem|undefined} [etag]
440
457
  * @property {number|undefined} [age]
package/src/preamble.js CHANGED
@@ -35,7 +35,7 @@ import {
35
35
  import { isValidHeader, isValidLikeHeader, isValidMethod } from './index.js'
36
36
 
37
37
  /** @import { ServerHttp2Stream, IncomingHttpHeaders } from 'node:http2' */
38
- /** @import { Config, RouteRequest, RouteAction, StreamID, RouteConditions, SecFetchMetadata } from './index.js' */
38
+ /** @import { Config, RouteRequest, RouteAction, StreamID, RouteConditions, SecFetchMetadata, RouteRequestAccept } from './index.js' */
39
39
 
40
40
  const { HTTP2_METHOD_OPTIONS, HTTP2_METHOD_TRACE } = http2.constants
41
41
 
@@ -258,21 +258,34 @@ export function preamble(preState, headers, servername) {
258
258
  //
259
259
  // content negotiation
260
260
  //
261
- const contentType = ContentType.parse(fullContentType)
262
- const acceptedEncoding = AcceptEncoding.select(fullAcceptEncoding, DEFAULT_SUPPORTED_ENCODINGS)
263
- const accept = Accept.select(fullAccept, DEFAULT_SUPPORTED_MIME_TYPES)
264
- const acceptedLanguage = AcceptLanguage.select(fullAcceptLanguage, DEFAULT_SUPPORTED_LANGUAGES)
261
+ const acceptItem = Accept.parse(fullAccept)
262
+ const acceptEncodingItem = AcceptEncoding.parse(fullAcceptEncoding)
263
+ const acceptLanguageItem = AcceptLanguage.parse(fullAcceptLanguage)
264
+
265
+ /** @type {RouteRequestAccept} */
265
266
  const acceptObject = {
266
- type: accept,
267
- encoding: acceptedEncoding,
268
- language: acceptedLanguage
267
+ parsed: {
268
+ type: acceptItem,
269
+ encoding: acceptEncodingItem,
270
+ language: acceptLanguageItem,
271
+ },
272
+
273
+ get type() { return Accept.selectFrom(acceptItem, DEFAULT_SUPPORTED_MIME_TYPES) },
274
+ get encoding() { return AcceptEncoding.selectFrom(acceptEncodingItem, DEFAULT_SUPPORTED_ENCODINGS) },
275
+ get language() { return AcceptLanguage.selectFrom(acceptLanguageItem, DEFAULT_SUPPORTED_LANGUAGES) },
276
+
277
+ select: {
278
+ type: acceptableTypes => Accept.selectItemFrom(acceptItem, acceptableTypes ?? DEFAULT_SUPPORTED_MIME_TYPES),
279
+ encoding: acceptableEncodings => AcceptEncoding.selectItemFrom(acceptEncodingItem, acceptableEncodings ?? DEFAULT_SUPPORTED_ENCODINGS),
280
+ language: acceptableLanguages => AcceptLanguage.selectItemFrom(acceptLanguageItem, acceptableLanguages ?? DEFAULT_SUPPORTED_LANGUAGES)
281
+ }
269
282
  }
270
283
 
271
284
  //
272
285
  // Trace
273
286
  //
274
287
  if(method === HTTP2_METHOD_TRACE) {
275
- if(!ALLOW_TRACE) { return { ...state, type: 'not-allowed', method, methods: [], url: requestUrl }}
288
+ if(!ALLOW_TRACE) { return { ...state, type: 'not-allowed', method, methods: [] }}
276
289
  const maxForwardsValue = maxForwards === undefined ? 0 : Number.parseInt(maxForwards)
277
290
  const preambleEnd = performance.now()
278
291
  state.meta.performance.push({ name: 'preamble-trace', duration: preambleEnd - preambleStart })
@@ -283,6 +296,7 @@ export function preamble(preState, headers, servername) {
283
296
  //
284
297
  // setup future body
285
298
  //
299
+ const contentType = ContentType.parse(fullContentType)
286
300
  const contentLength = fullContentLength === undefined ? undefined : Number.parseInt(fullContentLength, 10)
287
301
  const body = requestBody(stream, {
288
302
  byteLimit: BODY_BYTE_LENGTH,