@particle-academy/fancy-connector-core 0.0.0 → 0.2.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 +179 -103
- package/LICENSE +21 -21
- package/README.md +200 -175
- package/dist/index.cjs +95 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +143 -9
- package/dist/index.d.ts +143 -9
- package/dist/index.js +85 -10
- package/dist/index.js.map +1 -1
- package/package.json +71 -71
package/CHANGELOG.md
CHANGED
|
@@ -1,103 +1,179 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
All notable changes to `fancy-connector-core` are documented here, in
|
|
4
|
-
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) format.
|
|
5
|
-
|
|
6
|
-
**This package is pre-1.0, so breaking changes land in MINOR releases.** The
|
|
7
|
-
version number is not a promise it can keep yet; the entries below are.
|
|
8
|
-
|
|
9
|
-
## [Unreleased]
|
|
10
|
-
|
|
11
|
-
## [0.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
is
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `fancy-connector-core` are documented here, in
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) format.
|
|
5
|
+
|
|
6
|
+
**This package is pre-1.0, so breaking changes land in MINOR releases.** The
|
|
7
|
+
version number is not a promise it can keep yet; the entries below are.
|
|
8
|
+
|
|
9
|
+
## [Unreleased]
|
|
10
|
+
|
|
11
|
+
## [0.2.0] — unreleased
|
|
12
|
+
|
|
13
|
+
The reference consumer migrated five adapters onto `0.1.0`, ran its acceptance
|
|
14
|
+
suite, and filed four findings. All four were cases where the type or the
|
|
15
|
+
classifier was *safe* and still *wrong* — the hardest kind to notice from inside,
|
|
16
|
+
because nothing fails.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- **`classifyError` ignored the classification `httpFailure` attached.** It
|
|
21
|
+
re-derived from scratch, found no error code, and answered `ambiguous`
|
|
22
|
+
("nobody can tell") for a `400` the provider had been explicit about.
|
|
23
|
+
**This was never a retry bug** — `deliver()` reads `error.classified` first and
|
|
24
|
+
only falls back — but a host that calls the exported classifier directly, which
|
|
25
|
+
is a reasonable thing to do, was told nobody could tell. The **PHP twin already
|
|
26
|
+
did this**, so the two runtimes disagreed. **What a consumer must do: nothing,
|
|
27
|
+
unless you asserted the old answer.** If you pinned `classifyError` on an
|
|
28
|
+
`httpFailure` to `ambiguous`, that assertion now reads `rejected` or
|
|
29
|
+
`refused-explicitly` — which is the answer you wanted.
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
|
|
33
|
+
- **BREAKING (types only): `ChainRef` is `object`, was
|
|
34
|
+
`Record<string, string | number>`.** An ordinary interface does not satisfy an
|
|
35
|
+
index signature unless it declares one, so `interface PostRef { uri; cid }` did
|
|
36
|
+
**not** satisfy the old constraint — and the only other fix was to add an index
|
|
37
|
+
signature to a type used across a codebase in order to please a generic used in
|
|
38
|
+
one place. The reference consumer worked around it with `postChain<T & ChainRef>`
|
|
39
|
+
and two `as unknown as` casts, and could not use `ChainOutcome<T>` in its own
|
|
40
|
+
signatures at all. **What a consumer must do: delete the casts.** A constraint
|
|
41
|
+
everyone casts past is not enforcing anything.
|
|
42
|
+
|
|
43
|
+
### Added
|
|
44
|
+
|
|
45
|
+
- **`SandboxKind` gained `restricted-reach`** — the fifth shape, and the
|
|
46
|
+
dangerous one. A Meta app in Development Mode, an unaudited TikTok app that can
|
|
47
|
+
only post `SELF_ONLY`: same credentials, same endpoints, same estate, and only
|
|
48
|
+
the **audience** restricted. That is none of the previous four, and `none` —
|
|
49
|
+
which is what it was being forced into — is actively wrong. It is the shape
|
|
50
|
+
most likely to be mistaken for a sandbox, because it looks exactly like a
|
|
51
|
+
successful post that nobody can see: `ok: true`, a real id, no audience.
|
|
52
|
+
- **`SandboxKind` gained `unverified`** — *nobody has checked yet*. A real state,
|
|
53
|
+
and the type has to carry it: this is the field where being wrong sends someone
|
|
54
|
+
to a live estate believing it is a test one, so "researched but not verified"
|
|
55
|
+
must not be forced to pick one of the other five. A comment saying so is not a
|
|
56
|
+
type.
|
|
57
|
+
- **`sandboxIsSelectable()` / `sandboxRefusal()`** (`SandboxKind::isSelectable()`
|
|
58
|
+
/ `->refusal()` in PHP). Auto-resolution no longer picks "sandbox" for anything
|
|
59
|
+
that cannot be selected, and each refusal explains **its own** reason —
|
|
60
|
+
collapsing the three into "no sandbox available" would hide the one that
|
|
61
|
+
matters.
|
|
62
|
+
- **`providerProblems()`** — the sibling of `capabilityProblems()`, one level up.
|
|
63
|
+
Reports `implemented: true` with `sandbox: "unverified"`, a `verify` that does
|
|
64
|
+
not say what it **proves**, a `restricted-reach` provider whose summary never
|
|
65
|
+
mentions reach, and empty fields or setup on something claimed to be
|
|
66
|
+
implemented.
|
|
67
|
+
- **`ProviderAdapter.proves`** — what a verify does NOT cover, declared on the
|
|
68
|
+
adapter as well as on each result, so a surface can say it *before* anyone runs
|
|
69
|
+
the check.
|
|
70
|
+
- **`withResolvedLimit(rules, limit)`** — the supported way to resolve a
|
|
71
|
+
per-connection limit. A Mastodon instance publishes its own `max_toot_chars`;
|
|
72
|
+
baking a limit into a connector renders to 500 on an instance allowing 5000, or
|
|
73
|
+
to 5000 on one allowing 500, and both fail silently. `undefined` keeps the
|
|
74
|
+
declared limit, because *"I did not look"* is not *"there is no limit"* — and
|
|
75
|
+
`null` is a real answer meaning **uncounted**.
|
|
76
|
+
- **`CREDENTIAL_SCOPES`, `SANDBOX_KINDS`, `PROBLEM_SEVERITIES`,
|
|
77
|
+
`CANONICAL_METRICS`** — every string union that crosses a JSON boundary now
|
|
78
|
+
ships as **data** as well as a type. The compiler cannot follow a type across
|
|
79
|
+
JSON: when `scope` was renamed from `app`/`brand`, a consumer re-declaring the
|
|
80
|
+
field shape on its client kept compiling and its `scope === "brand"` silently
|
|
81
|
+
became never-true, so every credential field would have rendered as shared.
|
|
82
|
+
(PHP had this for free — an enum is runtime data, and `tryFrom` fails loudly on
|
|
83
|
+
a value nobody serves any more.)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
## [0.1.0] — unreleased
|
|
87
|
+
|
|
88
|
+
First cut. Extracted and generalised from the `_connector` shared runtime in the
|
|
89
|
+
fancy-flow node marketplace, then rebuilt around the requirements in The Ripple
|
|
90
|
+
Effect's packager brief, which came from five adapters dry-verified against real
|
|
91
|
+
APIs.
|
|
92
|
+
|
|
93
|
+
The connector **catalogue** lives in `fancy-connectors` and releases on its own
|
|
94
|
+
clock; this package is the runtime it is written on.
|
|
95
|
+
|
|
96
|
+
### Added
|
|
97
|
+
|
|
98
|
+
- **Core runtime, TypeScript and PHP, with zero runtime dependencies in either
|
|
99
|
+
ecosystem.** WebCrypto and `fetch` on Node; `hash()` and cURL behind a
|
|
100
|
+
swappable `Transport` in PHP.
|
|
101
|
+
- **`delivery` — a four-way failure classification.** `unreachable` /
|
|
102
|
+
`refused-explicitly` / `ambiguous` / `rejected`, with retryability a function
|
|
103
|
+
of the kind **and** the connector's declared idempotency. An unrecognised
|
|
104
|
+
error falls to `ambiguous`, never to safe.
|
|
105
|
+
- **`DeliveryDeclaration`** — `idempotent` + a cited `why` + `minIntervalMs` +
|
|
106
|
+
`rateSource` (`documented` | `self-imposed`) + a dated `Citation`.
|
|
107
|
+
- **`seam` — `ProviderAdapter` and `Connector`**, deliberately separate, plus
|
|
108
|
+
the written list of everything the package never owns: approval, liveness, the
|
|
109
|
+
approved-bytes comparison, consent, second review, journals, credential
|
|
110
|
+
storage.
|
|
111
|
+
- **`text`** — `measure(text, unit)` over graphemes / characters / UTF-8 bytes,
|
|
112
|
+
and a `ByteRange` type producible only by a function that encoded the string.
|
|
113
|
+
- **`render`** — pure, versioned (version **inside** the payload hash), loss
|
|
114
|
+
reported rather than applied, driven by a declarative `RenderRules` so most
|
|
115
|
+
providers need no rendering code at all.
|
|
116
|
+
- **`chain`** — root fixed, parent advancing, with the post function as an
|
|
117
|
+
argument so threading is testable without a live session.
|
|
118
|
+
- **`metrics`** — declared metric shapes, `compareShape()`,
|
|
119
|
+
`capabilityProblems()`, and `reported()` so *absent stays absent* is a
|
|
120
|
+
property of the code.
|
|
121
|
+
- **`probe`** — dry-verify against the real API with a deliberately invalid
|
|
122
|
+
credential, per-provider `authStatuses`, a 2xx counted as a failure, and
|
|
123
|
+
offline reported as **skipped** rather than failed.
|
|
124
|
+
- **`drift`** — API-drift *reporting*: a narrow projection of an OpenAPI
|
|
125
|
+
document against the operations a connector actually calls, a recorded-shape
|
|
126
|
+
fallback for the majority of providers that publish nothing, and `unchecked`
|
|
127
|
+
as a first-class outcome that is never collapsed into `clean`.
|
|
128
|
+
- **Explicit-credentials resolution.** Credentials passed straight in bypass the
|
|
129
|
+
connection registry entirely; an incomplete set is a loud failure rather than
|
|
130
|
+
a silent fall-through.
|
|
131
|
+
- **`CONNECTOR_API_VERSION` and `assertConnectorApi`** (`ConnectorApi` in PHP) —
|
|
132
|
+
the compatibility number that makes a separate release clock safe for a
|
|
133
|
+
catalogue whose connectors ship as VENDORED source. It moves only when the
|
|
134
|
+
surface a connector is written against moves, the window is the current
|
|
135
|
+
version and the one before, and a mismatch names which side is behind rather
|
|
136
|
+
than reporting "incompatible".
|
|
137
|
+
|
|
138
|
+
#### Two things the PHP core does differently, and why
|
|
139
|
+
|
|
140
|
+
- **A custom `Transport` must throw `TransportException`** — via
|
|
141
|
+
`unreachable()`, `ambiguous()` or `fromCurlErrno()` — to get the *always safe
|
|
142
|
+
to retry* case. PHP has no error-code vocabulary the way Node does, and
|
|
143
|
+
matching on message text is not an option: it is localised and changes between
|
|
144
|
+
HTTP-client versions. **What a consumer must do:** nothing if you use the
|
|
145
|
+
bundled `CurlTransport`. If you bind your own, throw `TransportException`
|
|
146
|
+
rather than a bare exception — a bare one is classified `Ambiguous`, which is
|
|
147
|
+
conservative and correct but gives up the retry a refused connection deserves.
|
|
148
|
+
- **`RunIdentity` is bridged rather than implemented.** PHP interfaces are
|
|
149
|
+
nominal, so `FancyFlow\Runtime\RunIdentity` — which matches the shape exactly —
|
|
150
|
+
is not an instance of ours. `ForeignRunIdentity::adapt()` wraps any object
|
|
151
|
+
carrying the five members, and every `Idempotency` entry point runs its
|
|
152
|
+
argument through it. **What a consumer must do: nothing.** Pass `$ctx->run`
|
|
153
|
+
straight in, exactly as the TypeScript side does.
|
|
154
|
+
|
|
155
|
+
### Fixed
|
|
156
|
+
|
|
157
|
+
- **An ambiguous failure was retried on a non-idempotent connector.** The
|
|
158
|
+
runtime this was extracted from classified a thrown transport as
|
|
159
|
+
`ConnectorTransient` with `retryable = true` and retried it twice. A thrown
|
|
160
|
+
transport includes a timeout, which may be a request the provider already
|
|
161
|
+
acted on — so any connector without an idempotency key could be retried into a
|
|
162
|
+
silent double write. **What a consumer must do: nothing, unless you relied on
|
|
163
|
+
that behaviour.** A connector that genuinely is safe to repeat now says so
|
|
164
|
+
with `idempotent: true` and keeps its retries; everything else becomes
|
|
165
|
+
conservative, which is the correct direction.
|
|
166
|
+
- **The sentence splitter broke URLs.** `splitToFit` treated every `.` as a
|
|
167
|
+
sentence terminator, so `https://example.test/x` was a candidate break point
|
|
168
|
+
and a link could land across two messages at a low limit. A terminator now
|
|
169
|
+
only ends a sentence when whitespace or the end follows it. **What a consumer
|
|
170
|
+
must do: nothing** — but any payload hash computed under the old splitting
|
|
171
|
+
will differ, so an approval recorded against it will correctly refuse.
|
|
172
|
+
- **The core no longer imports a workflow engine.** The run identity used for
|
|
173
|
+
idempotency keys is declared structurally, so `fancy-flow`'s `RunIdentity`
|
|
174
|
+
satisfies it with no dependency in either direction and a host that has never
|
|
175
|
+
heard of a workflow engine can implement it.
|
|
176
|
+
|
|
177
|
+
[Unreleased]: https://github.com/Particle-Academy/fancy-connector-core/compare/v0.2.0...HEAD
|
|
178
|
+
[0.2.0]: https://github.com/Particle-Academy/fancy-connector-core/compare/v0.1.0...v0.2.0
|
|
179
|
+
[0.1.0]: https://github.com/Particle-Academy/fancy-connector-core/releases/tag/v0.1.0
|
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Particle Academy
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Particle Academy
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|