@fun-xyz/fiat-contract 0.5.0 → 0.7.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # fiat-contract
2
2
 
3
- The **published contract** between `fun-backend` (emits envelopes) and `funkit`/`connect-core`
3
+ The **published contract** between `fun-backend` (emits step responses) and `funkit`/`connect-core`
4
4
  (renders them). Both repos test against it; neither owns it.
5
5
 
6
6
  Four things, zero runtime logic beyond validation:
@@ -10,14 +10,14 @@ 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 + 16 recorded envelopes (inlined as data — no filesystem, so React Native can bundle it) |
13
+ | `src/assert.ts` + `src/fixtures/` | `assertFiatStepResponse` · `assertLegalEmission` · `assertLegalReturn` · `walkTable` · fixture loader + 16 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 |
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
21
  | `@fun-xyz/fiat-contract` | 47.6 KB, needs zod | the above + 46 zod schemas + assertions + 16 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
@@ -29,7 +29,7 @@ Metro before RN 0.79 ignores `exports` entirely — then greps the emitted bundl
29
29
  schemas and executes it.
30
30
 
31
31
  Source of truth: [Fiat Client Contract](https://app.notion.com/p/3b9fc3b2a002815eb270fa4c818268cc)
32
- (§The envelope · §Conformance package · §split `InputSpec` — ACCEPTED) and
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
34
  (✅ Decisions · per-screen State details · Event bindings per flow state).
35
35
  Tracking: [Headless Fiat Onramp](https://linear.app/funxyz/project/headless-fiat-onramp-0147f4102399) ·
@@ -39,7 +39,7 @@ this package is [ENG-5268](https://linear.app/funxyz/issue/ENG-5268).
39
39
 
40
40
  1. **Errors are fields, never states.** Every fallible state carries `error?: FailureReason`.
41
41
  Stay-on-screen ⇒ an error field. Change-screen ⇒ a different state (session expiry just returns
42
- a `SESSION_AUTH` envelope — there is no error routing table).
42
+ a `SESSION_AUTH` step response — there is no error routing table).
43
43
  2. **`params` = server literals · `inputs` = collected specs · `expects` = injected surface
44
44
  results.** `body = {…params, …collected(inputs), …injected(expects)}`; a key collision across
45
45
  the three is a contract violation, not last-write-wins. zod rejects a `FieldSpec` hiding in
@@ -74,11 +74,11 @@ removals also ride a minor, with no deprecated aliases kept — consumers pin an
74
74
  import { assertFiatStepResponse, assertLegalEmission, walkTable } from '@fun-xyz/fiat-contract';
75
75
 
76
76
  assertLegalEmission(state, transitions); // per emission: adapter conformance
77
- assertFiatStepResponse(outgoing); // outgoing-envelope validation in dev/test
77
+ assertFiatStepResponse(outgoing); // outgoing step-response validation in dev/test
78
78
  const owed = walkTable((entry) => entry.allowedTransitions); // what the adapter must emit
79
79
  ```
80
80
 
81
- Its own suites: adapter conformance (fixture-driven + property-generated states), outgoing-envelope
81
+ Its own suites: adapter conformance (fixture-driven + property-generated states), outgoing step-response
82
82
  validation, and the scheduled provider-sandbox drift run diffed against `src/fixtures`.
83
83
 
84
84
  **`connect-core`** — the harness obeys `transitions` for sequencing and owns rendering.
@@ -91,7 +91,7 @@ FIXTURES.forEach(({ id }) => renderCold(loadFixture(id))); // stale-rule
91
91
  ```
92
92
 
93
93
  Its own suites: exhaustive `computePage` table-walk, fixture-driven cold-render tests, harness units
94
- (any-state handling, same-state error re-entry retains form values, one-live-envelope focus gating,
94
+ (any-state handling, same-state error re-entry retains form values, one-live-response focus gating,
95
95
  idempotency-key reuse).
96
96
 
97
97
  ## Usage
@@ -101,7 +101,7 @@ so these examples cannot drift from the API.
101
101
 
102
102
  ### Production: a backend route handler
103
103
 
104
- The backend *builds* envelopes, so its production use is entirely compile-time — the types are the
104
+ The backend *builds* step responses, so its production use is entirely compile-time — the types are the
105
105
  verification. Runtime assertions stay behind a dev/test guard.
106
106
 
107
107
  ```ts
@@ -142,17 +142,17 @@ production-safe: types erase, `./table` is 13.8 KB with no zod.
142
142
  import { isTerminal, stateKey } from '@fun-xyz/fiat-contract/table';
143
143
  import type { FiatStepResponse } from '@fun-xyz/fiat-contract/types';
144
144
 
145
- export function FiatScreen({ env }: { env: FiatStepResponse }) {
146
- const page = computePage(stateKey(env.state), clientLocal); // client owns state → screen
147
- const done = isTerminal(env.state); // table data, never inferred
145
+ export function FiatScreen({ stepResponse }: { stepResponse: FiatStepResponse }) {
146
+ const page = computePage(stateKey(stepResponse.state), clientLocal); // client owns state → screen
147
+ const done = isTerminal(stepResponse.state); // table data, never inferred
148
148
 
149
- if (env.state.kind === 'PAYMENT' && env.state.phase === 'INSTRUCT') {
150
- return render(page, env.state.instructions); // narrowed: instructions exists here
149
+ if (stepResponse.state.kind === 'PAYMENT' && stepResponse.state.phase === 'INSTRUCT') {
150
+ return render(page, stepResponse.state.instructions); // narrowed: instructions exists here
151
151
  }
152
- if (env.state.kind === 'ORDER' && env.state.phase === 'CREATED') {
153
- return render(page, { status: env.state.status, done });
152
+ if (stepResponse.state.kind === 'ORDER' && stepResponse.state.phase === 'CREATED') {
153
+ return render(page, { status: stepResponse.state.status, done });
154
154
  }
155
- return render(page, env.state);
155
+ return render(page, stepResponse.state);
156
156
  }
157
157
  ```
158
158
 
@@ -165,11 +165,11 @@ re-derive that rule locally.
165
165
  ```ts
166
166
  import type { FiatStepResponse, Transition } from '@fun-xyz/fiat-contract/types';
167
167
 
168
- function useTransitions(env: FiatStepResponse) {
168
+ function useTransitions(stepResponse: FiatStepResponse) {
169
169
  return {
170
- ctas: env.transitions.filter((t) => t.mode === 'SUBMIT'), // render buttons
171
- poll: env.transitions.find((t) => t.mode === 'AWAIT'), // harness schedules
172
- surface: env.transitions.find((t) => t.mode === 'CLIENT_SURFACE'), // harness mounts
170
+ ctas: stepResponse.transitions.filter((t) => t.mode === 'SUBMIT'), // render buttons
171
+ poll: stepResponse.transitions.find((t) => t.mode === 'AWAIT'), // harness schedules
172
+ surface: stepResponse.transitions.find((t) => t.mode === 'CLIENT_SURFACE'), // harness mounts
173
173
  };
174
174
  }
175
175
 
@@ -191,7 +191,7 @@ Screens never inspect the array themselves; they receive `ctas` and bind labels
191
191
 
192
192
  These import from the root, which carries zod. Test-time only.
193
193
 
194
- ### Validate an envelope at the boundary
194
+ ### Validate a step response at the boundary
195
195
 
196
196
  `assertFiatStepResponse` parses and returns a typed step response, or throws `ContractViolation`
197
197
  listing every problem. Use it on the way out of `fun-backend` (dev/test) and on the way in to
@@ -201,10 +201,10 @@ listing every problem. Use it on the way out of `fun-backend` (dev/test) and on
201
201
  import { assertFiatStepResponse, ContractViolation } from '@fun-xyz/fiat-contract';
202
202
 
203
203
  try {
204
- const envelope = assertFiatStepResponse(await res.json());
204
+ const stepResponse = assertFiatStepResponse(await res.json());
205
205
  // ^? FiatStepResponse — state is a narrowable discriminated union from here on
206
- if (envelope.state.kind === 'PAYMENT' && envelope.state.phase === 'INSTRUCT') {
207
- render(envelope.state.instructions); // narrowed: instructions exists, quote does not
206
+ if (stepResponse.state.kind === 'PAYMENT' && stepResponse.state.phase === 'INSTRUCT') {
207
+ render(stepResponse.state.instructions); // narrowed: instructions exists, quote does not
208
208
  }
209
209
  } catch (err) {
210
210
  if (err instanceof ContractViolation) console.error(err.issues); // ['state.quote: Required', …]
@@ -265,16 +265,16 @@ TRANSITION_TABLE['PAYMENT/INSTRUCT'].mayReturn; // ['ORDER/CREATED']
265
265
 
266
266
  ### Render every fixture cold (stale-rule survival)
267
267
 
268
- Any call may return any state, so every screen must render from a cold envelope with no prior
269
- context. The fixtures are the FE doc's own envelopes, so this is a test against the spec.
268
+ Any call may return any state, so every screen must render from a cold step response with no
269
+ prior context. The fixtures are the FE doc's own responses, so this is a test against the spec.
270
270
 
271
271
  ```ts
272
272
  import { FIXTURES, loadFixture, assertFixture } from '@fun-xyz/fiat-contract';
273
273
 
274
274
  FIXTURES.forEach(({ id, stateKey, docRef }) => {
275
275
  it(`${id} renders cold (${docRef})`, () => {
276
- const { envelope } = assertFixture(id); // validated + emission-legality checked
277
- expect(() => renderCold(envelope)).not.toThrow();
276
+ const { stepResponse } = assertFixture(id); // validated + emission-legality checked
277
+ expect(() => renderCold(stepResponse)).not.toThrow();
278
278
  expect(computePage(stateKey)).toBeDefined();
279
279
  });
280
280
  });
@@ -284,7 +284,7 @@ loadFixture('screen-10-kyc-on-hold'); // raw JSON, fresh deep copy, `unknown`
284
284
 
285
285
  ### Use a schema directly
286
286
 
287
- All 46 schemas are exported when you need to validate a fragment rather than a whole envelope.
287
+ All 46 schemas are exported when you need to validate a fragment rather than a whole step response.
288
288
  They are typed `z.ZodType<T>`, so you get `.parse` / `.safeParse` / `.optional()` — not `.shape` or
289
289
  `.extend`, deliberately.
290
290
 
@@ -435,7 +435,7 @@ The current documented shape is what ships; none of these are settled here.
435
435
 
436
436
  | Open item | How 0.1.0 encodes it |
437
437
  | --- | --- |
438
- | `orderId` placement | Both: optional on `PAYMENT{INSTRUCT}` (FE doc v0) **and** optional as an envelope sibling (contract worked example). One fixture of each. |
438
+ | `orderId` placement | Both: optional on `PAYMENT{INSTRUCT}` (FE doc v0) **and** optional beside `state` (contract worked example). One fixture of each. |
439
439
  | `PENDING_ORDER` removal | Kind ships, with the removal proposal flagged on the type, the table entry, and the fixture. Screen 12 stays frozen. |
440
440
  | `[OQ7]` failure enumeration | The published `FailureReason` taxonomy only. Expired instructions, partial payment, per-rail cancel eligibility, and terminal-vs-escalating rejections are flagged unenumerated. |
441
441
  | Screen 11 escalation trigger | Encoded as published (`SUBMIT GET /fiat/kyc` + `params: {tier}`) with the doc's own warning that a GET carrying params is not a real shape. |
@@ -443,11 +443,11 @@ The current documented shape is what ships; none of these are settled here.
443
443
  | Cancel placement | `cancel` is legal on `PAYMENT{INSTRUCT}` and `ORDER{CREATED}`, marked conditional on the placement decision. |
444
444
  | `FUN_AUTH` shape | `challenge: Record<string, JsonValue>`; the table entry is `docStatus: 'UNSPECIFIED'`, so `assertLegalEmission` reports it unjudgeable instead of guessing. |
445
445
  | `statusHistory` element shape | The documented minimum (`{status}`) — no invented timestamps. |
446
- | `QR_IMAGE` instruction | In the union per §The envelope, flagged against OQ1's "deliberately not pre-declared". |
446
+ | `QR_IMAGE` instruction | In the union per §The fiat step response, flagged against OQ1's "deliberately not pre-declared". |
447
447
 
448
- Two fixture gaps are declared in `FIXTURE_COVERAGE_GAPS` rather than filled with invented envelopes:
448
+ Two fixture gaps are declared in `FIXTURE_COVERAGE_GAPS` rather than filled with invented step responses:
449
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 envelope.
450
+ Every other state has a recorded step response.
451
451
 
452
452
  ## Not decided here — needs a human
453
453
 
package/dist/assert.d.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  * exactly two things: validate shapes (zod) and judge emissions against the table.
4
4
  *
5
5
  * Test *suites* live in the consumer repos next to the code they test:
6
- * - `fun-backend`: adapter conformance (every emission ∈ the legal set), outgoing-envelope
6
+ * - `fun-backend`: adapter conformance (every emission ∈ the legal set), outgoing step-response
7
7
  * validation in dev/test, the scheduled provider drift run.
8
8
  * - `connect-core`: exhaustive `computePage` table-walk, fixture-driven cold-render tests,
9
9
  * harness units.
@@ -49,7 +49,7 @@ export declare function assertLegalReturn(from: FlowState, to: FlowState): void;
49
49
  export declare function walkTable<T>(fn: (entry: TableEntry, key: StateKey) => T): T[];
50
50
  export interface ValidatedFixture {
51
51
  meta: FixtureMeta;
52
- envelope: FiatStepResponse;
52
+ stepResponse: FiatStepResponse;
53
53
  }
54
54
  /** Load one fixture, validate its shape, and assert its emission is legal for its state. */
55
55
  export declare function assertFixture(id: string): ValidatedFixture;
@@ -1,5 +1,5 @@
1
1
  // package.json
2
- var version = "0.5.0";
2
+ var version = "0.7.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
- 'If the PENDING_ORDER removal lands, QUOTE additionally returns PAYMENT{INSTRUCT} / ORDER{CREATED} directly. TODO(open-decision): contract \xA7"Proposal: remove PENDING_ORDER".'
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: {
@@ -352,6 +378,7 @@ var DOCUMENTED_ENDPOINTS = [
352
378
  "GET /fiat/payment-methods",
353
379
  "POST /fiat/quote",
354
380
  "POST /fiat/auth",
381
+ "POST /fiat/payment-session",
355
382
  "POST /fiat/session",
356
383
  "POST /fiat/session/verify",
357
384
  "GET /fiat/kyc",
@@ -366,6 +393,54 @@ var DOCUMENTED_ENDPOINTS = [
366
393
  "POST /fiat/orders/:id/cancel",
367
394
  "POST /fiat/orders/:id/surface-result"
368
395
  ];
396
+ var UNOFFERED_ENDPOINTS = [
397
+ {
398
+ endpoint: "GET /fiat/payment-methods",
399
+ reason: "Discovery, called before any flow state exists \u2014 there is no state to hang it off."
400
+ },
401
+ {
402
+ endpoint: "POST /fiat/kyc/document",
403
+ reason: "FILE fields stream to it directly; the harness owns that upload path and it is not a transition (see KYC/INPUT_REQUIRED notes)."
404
+ },
405
+ {
406
+ endpoint: "POST /fiat/instruments",
407
+ 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."
408
+ },
409
+ {
410
+ endpoint: "GET /fiat/orders/:id/instructions",
411
+ 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."
412
+ },
413
+ {
414
+ endpoint: "POST /fiat/orders/:id/reference",
415
+ reason: "The bank-reference submission is offered by no state. TODO(open-decision): PAYMENT{INSTRUCT} is the plausible home \u2014 confirm or wire it."
416
+ }
417
+ ];
418
+ var UNREACHABLE_STATES = [
419
+ {
420
+ stateKey: "FUN_AUTH",
421
+ 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."
422
+ }
423
+ ];
424
+ function unofferedEndpoints() {
425
+ const offered = /* @__PURE__ */ new Set();
426
+ for (const key of STATE_KEYS) {
427
+ for (const allowed of TRANSITION_TABLE[key].allowedTransitions) offered.add(allowed.endpoint);
428
+ }
429
+ return DOCUMENTED_ENDPOINTS.filter((endpoint) => !offered.has(endpoint));
430
+ }
431
+ function unreachableStates(from = "QUOTE") {
432
+ const seen = /* @__PURE__ */ new Set([from]);
433
+ const queue = [from];
434
+ while (queue.length > 0) {
435
+ for (const target of TRANSITION_TABLE[queue.pop()].mayReturn ?? []) {
436
+ if (!seen.has(target)) {
437
+ seen.add(target);
438
+ queue.push(target);
439
+ }
440
+ }
441
+ }
442
+ return STATE_KEYS.filter((key) => !seen.has(key));
443
+ }
369
444
  function stateKey(state) {
370
445
  switch (state.kind) {
371
446
  case "QUOTE":
@@ -408,8 +483,12 @@ export {
408
483
  TRANSITION_TABLE,
409
484
  STATE_KEYS,
410
485
  DOCUMENTED_ENDPOINTS,
486
+ UNOFFERED_ENDPOINTS,
487
+ UNREACHABLE_STATES,
488
+ unofferedEndpoints,
489
+ unreachableStates,
411
490
  stateKey,
412
491
  tableEntry,
413
492
  isTerminal
414
493
  };
415
- //# sourceMappingURL=chunk-3R5GGUAK.mjs.map
494
+ //# sourceMappingURL=chunk-OGNYHIOE.mjs.map
@@ -1,18 +1,18 @@
1
1
  /**
2
2
  * fiat-contract — recorded fixtures + loader.
3
3
  *
4
- * Every `.json` file in this directory is an envelope copied **verbatim** from the docs — the FE
5
- * doc's per-screen "State details" envelopes plus the contract doc's worked example. The one
4
+ * Every `.json` file in this directory is a step response copied **verbatim** from the docs — the
5
+ * FE doc's per-screen "State details" responses plus the contract doc's worked example. The one
6
6
  * exception is `screen-04-order-review.json`, whose payload the doc publishes *by reference*
7
7
  * ("quote: <same shape as Screen 1, refreshed>"): its quote is Screen 1's payload verbatim.
8
8
  * They are
9
9
  * already synthetic (`q_8f2`, `o_31c`, `eyJ…`, `"…"` placeholders); the redaction rule applies to
10
10
  * fixtures too, so never replace a fake session token, bank field, or PII value with a real one.
11
11
  *
12
- * Every fixture is a full envelope (`state` + `provider` + `transitions`). The contract doc's worked
12
+ * Every fixture is a full step response (`state` + `provider` + `transitions`). The contract doc's worked
13
13
  * example elides `provider` in its prose; the three `worked-example-*` fixtures add
14
14
  * `"provider": "TRANSAK"` so they validate as real responses — `provider` is always present on the
15
- * wire, and the contract has no half-envelope shape.
15
+ * wire, and the contract has no half-response shape.
16
16
  */
17
17
  import type { StateKey } from '../table';
18
18
  export type FixtureSource = 'FE_DOC' | 'CONTRACT_DOC';
@@ -20,7 +20,7 @@ export interface FixtureMeta {
20
20
  id: string;
21
21
  file: string;
22
22
  source: FixtureSource;
23
- /** Where in the source doc this envelope is published. */
23
+ /** Where in the source doc this step response is published. */
24
24
  docRef: string;
25
25
  /** FE doc screen number, when the fixture comes from a screen card. */
26
26
  screen?: number;
@@ -29,7 +29,7 @@ export interface FixtureMeta {
29
29
  }
30
30
  export declare const FIXTURES: readonly FixtureMeta[];
31
31
  /**
32
- * State entries with no published envelope to record. Declared so missing coverage is visible
32
+ * State entries with no published step response to record. Declared so missing coverage is visible
33
33
  * instead of silent — the fixture test asserts coverage equals (all state keys − these).
34
34
  */
35
35
  export declare const FIXTURE_COVERAGE_GAPS: readonly {
@@ -38,7 +38,7 @@ export declare const FIXTURE_COVERAGE_GAPS: readonly {
38
38
  }[];
39
39
  export declare const fixtureMeta: (id: string) => FixtureMeta;
40
40
  /**
41
- * Raw envelope — deliberately `unknown`, so callers validate before use.
41
+ * Raw step response — deliberately `unknown`, so callers validate before use.
42
42
  *
43
43
  * Reads from the generated `data.ts`: no filesystem, so this works under React Native, in a
44
44
  * browser, and in both CJS and ESM output. A fresh deep copy each call, so a consumer mutating a
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * fiat-contract — the published contract between `fun-backend` (emits envelopes) and
2
+ * fiat-contract — the published contract between `fun-backend` (emits step responses) and
3
3
  * `funkit`/`connect-core` (renders them). Types + zod schemas + the transition table as data +
4
4
  * recorded fixtures + assertion helpers. No runtime logic beyond validation and assertion.
5
5
  *