@fun-xyz/fiat-contract 0.5.0 → 0.6.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,7 +10,7 @@ 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
 
@@ -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.6.0";
3
3
 
4
4
  // src/table.ts
5
5
  var TABLE_VERSION = version;
@@ -412,4 +412,4 @@ export {
412
412
  tableEntry,
413
413
  isTerminal
414
414
  };
415
- //# sourceMappingURL=chunk-3R5GGUAK.mjs.map
415
+ //# sourceMappingURL=chunk-EHYDQU4T.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
  *
package/dist/index.js CHANGED
@@ -718,7 +718,7 @@ var $stepResponse = obj({
718
718
  var FiatStepResponseSchema = $stepResponse;
719
719
 
720
720
  // package.json
721
- var version = "0.5.0";
721
+ var version = "0.6.0";
722
722
 
723
723
  // src/table.ts
724
724
  var TABLE_VERSION = version;
@@ -1766,18 +1766,18 @@ var FIXTURES = [
1766
1766
  docRef: "\xA7Worked example, steps 4\u20135 (order + bank instructions)",
1767
1767
  stateKey: "PAYMENT/INSTRUCT",
1768
1768
  notes: [
1769
- "The envelope-sibling `orderId` case. TODO(open-decision): orderId placement (envelope sibling here vs inside state in the FE doc v0)."
1769
+ "The response-level `orderId` case. TODO(open-decision): orderId placement (beside `state` here vs inside it in the FE doc v0)."
1770
1770
  ]
1771
1771
  }
1772
1772
  ];
1773
1773
  var FIXTURE_COVERAGE_GAPS = [
1774
1774
  {
1775
1775
  stateKey: "FUN_AUTH",
1776
- reason: "No envelope exists in either doc \u2014 shape owned by the auth spike, and FUN_AUTH never renders in fomo. TODO(open-decision): contract \xA7Auth & ref binding, item 5."
1776
+ reason: "No step response exists in either doc \u2014 shape owned by the auth spike, and FUN_AUTH never renders in fomo. TODO(open-decision): contract \xA7Auth & ref binding, item 5."
1777
1777
  },
1778
1778
  {
1779
1779
  stateKey: "KYC/CAPTURE",
1780
- reason: "Dropped from v1 (FE Screen 8 tombstone, no UK module coverage); no envelope was ever published. Kind retained as handshake-gated vocabulary."
1780
+ reason: "Dropped from v1 (FE Screen 8 tombstone, no UK module coverage); no step response was ever published. Kind retained as handshake-gated vocabulary."
1781
1781
  }
1782
1782
  ];
1783
1783
  var fixtureMeta = (id) => {
@@ -1815,7 +1815,7 @@ var formatIssues = (error) => {
1815
1815
  };
1816
1816
  function assertFiatStepResponse(json) {
1817
1817
  const result = FiatStepResponseSchema.safeParse(json);
1818
- if (!result.success) throw new ContractViolation("invalid envelope", formatIssues(result.error));
1818
+ if (!result.success) throw new ContractViolation("invalid fiat step response", formatIssues(result.error));
1819
1819
  return result.data;
1820
1820
  }
1821
1821
  function endpointMatches(template, actual) {
@@ -1912,14 +1912,14 @@ function walkTable(fn) {
1912
1912
  function assertFixture(id) {
1913
1913
  const meta = fixtureMeta(id);
1914
1914
  const json = loadFixture(id);
1915
- const envelope = assertFiatStepResponse(json);
1916
- if (stateKey(envelope.state) !== meta.stateKey) {
1915
+ const stepResponse = assertFiatStepResponse(json);
1916
+ if (stateKey(stepResponse.state) !== meta.stateKey) {
1917
1917
  throw new ContractViolation(
1918
- `fixture ${id}: manifest says ${meta.stateKey}, envelope carries ${stateKey(envelope.state)}`
1918
+ `fixture ${id}: manifest says ${meta.stateKey}, step response carries ${stateKey(stepResponse.state)}`
1919
1919
  );
1920
1920
  }
1921
- assertLegalEmission(envelope.state, envelope.transitions);
1922
- return { meta, envelope };
1921
+ assertLegalEmission(stepResponse.state, stepResponse.transitions);
1922
+ return { meta, stepResponse };
1923
1923
  }
1924
1924
  function assertAllFixtures() {
1925
1925
  return FIXTURES.map((meta) => assertFixture(meta.id));
package/dist/index.mjs CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  isTerminal,
9
9
  stateKey,
10
10
  tableEntry
11
- } from "./chunk-3R5GGUAK.mjs";
11
+ } from "./chunk-EHYDQU4T.mjs";
12
12
 
13
13
  // src/codes.ts
14
14
  var COUNTRY_CURRENCY_LIST = [
@@ -1273,18 +1273,18 @@ var FIXTURES = [
1273
1273
  docRef: "\xA7Worked example, steps 4\u20135 (order + bank instructions)",
1274
1274
  stateKey: "PAYMENT/INSTRUCT",
1275
1275
  notes: [
1276
- "The envelope-sibling `orderId` case. TODO(open-decision): orderId placement (envelope sibling here vs inside state in the FE doc v0)."
1276
+ "The response-level `orderId` case. TODO(open-decision): orderId placement (beside `state` here vs inside it in the FE doc v0)."
1277
1277
  ]
1278
1278
  }
1279
1279
  ];
1280
1280
  var FIXTURE_COVERAGE_GAPS = [
1281
1281
  {
1282
1282
  stateKey: "FUN_AUTH",
1283
- reason: "No envelope exists in either doc \u2014 shape owned by the auth spike, and FUN_AUTH never renders in fomo. TODO(open-decision): contract \xA7Auth & ref binding, item 5."
1283
+ reason: "No step response exists in either doc \u2014 shape owned by the auth spike, and FUN_AUTH never renders in fomo. TODO(open-decision): contract \xA7Auth & ref binding, item 5."
1284
1284
  },
1285
1285
  {
1286
1286
  stateKey: "KYC/CAPTURE",
1287
- reason: "Dropped from v1 (FE Screen 8 tombstone, no UK module coverage); no envelope was ever published. Kind retained as handshake-gated vocabulary."
1287
+ reason: "Dropped from v1 (FE Screen 8 tombstone, no UK module coverage); no step response was ever published. Kind retained as handshake-gated vocabulary."
1288
1288
  }
1289
1289
  ];
1290
1290
  var fixtureMeta = (id) => {
@@ -1322,7 +1322,7 @@ var formatIssues = (error) => {
1322
1322
  };
1323
1323
  function assertFiatStepResponse(json) {
1324
1324
  const result = FiatStepResponseSchema.safeParse(json);
1325
- if (!result.success) throw new ContractViolation("invalid envelope", formatIssues(result.error));
1325
+ if (!result.success) throw new ContractViolation("invalid fiat step response", formatIssues(result.error));
1326
1326
  return result.data;
1327
1327
  }
1328
1328
  function endpointMatches(template, actual) {
@@ -1419,14 +1419,14 @@ function walkTable(fn) {
1419
1419
  function assertFixture(id) {
1420
1420
  const meta = fixtureMeta(id);
1421
1421
  const json = loadFixture(id);
1422
- const envelope = assertFiatStepResponse(json);
1423
- if (stateKey(envelope.state) !== meta.stateKey) {
1422
+ const stepResponse = assertFiatStepResponse(json);
1423
+ if (stateKey(stepResponse.state) !== meta.stateKey) {
1424
1424
  throw new ContractViolation(
1425
- `fixture ${id}: manifest says ${meta.stateKey}, envelope carries ${stateKey(envelope.state)}`
1425
+ `fixture ${id}: manifest says ${meta.stateKey}, step response carries ${stateKey(stepResponse.state)}`
1426
1426
  );
1427
1427
  }
1428
- assertLegalEmission(envelope.state, envelope.transitions);
1429
- return { meta, envelope };
1428
+ assertLegalEmission(stepResponse.state, stepResponse.transitions);
1429
+ return { meta, stepResponse };
1430
1430
  }
1431
1431
  function assertAllFixtures() {
1432
1432
  return FIXTURES.map((meta) => assertFixture(meta.id));
package/dist/schemas.d.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * fiat-contract — zod schemas
3
3
  *
4
4
  * zod is the single runtime validator: TypeScript cannot validate at the boundary, and the
5
- * table (`src/table.ts`) cannot reject a *malformed* envelope — only an illegal *sequence*.
5
+ * table (`src/table.ts`) cannot reject a *malformed* step response — only an illegal *sequence*.
6
6
  *
7
7
  * Every object schema is `.strict()`: an undeclared key is a contract violation, not extra data.
8
8
  *
package/dist/table.d.ts CHANGED
@@ -10,7 +10,7 @@
10
10
  *
11
11
  * Rules the entries obey:
12
12
  * - Terminality of `ORDER{CREATED}` rides `status`, because terminal outcomes are OrderStatus,
13
- * never kinds (contract §The envelope). The set is enumerated below as
13
+ * never kinds (contract §The fiat step response). The set is enumerated below as
14
14
  * `TERMINAL_ORDER_STATUSES` — clients read it, they never derive it.
15
15
  * - A terminal *state* carries no transitions. A terminal *status* still can: `FAILED` with a
16
16
  * retryable `failureReason` carries the recovery CTA (FE doc Screen 14).
@@ -32,7 +32,7 @@ export declare const TABLE_VERSION: string;
32
32
  */
33
33
  export type StateKey = 'QUOTE' | 'FUN_AUTH' | 'SESSION_AUTH' | 'KYC/CAPTURE' | 'KYC/INPUT_REQUIRED' | 'KYC/NO_ACTION_REQUIRED:IN_REVIEW' | 'KYC/NO_ACTION_REQUIRED:ON_HOLD' | 'KYC/NO_ACTION_REQUIRED:REJECTED' | 'PENDING_ORDER' | 'BLOCKED' | 'PAYMENT/CAPTURE' | 'PAYMENT/INSTRUCT' | 'ORDER/AWAITING_CONFIRMATION' | 'ORDER/CREATED';
34
34
  /**
35
- * Terminal per the OrderStatus machine's monotonic ranks (contract §The envelope).
35
+ * Terminal per the OrderStatus machine's monotonic ranks (contract §The fiat step response).
36
36
  *
37
37
  * `CAPTURE_ORPHANED` is deliberately absent — see the TODO(open-decision) on the value itself.
38
38
  * Non-terminal is the safe default for an unknown status: a client that wrongly believes the flow
@@ -48,7 +48,7 @@ export type TerminalOrderStatus = (typeof TERMINAL_ORDER_STATUSES)[number];
48
48
  export type EndpointTemplate = FiatEndpoint;
49
49
  export interface AllowedTransition {
50
50
  /**
51
- * The id observed in the docs' envelopes. Informational: ids are localization keys and the
51
+ * The id observed in the docs' step responses. Informational: ids are localization keys and the
52
52
  * docs explicitly allow unknown ids (the client falls back to `labelFallback`), so
53
53
  * `assertLegalEmission` matches on `mode` + `endpoint`, not on `id`.
54
54
  */
package/dist/table.js CHANGED
@@ -32,7 +32,7 @@ __export(table_exports, {
32
32
  module.exports = __toCommonJS(table_exports);
33
33
 
34
34
  // package.json
35
- var version = "0.5.0";
35
+ var version = "0.6.0";
36
36
 
37
37
  // src/table.ts
38
38
  var TABLE_VERSION = version;
package/dist/table.mjs CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  isTerminal,
8
8
  stateKey,
9
9
  tableEntry
10
- } from "./chunk-3R5GGUAK.mjs";
10
+ } from "./chunk-EHYDQU4T.mjs";
11
11
  export {
12
12
  DOCUMENTED_ENDPOINTS,
13
13
  STATE_KEYS,
package/dist/types.d.ts CHANGED
@@ -1,13 +1,13 @@
1
1
  /**
2
2
  * fiat-contract — types
3
3
  *
4
- * The published contract between `fun-backend` (emits envelopes) and
4
+ * The published contract between `fun-backend` (emits step responses) and
5
5
  * `funkit`/`connect-core` (renders them). Zero runtime logic lives here.
6
6
  *
7
7
  * Sources of truth (do not extend this file from anywhere else):
8
8
  * - Contract doc: Fiat Client Contract — Step-Driven Flow & Endpoint Schematics
9
9
  * https://app.notion.com/p/3b9fc3b2a002815eb270fa4c818268cc
10
- * §"The envelope" · §"Conformance package — concrete spec"
10
+ * §"The fiat step response" · §"Conformance package — concrete spec"
11
11
  * §"Proposal (2026-08-13): split InputSpec → params + inputs + expects" (ACCEPTED)
12
12
  * - FE doc: Fiat Frontend — State Machine & Screen Map
13
13
  * https://app.notion.com/p/3bbfc3b2a00281c994c2cebd17b1d6d3
@@ -59,12 +59,12 @@ export type HttpVerb = 'GET' | 'POST';
59
59
  export type FiatEndpoint = 'GET /fiat/payment-methods' | 'POST /fiat/quote' | 'POST /fiat/auth' | 'POST /fiat/payment-session' | 'POST /fiat/session' | 'POST /fiat/session/verify' | 'GET /fiat/kyc' | 'POST /fiat/kyc/form' | 'POST /fiat/kyc/document' | 'POST /fiat/instruments' | 'POST /fiat/orders' | `GET /fiat/orders/${string}` | `GET /fiat/orders/${string}/instructions` | `POST /fiat/orders/${string}/confirm-payment` | `POST /fiat/orders/${string}/reference` | `POST /fiat/orders/${string}/cancel` | `POST /fiat/orders/${string}/surface-result`;
60
60
  /**
61
61
  * FiatProvider is metadata: it selects the embedded provider SDK for CLIENT_SURFACE steps and
62
- * feeds analytics. It never drives sequencing (contract §The envelope).
62
+ * feeds analytics. It never drives sequencing (contract §The fiat step response).
63
63
  * TODO(open-decision): the docs name only Transak as a v1 provider for the headless flow
64
64
  * (contract §Decisions 3; Swapped-headless does not exist yet, Banxa is Tradeoff #4).
65
65
  * `SWAPPED` added at fun-backend's request (2026-08-17) so its legacy fops payment-provider id
66
66
  * has one shared vocabulary instead of a second fun-backend-local enum — it never appears as a
67
- * `FiatStepResponse.provider` value since Swapped's fops flow doesn't emit this envelope.
67
+ * `FiatStepResponse.provider` value since Swapped's fops flow doesn't emit this response.
68
68
  */
69
69
  export type FiatProvider = 'TRANSAK' | 'SWAPPED';
70
70
  /**
@@ -101,7 +101,7 @@ export type FailureCategory = 'PAYMENT_DECLINED' | 'KYC_REJECTED' | 'PROVIDER_OU
101
101
  * (the FE doc's Screen 11 note writes `SUPPORT` as shorthand — same value).
102
102
  */
103
103
  export type Recovery = 'REQUOTE' | 'RETRY_PAYMENT' | 'CONTACT_SUPPORT' | 'NONE';
104
- /** Three expiries, three recoveries (contract §The envelope, FailureReason comment). */
104
+ /** Three expiries, three recoveries (contract §The fiat step response, FailureReason comment). */
105
105
  export type ExpiredWhich = 'QUOTE' | 'PAYMENT_WINDOW' | 'INSTRUCTIONS';
106
106
  /**
107
107
  * `code?` is a copy key (e.g. `BAD_CODE`), not an error code to branch on.
@@ -121,7 +121,7 @@ export type FailureReason = {
121
121
  code?: string;
122
122
  };
123
123
  /**
124
- * Amounts are decimal strings in the docs' envelopes ("100.00") — never numbers.
124
+ * Amounts are decimal strings in the docs' step responses ("100.00") — never numbers.
125
125
  * Named to pair with `CryptoAmount`: every use site is a `fiat:` field.
126
126
  * `currency` is ISO 4217 — see `./codes`.
127
127
  */
@@ -172,7 +172,7 @@ export interface OrderRef {
172
172
  }
173
173
  /**
174
174
  * Payload of `ORDER{CREATED}` (FE doc Screens 7 · 13 · 14 · 15).
175
- * `crypto` is optional because the Screen 14/15 envelopes omit it (fiat-only failure/refund views).
175
+ * `crypto` is optional because the Screen 14/15 responses omit it (fiat-only failure/refund views).
176
176
  */
177
177
  export interface OrderSummary {
178
178
  orderId: string;
@@ -228,8 +228,8 @@ export interface InstructionField {
228
228
  /**
229
229
  * Payload of `PAYMENT{INSTRUCT}` — push rails. Values are PII-adjacent: never logged.
230
230
  * TODO(open-decision): contract OQ1 lists `QR_IMAGE` as *deliberately not pre-declared* in the
231
- * v1 vocabulary while §The envelope's Instructions bullet enumerates it. Kept in the union per
232
- * the envelope section (and the conformance-package spec); handshake-gate it if OQ1 lands the
231
+ * v1 vocabulary while §The fiat step response's Instructions bullet enumerates it. Kept in the union
232
+ * per that section (and the conformance-package spec); handshake-gate it if OQ1 lands the
233
233
  * other way.
234
234
  * TODO(open-decision): contract OQ7 — expired instructions have no documented recovery and no
235
235
  * re-issue endpoint exists.
@@ -437,7 +437,7 @@ export type FlowState = {
437
437
  * empty transitions, NOT terminal.
438
438
  * - `OrderStatus.ON_HOLD` = post-order compliance or amount mismatch, resolves to
439
439
  * `PROCESSING` or `REFUNDED`.
440
- * They cannot be confused in code — different types, different positions in the envelope
440
+ * They cannot be confused in code — different types, different positions in the response
441
441
  * but they can be confused in conversation, so say which one you mean.
442
442
  */
443
443
  reason: 'IN_REVIEW' | 'ON_HOLD' | 'REJECTED';
@@ -480,7 +480,7 @@ export type FlowState = {
480
480
  kind: 'PAYMENT';
481
481
  phase: 'INSTRUCT';
482
482
  /**
483
- * TODO(open-decision): `orderId` placement — envelope sibling (contract §Worked example,
483
+ * TODO(open-decision): `orderId` placement — beside `state` (contract §Worked example,
484
484
  * steps 4–5) vs inside state (FE doc v0, Screen 6). Both are accepted here; the FE doc's
485
485
  * in-state placement is the current shape, `FiatStepResponse.orderId` covers the other.
486
486
  */
@@ -495,7 +495,7 @@ export type FlowState = {
495
495
  } | {
496
496
  kind: 'ORDER';
497
497
  phase: 'CREATED';
498
- /** Absent in the contract's terse worked-example envelopes, present in every FE screen. */
498
+ /** Absent in the contract's terse worked-example responses, present in every FE screen. */
499
499
  order?: OrderSummary;
500
500
  status: OrderStatus;
501
501
  statusHistory?: StatusHistoryEntry[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fun-xyz/fiat-contract",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Published conformance contract for Fun's headless fiat onramp: FlowState/Transition types, zod schemas, the transition table as data, recorded fixtures, assertion helpers.",
5
5
  "license": "MIT",
6
6
  "repository": {