@fun-xyz/fiat-contract 0.6.0 → 0.8.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/README.md +15 -12
- package/dist/{chunk-EHYDQU4T.mjs → chunk-LVVQ46YG.mjs} +112 -26
- package/dist/fixtures/index.d.ts +3 -4
- package/dist/index.js +407 -103
- package/dist/index.mjs +306 -80
- package/dist/schemas.d.ts +4 -1
- package/dist/table.d.ts +39 -5
- package/dist/table.js +112 -26
- package/dist/table.mjs +11 -3
- package/dist/types.d.ts +56 -34
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -10,19 +10,19 @@ Four things, zero runtime logic beyond validation:
|
|
|
10
10
|
| `src/types.ts` | `FlowState` · `Transition` · `FailureReason` · `Surface` · `Instructions` · `FormDescriptor` · `OrderStatus` · `FiatStepResponse` |
|
|
11
11
|
| `src/schemas.ts` | zod mirrors of every type — the single runtime validator |
|
|
12
12
|
| `src/table.ts` | the transition table **as data**: per state, its legal transition set, the states any call from it may return, and `terminal: boolean` |
|
|
13
|
-
| `src/assert.ts` + `src/fixtures/` | `assertFiatStepResponse` · `assertLegalEmission` · `assertLegalReturn` · `walkTable` · fixture loader +
|
|
13
|
+
| `src/assert.ts` + `src/fixtures/` | `assertFiatStepResponse` · `assertLegalEmission` · `assertLegalReturn` · `walkTable` · fixture loader + 18 recorded step responses (inlined as data — no filesystem, so React Native can bundle it) |
|
|
14
14
|
|
|
15
15
|
## Three entry points — production vs test-time
|
|
16
16
|
|
|
17
17
|
| Import | Weight | Contains | Used by |
|
|
18
18
|
| --- | --- | --- | --- |
|
|
19
19
|
| `@fun-xyz/fiat-contract/types` | **0.1 KB** (types erase) | every type; no runtime values | production, both repos |
|
|
20
|
-
| `@fun-xyz/fiat-contract/table` | **13.8 KB**, zero deps | `TRANSITION_TABLE`, `stateKey`, `tableEntry`, `isTerminal`, `walkTable`, `TABLE_VERSION`, `TERMINAL_ORDER_STATUSES`, `DOCUMENTED_ENDPOINTS` | **production frontend** + backend |
|
|
21
|
-
| `@fun-xyz/fiat-contract` | 47.6 KB, needs zod | the above + 46 zod schemas + assertions +
|
|
20
|
+
| `@fun-xyz/fiat-contract/table` | **13.8 KB**, zero deps | `TRANSITION_TABLE`, `stateKey`, `tableEntry`, `isTerminal`, `walkTable`, `TABLE_VERSION`, `TERMINAL_ORDER_STATUSES`, `DOCUMENTED_ENDPOINTS`, `UNOFFERED_ENDPOINTS`, `UNREACHABLE_STATES`, `unofferedEndpoints`, `unreachableStates` | **production frontend** + backend |
|
|
21
|
+
| `@fun-xyz/fiat-contract` | 47.6 KB, needs zod | the above + 46 zod schemas + assertions + 18 fixtures | tests, and backend dev/test guards |
|
|
22
22
|
|
|
23
23
|
`./table` is not a micro-optimisation. Terminality is table data a **shipped** client must read
|
|
24
24
|
(litmus rule 3 — clients never infer it), and Metro has no cross-module tree-shaking on by default,
|
|
25
|
-
so importing `isTerminal` from the root would ship zod and all
|
|
25
|
+
so importing `isTerminal` from the root would ship zod and all 18 fixtures into a React Native
|
|
26
26
|
bundle. Two CI jobs hold that line: `consumer` asserts requiring `./table` never loads zod into the
|
|
27
27
|
process, and `metro` bundles `./table` with real Metro — in **both** package-exports modes, since
|
|
28
28
|
Metro before RN 0.79 ignores `exports` entirely — then greps the emitted bundle for zod, fixtures and
|
|
@@ -31,7 +31,9 @@ schemas and executes it.
|
|
|
31
31
|
Source of truth: [Fiat Client Contract](https://app.notion.com/p/3b9fc3b2a002815eb270fa4c818268cc)
|
|
32
32
|
(§The fiat step response · §Conformance package · §split `InputSpec` — ACCEPTED) and
|
|
33
33
|
[Fiat Frontend — State Machine & Screen Map](https://app.notion.com/p/3bbfc3b2a00281c994c2cebd17b1d6d3)
|
|
34
|
-
(✅ Decisions · per-screen State details · Event bindings per flow state)
|
|
34
|
+
(✅ Decisions · per-screen State details · Event bindings per flow state), plus
|
|
35
|
+
[Fiat KYC — User Flow & API Interfaces](https://app.notion.com/p/3cefc3b2a00281c88e7fe771f0c64332)
|
|
36
|
+
for quote-bound KYC states and endpoints.
|
|
35
37
|
Tracking: [Headless Fiat Onramp](https://linear.app/funxyz/project/headless-fiat-onramp-0147f4102399) ·
|
|
36
38
|
this package is [ENG-5268](https://linear.app/funxyz/issue/ENG-5268).
|
|
37
39
|
|
|
@@ -52,6 +54,9 @@ this package is [ENG-5268](https://linear.app/funxyz/issue/ENG-5268).
|
|
|
52
54
|
string; `GET /fiat/orders/:id` under `SUBMIT` is legal.
|
|
53
55
|
5. **Fixtures stay synthetic.** `q_8f2`, `o_31c`, `eyJ…`, `"…"` — never paste a real session token,
|
|
54
56
|
bank field, or PII value into a fixture. Redaction applies to fixtures too.
|
|
57
|
+
6. **Surface kind names the renderer; provider selects the adapter.** Sumsub capture is
|
|
58
|
+
`{ kind: 'KYC_SDK', provider: 'SUMSUB' }`. It is not `URL_EMBED`: the client mounts an SDK,
|
|
59
|
+
not a URL or iframe.
|
|
55
60
|
|
|
56
61
|
## Versioning — the package version IS the table version
|
|
57
62
|
|
|
@@ -247,7 +252,7 @@ The table is data, so a runtime walk proves every state resolves to a screen —
|
|
|
247
252
|
exhaustiveness alone, because it also fails when a *new* state is added to the contract.
|
|
248
253
|
|
|
249
254
|
```ts
|
|
250
|
-
import { walkTable, isTerminal, TRANSITION_TABLE } from '@fun-xyz/fiat-contract';
|
|
255
|
+
import { assertFixture, walkTable, isTerminal, TRANSITION_TABLE } from '@fun-xyz/fiat-contract';
|
|
251
256
|
|
|
252
257
|
it('every contract state resolves to a screen', () => {
|
|
253
258
|
walkTable((entry) => {
|
|
@@ -256,7 +261,7 @@ it('every contract state resolves to a screen', () => {
|
|
|
256
261
|
});
|
|
257
262
|
|
|
258
263
|
// Terminality is table data — never `transitions.length === 0`
|
|
259
|
-
isTerminal(
|
|
264
|
+
isTerminal(assertFixture('screen-10-kyc-on-hold').stepResponse.state); // false: empty, not over
|
|
260
265
|
isTerminal({ kind: 'ORDER', phase: 'CREATED', status: 'SETTLED' }); // true
|
|
261
266
|
isTerminal({ kind: 'ORDER', phase: 'CREATED', status: 'PROCESSING' }); // false
|
|
262
267
|
|
|
@@ -437,17 +442,15 @@ The current documented shape is what ships; none of these are settled here.
|
|
|
437
442
|
| --- | --- |
|
|
438
443
|
| `orderId` placement | Both: optional on `PAYMENT{INSTRUCT}` (FE doc v0) **and** optional beside `state` (contract worked example). One fixture of each. |
|
|
439
444
|
| `PENDING_ORDER` removal | Kind ships, with the removal proposal flagged on the type, the table entry, and the fixture. Screen 12 stays frozen. |
|
|
440
|
-
| `[OQ7]` failure enumeration | The published `FailureReason` taxonomy only. Expired instructions, partial payment, per-rail cancel eligibility
|
|
441
|
-
|
|
|
445
|
+
| `[OQ7]` failure enumeration | The published `FailureReason` taxonomy only. Expired instructions, partial payment, and per-rail cancel eligibility remain unenumerated; KYC rejection is terminal only for an explicit non-retryable outcome. |
|
|
446
|
+
| Pre-order KYC sequencing | KYC states carry their quote; SDK submission reports to `POST /fiat/kyc/capture`; pending review polls the quote resource. There is no `GET /fiat/kyc`. |
|
|
442
447
|
| Card capture report target | Both topologies are legal in the table (`POST /fiat/orders` for capture-then-order; `POST /fiat/orders/:id/surface-result` otherwise) pending the Transak answer. |
|
|
443
448
|
| Cancel placement | `cancel` is legal on `PAYMENT{INSTRUCT}` and `ORDER{CREATED}`, marked conditional on the placement decision. |
|
|
444
449
|
| `FUN_AUTH` shape | `challenge: Record<string, JsonValue>`; the table entry is `docStatus: 'UNSPECIFIED'`, so `assertLegalEmission` reports it unjudgeable instead of guessing. |
|
|
445
450
|
| `statusHistory` element shape | The documented minimum (`{status}`) — no invented timestamps. |
|
|
446
451
|
| `QR_IMAGE` instruction | In the union per §The fiat step response, flagged against OQ1's "deliberately not pre-declared". |
|
|
447
452
|
|
|
448
|
-
|
|
449
|
-
`FUN_AUTH` (shape owned by the auth spike) and `KYC/CAPTURE` (Screen 8 tombstone, dropped from v1).
|
|
450
|
-
Every other state has a recorded step response.
|
|
453
|
+
`FUN_AUTH` is the only fixture gap declared in `FIXTURE_COVERAGE_GAPS`; its shape remains owned by the auth spike. Every other state has a recorded step response.
|
|
451
454
|
|
|
452
455
|
## Not decided here — needs a human
|
|
453
456
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// package.json
|
|
2
|
-
var version = "0.
|
|
2
|
+
var version = "0.8.0";
|
|
3
3
|
|
|
4
4
|
// src/table.ts
|
|
5
5
|
var TABLE_VERSION = version;
|
|
@@ -30,22 +30,46 @@ var TRANSITION_TABLE = {
|
|
|
30
30
|
endpoint: "POST /fiat/session",
|
|
31
31
|
when: "no valid provider session token (own-tables check)"
|
|
32
32
|
},
|
|
33
|
+
{
|
|
34
|
+
id: "open_session",
|
|
35
|
+
mode: "SUBMIT",
|
|
36
|
+
endpoint: "POST /fiat/payment-session",
|
|
37
|
+
when: "returning user, session + KYC valid, capture-first rail \u2014 the method authorises before any order exists",
|
|
38
|
+
note: "Returns PAYMENT{CAPTURE} carrying the Surface. Without this entry the capture-first rail has no legal way in: a transition-following client takes POST /fiat/orders instead, and the backend refuses that when the provider declares the capture-first capability and no authorisation is present. Named open_session per the Fiat Contract 0.7 doc, which adds quote-time-minted surface rows beside it."
|
|
39
|
+
},
|
|
33
40
|
{
|
|
34
41
|
id: "continue",
|
|
35
42
|
mode: "SUBMIT",
|
|
36
43
|
endpoint: "POST /fiat/orders",
|
|
37
|
-
when: "returning user, session + KYC valid \u2014 the quote screen doubles as review"
|
|
44
|
+
when: "returning user, session + KYC valid, order-first rail \u2014 the quote screen doubles as review",
|
|
45
|
+
note: "Narrowed to the order-first rail: on a capture-first method the order cannot be created before the payment is authorised, so the sibling payment-session entry is the one that applies."
|
|
38
46
|
},
|
|
39
47
|
{
|
|
40
48
|
id: "continue",
|
|
41
49
|
mode: "SUBMIT",
|
|
42
50
|
endpoint: "POST /fiat/auth",
|
|
43
51
|
when: "no ambient Fun identity (non-fomo surfaces only)"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: "capture",
|
|
55
|
+
mode: "CLIENT_SURFACE",
|
|
56
|
+
endpoint: "POST /fiat/orders",
|
|
57
|
+
when: "capture-then-order rail whose session is minted at quote time (inline pay sheet)",
|
|
58
|
+
note: "The 0.7 row that makes the single-screen Apple Pay design legal: the surface rides QUOTE, the provider component IS the primary CTA, and its result creates the order. Apple policy forbids a visible step between an Apple-branded tap and the sheet, and TransakApplePay exposes no programmatic trigger, so a two-step via payment-session cannot serve this rail. Requires state.surface (\u03942)."
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
id: "capture",
|
|
62
|
+
mode: "CLIENT_SURFACE",
|
|
63
|
+
endpoint: "POST /fiat/orders/:id/surface-result",
|
|
64
|
+
when: "order-then-capture rail whose provider order was created with the quote-time session",
|
|
65
|
+
note: "Same quote-time placement, other topology: the order already exists, so the surface reports against it rather than creating one."
|
|
44
66
|
}
|
|
45
67
|
],
|
|
46
68
|
mayReturn: [
|
|
47
69
|
"SESSION_AUTH",
|
|
48
70
|
...KYC_ANY,
|
|
71
|
+
"PAYMENT/CAPTURE",
|
|
72
|
+
"PAYMENT/INSTRUCT",
|
|
49
73
|
"PENDING_ORDER",
|
|
50
74
|
"ORDER/AWAITING_CONFIRMATION",
|
|
51
75
|
"ORDER/CREATED",
|
|
@@ -53,8 +77,10 @@ var TRANSITION_TABLE = {
|
|
|
53
77
|
],
|
|
54
78
|
terminal: false,
|
|
55
79
|
notes: [
|
|
56
|
-
"Re-quote on amount change is a client-local event, not a transition.",
|
|
57
|
-
|
|
80
|
+
"Re-quote on amount change is a client-local event, not a transition. A new quote carries a new surface; the client remounts the adapter rather than reusing the old session.",
|
|
81
|
+
"A surface on QUOTE and a SUBMIT to the same endpoint may not coexist \u2014 two paths to one order. assertLegalEmission enforces it; the server picks one per rail.",
|
|
82
|
+
`Both PAYMENT states are reachable directly from QUOTE: the FE doc canvas draws S1 \u2192 S5 (card, capture-first) and S1 \u2192 S6 (bank). This entry was first ported from the FE doc's bindings table, whose "May receive back" column omits both \u2014 the canvas is the correct source of the two.`,
|
|
83
|
+
'TODO(open-decision): contract \xA7"Proposal: remove PENDING_ORDER" would additionally have QUOTE return ORDER{CREATED} for an active order. That decision does not gate the PAYMENT edges above.'
|
|
58
84
|
]
|
|
59
85
|
},
|
|
60
86
|
FUN_AUTH: {
|
|
@@ -99,21 +125,23 @@ var TRANSITION_TABLE = {
|
|
|
99
125
|
{
|
|
100
126
|
id: "capture",
|
|
101
127
|
mode: "CLIENT_SURFACE",
|
|
102
|
-
endpoint: "
|
|
103
|
-
note:
|
|
128
|
+
endpoint: "POST /fiat/kyc/capture",
|
|
129
|
+
note: "Reports SDK submission; provider eligibility is re-evaluated through quote orchestration."
|
|
104
130
|
}
|
|
105
131
|
],
|
|
106
132
|
mayReturn: [
|
|
133
|
+
"KYC/CAPTURE",
|
|
107
134
|
"KYC/INPUT_REQUIRED",
|
|
108
135
|
"KYC/NO_ACTION_REQUIRED:IN_REVIEW",
|
|
109
136
|
"KYC/NO_ACTION_REQUIRED:ON_HOLD",
|
|
110
137
|
"KYC/NO_ACTION_REQUIRED:REJECTED",
|
|
138
|
+
"QUOTE",
|
|
139
|
+
"PAYMENT/CAPTURE",
|
|
140
|
+
"PAYMENT/INSTRUCT",
|
|
111
141
|
"ORDER/AWAITING_CONFIRMATION"
|
|
112
142
|
],
|
|
113
143
|
terminal: false,
|
|
114
|
-
notes: [
|
|
115
|
-
"Dropped from v1 (no UK headless-module coverage) \u2014 FE Screen 8 is a tombstone. Kind retained as vocabulary; the handshake (supportedStepKinds) keeps the backend from routing v1 SDKs here."
|
|
116
|
-
]
|
|
144
|
+
notes: ["Carries the handshake-gated KYC_SDK surface; provider selects the identity-vendor adapter."]
|
|
117
145
|
},
|
|
118
146
|
"KYC/INPUT_REQUIRED": {
|
|
119
147
|
key: "KYC/INPUT_REQUIRED",
|
|
@@ -125,16 +153,20 @@ var TRANSITION_TABLE = {
|
|
|
125
153
|
{ id: "submit_round", mode: "SUBMIT", endpoint: "POST /fiat/kyc/form" }
|
|
126
154
|
],
|
|
127
155
|
mayReturn: [
|
|
156
|
+
"KYC/CAPTURE",
|
|
128
157
|
"KYC/INPUT_REQUIRED",
|
|
129
158
|
"KYC/NO_ACTION_REQUIRED:IN_REVIEW",
|
|
130
159
|
"KYC/NO_ACTION_REQUIRED:ON_HOLD",
|
|
131
160
|
"KYC/NO_ACTION_REQUIRED:REJECTED",
|
|
161
|
+
"QUOTE",
|
|
162
|
+
"PAYMENT/CAPTURE",
|
|
163
|
+
"PAYMENT/INSTRUCT",
|
|
132
164
|
"ORDER/AWAITING_CONFIRMATION"
|
|
133
165
|
],
|
|
134
166
|
terminal: false,
|
|
135
167
|
notes: [
|
|
136
168
|
"One POST per round; conditional requirements may open another round.",
|
|
137
|
-
"FILE fields
|
|
169
|
+
"FILE fields use POST /fiat/kyc/document to obtain or report a direct provider/vendor upload; document bytes never transit Fun. The harness owns this auxiliary path, not a transition.",
|
|
138
170
|
"Hosted KYC links arrive as HOSTED_LINK form fields, not as a Surface."
|
|
139
171
|
]
|
|
140
172
|
},
|
|
@@ -145,12 +177,16 @@ var TRANSITION_TABLE = {
|
|
|
145
177
|
reason: "IN_REVIEW",
|
|
146
178
|
screens: [9],
|
|
147
179
|
docStatus: "SPECIFIED",
|
|
148
|
-
allowedTransitions: [{ id: "poll", mode: "AWAIT", endpoint: "GET /fiat/
|
|
180
|
+
allowedTransitions: [{ id: "poll", mode: "AWAIT", endpoint: "GET /fiat/quotes/:quoteRef" }],
|
|
149
181
|
mayReturn: [
|
|
150
182
|
"KYC/NO_ACTION_REQUIRED:IN_REVIEW",
|
|
183
|
+
"KYC/CAPTURE",
|
|
151
184
|
"KYC/INPUT_REQUIRED",
|
|
152
185
|
"KYC/NO_ACTION_REQUIRED:ON_HOLD",
|
|
153
186
|
"KYC/NO_ACTION_REQUIRED:REJECTED",
|
|
187
|
+
"QUOTE",
|
|
188
|
+
"PAYMENT/CAPTURE",
|
|
189
|
+
"PAYMENT/INSTRUCT",
|
|
154
190
|
"ORDER/AWAITING_CONFIRMATION"
|
|
155
191
|
],
|
|
156
192
|
terminal: false,
|
|
@@ -180,20 +216,11 @@ var TRANSITION_TABLE = {
|
|
|
180
216
|
reason: "REJECTED",
|
|
181
217
|
screens: [11],
|
|
182
218
|
docStatus: "SPECIFIED",
|
|
183
|
-
allowedTransitions: [
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
mode: "SUBMIT",
|
|
187
|
-
endpoint: "GET /fiat/kyc",
|
|
188
|
-
when: "an escalation round is offered",
|
|
189
|
-
note: "TODO(open-decision): placeholder shape \u2014 the FE doc flags that a GET carrying params is not a real request shape, and OQ7 owns which rejections escalate. FE doc open question 7."
|
|
190
|
-
}
|
|
191
|
-
],
|
|
192
|
-
mayReturn: KYC_ANY,
|
|
193
|
-
terminal: false,
|
|
219
|
+
allowedTransitions: [],
|
|
220
|
+
mayReturn: [],
|
|
221
|
+
terminal: true,
|
|
194
222
|
notes: [
|
|
195
|
-
"
|
|
196
|
-
"TODO(open-decision): contract OQ7 \u2014 terminal vs escalating rejections are not enumerated."
|
|
223
|
+
"Only explicit non-retryable outcomes reach REJECTED. Correctable failures return CAPTURE or INPUT_REQUIRED; provider-specific declines re-route before becoming user rejection."
|
|
197
224
|
]
|
|
198
225
|
},
|
|
199
226
|
PENDING_ORDER: {
|
|
@@ -352,10 +379,13 @@ var DOCUMENTED_ENDPOINTS = [
|
|
|
352
379
|
"GET /fiat/payment-methods",
|
|
353
380
|
"POST /fiat/quote",
|
|
354
381
|
"POST /fiat/auth",
|
|
382
|
+
"POST /fiat/payment-session",
|
|
355
383
|
"POST /fiat/session",
|
|
356
384
|
"POST /fiat/session/verify",
|
|
357
|
-
"GET /fiat/
|
|
385
|
+
"GET /fiat/quotes/:quoteRef",
|
|
358
386
|
"POST /fiat/kyc/form",
|
|
387
|
+
"POST /fiat/kyc/capture",
|
|
388
|
+
"POST /fiat/kyc/sdk-token",
|
|
359
389
|
"POST /fiat/kyc/document",
|
|
360
390
|
"POST /fiat/instruments",
|
|
361
391
|
"POST /fiat/orders",
|
|
@@ -366,6 +396,58 @@ var DOCUMENTED_ENDPOINTS = [
|
|
|
366
396
|
"POST /fiat/orders/:id/cancel",
|
|
367
397
|
"POST /fiat/orders/:id/surface-result"
|
|
368
398
|
];
|
|
399
|
+
var UNOFFERED_ENDPOINTS = [
|
|
400
|
+
{
|
|
401
|
+
endpoint: "POST /fiat/kyc/sdk-token",
|
|
402
|
+
reason: "SDK token refresh is adapter-driven rather than a flow transition."
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
endpoint: "GET /fiat/payment-methods",
|
|
406
|
+
reason: "Discovery, called before any flow state exists \u2014 there is no state to hang it off."
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
endpoint: "POST /fiat/kyc/document",
|
|
410
|
+
reason: "FILE fields use it for a direct provider/vendor upload handshake and completion report; the harness owns that auxiliary path and it is not a transition (see KYC/INPUT_REQUIRED notes)."
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
endpoint: "POST /fiat/instruments",
|
|
414
|
+
reason: "No state offers it and no published doc explains why. TODO(open-decision): saved instruments may simply be unreached in v1 \u2014 confirm, or wire it to the state that should offer it."
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
endpoint: "GET /fiat/orders/:id/instructions",
|
|
418
|
+
reason: "PAYMENT{INSTRUCT} carries its instructions in state and offers no fetch. TODO(open-decision): that state also notes no re-issue endpoint exists, which sits oddly beside this one being published \u2014 confirm or retire it."
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
endpoint: "POST /fiat/orders/:id/reference",
|
|
422
|
+
reason: "The bank-reference submission is offered by no state. TODO(open-decision): PAYMENT{INSTRUCT} is the plausible home \u2014 confirm or wire it."
|
|
423
|
+
}
|
|
424
|
+
];
|
|
425
|
+
var UNREACHABLE_STATES = [
|
|
426
|
+
{
|
|
427
|
+
stateKey: "FUN_AUTH",
|
|
428
|
+
reason: "No bindings row published (docStatus UNSPECIFIED) and never rendered in fomo, where identity is ambient. Entered out-of-band, not by a mayReturn edge."
|
|
429
|
+
}
|
|
430
|
+
];
|
|
431
|
+
function unofferedEndpoints() {
|
|
432
|
+
const offered = /* @__PURE__ */ new Set();
|
|
433
|
+
for (const key of STATE_KEYS) {
|
|
434
|
+
for (const allowed of TRANSITION_TABLE[key].allowedTransitions) offered.add(allowed.endpoint);
|
|
435
|
+
}
|
|
436
|
+
return DOCUMENTED_ENDPOINTS.filter((endpoint) => !offered.has(endpoint));
|
|
437
|
+
}
|
|
438
|
+
function unreachableStates(from = "QUOTE") {
|
|
439
|
+
const seen = /* @__PURE__ */ new Set([from]);
|
|
440
|
+
const queue = [from];
|
|
441
|
+
while (queue.length > 0) {
|
|
442
|
+
for (const target of TRANSITION_TABLE[queue.pop()].mayReturn ?? []) {
|
|
443
|
+
if (!seen.has(target)) {
|
|
444
|
+
seen.add(target);
|
|
445
|
+
queue.push(target);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
return STATE_KEYS.filter((key) => !seen.has(key));
|
|
450
|
+
}
|
|
369
451
|
function stateKey(state) {
|
|
370
452
|
switch (state.kind) {
|
|
371
453
|
case "QUOTE":
|
|
@@ -408,8 +490,12 @@ export {
|
|
|
408
490
|
TRANSITION_TABLE,
|
|
409
491
|
STATE_KEYS,
|
|
410
492
|
DOCUMENTED_ENDPOINTS,
|
|
493
|
+
UNOFFERED_ENDPOINTS,
|
|
494
|
+
UNREACHABLE_STATES,
|
|
495
|
+
unofferedEndpoints,
|
|
496
|
+
unreachableStates,
|
|
411
497
|
stateKey,
|
|
412
498
|
tableEntry,
|
|
413
499
|
isTerminal
|
|
414
500
|
};
|
|
415
|
-
//# sourceMappingURL=chunk-
|
|
501
|
+
//# sourceMappingURL=chunk-LVVQ46YG.mjs.map
|
package/dist/fixtures/index.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* fiat-contract — recorded fixtures + loader.
|
|
3
3
|
*
|
|
4
|
-
* Every `.json` file in this directory
|
|
5
|
-
*
|
|
6
|
-
* exception is `screen-04-order-review.json`, whose payload the doc publishes *by reference*
|
|
4
|
+
* Every `.json` file in this directory records a step response from the source named in its
|
|
5
|
+
* metadata. `screen-04-order-review.json` is the one payload published *by reference*
|
|
7
6
|
* ("quote: <same shape as Screen 1, refreshed>"): its quote is Screen 1's payload verbatim.
|
|
8
7
|
* They are
|
|
9
8
|
* already synthetic (`q_8f2`, `o_31c`, `eyJ…`, `"…"` placeholders); the redaction rule applies to
|
|
@@ -15,7 +14,7 @@
|
|
|
15
14
|
* wire, and the contract has no half-response shape.
|
|
16
15
|
*/
|
|
17
16
|
import type { StateKey } from '../table';
|
|
18
|
-
export type FixtureSource = 'FE_DOC' | 'CONTRACT_DOC';
|
|
17
|
+
export type FixtureSource = 'FE_DOC' | 'CONTRACT_DOC' | 'KYC_DOC';
|
|
19
18
|
export interface FixtureMeta {
|
|
20
19
|
id: string;
|
|
21
20
|
file: string;
|