@particle-academy/fancy-connector-core 0.3.1 → 0.4.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 +42 -0
- package/dist/index.cjs +7 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +7 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,48 @@ 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.4.0] - 2026-09-12
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- **An idempotency key was fitted to the catalogue CEILING, never to the
|
|
14
|
+
provider's own limit.** `MAX_KEY_LENGTH` / `MAX_IDEMPOTENCY_KEY_LENGTH` is 255
|
|
15
|
+
— the widest any provider in the catalogue accepts. A provider declares its
|
|
16
|
+
own, and the connector index carries it as `idempotencyMaxLength`; Discord's
|
|
17
|
+
`discord_message` declares **25**.
|
|
18
|
+
|
|
19
|
+
So a legitimate engine-derived key — `lane_<16 hex>:subject`, 29 characters —
|
|
20
|
+
came back unshortened and the connector's own validation refused it:
|
|
21
|
+
`message_create: idempotencyKey must be at most 25 characters`. The run failed
|
|
22
|
+
at the node, and the only workaround available to a host was choosing a
|
|
23
|
+
shorter run identity, which is a workflow-authoring decision being forced by a
|
|
24
|
+
string length in a library.
|
|
25
|
+
|
|
26
|
+
The subtle part is that nothing was malformed: the key was correct and the
|
|
27
|
+
validation was correct. This package simply never asked how long the key was
|
|
28
|
+
allowed to be.
|
|
29
|
+
|
|
30
|
+
`keyFor(..., maxLength:)` in PHP and `{ maxLength }` in `IdempotencyOptions`
|
|
31
|
+
now carry the provider's limit, and the smaller of it and the ceiling always
|
|
32
|
+
applies — a descriptor claiming more than any provider accepts is one to
|
|
33
|
+
distrust, not obey. The digest still makes the shortened key **stable across
|
|
34
|
+
attempts**, which is the entire point of one: a key that varied per attempt
|
|
35
|
+
would defeat the dedupe it exists to provide.
|
|
36
|
+
|
|
37
|
+
A limit too small to carry `<head>~<digest>` yields the digest alone. That
|
|
38
|
+
loses the greppable prefix, which is a real cost — but a key that is hard to
|
|
39
|
+
trace still dedupes, while the alternative is a negative slice length.
|
|
40
|
+
|
|
41
|
+
**Both runtimes had this, identically**, so the parity suite comparing them
|
|
42
|
+
stayed green throughout. Agreement is not correctness, and that is the whole
|
|
43
|
+
reason this needed an outside report to surface: it came from the connector
|
|
44
|
+
lab, which hit it on Discord and worked around it by keeping its run key
|
|
45
|
+
artificially short.
|
|
46
|
+
|
|
47
|
+
Not a breaking change — the parameter is optional and omitting it preserves
|
|
48
|
+
the previous behaviour exactly. It lands as a MINOR because callers that
|
|
49
|
+
should pass a limit now can, and the connector emitters will.
|
|
50
|
+
|
|
9
51
|
## [0.3.1] - 2026-08-20
|
|
10
52
|
|
|
11
53
|
### Fixed
|
package/dist/index.cjs
CHANGED
|
@@ -937,7 +937,7 @@ function idempotencyKeyFor(ctx, nodeId, options = {}) {
|
|
|
937
937
|
const windowSeconds = options.windowSeconds === void 0 ? DEFAULT_IDEMPOTENCY_WINDOW_SECONDS : options.windowSeconds;
|
|
938
938
|
if (!identity) {
|
|
939
939
|
const runKey = runKeyFrom(ctx);
|
|
940
|
-
return runKey === null ? null : fit(`${runKey}:${nodeId}
|
|
940
|
+
return runKey === null ? null : fit(`${runKey}:${nodeId}`, options.maxLength);
|
|
941
941
|
}
|
|
942
942
|
if (!identity.isReplaySafe(windowSeconds, options.now ?? /* @__PURE__ */ new Date())) {
|
|
943
943
|
throw new ConnectorIdempotencyExpiredError(
|
|
@@ -949,13 +949,15 @@ function idempotencyKeyFor(ctx, nodeId, options = {}) {
|
|
|
949
949
|
}
|
|
950
950
|
);
|
|
951
951
|
}
|
|
952
|
-
return fit(identity.stepKey(nodeId, options.occurrence));
|
|
952
|
+
return fit(identity.stepKey(nodeId, options.occurrence), options.maxLength);
|
|
953
953
|
}
|
|
954
954
|
var NO_IDEMPOTENCY_KEY_WARNING = "no run identity available, so this write is being sent WITHOUT an idempotency key. A retry would repeat the effect rather than recover from it \u2014 the node is marked unsafe-to-replay, so a durable runner will give it one attempt. Durable runs supply an identity automatically; for a synchronous run, pass `run` in RunOptions.";
|
|
955
|
-
function fit(key) {
|
|
956
|
-
|
|
955
|
+
function fit(key, maxLength) {
|
|
956
|
+
const limit = maxLength === void 0 || maxLength === null ? MAX_IDEMPOTENCY_KEY_LENGTH : Math.max(1, Math.min(maxLength, MAX_IDEMPOTENCY_KEY_LENGTH));
|
|
957
|
+
if (key.length <= limit) return key;
|
|
957
958
|
const digest = fnv1a(key);
|
|
958
|
-
|
|
959
|
+
if (limit <= digest.length + 1) return digest.slice(0, limit);
|
|
960
|
+
const head = key.slice(0, limit - digest.length - 1);
|
|
959
961
|
return `${head}~${digest}`;
|
|
960
962
|
}
|
|
961
963
|
function fnv1a(value) {
|