@particle-academy/fancy-conformance 0.21.1 → 0.21.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/VERSION +1 -1
- package/package.json +1 -1
- package/suites/flow/connector-runs/cases.json +29 -10
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.21.
|
|
1
|
+
0.21.2
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@particle-academy/fancy-conformance",
|
|
3
|
-
"version": "0.21.
|
|
3
|
+
"version": "0.21.2",
|
|
4
4
|
"description": "Shared cross-language conformance fixtures for the Fancy suite. One contract, N implementations, and a single table that every implementation asserts in its own CI \u2014 so 'parity' is a test result rather than a claim. Ships the fixture data itself, so a Rust, Go or Python runner can consume it without a JavaScript toolchain.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -6,7 +6,12 @@
|
|
|
6
6
|
"id": "0001-stripe-customer-fake",
|
|
7
7
|
"title": "A Stripe customer node in fake mode publishes the faker's authored fields and the node's own config.",
|
|
8
8
|
"since": "0.21.0",
|
|
9
|
-
"tags": [
|
|
9
|
+
"tags": [
|
|
10
|
+
"connector",
|
|
11
|
+
"stripe",
|
|
12
|
+
"fake",
|
|
13
|
+
"idempotency"
|
|
14
|
+
],
|
|
10
15
|
"input": {
|
|
11
16
|
"schema": {
|
|
12
17
|
"$schema": "https://particle.academy/schemas/workflow/v1.json",
|
|
@@ -19,7 +24,10 @@
|
|
|
19
24
|
"kind": "@particle-academy/manual_trigger",
|
|
20
25
|
"label": "Run it",
|
|
21
26
|
"config": {},
|
|
22
|
-
"position": {
|
|
27
|
+
"position": {
|
|
28
|
+
"x": 0,
|
|
29
|
+
"y": 0
|
|
30
|
+
}
|
|
23
31
|
},
|
|
24
32
|
{
|
|
25
33
|
"id": "customer",
|
|
@@ -30,14 +38,20 @@
|
|
|
30
38
|
"email": "ada@example.test",
|
|
31
39
|
"name": "Ada Lovelace"
|
|
32
40
|
},
|
|
33
|
-
"position": {
|
|
41
|
+
"position": {
|
|
42
|
+
"x": 280,
|
|
43
|
+
"y": 0
|
|
44
|
+
}
|
|
34
45
|
},
|
|
35
46
|
{
|
|
36
47
|
"id": "out",
|
|
37
48
|
"kind": "@particle-academy/output",
|
|
38
49
|
"label": "Result",
|
|
39
50
|
"config": {},
|
|
40
|
-
"position": {
|
|
51
|
+
"position": {
|
|
52
|
+
"x": 560,
|
|
53
|
+
"y": 0
|
|
54
|
+
}
|
|
41
55
|
}
|
|
42
56
|
],
|
|
43
57
|
"edges": [
|
|
@@ -62,14 +76,17 @@
|
|
|
62
76
|
"runIdentity": {
|
|
63
77
|
"derivation": "\"lab\" + sha256(canonicalJson(input.schema))[0:8]",
|
|
64
78
|
"canonicalJson": [
|
|
65
|
-
"Object keys sorted
|
|
79
|
+
"Object keys sorted by Unicode CODE POINT, at every depth. Array order is preserved. (Code point order is what PHP's bytewise UTF-8 sort, Python and Rust produce natively; JavaScript's default sort is UTF-16 code-unit order and must not be used — they diverge when a supplementary-plane character sits beside one in U+E000–U+FFFF.)",
|
|
66
80
|
"No insignificant whitespace: separators are exactly `,` and `:`.",
|
|
67
|
-
"UTF-8 output. Escape only what JSON requires — PHP must pass JSON_UNESCAPED_SLASHES and JSON_UNESCAPED_UNICODE, because its default escapes `/` and every non-ASCII character and neither matches the other
|
|
68
|
-
"
|
|
81
|
+
"UTF-8 output. Escape only what JSON requires — PHP must pass JSON_UNESCAPED_SLASHES and JSON_UNESCAPED_UNICODE, because its default escapes `/` and every non-ASCII character and neither matches the other runtimes.",
|
|
82
|
+
"An empty object and an empty array are ONE value: emit `[]`. PHP's `json_decode(..., true)` cannot tell them apart and this package's own PHP loader decodes that way, so `[]` is the only spelling every runtime can reach.",
|
|
83
|
+
"The hashed input contains INTEGERS ONLY. No fractional value and no `x.0` literal. After a parse no runtime can recover how a number was written, and they disagree on how to print a float — measured: Python emits `1.0`, JavaScript `1`, PHP `1` or `1.0` depending on JSON_PRESERVE_ZERO_FRACTION. An integer prints identically everywhere with no flags. Enforced by a test over the case, not left to the author.",
|
|
84
|
+
"Every object key in the hashed input is ASCII. Enforced by a test. With ASCII keys every ordering rule above coincides, so the code-point-vs-code-unit divergence cannot be reached silently — a case that needs a non-ASCII key has to change that test first, and in doing so read why."
|
|
69
85
|
],
|
|
70
86
|
"note": "Supplied, not optional. The connector derives its idempotency key from this, and some connectors refuse to write without one — a runner that omits it exercises a different path.",
|
|
71
|
-
"
|
|
72
|
-
"
|
|
87
|
+
"value": "labd5f9ddb2",
|
|
88
|
+
"valueProvenance": "Pinned 2026-09-13. Derived independently by THREE runtimes under the rules above and identical in all of them: the connector lab's PHP lane and its Node lane (weaver), and a Python re-derivation made when landing it. `value` is authoritative; `derivation` and `canonicalJson` stay as provenance and so a runner can prove it agrees. A runner SHOULD derive the identity itself and refuse a run whose supplied value differs, rather than trust the literal blindly — that is what stops two lanes drifting while each still reads a pinned string.",
|
|
89
|
+
"history": "0.21.0 wrote `canonical json` without defining it. 0.21.1 defined four rules. Implementing those in PHP and Node found they still produced two different keys — `labe4b755a3` (Node, `{}` survives a parse) and `labd5f9ddb2` (PHP, `{}` collapses to `[]`) — because the definition never said what an empty object is. The loader decides that, not the case, and the PHP loader decodes to arrays. Found by the connector lab by comparing the two byte for byte; the lab also found its own PHP canonicaliser had been emitting lists with integer keys, self-consistent since it was written and so invisible to every digest it produced."
|
|
73
90
|
}
|
|
74
91
|
},
|
|
75
92
|
"expected": {
|
|
@@ -86,7 +103,9 @@
|
|
|
86
103
|
"matches": {
|
|
87
104
|
"out.data.id": "^cus_fake_[0-9a-f]{12}$"
|
|
88
105
|
},
|
|
89
|
-
"deterministic": [
|
|
106
|
+
"deterministic": [
|
|
107
|
+
"out.data.id"
|
|
108
|
+
]
|
|
90
109
|
}
|
|
91
110
|
},
|
|
92
111
|
"notes": [
|