@nxtedition/lib 19.8.0 → 19.8.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 (3) hide show
  1. package/couch.js +64 -156
  2. package/package.json +16 -14
  3. package/weakCache.d.ts +4 -0
package/couch.js CHANGED
@@ -9,7 +9,7 @@ import urljoin from 'url-join'
9
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
+ import { interceptors } from '@nxtedition/nxt-undici'
13
13
 
14
14
  // https://github.com/fastify/fastify/blob/main/lib/reqIdGenFactory.js
15
15
  // 2,147,483,647 (2^31 − 1) stands for max SMI value (an internal optimization of V8).
@@ -887,141 +887,6 @@ function _normalizeAllDocsOptions({
887
887
  }
888
888
  }
889
889
 
890
- class SignalHandler extends undici.DecoratorHandler {
891
- #signal
892
- #handler
893
- #abort
894
-
895
- constructor({ signal, handler }) {
896
- super(handler)
897
- this.#signal = signal
898
- this.#handler = handler
899
- }
900
-
901
- onConnect(abort) {
902
- if (this.#signal?.aborted) {
903
- abort(this.#signal.reason)
904
- } else {
905
- if (this.#signal) {
906
- this.#abort = () => abort(this.#signal.reason)
907
- this.#signal.addEventListener('abort', this.#abort)
908
- }
909
- this.#handler.onConnect?.(abort)
910
- }
911
- }
912
-
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
- #contentType
929
- #opts
930
- #handler
931
- #error
932
- #headers
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) {
948
- this.#statusCode = statusCode
949
-
950
- if (this.#statusCode < 200 || this.#statusCode > 300) {
951
- this.#headers = Array.isArray(headers) ? undiciUtil.parseHeaders(headers) : headers
952
-
953
- this.#error = new Error(statusText ?? statusCode)
954
-
955
- this.#contentType = this.#headers['content-type']
956
- ? parseContentType(this.#headers['content-type'] ?? '')?.type
957
- : null
958
-
959
- if (this.#contentType !== 'application/json' && this.#contentType !== 'plain/text') {
960
- throw this.#error
961
- }
962
-
963
- this.#decoder = new TextDecoder()
964
- } else {
965
- return this.#handler.onHeaders?.(statusCode, headers, resume, statusText)
966
- }
967
- }
968
-
969
- onData(data) {
970
- if (this.#error) {
971
- this.#str += this.#decoder?.decode(data, { stream: true }) ?? ''
972
- } else {
973
- return this.#handler?.onData(data)
974
- }
975
- }
976
-
977
- onComplete(...args) {
978
- if (this.#error) {
979
- this.#str += this.#decoder?.decode(undefined, { stream: false }) ?? ''
980
-
981
- if (this.#contentType === 'application/json') {
982
- let body
983
- let reason
984
- let error
985
- try {
986
- body = JSON.parse(this.#str)
987
- reason = body.reason
988
- error = body.error
989
- } catch {
990
- body = this.#str
991
- // Do nothing...
992
- }
993
-
994
- throw Object.assign(this.#error, {
995
- reason,
996
- error,
997
- data: {
998
- req: this.#opts,
999
- res: {
1000
- body,
1001
- statusCode: this.#statusCode,
1002
- },
1003
- },
1004
- })
1005
- } else if (this.#contentType === 'plain/text') {
1006
- throw Object.assign(this.#error, {
1007
- data: {
1008
- ureq: this.#opts,
1009
- ures: {
1010
- body: this.#str,
1011
- statusCode: this.#statusCode,
1012
- headers: this.#headers,
1013
- },
1014
- },
1015
- })
1016
- }
1017
-
1018
- assert(false)
1019
- } else {
1020
- this.#handler.onComplete?.(...args)
1021
- }
1022
- }
1023
- }
1024
-
1025
890
  class StreamOutput extends stream.Readable {
1026
891
  #str
1027
892
  #resume
@@ -1036,8 +901,8 @@ class StreamOutput extends stream.Readable {
1036
901
  ttfb: -1,
1037
902
  }
1038
903
 
1039
- constructor({ highWaterMark = 256 }) {
1040
- super({ objectMode: true, highWaterMark })
904
+ constructor({ signal, highWaterMark = 256 }) {
905
+ super({ objectMode: true, highWaterMark, signal })
1041
906
  }
1042
907
 
1043
908
  get headers() {
@@ -1136,21 +1001,33 @@ class PromiseOutput {
1136
1001
  #decoder
1137
1002
  #headers
1138
1003
  #startTime = performance.now()
1004
+ #signal
1005
+ #abort
1139
1006
  #stats = {
1140
1007
  connect: -1,
1141
1008
  headers: -1,
1142
- ttfb: -1,
1009
+ data: -1,
1010
+ complete: -1,
1011
+ error: -1,
1143
1012
  }
1144
1013
 
1145
- constructor({ resolve, reject }) {
1014
+ constructor({ resolve, reject, signal }) {
1146
1015
  this.#resolve = resolve
1147
1016
  this.#reject = reject
1017
+ this.#signal = signal
1148
1018
  }
1149
1019
 
1150
- onConnect() {
1020
+ onConnect(abort) {
1151
1021
  this.#stats.connect = performance.now() - this.#startTime
1152
1022
  this.#decoder = new TextDecoder()
1153
1023
  this.#str = ''
1024
+
1025
+ if (this.#signal?.aborted) {
1026
+ abort()
1027
+ } else {
1028
+ this.#abort = abort
1029
+ this.#signal?.addEventListener('abort', this.#abort)
1030
+ }
1154
1031
  }
1155
1032
 
1156
1033
  onHeaders(statusCode, rawHeaders, resume, statusText) {
@@ -1165,26 +1042,46 @@ class PromiseOutput {
1165
1042
  }
1166
1043
 
1167
1044
  onData(data) {
1168
- if (this.#stats.ttfb === -1) {
1169
- this.#stats.ttfb = performance.now() - this.#startTime - this.#stats.headers
1045
+ if (this.#stats.data === -1) {
1046
+ this.#stats.data = performance.now() - this.#startTime - this.#stats.headers
1170
1047
  }
1171
1048
 
1172
1049
  this.#str += this.#decoder.decode(data, { stream: true })
1173
1050
  }
1174
1051
 
1175
1052
  onComplete() {
1053
+ if (this.#stats.complete === -1) {
1054
+ this.#stats.complete = performance.now() - this.#startTime - this.#stats.data
1055
+ }
1056
+
1176
1057
  this.#str += this.#decoder.decode(undefined, { stream: false })
1177
1058
 
1178
- this.#resolve(
1179
- Object.assign(JSON.parse(this.#str), { headers: this.#headers, stats: this.#stats }),
1180
- )
1059
+ const val = Object.assign(JSON.parse(this.#str), { headers: this.#headers, stats: this.#stats })
1060
+ this.#onDone(null, val)
1181
1061
  }
1182
1062
 
1183
1063
  onError(err) {
1184
- this.#reject(err)
1064
+ if (this.#stats.error === -1) {
1065
+ this.#stats.error = performance.now() - this.#startTime - this.#stats.data
1066
+ }
1067
+
1068
+ this.#onDone(err)
1069
+ }
1070
+
1071
+ #onDone(err, val) {
1072
+ if (err) {
1073
+ this.#reject(err)
1074
+ } else {
1075
+ this.#resolve(val)
1076
+ }
1077
+
1078
+ if (this.#abort) {
1079
+ this.#signal?.removeEventListener('abort', this.#abort)
1080
+ }
1185
1081
  }
1186
1082
  }
1187
1083
 
1084
+ const dispatcherCache = new WeakMap()
1188
1085
  export function request(
1189
1086
  url,
1190
1087
  {
@@ -1194,6 +1091,7 @@ export function request(
1194
1091
  headers,
1195
1092
  dispatcher = undici.getGlobalDispatcher(),
1196
1093
  signal,
1094
+ logger,
1197
1095
  stream,
1198
1096
  },
1199
1097
  ) {
@@ -1211,26 +1109,36 @@ export function request(
1211
1109
  headers: {
1212
1110
  'content-type': body != null && typeof body === 'object' ? 'application/json' : 'plain/text',
1213
1111
  'user-agent': globalThis.userAgent ?? 'nxt-lib',
1214
- 'request-id': genReqId(),
1215
1112
  accept: 'application/json',
1216
1113
  ...headers,
1217
1114
  },
1115
+ logger,
1218
1116
  body: body != null && typeof body === 'object' ? JSON.stringify(body) : body,
1219
1117
  }
1220
1118
 
1221
- dispatcher = dispatcher.compose(
1222
- (dispatch) => (opts, handler) => dispatch(opts, new ErrorHandler({ opts, handler })),
1223
- (dispatch) => (opts, handler) => dispatch(opts, new SignalHandler({ signal, handler })),
1224
- )
1119
+ let wrappedDispatcher = dispatcherCache.get(dispatcher)
1120
+ if (!wrappedDispatcher) {
1121
+ wrappedDispatcher = dispatcher.compose(
1122
+ interceptors.responseError,
1123
+ interceptors.log,
1124
+ interceptors.requestId,
1125
+ interceptors.responseRetry,
1126
+ interceptors.responseVerify,
1127
+ interceptors.redirect,
1128
+ interceptors.cache,
1129
+ interceptors.proxy,
1130
+ )
1131
+ dispatcherCache.set(dispatcher, wrappedDispatcher)
1132
+ }
1225
1133
 
1226
1134
  if (stream) {
1227
- const handler = new StreamOutput(stream)
1228
- dispatcher.dispatch(opts, handler)
1135
+ const handler = new StreamOutput({ signal, ...stream })
1136
+ wrappedDispatcher.dispatch(opts, handler)
1229
1137
  return handler
1230
1138
  } else {
1231
1139
  return new Promise((resolve, reject) => {
1232
- const handler = new PromiseOutput({ resolve, reject })
1233
- dispatcher.dispatch(opts, handler)
1140
+ const handler = new PromiseOutput({ resolve, reject, signal })
1141
+ wrappedDispatcher.dispatch(opts, handler)
1234
1142
  })
1235
1143
  }
1236
1144
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxtedition/lib",
3
- "version": "19.8.0",
3
+ "version": "19.8.2",
4
4
  "license": "MIT",
5
5
  "author": "Robert Nagy <robert.nagy@boffins.se>",
6
6
  "type": "module",
@@ -26,6 +26,7 @@
26
26
  "timers.js",
27
27
  "trace.js",
28
28
  "weakCache.js",
29
+ "weakCache.d.ts",
29
30
  "couch.js",
30
31
  "app.js",
31
32
  "errors.js",
@@ -74,13 +75,14 @@
74
75
  }
75
76
  },
76
77
  "eslintIgnore": [
77
- "/__tests__"
78
+ "/__tests__",
79
+ "**/*.d.ts"
78
80
  ],
79
81
  "dependencies": {
80
- "@aws-sdk/client-s3": "^3.554.0",
82
+ "@aws-sdk/client-s3": "^3.596.0",
81
83
  "@elastic/elasticsearch": "^8.13.1",
82
- "@elastic/transport": "^8.5.1",
83
- "@nxtedition/nxt-undici": "^2.0.46",
84
+ "@elastic/transport": "^8.6.0",
85
+ "@nxtedition/nxt-undici": "^2.2.5",
84
86
  "content-type": "^1.0.5",
85
87
  "date-fns": "^3.6.0",
86
88
  "fast-querystring": "^1.1.1",
@@ -95,31 +97,31 @@
95
97
  "nested-error-stacks": "^2.1.1",
96
98
  "object-hash": "^3.0.0",
97
99
  "p-queue": "^8.0.1",
98
- "pino": "^8.20.0",
100
+ "pino": "^9.2.0",
99
101
  "qs": "^6.12.1",
100
102
  "request-target": "^1.0.2",
101
103
  "smpte-timecode": "^1.3.5",
102
104
  "split-string": "^6.0.0",
103
105
  "split2": "^4.2.0",
104
106
  "toobusy-js": "^0.5.1",
105
- "undici": "^6.13.0",
107
+ "undici": "^6.18.2",
106
108
  "url-join": "^5.0.0"
107
109
  },
108
110
  "devDependencies": {
109
- "@nxtedition/deepstream.io-client-js": ">=24.1.20",
110
- "@types/lodash": "^4.17.0",
111
- "@types/node": "^20.12.7",
111
+ "@nxtedition/deepstream.io-client-js": ">=24.2.4",
112
+ "@types/lodash": "^4.17.5",
113
+ "@types/node": "^20.14.2",
112
114
  "eslint": "^8.0.0",
113
115
  "eslint-config-prettier": "^9.1.0",
114
116
  "eslint-config-standard": "^17.0.0",
115
117
  "eslint-plugin-import": "^2.29.1",
116
- "eslint-plugin-n": "^17.2.1",
118
+ "eslint-plugin-n": "^17.9.0",
117
119
  "eslint-plugin-node": "^11.1.0",
118
- "eslint-plugin-promise": "^6.0.0",
120
+ "eslint-plugin-promise": "^6.2.0",
119
121
  "husky": "^9.0.11",
120
- "lint-staged": "^15.2.2",
122
+ "lint-staged": "^15.2.7",
121
123
  "pinst": "^3.0.0",
122
- "prettier": "^3.2.5",
124
+ "prettier": "^3.3.2",
123
125
  "rxjs": "^7.5.6",
124
126
  "send": "^0.18.0"
125
127
  },
package/weakCache.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ export function makeWeakCache<FactoryArgs extends any[], CachedType>(
2
+ valueSelector: (...args: FactoryArgs) => CachedType,
3
+ keySelector?: (...args: FactoryArgs) => string,
4
+ ): (...args: FactoryArgs) => CachedType