@entelligentsia/forgecli 0.7.10 → 0.8.4
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/CHANGELOG.md +74 -0
- package/dist/CHANGELOG-forge-plugin.md +70 -0
- package/dist/CHANGELOG-pi.md +63 -0
- package/dist/bin/argv.d.ts +2 -2
- package/dist/bin/argv.js +10 -0
- package/dist/bin/argv.js.map +1 -1
- package/dist/bin/env-defaults.d.ts +1 -0
- package/dist/bin/env-defaults.js +13 -0
- package/dist/bin/env-defaults.js.map +1 -0
- package/dist/bin/forge.js +9 -0
- package/dist/bin/forge.js.map +1 -1
- package/dist/bin/update-cli.d.ts +9 -0
- package/dist/bin/update-cli.js +120 -0
- package/dist/bin/update-cli.js.map +1 -0
- package/dist/extensions/forgecli/index.js +3 -3
- package/dist/extensions/forgecli/index.js.map +1 -1
- package/dist/extensions/forgecli/update-check.js +1 -1
- package/dist/extensions/forgecli/update-check.js.map +1 -1
- package/dist/extensions/forgecli/whats-new-widget.d.ts +5 -5
- package/dist/extensions/forgecli/whats-new-widget.js +11 -11
- package/dist/extensions/forgecli/whats-new-widget.js.map +1 -1
- package/dist/extensions/forgecli/whats-new.js +6 -5
- package/dist/extensions/forgecli/whats-new.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts +27 -98
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.js +62 -132
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.js +25 -15
- package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.js +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.js +17 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.js +8 -2
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses.js +17 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-responses.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/simple-options.js +8 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/simple-options.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/package.json +2 -2
- package/node_modules/@earendil-works/pi-coding-agent/CHANGELOG.md +63 -0
- package/node_modules/@earendil-works/pi-coding-agent/README.md +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/cli/config-selector.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/cli/config-selector.js +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/cli/config-selector.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/cli.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/cli.js +6 -10
- package/node_modules/@earendil-works/pi-coding-agent/dist/cli.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/config.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/config.js +12 -3
- package/node_modules/@earendil-works/pi-coding-agent/dist/config.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.d.ts +1 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js +30 -15
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/compaction.d.ts +3 -3
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/compaction.js +23 -13
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.d.ts +4 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.js +58 -38
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/slash-commands.js +0 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/system-prompt.js +3 -2
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/config-selector.d.ts +2 -2
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/config-selector.js +7 -4
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/config-selector.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js +6 -2
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/package-manager-cli.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/package-manager-cli.js +3 -4
- package/node_modules/@earendil-works/pi-coding-agent/dist/package-manager-cli.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/utils/changelog.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/utils/changelog.js +2 -2
- package/node_modules/@earendil-works/pi-coding-agent/dist/utils/changelog.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/utils/child-process.d.ts +7 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/utils/child-process.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/utils/child-process.js +60 -7
- package/node_modules/@earendil-works/pi-coding-agent/dist/utils/child-process.js.map +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/docs/packages.md +2 -2
- package/node_modules/@earendil-works/pi-coding-agent/docs/settings.md +1 -3
- package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/sandbox/package.json +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/with-deps/package.json +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/package.json +6 -6
- package/node_modules/@earendil-works/pi-tui/package.json +2 -2
- package/node_modules/@protobufjs/fetch/CHANGELOG.md +8 -0
- package/node_modules/@protobufjs/fetch/index.d.ts +7 -7
- package/node_modules/@protobufjs/fetch/index.js +4 -7
- package/node_modules/@protobufjs/fetch/package.json +7 -5
- package/node_modules/@protobufjs/fetch/tests/data/file.txt +1 -0
- package/node_modules/@protobufjs/fetch/tests/index.js +150 -8
- package/node_modules/@protobufjs/fetch/util/fs.js +11 -0
- package/node_modules/@protobufjs/inquire/CHANGELOG.md +8 -0
- package/node_modules/@protobufjs/inquire/index.d.ts +1 -0
- package/node_modules/@protobufjs/inquire/index.js +1 -0
- package/node_modules/@protobufjs/inquire/package.json +1 -1
- package/node_modules/protobufjs/dist/light/protobuf.js +187 -153
- package/node_modules/protobufjs/dist/light/protobuf.js.map +1 -1
- package/node_modules/protobufjs/dist/light/protobuf.min.js +3 -3
- package/node_modules/protobufjs/dist/light/protobuf.min.js.map +1 -1
- package/node_modules/protobufjs/dist/minimal/protobuf.js +14 -5
- package/node_modules/protobufjs/dist/minimal/protobuf.js.map +1 -1
- package/node_modules/protobufjs/dist/minimal/protobuf.min.js +3 -3
- package/node_modules/protobufjs/dist/minimal/protobuf.min.js.map +1 -1
- package/node_modules/protobufjs/dist/protobuf.js +207 -173
- package/node_modules/protobufjs/dist/protobuf.js.map +1 -1
- package/node_modules/protobufjs/dist/protobuf.min.js +3 -3
- package/node_modules/protobufjs/dist/protobuf.min.js.map +1 -1
- package/node_modules/protobufjs/package.json +6 -3
- package/node_modules/protobufjs/src/util/fs.js +11 -0
- package/node_modules/protobufjs/src/util/minimal.js +10 -2
- package/node_modules/protobufjs/src/util.js +1 -1
- package/node_modules/undici/README.md +14 -5
- package/node_modules/undici/docs/docs/api/Client.md +4 -2
- package/node_modules/undici/docs/docs/api/Dispatcher.md +62 -27
- package/node_modules/undici/docs/docs/api/GlobalInstallation.md +7 -5
- package/node_modules/undici/docs/docs/api/H2CClient.md +1 -1
- package/node_modules/undici/docs/docs/api/RedirectHandler.md +14 -9
- package/node_modules/undici/docs/docs/api/RetryAgent.md +0 -1
- package/node_modules/undici/docs/docs/api/RetryHandler.md +12 -14
- package/node_modules/undici/docs/docs/api/SnapshotAgent.md +23 -0
- package/node_modules/undici/docs/docs/best-practices/migrating-from-v7-to-v8.md +231 -0
- package/node_modules/undici/index.js +4 -2
- package/node_modules/undici/lib/api/api-connect.js +13 -11
- package/node_modules/undici/lib/api/api-pipeline.js +26 -13
- package/node_modules/undici/lib/api/api-request.js +45 -21
- package/node_modules/undici/lib/api/api-stream.js +81 -20
- package/node_modules/undici/lib/api/api-upgrade.js +21 -11
- package/node_modules/undici/lib/api/readable.js +3 -2
- package/node_modules/undici/lib/cache/memory-cache-store.js +1 -1
- package/node_modules/undici/lib/cache/sqlite-cache-store.js +6 -4
- package/node_modules/undici/lib/core/connect.js +17 -1
- package/node_modules/undici/lib/core/constants.js +1 -24
- package/node_modules/undici/lib/core/errors.js +2 -2
- package/node_modules/undici/lib/core/request.js +115 -18
- package/node_modules/undici/lib/core/socks5-client.js +24 -9
- package/node_modules/undici/lib/core/socks5-utils.js +32 -23
- package/node_modules/undici/lib/core/symbols.js +1 -0
- package/node_modules/undici/lib/core/util.js +70 -43
- package/node_modules/undici/lib/dispatcher/agent.js +47 -33
- package/node_modules/undici/lib/dispatcher/balanced-pool.js +21 -26
- package/node_modules/undici/lib/dispatcher/client-h1.js +98 -39
- package/node_modules/undici/lib/dispatcher/client-h2.js +603 -272
- package/node_modules/undici/lib/dispatcher/client.js +12 -5
- package/node_modules/undici/lib/dispatcher/dispatcher-base.js +24 -5
- package/node_modules/undici/lib/dispatcher/dispatcher.js +0 -4
- package/node_modules/undici/lib/dispatcher/dispatcher1-wrapper.js +107 -0
- package/node_modules/undici/lib/dispatcher/h2c-client.js +5 -5
- package/node_modules/undici/lib/dispatcher/pool-base.js +28 -10
- package/node_modules/undici/lib/dispatcher/pool.js +31 -6
- package/node_modules/undici/lib/dispatcher/proxy-agent.js +38 -13
- package/node_modules/undici/lib/dispatcher/round-robin-pool.js +31 -9
- package/node_modules/undici/lib/dispatcher/socks5-proxy-agent.js +95 -80
- package/node_modules/undici/lib/global.js +13 -1
- package/node_modules/undici/lib/handler/cache-handler.js +16 -8
- package/node_modules/undici/lib/handler/decorator-handler.js +1 -2
- package/node_modules/undici/lib/handler/redirect-handler.js +5 -51
- package/node_modules/undici/lib/handler/retry-handler.js +15 -2
- package/node_modules/undici/lib/interceptor/cache.js +30 -17
- package/node_modules/undici/lib/interceptor/decompress.js +28 -2
- package/node_modules/undici/lib/interceptor/dns.js +1 -1
- package/node_modules/undici/lib/interceptor/redirect.js +3 -3
- package/node_modules/undici/lib/llhttp/llhttp-wasm.js +1 -1
- package/node_modules/undici/lib/llhttp/llhttp_simd-wasm.js +1 -1
- package/node_modules/undici/lib/mock/mock-agent.js +8 -8
- package/node_modules/undici/lib/mock/mock-call-history.js +15 -15
- package/node_modules/undici/lib/mock/mock-utils.js +37 -22
- package/node_modules/undici/lib/mock/snapshot-agent.js +16 -6
- package/node_modules/undici/lib/mock/snapshot-recorder.js +38 -3
- package/node_modules/undici/lib/util/cache.js +8 -7
- package/node_modules/undici/lib/util/runtime-features.js +3 -34
- package/node_modules/undici/lib/web/cache/cache.js +6 -8
- package/node_modules/undici/lib/web/eventsource/eventsource-stream.js +245 -150
- package/node_modules/undici/lib/web/fetch/body.js +3 -9
- package/node_modules/undici/lib/web/fetch/formdata-parser.js +17 -6
- package/node_modules/undici/lib/web/fetch/formdata.js +21 -2
- package/node_modules/undici/lib/web/fetch/index.js +214 -221
- package/node_modules/undici/lib/web/webidl/index.js +7 -9
- package/node_modules/undici/lib/web/websocket/frame.js +1 -7
- package/node_modules/undici/lib/web/websocket/permessage-deflate.js +13 -31
- package/node_modules/undici/lib/web/websocket/receiver.js +62 -22
- package/node_modules/undici/lib/web/websocket/stream/websocketstream.js +11 -17
- package/node_modules/undici/lib/web/websocket/websocket.js +6 -1
- package/node_modules/undici/package.json +9 -9
- package/node_modules/undici/types/agent.d.ts +0 -2
- package/node_modules/undici/types/client.d.ts +25 -19
- package/node_modules/undici/types/dispatcher.d.ts +7 -27
- package/node_modules/undici/types/dispatcher1-wrapper.d.ts +7 -0
- package/node_modules/undici/types/formdata.d.ts +0 -6
- package/node_modules/undici/types/h2c-client.d.ts +6 -6
- package/node_modules/undici/types/header.d.ts +5 -0
- package/node_modules/undici/types/index.d.ts +3 -1
- package/node_modules/undici/types/interceptors.d.ts +1 -1
- package/node_modules/undici/types/pool.d.ts +0 -2
- package/node_modules/undici/types/proxy-agent.d.ts +2 -2
- package/node_modules/undici/types/round-robin-pool.d.ts +0 -2
- package/node_modules/undici/types/snapshot-agent.d.ts +4 -0
- package/node_modules/undici/types/socks5-proxy-agent.d.ts +2 -2
- package/node_modules/undici/types/webidl.d.ts +0 -1
- package/package.json +7 -8
- package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/custom-provider-anthropic/package-lock.json +0 -24
- package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/sandbox/package-lock.json +0 -92
- package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/with-deps/package-lock.json +0 -31
- package/node_modules/undici/lib/handler/unwrap-handler.js +0 -100
- package/node_modules/undici/lib/handler/wrap-handler.js +0 -105
- package/node_modules/undici/lib/llhttp/.gitkeep +0 -0
- package/node_modules/undici/lib/util/promise.js +0 -28
- package/skills/refresh-kb-links/SKILL.md +0 -217
- package/skills/store-custodian/SKILL.md +0 -163
- package/skills/store-query-grammar/SKILL.md +0 -145
- package/skills/store-query-nlp/SKILL.md +0 -110
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const assert = require('node:assert')
|
|
4
|
-
const { finished } = require('node:stream')
|
|
5
4
|
const { AsyncResource } = require('node:async_hooks')
|
|
6
5
|
const { InvalidArgumentError, InvalidReturnValueError } = require('../core/errors')
|
|
7
6
|
const util = require('../core/util')
|
|
@@ -9,6 +8,54 @@ const { addSignal, removeSignal } = require('./abort-signal')
|
|
|
9
8
|
|
|
10
9
|
function noop () {}
|
|
11
10
|
|
|
11
|
+
function getWritableError (stream) {
|
|
12
|
+
return stream.errored ?? stream.writableErrored ?? stream._writableState?.errored
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function createPrematureCloseError () {
|
|
16
|
+
const err = new Error('Premature close')
|
|
17
|
+
err.code = 'ERR_STREAM_PREMATURE_CLOSE'
|
|
18
|
+
return err
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function trackWritableLifecycle (stream, callback) {
|
|
22
|
+
let done = false
|
|
23
|
+
|
|
24
|
+
const cleanup = () => {
|
|
25
|
+
stream.removeListener('close', onClose)
|
|
26
|
+
stream.removeListener('error', onError)
|
|
27
|
+
stream.removeListener('finish', onFinish)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const finish = (err, fromErrorEvent = false) => {
|
|
31
|
+
if (done) {
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
done = true
|
|
36
|
+
cleanup()
|
|
37
|
+
callback(err, fromErrorEvent)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const onClose = () => {
|
|
41
|
+
const err = getWritableError(stream)
|
|
42
|
+
finish(err ?? (!stream.writableFinished ? createPrematureCloseError() : undefined))
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const onError = (err) => finish(err, true)
|
|
46
|
+
const onFinish = () => finish()
|
|
47
|
+
|
|
48
|
+
stream.on('close', onClose)
|
|
49
|
+
stream.on('error', onError)
|
|
50
|
+
stream.on('finish', onFinish)
|
|
51
|
+
|
|
52
|
+
if (stream.closed) {
|
|
53
|
+
process.nextTick(onClose)
|
|
54
|
+
} else if (stream.writableFinished) {
|
|
55
|
+
process.nextTick(onFinish)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
12
59
|
class StreamHandler extends AsyncResource {
|
|
13
60
|
constructor (opts, factory, callback) {
|
|
14
61
|
if (!opts || typeof opts !== 'object') {
|
|
@@ -53,39 +100,44 @@ class StreamHandler extends AsyncResource {
|
|
|
53
100
|
this.res = null
|
|
54
101
|
this.abort = null
|
|
55
102
|
this.context = null
|
|
103
|
+
this.controller = null
|
|
56
104
|
this.trailers = null
|
|
57
105
|
this.body = body
|
|
58
106
|
this.onInfo = onInfo || null
|
|
59
107
|
|
|
60
108
|
if (util.isStream(body)) {
|
|
61
109
|
body.on('error', (err) => {
|
|
62
|
-
this.
|
|
110
|
+
this.onResponseError(this.controller, err)
|
|
63
111
|
})
|
|
64
112
|
}
|
|
65
113
|
|
|
66
114
|
addSignal(this, signal)
|
|
67
115
|
}
|
|
68
116
|
|
|
69
|
-
|
|
117
|
+
onRequestStart (controller, context) {
|
|
70
118
|
if (this.reason) {
|
|
71
|
-
abort(this.reason)
|
|
119
|
+
controller.abort(this.reason)
|
|
72
120
|
return
|
|
73
121
|
}
|
|
74
122
|
|
|
75
123
|
assert(this.callback)
|
|
76
124
|
|
|
77
|
-
this.
|
|
125
|
+
this.controller = controller
|
|
126
|
+
this.abort = (reason) => controller.abort(reason)
|
|
78
127
|
this.context = context
|
|
79
128
|
}
|
|
80
129
|
|
|
81
|
-
|
|
130
|
+
onResponseStart (controller, statusCode, headers, _statusMessage) {
|
|
82
131
|
const { factory, opaque, context, responseHeaders } = this
|
|
83
132
|
|
|
84
|
-
const
|
|
133
|
+
const rawHeaders = controller?.rawHeaders
|
|
134
|
+
const responseHeaderData = responseHeaders === 'raw'
|
|
135
|
+
? util.parseRawHeaders(rawHeaders)
|
|
136
|
+
: headers
|
|
85
137
|
|
|
86
138
|
if (statusCode < 200) {
|
|
87
139
|
if (this.onInfo) {
|
|
88
|
-
this.onInfo({ statusCode, headers })
|
|
140
|
+
this.onInfo({ statusCode, headers: responseHeaderData })
|
|
89
141
|
}
|
|
90
142
|
return
|
|
91
143
|
}
|
|
@@ -98,7 +150,7 @@ class StreamHandler extends AsyncResource {
|
|
|
98
150
|
|
|
99
151
|
const res = this.runInAsyncScope(factory, null, {
|
|
100
152
|
statusCode,
|
|
101
|
-
headers,
|
|
153
|
+
headers: responseHeaderData,
|
|
102
154
|
opaque,
|
|
103
155
|
context
|
|
104
156
|
})
|
|
@@ -112,24 +164,23 @@ class StreamHandler extends AsyncResource {
|
|
|
112
164
|
throw new InvalidReturnValueError('expected Writable')
|
|
113
165
|
}
|
|
114
166
|
|
|
115
|
-
|
|
116
|
-
finished(res, { readable: false }, (err) => {
|
|
167
|
+
trackWritableLifecycle(res, (err, fromErrorEvent) => {
|
|
117
168
|
const { callback, res, opaque, trailers, abort } = this
|
|
118
169
|
|
|
119
170
|
this.res = null
|
|
120
171
|
if (err || !res?.readable) {
|
|
121
|
-
util.destroy(res, err)
|
|
172
|
+
util.destroy(res, fromErrorEvent ? undefined : err)
|
|
122
173
|
}
|
|
123
174
|
|
|
124
175
|
this.callback = null
|
|
125
176
|
this.runInAsyncScope(callback, null, err || null, { opaque, trailers })
|
|
126
177
|
|
|
127
178
|
if (err) {
|
|
128
|
-
abort()
|
|
179
|
+
abort(err)
|
|
129
180
|
}
|
|
130
181
|
})
|
|
131
182
|
|
|
132
|
-
res.on('drain', resume)
|
|
183
|
+
res.on('drain', () => controller.resume())
|
|
133
184
|
|
|
134
185
|
this.res = res
|
|
135
186
|
|
|
@@ -137,16 +188,24 @@ class StreamHandler extends AsyncResource {
|
|
|
137
188
|
? res.writableNeedDrain
|
|
138
189
|
: res._writableState?.needDrain
|
|
139
190
|
|
|
140
|
-
|
|
191
|
+
if (needDrain === true) {
|
|
192
|
+
controller.pause()
|
|
193
|
+
}
|
|
141
194
|
}
|
|
142
195
|
|
|
143
|
-
|
|
196
|
+
onResponseData (controller, chunk) {
|
|
144
197
|
const { res } = this
|
|
145
198
|
|
|
146
|
-
|
|
199
|
+
if (!res) {
|
|
200
|
+
return
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (res.write(chunk) === false) {
|
|
204
|
+
controller.pause()
|
|
205
|
+
}
|
|
147
206
|
}
|
|
148
207
|
|
|
149
|
-
|
|
208
|
+
onResponseEnd (_controller, trailers) {
|
|
150
209
|
const { res } = this
|
|
151
210
|
|
|
152
211
|
removeSignal(this)
|
|
@@ -155,12 +214,14 @@ class StreamHandler extends AsyncResource {
|
|
|
155
214
|
return
|
|
156
215
|
}
|
|
157
216
|
|
|
158
|
-
|
|
217
|
+
if (trailers && typeof trailers === 'object') {
|
|
218
|
+
this.trailers = trailers
|
|
219
|
+
}
|
|
159
220
|
|
|
160
221
|
res.end()
|
|
161
222
|
}
|
|
162
223
|
|
|
163
|
-
|
|
224
|
+
onResponseError (_controller, err) {
|
|
164
225
|
const { res, callback, opaque, body } = this
|
|
165
226
|
|
|
166
227
|
removeSignal(this)
|
|
@@ -34,40 +34,51 @@ class UpgradeHandler extends AsyncResource {
|
|
|
34
34
|
addSignal(this, signal)
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
onRequestStart (controller, context) {
|
|
38
38
|
if (this.reason) {
|
|
39
|
-
abort(this.reason)
|
|
39
|
+
controller.abort(this.reason)
|
|
40
40
|
return
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
assert(this.callback)
|
|
44
44
|
|
|
45
|
-
this.abort = abort
|
|
46
|
-
this.context =
|
|
45
|
+
this.abort = (reason) => controller.abort(reason)
|
|
46
|
+
this.context = context
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
onResponseStart () {
|
|
50
50
|
throw new SocketError('bad upgrade', null)
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
onRequestUpgrade (controller, statusCode, headers, socket) {
|
|
54
|
+
const expectedStatusCode = socket[kHTTP2Stream] === true ? 200 : 101
|
|
55
|
+
|
|
56
|
+
if (statusCode !== expectedStatusCode) {
|
|
57
|
+
const socketInfo = socket[kHTTP2Stream] === true ? null : util.getSocketInfo(socket)
|
|
58
|
+
controller.abort(new SocketError('bad upgrade', socketInfo))
|
|
59
|
+
return
|
|
60
|
+
}
|
|
55
61
|
|
|
56
62
|
const { callback, opaque, context } = this
|
|
57
63
|
|
|
58
64
|
removeSignal(this)
|
|
59
65
|
|
|
60
66
|
this.callback = null
|
|
61
|
-
|
|
67
|
+
|
|
68
|
+
const rawHeaders = controller?.rawHeaders
|
|
69
|
+
const responseHeaders = this.responseHeaders === 'raw'
|
|
70
|
+
? util.parseRawHeaders(rawHeaders)
|
|
71
|
+
: headers
|
|
72
|
+
|
|
62
73
|
this.runInAsyncScope(callback, null, null, {
|
|
63
|
-
headers,
|
|
74
|
+
headers: responseHeaders,
|
|
64
75
|
socket,
|
|
65
76
|
opaque,
|
|
66
77
|
context
|
|
67
78
|
})
|
|
68
79
|
}
|
|
69
80
|
|
|
70
|
-
|
|
81
|
+
onResponseError (_controller, err) {
|
|
71
82
|
const { callback, opaque } = this
|
|
72
83
|
|
|
73
84
|
removeSignal(this)
|
|
@@ -97,7 +108,6 @@ function upgrade (opts, callback) {
|
|
|
97
108
|
method: opts.method || 'GET',
|
|
98
109
|
upgrade: opts.protocol || 'Websocket'
|
|
99
110
|
}
|
|
100
|
-
|
|
101
111
|
this.dispatch(upgradeOpts, upgradeHandler)
|
|
102
112
|
} catch (err) {
|
|
103
113
|
if (typeof callback !== 'function') {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const assert = require('node:assert')
|
|
4
|
+
const { addAbortListener } = require('node:events')
|
|
4
5
|
const { Readable } = require('node:stream')
|
|
5
6
|
const { RequestAbortedError, NotSupportedError, InvalidArgumentError, AbortError } = require('../core/errors')
|
|
6
7
|
const util = require('../core/util')
|
|
@@ -293,10 +294,10 @@ class BodyReadable extends Readable {
|
|
|
293
294
|
const onAbort = () => {
|
|
294
295
|
this.destroy(signal.reason ?? new AbortError())
|
|
295
296
|
}
|
|
296
|
-
signal
|
|
297
|
+
const abortListener = addAbortListener(signal, onAbort)
|
|
297
298
|
this
|
|
298
299
|
.on('close', function () {
|
|
299
|
-
|
|
300
|
+
abortListener[Symbol.dispose]()
|
|
300
301
|
if (signal.aborted) {
|
|
301
302
|
reject(signal.reason ?? new AbortError())
|
|
302
303
|
} else {
|
|
@@ -173,6 +173,7 @@ module.exports = class SqliteCacheStore {
|
|
|
173
173
|
headers = ?,
|
|
174
174
|
etag = ?,
|
|
175
175
|
cacheControlDirectives = ?,
|
|
176
|
+
vary = ?,
|
|
176
177
|
cachedAt = ?,
|
|
177
178
|
staleAt = ?
|
|
178
179
|
WHERE
|
|
@@ -216,7 +217,7 @@ module.exports = class SqliteCacheStore {
|
|
|
216
217
|
SELECT
|
|
217
218
|
id
|
|
218
219
|
FROM cacheInterceptorV${VERSION}
|
|
219
|
-
ORDER BY cachedAt
|
|
220
|
+
ORDER BY cachedAt ASC
|
|
220
221
|
LIMIT ?
|
|
221
222
|
)
|
|
222
223
|
`)
|
|
@@ -278,12 +279,12 @@ module.exports = class SqliteCacheStore {
|
|
|
278
279
|
value.headers ? JSON.stringify(value.headers) : null,
|
|
279
280
|
value.etag ? value.etag : null,
|
|
280
281
|
value.cacheControlDirectives ? JSON.stringify(value.cacheControlDirectives) : null,
|
|
282
|
+
value.vary ? JSON.stringify(value.vary) : null,
|
|
281
283
|
value.cachedAt,
|
|
282
284
|
value.staleAt,
|
|
283
285
|
existingValue.id
|
|
284
286
|
)
|
|
285
287
|
} else {
|
|
286
|
-
this.#prune()
|
|
287
288
|
// New response, let's insert it
|
|
288
289
|
this.#insertValueQuery.run(
|
|
289
290
|
url,
|
|
@@ -299,6 +300,7 @@ module.exports = class SqliteCacheStore {
|
|
|
299
300
|
value.cachedAt,
|
|
300
301
|
value.staleAt
|
|
301
302
|
)
|
|
303
|
+
this.#prune()
|
|
302
304
|
}
|
|
303
305
|
}
|
|
304
306
|
|
|
@@ -323,7 +325,7 @@ module.exports = class SqliteCacheStore {
|
|
|
323
325
|
write (chunk, encoding, callback) {
|
|
324
326
|
size += chunk.byteLength
|
|
325
327
|
|
|
326
|
-
if (size
|
|
328
|
+
if (size <= store.#maxEntrySize) {
|
|
327
329
|
body.push(chunk)
|
|
328
330
|
} else {
|
|
329
331
|
this.destroy()
|
|
@@ -409,7 +411,7 @@ module.exports = class SqliteCacheStore {
|
|
|
409
411
|
const now = Date.now()
|
|
410
412
|
for (const value of values) {
|
|
411
413
|
if (now >= value.deleteAt && !canBeExpired) {
|
|
412
|
-
|
|
414
|
+
continue
|
|
413
415
|
}
|
|
414
416
|
|
|
415
417
|
let matches = true
|
|
@@ -38,6 +38,22 @@ const SessionCache = class WeakSessionCache {
|
|
|
38
38
|
return
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
if (this._sessionCache.has(sessionKey)) {
|
|
42
|
+
this._sessionCache.delete(sessionKey)
|
|
43
|
+
} else if (this._sessionCache.size >= this._maxCachedSessions) {
|
|
44
|
+
for (const [key, ref] of this._sessionCache) {
|
|
45
|
+
if (ref.deref() === undefined) {
|
|
46
|
+
this._sessionCache.delete(key)
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const oldest = this._sessionCache.keys().next()
|
|
52
|
+
if (!oldest.done) {
|
|
53
|
+
this._sessionCache.delete(oldest.value)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
41
57
|
this._sessionCache.set(sessionKey, new WeakRef(session))
|
|
42
58
|
this._sessionRegistry.register(session, sessionKey)
|
|
43
59
|
}
|
|
@@ -51,7 +67,7 @@ function buildConnector ({ allowH2, useH2c, maxCachedSessions, socketPath, timeo
|
|
|
51
67
|
const options = { path: socketPath, ...opts }
|
|
52
68
|
const sessionCache = new SessionCache(maxCachedSessions == null ? 100 : maxCachedSessions)
|
|
53
69
|
timeout = timeout == null ? 10e3 : timeout
|
|
54
|
-
allowH2 = allowH2 != null ? allowH2 :
|
|
70
|
+
allowH2 = allowH2 != null ? allowH2 : true
|
|
55
71
|
return function connect ({ hostname, host, protocol, port, servername, localAddress, httpSocket }, callback) {
|
|
56
72
|
let socket
|
|
57
73
|
if (protocol === 'https:') {
|
|
@@ -107,28 +107,6 @@ const headerNameLowerCasedRecord = {}
|
|
|
107
107
|
// Note: object prototypes should not be able to be referenced. e.g. `Object#hasOwnProperty`.
|
|
108
108
|
Object.setPrototypeOf(headerNameLowerCasedRecord, null)
|
|
109
109
|
|
|
110
|
-
/**
|
|
111
|
-
* @type {Record<Lowercase<typeof wellknownHeaderNames[number]>, Buffer>}
|
|
112
|
-
*/
|
|
113
|
-
const wellknownHeaderNameBuffers = {}
|
|
114
|
-
|
|
115
|
-
// Note: object prototypes should not be able to be referenced. e.g. `Object#hasOwnProperty`.
|
|
116
|
-
Object.setPrototypeOf(wellknownHeaderNameBuffers, null)
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* @param {string} header Lowercased header
|
|
120
|
-
* @returns {Buffer}
|
|
121
|
-
*/
|
|
122
|
-
function getHeaderNameAsBuffer (header) {
|
|
123
|
-
let buffer = wellknownHeaderNameBuffers[header]
|
|
124
|
-
|
|
125
|
-
if (buffer === undefined) {
|
|
126
|
-
buffer = Buffer.from(header)
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return buffer
|
|
130
|
-
}
|
|
131
|
-
|
|
132
110
|
for (let i = 0; i < wellknownHeaderNames.length; ++i) {
|
|
133
111
|
const key = wellknownHeaderNames[i]
|
|
134
112
|
const lowerCasedKey = key.toLowerCase()
|
|
@@ -138,6 +116,5 @@ for (let i = 0; i < wellknownHeaderNames.length; ++i) {
|
|
|
138
116
|
|
|
139
117
|
module.exports = {
|
|
140
118
|
wellknownHeaderNames,
|
|
141
|
-
headerNameLowerCasedRecord
|
|
142
|
-
getHeaderNameAsBuffer
|
|
119
|
+
headerNameLowerCasedRecord
|
|
143
120
|
}
|
|
@@ -163,8 +163,8 @@ class RequestAbortedError extends AbortError {
|
|
|
163
163
|
|
|
164
164
|
const kInformationalError = Symbol.for('undici.error.UND_ERR_INFO')
|
|
165
165
|
class InformationalError extends UndiciError {
|
|
166
|
-
constructor (message) {
|
|
167
|
-
super(message)
|
|
166
|
+
constructor (message, options) {
|
|
167
|
+
super(message, options)
|
|
168
168
|
this.name = 'InformationalError'
|
|
169
169
|
this.message = message || 'Request information'
|
|
170
170
|
this.code = 'UND_ERR_INFO'
|
|
@@ -16,6 +16,7 @@ const {
|
|
|
16
16
|
hasSafeIterator,
|
|
17
17
|
isBlobLike,
|
|
18
18
|
serializePathWithQuery,
|
|
19
|
+
parseHeaders,
|
|
19
20
|
assertRequestHandler,
|
|
20
21
|
getServerName,
|
|
21
22
|
normalizedMethodRecords,
|
|
@@ -27,7 +28,71 @@ const { headerNameLowerCasedRecord } = require('./constants')
|
|
|
27
28
|
// Verifies that a given path is valid does not contain control chars \x00 to \x20
|
|
28
29
|
const invalidPathRegex = /[^\u0021-\u00ff]/
|
|
29
30
|
|
|
31
|
+
function isValidContentLengthHeaderValue (val) {
|
|
32
|
+
if (typeof val !== 'string' || val.length === 0) {
|
|
33
|
+
return false
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
for (let i = 0; i < val.length; i++) {
|
|
37
|
+
const charCode = val.charCodeAt(i)
|
|
38
|
+
if (charCode < 48 || charCode > 57) {
|
|
39
|
+
return false
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return true
|
|
44
|
+
}
|
|
45
|
+
|
|
30
46
|
const kHandler = Symbol('handler')
|
|
47
|
+
const kController = Symbol('controller')
|
|
48
|
+
const kResume = Symbol('resume')
|
|
49
|
+
|
|
50
|
+
class RequestController {
|
|
51
|
+
#paused = false
|
|
52
|
+
#reason = null
|
|
53
|
+
#aborted = false
|
|
54
|
+
#abort
|
|
55
|
+
|
|
56
|
+
[kResume] = null
|
|
57
|
+
|
|
58
|
+
rawHeaders = null
|
|
59
|
+
rawTrailers = null
|
|
60
|
+
|
|
61
|
+
constructor (abort) {
|
|
62
|
+
this.#abort = abort
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
pause () {
|
|
66
|
+
this.#paused = true
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
resume () {
|
|
70
|
+
if (this.#paused) {
|
|
71
|
+
this.#paused = false
|
|
72
|
+
this[kResume]?.()
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
abort (reason) {
|
|
77
|
+
if (!this.#aborted) {
|
|
78
|
+
this.#aborted = true
|
|
79
|
+
this.#reason = reason
|
|
80
|
+
this.#abort(reason)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
get aborted () {
|
|
85
|
+
return this.#aborted
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
get reason () {
|
|
89
|
+
return this.#reason
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
get paused () {
|
|
93
|
+
return this.#paused
|
|
94
|
+
}
|
|
95
|
+
}
|
|
31
96
|
|
|
32
97
|
class Request {
|
|
33
98
|
constructor (origin, {
|
|
@@ -241,23 +306,26 @@ class Request {
|
|
|
241
306
|
}
|
|
242
307
|
}
|
|
243
308
|
|
|
244
|
-
|
|
309
|
+
onRequestStart (abort, context) {
|
|
245
310
|
assert(!this.aborted)
|
|
246
311
|
assert(!this.completed)
|
|
247
312
|
|
|
313
|
+
this[kController] = new RequestController(abort)
|
|
314
|
+
|
|
248
315
|
if (this.error) {
|
|
249
|
-
abort(this.error)
|
|
250
|
-
|
|
251
|
-
this.abort = abort
|
|
252
|
-
return this[kHandler].onConnect(abort)
|
|
316
|
+
this[kController].abort(this.error)
|
|
317
|
+
return
|
|
253
318
|
}
|
|
319
|
+
|
|
320
|
+
this.abort = abort
|
|
321
|
+
return this[kHandler].onRequestStart(this[kController], context)
|
|
254
322
|
}
|
|
255
323
|
|
|
256
324
|
onResponseStarted () {
|
|
257
325
|
return this[kHandler].onResponseStarted?.()
|
|
258
326
|
}
|
|
259
327
|
|
|
260
|
-
|
|
328
|
+
onResponseStart (statusCode, headers, resume, statusText) {
|
|
261
329
|
assert(!this.aborted)
|
|
262
330
|
assert(!this.completed)
|
|
263
331
|
|
|
@@ -265,36 +333,56 @@ class Request {
|
|
|
265
333
|
channels.headers.publish({ request: this, response: { statusCode, headers, statusText } })
|
|
266
334
|
}
|
|
267
335
|
|
|
336
|
+
const controller = this[kController]
|
|
337
|
+
if (controller) {
|
|
338
|
+
controller[kResume] = resume
|
|
339
|
+
controller.rawHeaders = headers
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const parsedHeaders = Array.isArray(headers) ? parseHeaders(headers) : headers
|
|
343
|
+
|
|
268
344
|
try {
|
|
269
|
-
|
|
345
|
+
this[kHandler].onResponseStart?.(controller, statusCode, parsedHeaders, statusText)
|
|
346
|
+
return !controller?.paused
|
|
270
347
|
} catch (err) {
|
|
271
348
|
this.abort(err)
|
|
349
|
+
return false
|
|
272
350
|
}
|
|
273
351
|
}
|
|
274
352
|
|
|
275
|
-
|
|
353
|
+
onResponseData (chunk) {
|
|
276
354
|
assert(!this.aborted)
|
|
277
355
|
assert(!this.completed)
|
|
278
356
|
|
|
279
357
|
if (channels.bodyChunkReceived.hasSubscribers) {
|
|
280
358
|
channels.bodyChunkReceived.publish({ request: this, chunk })
|
|
281
359
|
}
|
|
360
|
+
|
|
361
|
+
const controller = this[kController]
|
|
282
362
|
try {
|
|
283
|
-
|
|
363
|
+
this[kHandler].onResponseData?.(controller, chunk)
|
|
364
|
+
return !controller?.paused
|
|
284
365
|
} catch (err) {
|
|
285
366
|
this.abort(err)
|
|
286
367
|
return false
|
|
287
368
|
}
|
|
288
369
|
}
|
|
289
370
|
|
|
290
|
-
|
|
371
|
+
onRequestUpgrade (statusCode, headers, socket) {
|
|
291
372
|
assert(!this.aborted)
|
|
292
373
|
assert(!this.completed)
|
|
293
374
|
|
|
294
|
-
|
|
375
|
+
const controller = this[kController]
|
|
376
|
+
if (controller) {
|
|
377
|
+
controller.rawHeaders = headers
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const parsedHeaders = Array.isArray(headers) ? parseHeaders(headers) : headers
|
|
381
|
+
|
|
382
|
+
return this[kHandler].onRequestUpgrade?.(controller, statusCode, parsedHeaders, socket)
|
|
295
383
|
}
|
|
296
384
|
|
|
297
|
-
|
|
385
|
+
onResponseEnd (trailers) {
|
|
298
386
|
this.onFinally()
|
|
299
387
|
|
|
300
388
|
assert(!this.aborted)
|
|
@@ -305,15 +393,22 @@ class Request {
|
|
|
305
393
|
channels.trailers.publish({ request: this, trailers })
|
|
306
394
|
}
|
|
307
395
|
|
|
396
|
+
const controller = this[kController]
|
|
397
|
+
if (controller) {
|
|
398
|
+
controller.rawTrailers = trailers
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const parsedTrailers = Array.isArray(trailers) ? parseHeaders(trailers) : trailers
|
|
402
|
+
|
|
308
403
|
try {
|
|
309
|
-
return this[kHandler].
|
|
404
|
+
return this[kHandler].onResponseEnd?.(controller, parsedTrailers)
|
|
310
405
|
} catch (err) {
|
|
311
406
|
// TODO (fix): This might be a bad idea?
|
|
312
|
-
this.
|
|
407
|
+
this.onResponseError(err)
|
|
313
408
|
}
|
|
314
409
|
}
|
|
315
410
|
|
|
316
|
-
|
|
411
|
+
onResponseError (error) {
|
|
317
412
|
this.onFinally()
|
|
318
413
|
|
|
319
414
|
if (channels.error.hasSubscribers) {
|
|
@@ -325,7 +420,9 @@ class Request {
|
|
|
325
420
|
}
|
|
326
421
|
this.aborted = true
|
|
327
422
|
|
|
328
|
-
|
|
423
|
+
const controller = this[kController]
|
|
424
|
+
|
|
425
|
+
return this[kHandler].onResponseError?.(controller, error)
|
|
329
426
|
}
|
|
330
427
|
|
|
331
428
|
onFinally () {
|
|
@@ -402,10 +499,10 @@ function processHeader (request, key, val) {
|
|
|
402
499
|
if (request.contentLength !== null) {
|
|
403
500
|
throw new InvalidArgumentError('duplicate content-length header')
|
|
404
501
|
}
|
|
405
|
-
|
|
406
|
-
if (!Number.isFinite(request.contentLength)) {
|
|
502
|
+
if (!isValidContentLengthHeaderValue(val)) {
|
|
407
503
|
throw new InvalidArgumentError('invalid content-length header')
|
|
408
504
|
}
|
|
505
|
+
request.contentLength = parseInt(val, 10)
|
|
409
506
|
} else if (request.contentType === null && headerName === 'content-type') {
|
|
410
507
|
request.contentType = val
|
|
411
508
|
request.headers.push(key, val)
|