@particle-academy/fancy-connector-core 0.2.0 → 0.3.0
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 +37 -0
- package/dist/index.cjs +19 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +19 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,43 @@ All notable changes to `fancy-connector-core` are documented here, in
|
|
|
6
6
|
**This package is pre-1.0, so breaking changes land in MINOR releases.** The
|
|
7
7
|
version number is not a promise it can keep yet; the entries below are.
|
|
8
8
|
|
|
9
|
+
## [0.3.0] - 2026-08-19
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- **`ECONNRESET` and `EPIPE` are `ambiguous`, not `unreachable`. This was a live
|
|
14
|
+
duplicate-send hazard.** Both were listed as codes proving the request never
|
|
15
|
+
left. They do not. A peer sends RST whenever it tears the connection down,
|
|
16
|
+
*including after it has received and acted on the request but before the
|
|
17
|
+
response comes back*; `EPIPE` is the same story from our side. Node surfaces
|
|
18
|
+
the safe case and the unsafe case as the same code, so the code cannot carry
|
|
19
|
+
the claim.
|
|
20
|
+
|
|
21
|
+
Because `shouldRetry` returns true for `unreachable` **without consulting
|
|
22
|
+
`idempotent`**, a reset produced up to three sends of a connector that had
|
|
23
|
+
explicitly declared `idempotent: false`. For a consumer publishing to social
|
|
24
|
+
networks that is a second public post that cannot be withdrawn.
|
|
25
|
+
|
|
26
|
+
This is the same defect as the 5xx / thrown-transport split fixed earlier, and
|
|
27
|
+
it contradicted this module's own stated rule -- *"an unknown failure treated
|
|
28
|
+
as unreachable would be retried, and the one thing worse than a lost send is
|
|
29
|
+
two sends nobody approved."* The two codes were standing exceptions to a rule
|
|
30
|
+
that should have covered them.
|
|
31
|
+
|
|
32
|
+
**What a consumer must DO:** nothing, if your connectors declare `idempotent`
|
|
33
|
+
honestly -- the fix only removes retries that were never safe. If you have a
|
|
34
|
+
connector marked `idempotent: true` that is not actually safe to repeat, this
|
|
35
|
+
release does not save you; that field is still load-bearing. If you were
|
|
36
|
+
relying on resets being retried for a genuinely idempotent connector, they
|
|
37
|
+
still are: `ambiguous` retries when `idempotent: true`.
|
|
38
|
+
|
|
39
|
+
`ECONNREFUSED`, `ENOTFOUND`, `EAI_AGAIN`, `EHOSTUNREACH` and `ENETUNREACH`
|
|
40
|
+
stay `unreachable` -- they do prove nothing was delivered -- and a test now
|
|
41
|
+
pins them there so the fix cannot overcorrect.
|
|
42
|
+
|
|
43
|
+
Found by a consumer's conformance fixture disagreeing with the core on its
|
|
44
|
+
first run, with a reproduction.
|
|
45
|
+
|
|
9
46
|
## [Unreleased]
|
|
10
47
|
|
|
11
48
|
## [0.2.0] — unreleased
|
package/dist/index.cjs
CHANGED
|
@@ -158,16 +158,33 @@ function satisfiesMinimum(coreVersion, minimum) {
|
|
|
158
158
|
|
|
159
159
|
// src/delivery.ts
|
|
160
160
|
var UNREACHABLE_CODES = /* @__PURE__ */ new Set([
|
|
161
|
+
// Every code here must PROVE the request never reached an application:
|
|
162
|
+
// nothing listening, no route, or a name that never resolved. If a code can
|
|
163
|
+
// also occur after the peer received and acted on the request, it belongs in
|
|
164
|
+
// AMBIGUOUS_CODES -- `shouldRetry` retries `unreachable` without consulting
|
|
165
|
+
// `idempotent`, so a wrong entry here is a duplicate send, not a slow one.
|
|
161
166
|
"ECONNREFUSED",
|
|
162
167
|
"ENOTFOUND",
|
|
163
168
|
"EAI_AGAIN",
|
|
164
169
|
"EHOSTUNREACH",
|
|
165
170
|
"ENETUNREACH",
|
|
166
|
-
"ECONNRESET",
|
|
167
|
-
"EPIPE",
|
|
168
171
|
"ERR_SOCKET_CONNECTION_TIMEOUT"
|
|
169
172
|
]);
|
|
170
173
|
var AMBIGUOUS_CODES = /* @__PURE__ */ new Set([
|
|
174
|
+
// A peer sends RST whenever it tears the connection down -- INCLUDING after
|
|
175
|
+
// it has received and acted on the request but before the response comes
|
|
176
|
+
// back. EPIPE is the same story from our side: we wrote to a socket the peer
|
|
177
|
+
// had already closed, and whether an earlier complete request was processed
|
|
178
|
+
// first is not knowable from here. Node surfaces the safe case and the unsafe
|
|
179
|
+
// case as the same code, so the code cannot carry the claim.
|
|
180
|
+
//
|
|
181
|
+
// These two sat in UNREACHABLE_CODES until 0.3.0, which made them
|
|
182
|
+
// unconditionally retryable and produced three sends of connectors that had
|
|
183
|
+
// declared `idempotent: false`. Same defect as the 5xx / thrown-transport
|
|
184
|
+
// split this module already fixed once, and a stated exception to a rule that
|
|
185
|
+
// should have covered them.
|
|
186
|
+
"ECONNRESET",
|
|
187
|
+
"EPIPE",
|
|
171
188
|
"ETIMEDOUT",
|
|
172
189
|
"UND_ERR_HEADERS_TIMEOUT",
|
|
173
190
|
"UND_ERR_BODY_TIMEOUT",
|