@nxtedition/lib 19.1.0 → 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.
- package/couch.js +229 -97
- package/package.json +8 -7
package/couch.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import assert from 'node:assert'
|
|
2
|
+
import stream from 'node:stream'
|
|
2
3
|
import querystring from 'node:querystring'
|
|
3
4
|
import createError from 'http-errors'
|
|
4
5
|
import { makeWeakCache } from './weakCache.js'
|
|
5
6
|
import tp from 'timers/promises'
|
|
6
7
|
import { defaultDelay as delay } from './http.js'
|
|
7
8
|
import urljoin from 'url-join'
|
|
8
|
-
import undici from 'undici'
|
|
9
|
+
import undici, { util as undiciUtil } from 'undici'
|
|
9
10
|
import { AbortError } from './errors.js'
|
|
10
11
|
import split2 from 'split2'
|
|
11
|
-
import
|
|
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,101 +887,96 @@ function _normalizeAllDocsOptions({
|
|
|
886
887
|
}
|
|
887
888
|
}
|
|
888
889
|
|
|
889
|
-
class
|
|
890
|
-
#state = 0
|
|
891
|
-
#str = ''
|
|
892
|
-
#decoder = new TextDecoder()
|
|
893
|
-
#resolve
|
|
894
|
-
#reject
|
|
895
|
-
#resume
|
|
896
|
-
#abort
|
|
897
|
-
#onAbort
|
|
890
|
+
class SignalHandler extends undici.DecoratorHandler {
|
|
898
891
|
#signal
|
|
899
|
-
#
|
|
900
|
-
#
|
|
901
|
-
#statusText
|
|
902
|
-
#opts
|
|
892
|
+
#handler
|
|
893
|
+
#abort
|
|
903
894
|
|
|
904
|
-
constructor({
|
|
905
|
-
|
|
906
|
-
this.#opts = opts
|
|
907
|
-
this.#resolve = resolve
|
|
908
|
-
this.#reject = reject
|
|
895
|
+
constructor({ signal, handler }) {
|
|
896
|
+
super(handler)
|
|
909
897
|
this.#signal = signal
|
|
898
|
+
this.#handler = handler
|
|
910
899
|
}
|
|
911
900
|
|
|
912
901
|
onConnect(abort) {
|
|
913
902
|
if (this.#signal?.aborted) {
|
|
914
903
|
abort(this.#signal.reason)
|
|
915
904
|
} else {
|
|
916
|
-
this.#abort = abort
|
|
917
905
|
if (this.#signal) {
|
|
918
|
-
this.#
|
|
919
|
-
this.#signal.addEventListener('abort', this.#
|
|
906
|
+
this.#abort = () => abort(this.#signal.reason)
|
|
907
|
+
this.#signal.addEventListener('abort', this.#abort)
|
|
920
908
|
}
|
|
909
|
+
this.#handler.onConnect?.(abort)
|
|
921
910
|
}
|
|
922
911
|
}
|
|
923
912
|
|
|
924
|
-
|
|
925
|
-
this.#
|
|
926
|
-
this.#
|
|
927
|
-
|
|
913
|
+
onComplete(...args) {
|
|
914
|
+
this.#signal?.removeEventListener('abort', this.#abort)
|
|
915
|
+
this.#handler.onComplete?.(...args)
|
|
916
|
+
}
|
|
928
917
|
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
918
|
+
onError(...args) {
|
|
919
|
+
this.#signal?.removeEventListener('abort', this.#abort)
|
|
920
|
+
this.#handler.onError?.(...args)
|
|
932
921
|
}
|
|
922
|
+
}
|
|
933
923
|
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
for (const line of lines) {
|
|
944
|
-
if (this.#state === 0) {
|
|
945
|
-
if (line.endsWith('[')) {
|
|
946
|
-
this.#state = 1
|
|
947
|
-
} else {
|
|
948
|
-
throw new Error('unexpected data')
|
|
949
|
-
}
|
|
950
|
-
} else if (this.#state === 1) {
|
|
951
|
-
if (line.startsWith(']')) {
|
|
952
|
-
this.#state = 2
|
|
953
|
-
} else {
|
|
954
|
-
rows.push(JSON.parse(line.slice(0, line.lastIndexOf('}') + 1)))
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
}
|
|
924
|
+
class ErrorHandler extends undici.DecoratorHandler {
|
|
925
|
+
#str
|
|
926
|
+
#decoder
|
|
927
|
+
#statusCode
|
|
928
|
+
#statusText
|
|
929
|
+
#contentType
|
|
930
|
+
#opts
|
|
931
|
+
#handler
|
|
932
|
+
#error
|
|
958
933
|
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
}
|
|
934
|
+
constructor({ opts, handler }) {
|
|
935
|
+
super(handler)
|
|
962
936
|
|
|
963
|
-
|
|
937
|
+
this.#handler = handler
|
|
938
|
+
this.#opts = opts
|
|
939
|
+
}
|
|
964
940
|
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
941
|
+
onConnect(...args) {
|
|
942
|
+
this.#str = ''
|
|
943
|
+
this.#decoder = null
|
|
944
|
+
this.#handler.onConnect?.(...args)
|
|
945
|
+
}
|
|
968
946
|
|
|
969
|
-
|
|
947
|
+
onHeaders(statusCode, headers, resume, statusText) {
|
|
948
|
+
this.#statusCode = statusCode
|
|
949
|
+
this.#statusText = statusText
|
|
950
|
+
|
|
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)
|
|
970
955
|
|
|
971
|
-
|
|
956
|
+
this.#contentType = parseContentType(headers['content-type'] ?? '')?.type
|
|
957
|
+
if (this.#contentType !== 'application/json' && this.#contentType !== 'plain/text') {
|
|
958
|
+
throw this.#error
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
this.#decoder = new TextDecoder()
|
|
962
|
+
} else {
|
|
963
|
+
return this.#handler.onHeaders?.(statusCode, headers, resume, statusText)
|
|
972
964
|
}
|
|
973
965
|
}
|
|
974
966
|
|
|
975
|
-
|
|
976
|
-
|
|
967
|
+
onData(data) {
|
|
968
|
+
if (this.#error) {
|
|
969
|
+
this.#str += this.#decoder?.decode(data, { stream: true }) ?? ''
|
|
970
|
+
} else {
|
|
971
|
+
return this.#handler?.onData(data)
|
|
972
|
+
}
|
|
973
|
+
}
|
|
977
974
|
|
|
978
|
-
|
|
979
|
-
|
|
975
|
+
onComplete(...args) {
|
|
976
|
+
if (this.#error) {
|
|
977
|
+
this.#str += this.#decoder?.decode(undefined, { stream: false }) ?? ''
|
|
980
978
|
|
|
981
|
-
if (this.#
|
|
982
|
-
this.onFinally(null, this.#next ? null : JSON.parse(this.#str))
|
|
983
|
-
} else {
|
|
979
|
+
if (this.#contentType === 'application/json') {
|
|
984
980
|
let body
|
|
985
981
|
let reason
|
|
986
982
|
let error
|
|
@@ -989,46 +985,167 @@ class Handler {
|
|
|
989
985
|
reason = body.reason
|
|
990
986
|
error = body.error
|
|
991
987
|
} catch {
|
|
988
|
+
body = this.#str
|
|
992
989
|
// Do nothing...
|
|
993
990
|
}
|
|
994
991
|
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
992
|
+
throw Object.assign(this.#error, {
|
|
993
|
+
reason,
|
|
994
|
+
error,
|
|
995
|
+
data: {
|
|
996
|
+
req: this.#opts,
|
|
997
|
+
res: {
|
|
998
|
+
body,
|
|
999
|
+
statusCode: this.#statusCode,
|
|
1000
|
+
},
|
|
1001
|
+
},
|
|
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,
|
|
1005
1010
|
},
|
|
1006
|
-
}
|
|
1007
|
-
)
|
|
1011
|
+
},
|
|
1012
|
+
})
|
|
1008
1013
|
}
|
|
1014
|
+
|
|
1015
|
+
assert(false)
|
|
1009
1016
|
} else {
|
|
1010
|
-
this.
|
|
1017
|
+
this.#handler.onComplete?.(...args)
|
|
1011
1018
|
}
|
|
1012
1019
|
}
|
|
1020
|
+
}
|
|
1013
1021
|
|
|
1014
|
-
|
|
1015
|
-
|
|
1022
|
+
class StreamOutput extends stream.Readable {
|
|
1023
|
+
#str
|
|
1024
|
+
#resume
|
|
1025
|
+
#abort
|
|
1026
|
+
#state
|
|
1027
|
+
#decoder
|
|
1028
|
+
#batched
|
|
1029
|
+
#didPush = false
|
|
1030
|
+
|
|
1031
|
+
constructor({ batched = false, highWaterMark = batched ? 2 : 128 }) {
|
|
1032
|
+
super({ objectMode: true, highWaterMark })
|
|
1033
|
+
this.#batched = batched
|
|
1016
1034
|
}
|
|
1017
1035
|
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
}
|
|
1036
|
+
_read() {
|
|
1037
|
+
this.#resume?.()
|
|
1038
|
+
}
|
|
1022
1039
|
|
|
1023
|
-
|
|
1024
|
-
|
|
1040
|
+
_destroy(err, callback) {
|
|
1041
|
+
this.#abort?.(err)
|
|
1042
|
+
callback(err)
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
onConnect(abort) {
|
|
1046
|
+
if (this.destroyed) {
|
|
1047
|
+
abort(this.errored)
|
|
1048
|
+
} else if (!this.#didPush) {
|
|
1049
|
+
this.#state = 0
|
|
1050
|
+
this.#str = ''
|
|
1051
|
+
this.#abort = abort
|
|
1052
|
+
this.#decoder = new TextDecoder()
|
|
1025
1053
|
} else {
|
|
1026
|
-
|
|
1054
|
+
throw new Error('unusable')
|
|
1027
1055
|
}
|
|
1028
1056
|
}
|
|
1057
|
+
|
|
1058
|
+
onHeaders(statusCode, rawHeaders, resume, statusText) {
|
|
1059
|
+
if (statusCode >= 300 || statusCode < 200) {
|
|
1060
|
+
throw new Error('invalid status code: ' + statusCode)
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
this.#resume = resume
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
onData(data) {
|
|
1067
|
+
const lines = this.#decoder.decode(data, { stream: true }).split(/\r?\n+/)
|
|
1068
|
+
lines[0] = this.#str + lines[0]
|
|
1069
|
+
this.#str = lines.pop() ?? ''
|
|
1070
|
+
|
|
1071
|
+
const rows = this.#batched ? [] : null
|
|
1072
|
+
for (const line of lines) {
|
|
1073
|
+
if (this.#state === 0) {
|
|
1074
|
+
if (line.endsWith('[')) {
|
|
1075
|
+
this.#state = 1
|
|
1076
|
+
} else {
|
|
1077
|
+
throw new Error('unexpected data')
|
|
1078
|
+
}
|
|
1079
|
+
} else if (this.#state === 1) {
|
|
1080
|
+
if (line.startsWith(']')) {
|
|
1081
|
+
this.#state = 2
|
|
1082
|
+
} else {
|
|
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
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
if (rows) {
|
|
1095
|
+
this.push(rows)
|
|
1096
|
+
this.#didPush = true
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
return this.readableLength < this.readableHighWaterMark
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
onComplete() {
|
|
1103
|
+
assert(this.#state === 2)
|
|
1104
|
+
this.push(null)
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
onError(err) {
|
|
1108
|
+
this.destroy(err)
|
|
1109
|
+
}
|
|
1029
1110
|
}
|
|
1030
1111
|
|
|
1031
|
-
|
|
1112
|
+
class PromiseOutput {
|
|
1113
|
+
#resolve
|
|
1114
|
+
#reject
|
|
1115
|
+
#str
|
|
1116
|
+
#decoder
|
|
1117
|
+
|
|
1118
|
+
constructor({ resolve, reject }) {
|
|
1119
|
+
this.#resolve = resolve
|
|
1120
|
+
this.#reject = reject
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
onConnect() {
|
|
1124
|
+
this.#decoder = new TextDecoder()
|
|
1125
|
+
this.#str = ''
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
onHeaders(statusCode, rawHeaders, resume, statusText) {
|
|
1129
|
+
if (statusCode >= 300 || statusCode < 200) {
|
|
1130
|
+
throw new Error('invalid status code: ' + statusCode)
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
onData(data) {
|
|
1135
|
+
this.#str += this.#decoder.decode(data, { stream: true })
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
onComplete() {
|
|
1139
|
+
this.#str += this.#decoder.decode(undefined, { stream: false })
|
|
1140
|
+
this.#resolve(JSON.parse(this.#str))
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
onError(err) {
|
|
1144
|
+
this.#reject(err)
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
export function request(
|
|
1032
1149
|
url,
|
|
1033
1150
|
{
|
|
1034
1151
|
body,
|
|
@@ -1037,8 +1154,8 @@ export async function request(
|
|
|
1037
1154
|
headers,
|
|
1038
1155
|
dispatcher = undici.getGlobalDispatcher(),
|
|
1039
1156
|
signal,
|
|
1157
|
+
stream,
|
|
1040
1158
|
},
|
|
1041
|
-
next,
|
|
1042
1159
|
) {
|
|
1043
1160
|
url = new URL(url)
|
|
1044
1161
|
if (url.search) {
|
|
@@ -1050,12 +1167,27 @@ export async function request(
|
|
|
1050
1167
|
method,
|
|
1051
1168
|
headers: {
|
|
1052
1169
|
'content-type': body != null && typeof body === 'object' ? 'application/json' : 'plain/text',
|
|
1170
|
+
'user-agent': globalThis.userAgent,
|
|
1171
|
+
'request-id': genReqId(),
|
|
1053
1172
|
accept: 'application/json',
|
|
1054
1173
|
...headers,
|
|
1055
1174
|
},
|
|
1056
1175
|
body: body != null && typeof body === 'object' ? JSON.stringify(body) : body,
|
|
1057
1176
|
}
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1177
|
+
|
|
1178
|
+
dispatcher = dispatcher.compose(
|
|
1179
|
+
(dispatch) => (opts, handler) => dispatch(opts, new ErrorHandler({ opts, handler })),
|
|
1180
|
+
(dispatch) => (opts, handler) => dispatch(opts, new SignalHandler({ signal, handler })),
|
|
1181
|
+
)
|
|
1182
|
+
|
|
1183
|
+
if (stream) {
|
|
1184
|
+
const handler = new StreamOutput(stream)
|
|
1185
|
+
dispatcher.dispatch(opts, handler)
|
|
1186
|
+
return handler
|
|
1187
|
+
} else {
|
|
1188
|
+
return new Promise((resolve, reject) => {
|
|
1189
|
+
const handler = new PromiseOutput({ resolve, reject })
|
|
1190
|
+
dispatcher.dispatch(opts, handler)
|
|
1191
|
+
})
|
|
1192
|
+
}
|
|
1061
1193
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nxtedition/lib",
|
|
3
|
-
"version": "19.1.
|
|
3
|
+
"version": "19.1.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Robert Nagy <robert.nagy@boffins.se>",
|
|
6
6
|
"type": "module",
|
|
@@ -75,10 +75,11 @@
|
|
|
75
75
|
"/__tests__"
|
|
76
76
|
],
|
|
77
77
|
"dependencies": {
|
|
78
|
-
"@aws-sdk/client-s3": "^3.
|
|
78
|
+
"@aws-sdk/client-s3": "^3.554.0",
|
|
79
79
|
"@elastic/elasticsearch": "^8.13.1",
|
|
80
|
-
"@elastic/transport": "^8.5.
|
|
81
|
-
"@nxtedition/nxt-undici": "^2.0.
|
|
80
|
+
"@elastic/transport": "^8.5.1",
|
|
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",
|
|
@@ -94,13 +95,13 @@
|
|
|
94
95
|
"p-queue": "^8.0.1",
|
|
95
96
|
"pino": "^8.20.0",
|
|
96
97
|
"pino-std-serializers": "^6.2.2",
|
|
97
|
-
"qs": "^6.12.
|
|
98
|
+
"qs": "^6.12.1",
|
|
98
99
|
"request-target": "^1.0.2",
|
|
99
100
|
"smpte-timecode": "^1.3.5",
|
|
100
101
|
"split-string": "^6.0.0",
|
|
101
102
|
"split2": "^4.2.0",
|
|
102
103
|
"toobusy-js": "^0.5.1",
|
|
103
|
-
"undici": "^6.
|
|
104
|
+
"undici": "^6.13.0",
|
|
104
105
|
"url-join": "^5.0.0"
|
|
105
106
|
},
|
|
106
107
|
"devDependencies": {
|
|
@@ -111,7 +112,7 @@
|
|
|
111
112
|
"eslint-config-prettier": "^9.1.0",
|
|
112
113
|
"eslint-config-standard": "^17.0.0",
|
|
113
114
|
"eslint-plugin-import": "^2.29.1",
|
|
114
|
-
"eslint-plugin-n": "^17.1
|
|
115
|
+
"eslint-plugin-n": "^17.2.1",
|
|
115
116
|
"eslint-plugin-node": "^11.1.0",
|
|
116
117
|
"eslint-plugin-promise": "^6.0.0",
|
|
117
118
|
"husky": "^9.0.11",
|