@nxtedition/lib 19.1.1 → 19.1.2

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 (2) hide show
  1. package/couch.js +109 -60
  2. package/package.json +2 -1
package/couch.js CHANGED
@@ -6,9 +6,10 @@ import { makeWeakCache } from './weakCache.js'
6
6
  import tp from 'timers/promises'
7
7
  import { defaultDelay as delay } from './http.js'
8
8
  import urljoin from 'url-join'
9
- import undici from 'undici'
9
+ import undici, { util as undiciUtil } from 'undici'
10
10
  import { AbortError } from './errors.js'
11
11
  import split2 from 'split2'
12
+ import { parse as parseContentType } from 'content-type'
12
13
 
13
14
  // https://github.com/fastify/fastify/blob/main/lib/reqIdGenFactory.js
14
15
  // 2,147,483,647 (2^31 − 1) stands for max SMI value (an internal optimization of V8).
@@ -886,22 +887,15 @@ function _normalizeAllDocsOptions({
886
887
  }
887
888
  }
888
889
 
889
- class Handler extends undici.DecoratorHandler {
890
- #str
891
- #decoder
892
- #onAbort
890
+ class SignalHandler extends undici.DecoratorHandler {
893
891
  #signal
894
- #statusCode
895
- #statusText
896
- #opts
897
892
  #handler
893
+ #abort
898
894
 
899
- constructor({ opts, signal, handler }) {
895
+ constructor({ signal, handler }) {
900
896
  super(handler)
901
-
902
- this.#handler = handler
903
- this.#opts = opts
904
897
  this.#signal = signal
898
+ this.#handler = handler
905
899
  }
906
900
 
907
901
  onConnect(abort) {
@@ -909,50 +903,93 @@ class Handler extends undici.DecoratorHandler {
909
903
  abort(this.#signal.reason)
910
904
  } else {
911
905
  if (this.#signal) {
912
- this.#onAbort = () => abort(this.#signal.reason)
913
- this.#signal.addEventListener('abort', this.#onAbort)
906
+ this.#abort = () => abort(this.#signal.reason)
907
+ this.#signal.addEventListener('abort', this.#abort)
914
908
  }
915
-
916
- this.#str = ''
917
- this.#decoder = null
918
909
  this.#handler.onConnect?.(abort)
919
910
  }
920
911
  }
921
912
 
922
- onHeaders(statusCode, rawHeaders, resume, statusText) {
913
+ onComplete(...args) {
914
+ this.#signal?.removeEventListener('abort', this.#abort)
915
+ this.#handler.onComplete?.(...args)
916
+ }
917
+
918
+ onError(...args) {
919
+ this.#signal?.removeEventListener('abort', this.#abort)
920
+ this.#handler.onError?.(...args)
921
+ }
922
+ }
923
+
924
+ class ErrorHandler extends undici.DecoratorHandler {
925
+ #str
926
+ #decoder
927
+ #statusCode
928
+ #statusText
929
+ #contentType
930
+ #opts
931
+ #handler
932
+ #error
933
+
934
+ constructor({ opts, handler }) {
935
+ super(handler)
936
+
937
+ this.#handler = handler
938
+ this.#opts = opts
939
+ }
940
+
941
+ onConnect(...args) {
942
+ this.#str = ''
943
+ this.#decoder = null
944
+ this.#handler.onConnect?.(...args)
945
+ }
946
+
947
+ onHeaders(statusCode, headers, resume, statusText) {
923
948
  this.#statusCode = statusCode
924
949
  this.#statusText = statusText
925
950
 
926
- if (this.#statusCode >= 200 && this.#statusCode < 300) {
927
- this.#handler.onHeaders?.(statusCode, rawHeaders, resume, statusText)
928
- } else {
951
+ if (this.#statusCode < 200 || this.#statusCode > 300) {
952
+ headers = Array.isArray(headers) ? undiciUtil.parseHeaders(headers) : headers
953
+
954
+ this.#error = new Error(this.#statusText ?? this.#statusCode)
955
+
956
+ this.#contentType = parseContentType(headers['content-type'] ?? '')?.type
957
+ if (this.#contentType !== 'application/json' && this.#contentType !== 'plain/text') {
958
+ throw this.#error
959
+ }
960
+
929
961
  this.#decoder = new TextDecoder()
962
+ } else {
963
+ return this.#handler.onHeaders?.(statusCode, headers, resume, statusText)
930
964
  }
931
965
  }
932
966
 
933
967
  onData(data) {
934
- if (this.#decoder) {
935
- this.#str += this.#decoder.decode(data, { stream: true })
968
+ if (this.#error) {
969
+ this.#str += this.#decoder?.decode(data, { stream: true }) ?? ''
936
970
  } else {
937
971
  return this.#handler?.onData(data)
938
972
  }
939
973
  }
940
974
 
941
- onComplete(trailers) {
942
- if (this.#decoder) {
943
- let body
944
- let reason
945
- let error
946
- try {
947
- body = JSON.parse(this.#str)
948
- reason = body.reason
949
- error = body.error
950
- } catch {
951
- // Do nothing...
952
- }
975
+ onComplete(...args) {
976
+ if (this.#error) {
977
+ this.#str += this.#decoder?.decode(undefined, { stream: false }) ?? ''
953
978
 
954
- this.onFinally(
955
- Object.assign(new Error(this.#statusText ?? reason), {
979
+ if (this.#contentType === 'application/json') {
980
+ let body
981
+ let reason
982
+ let error
983
+ try {
984
+ body = JSON.parse(this.#str)
985
+ reason = body.reason
986
+ error = body.error
987
+ } catch {
988
+ body = this.#str
989
+ // Do nothing...
990
+ }
991
+
992
+ throw Object.assign(this.#error, {
956
993
  reason,
957
994
  error,
958
995
  data: {
@@ -962,26 +999,22 @@ class Handler extends undici.DecoratorHandler {
962
999
  statusCode: this.#statusCode,
963
1000
  },
964
1001
  },
965
- }),
966
- )
967
- } else {
968
- this.onFinally(null, trailers)
969
- }
970
- }
971
-
972
- onError(err) {
973
- this.onFinally(err)
974
- }
975
-
976
- onFinally(err, val) {
977
- if (this.#signal) {
978
- this.#signal.removeEventListener('abort', this.#onAbort)
979
- }
1002
+ })
1003
+ } else if (this.#contentType === 'plain/text') {
1004
+ throw Object.assign(this.#error, {
1005
+ data: {
1006
+ req: this.#opts,
1007
+ res: {
1008
+ body: this.#str,
1009
+ statusCode: this.#statusCode,
1010
+ },
1011
+ },
1012
+ })
1013
+ }
980
1014
 
981
- if (err) {
982
- this.#handler.onError?.(err)
1015
+ assert(false)
983
1016
  } else {
984
- this.#handler.onComplete?.(val)
1017
+ this.#handler.onComplete?.(...args)
985
1018
  }
986
1019
  }
987
1020
  }
@@ -992,10 +1025,12 @@ class StreamOutput extends stream.Readable {
992
1025
  #abort
993
1026
  #state
994
1027
  #decoder
1028
+ #batched
995
1029
  #didPush = false
996
1030
 
997
- constructor({ highWaterMark }) {
1031
+ constructor({ batched = false, highWaterMark = batched ? 2 : 128 }) {
998
1032
  super({ objectMode: true, highWaterMark })
1033
+ this.#batched = batched
999
1034
  }
1000
1035
 
1001
1036
  _read() {
@@ -1033,6 +1068,7 @@ class StreamOutput extends stream.Readable {
1033
1068
  lines[0] = this.#str + lines[0]
1034
1069
  this.#str = lines.pop() ?? ''
1035
1070
 
1071
+ const rows = this.#batched ? [] : null
1036
1072
  for (const line of lines) {
1037
1073
  if (this.#state === 0) {
1038
1074
  if (line.endsWith('[')) {
@@ -1044,12 +1080,22 @@ class StreamOutput extends stream.Readable {
1044
1080
  if (line.startsWith(']')) {
1045
1081
  this.#state = 2
1046
1082
  } else {
1047
- this.push(JSON.parse(line.slice(0, line.lastIndexOf('}') + 1)))
1048
- this.#didPush = true
1083
+ const row = JSON.parse(line.slice(0, line.lastIndexOf('}') + 1))
1084
+ if (rows) {
1085
+ rows.push(row)
1086
+ } else {
1087
+ this.push(row)
1088
+ this.#didPush = true
1089
+ }
1049
1090
  }
1050
1091
  }
1051
1092
  }
1052
1093
 
1094
+ if (rows) {
1095
+ this.push(rows)
1096
+ this.#didPush = true
1097
+ }
1098
+
1053
1099
  return this.readableLength < this.readableHighWaterMark
1054
1100
  }
1055
1101
 
@@ -1121,6 +1167,8 @@ export function request(
1121
1167
  method,
1122
1168
  headers: {
1123
1169
  'content-type': body != null && typeof body === 'object' ? 'application/json' : 'plain/text',
1170
+ 'user-agent': globalThis.userAgent,
1171
+ 'request-id': genReqId(),
1124
1172
  accept: 'application/json',
1125
1173
  ...headers,
1126
1174
  },
@@ -1128,11 +1176,12 @@ export function request(
1128
1176
  }
1129
1177
 
1130
1178
  dispatcher = dispatcher.compose(
1131
- (dispatch) => (opts, handler) => dispatch(opts, new Handler({ opts, signal, handler })),
1179
+ (dispatch) => (opts, handler) => dispatch(opts, new ErrorHandler({ opts, handler })),
1180
+ (dispatch) => (opts, handler) => dispatch(opts, new SignalHandler({ signal, handler })),
1132
1181
  )
1133
1182
 
1134
1183
  if (stream) {
1135
- const handler = new StreamOutput({ highWaterMark: stream.highWaterMark ?? 128 })
1184
+ const handler = new StreamOutput(stream)
1136
1185
  dispatcher.dispatch(opts, handler)
1137
1186
  return handler
1138
1187
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxtedition/lib",
3
- "version": "19.1.1",
3
+ "version": "19.1.2",
4
4
  "license": "MIT",
5
5
  "author": "Robert Nagy <robert.nagy@boffins.se>",
6
6
  "type": "module",
@@ -79,6 +79,7 @@
79
79
  "@elastic/elasticsearch": "^8.13.1",
80
80
  "@elastic/transport": "^8.5.1",
81
81
  "@nxtedition/nxt-undici": "^2.0.46",
82
+ "content-type": "^1.0.5",
82
83
  "date-fns": "^3.6.0",
83
84
  "fast-querystring": "^1.1.1",
84
85
  "hasha": "^6.0.0",