@fun-xyz/fiat-contract 0.2.1

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.
@@ -0,0 +1,486 @@
1
+ /**
2
+ * fiat-contract — types
3
+ *
4
+ * The published contract between `fun-backend` (emits envelopes) and
5
+ * `funkit`/`connect-core` (renders them). Zero runtime logic lives here.
6
+ *
7
+ * Sources of truth (do not extend this file from anywhere else):
8
+ * - Contract doc: Fiat Client Contract — Step-Driven Flow & Endpoint Schematics
9
+ * https://app.notion.com/p/3b9fc3b2a002815eb270fa4c818268cc
10
+ * §"The envelope" · §"Conformance package — concrete spec"
11
+ * §"Proposal (2026-08-13): split InputSpec → params + inputs + expects" (ACCEPTED)
12
+ * - FE doc: Fiat Frontend — State Machine & Screen Map
13
+ * https://app.notion.com/p/3bbfc3b2a00281c994c2cebd17b1d6d3
14
+ * §"✅ Decisions" · per-screen "State details" · §"Event bindings per flow state"
15
+ *
16
+ * Litmus rules encoded here:
17
+ * - errors are fields, never states (`error?: FailureReason` on every fallible state)
18
+ * - `params` = server literals · `inputs` = collected FieldSpecs · `expects` = injected surface results
19
+ * - terminality comes from the table (`src/table.ts`), never from `transitions.length`
20
+ */
21
+ /**
22
+ * JSON value. `params` carry these — opaque literals the client spreads verbatim.
23
+ * Named `JsonValue` rather than `Json` because this package's surface is re-exported wholesale;
24
+ * a bare `Json` is a generic name to put in a consumer's import scope for no benefit.
25
+ */
26
+ export type JsonValue = string | number | boolean | null | JsonValue[] | {
27
+ [key: string]: JsonValue;
28
+ };
29
+ /**
30
+ * The harness parses the verb off the endpoint string — SUBMIT is not "POST".
31
+ *
32
+ * Defined locally on purpose: neither the TS lib nor `@types/node` publishes an HTTP-method union
33
+ * (`http.METHODS` is a runtime `string[]`), and the one in `fun-backend`'s `backend-utils`
34
+ * (`GET | PATCH | POST | PUT | DELETE`) is not exported from that package's root — and depending on
35
+ * the backend would invert this package's direction anyway. Deliberately two members, not five: the
36
+ * `/fiat/*` surface uses only these, and a wider union would let the backend emit a verb the
37
+ * harness has no path for.
38
+ */
39
+ export type HttpVerb = 'GET' | 'POST';
40
+ /**
41
+ * Endpoint strings as emitted: `"POST /fiat/session/verify"`, `"GET /fiat/orders/o_31c"`.
42
+ *
43
+ * A **bounded** union of the documented `/fiat/*` surface (contract §Endpoints), not the open
44
+ * `` `${HttpVerb} /fiat/${string}` `` it started as: bounded catches a typo'd endpoint family
45
+ * (`POST /fiat/orderz`) at compile time, and measured *cheaper* than the open pattern
46
+ * (77,680 vs 77,688 types across this package).
47
+ *
48
+ * On the fun-backend#3984 hazard (unbounded pattern prefixes OOM'd every build at 1.1M types):
49
+ * that blowup came from pattern types keying **mapped types**, where every `keyof` and indexed
50
+ * access re-expands across all members. This type is only ever a field value — never in `keyof`,
51
+ * never a `Record` key, never a mapped-type key — so assignability is a single match per member.
52
+ * Keep it that way: `Record<FiatEndpoint, …>` is the shape that would make this expensive.
53
+ *
54
+ * Per-order paths stay patterns because the id is runtime data (`o_31c`); TS template literals
55
+ * cannot express "no slash", so `GET /fiat/orders/${string}` also admits sub-paths. Precision at
56
+ * that level is the table's job (`allowedTransitions` + `endpointMatches`), not the type's.
57
+ */
58
+ export type FiatEndpoint = 'GET /fiat/payment-methods' | 'POST /fiat/quote' | 'POST /fiat/auth' | '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`;
59
+ /**
60
+ * FiatProvider is metadata: it selects the embedded provider SDK for CLIENT_SURFACE steps and
61
+ * feeds analytics. It never drives sequencing (contract §The envelope).
62
+ * TODO(open-decision): the docs name only Transak as a v1 provider for the headless flow
63
+ * (contract §Decisions 3; Swapped-headless does not exist yet, Banxa is Tradeoff #4).
64
+ * `SWAPPED` added at fun-backend's request (2026-08-17) so its legacy fops payment-provider id
65
+ * has one shared vocabulary instead of a second fun-backend-local enum — it never appears as a
66
+ * `StepResponse.provider` value since Swapped's fops flow doesn't emit this envelope.
67
+ */
68
+ export type FiatProvider = 'TRANSAK' | 'SWAPPED';
69
+ /**
70
+ * The order status vocabulary. Monotonic ranks, no backwards moves. Terminal outcomes are
71
+ * OrderStatus, never FlowState kinds.
72
+ *
73
+ * **This package is the source of truth** (Charles, review 2026-08-14): `fun-backend` should
74
+ * reference these values for the `fiat_orders` table rather than declaring its own copy, so the
75
+ * enum cannot drift between the wire contract and the database.
76
+ *
77
+ * The enum mirrors the backend's real statuses as closely as possible; presentation mapping is the
78
+ * client's job (Jeremy, same thread) — the FE may collapse several of these into one visual state,
79
+ * but the contract does not pre-collapse them.
80
+ */
81
+ export type OrderStatus = 'CREATING' | 'AWAITING_PAYMENT' | 'PAYMENT_VERIFYING' | 'PROCESSING' | 'PENDING_DELIVERY' | 'ON_HOLD' | 'CRYPTO_SENT' | 'SETTLED' | 'EXPIRED' | 'CANCELLED' | 'FAILED' | 'REFUNDED'
82
+ /**
83
+ * A capture that never became an order — the pull-rail orphan (contract Flow A's
84
+ * capture-then-order topology: the component yields a `requestId`, then `createOrder` runs).
85
+ *
86
+ * Added from Charles's review (2026-08-14): it exists in the backend's status set and was missing
87
+ * here. Encoded as a first-class value rather than pre-mapped to `PROCESSING`, because the enum
88
+ * should mirror the source of truth and let the client collapse it for display.
89
+ *
90
+ * TODO(open-decision): its semantics, its position in the monotonic ranks, and its recovery are
91
+ * not published in either doc — `domains/fiat` has not landed in `fun-backend` yet. Treated as
92
+ * NON-terminal here (see `TERMINAL_ORDER_STATUSES`), which is the safe default: a client that
93
+ * wrongly thinks the flow is over stops polling, while one that keeps polling lets the reconciler
94
+ * resolve it. Confirm against the backend's rank map when it lands.
95
+ */
96
+ | 'CAPTURE_ORPHANED';
97
+ export type FailureCategory = 'PAYMENT_DECLINED' | 'KYC_REJECTED' | 'PROVIDER_OUTAGE' | 'USER_ERROR' | 'CANCELLED' | 'EXPIRED';
98
+ /**
99
+ * Recovery affordance the client renders. Contract spelling is `CONTACT_SUPPORT`
100
+ * (the FE doc's Screen 11 note writes `SUPPORT` as shorthand — same value).
101
+ */
102
+ export type Recovery = 'REQUOTE' | 'RETRY_PAYMENT' | 'CONTACT_SUPPORT' | 'NONE';
103
+ /** Three expiries, three recoveries (contract §The envelope, FailureReason comment). */
104
+ export type ExpiredWhich = 'QUOTE' | 'PAYMENT_WINDOW' | 'INSTRUCTIONS';
105
+ /**
106
+ * `code?` is a copy key (e.g. `BAD_CODE`), not an error code to branch on.
107
+ * TODO(open-decision): contract OQ7 — the failure/partial-payment enumeration is incomplete.
108
+ * This is the taxonomy as published; new categories land with OQ7.
109
+ */
110
+ export type FailureReason = {
111
+ category: Exclude<FailureCategory, 'EXPIRED'>;
112
+ retryable: boolean;
113
+ recovery: Recovery;
114
+ code?: string;
115
+ } | {
116
+ category: 'EXPIRED';
117
+ which: ExpiredWhich;
118
+ retryable: boolean;
119
+ recovery: Recovery;
120
+ code?: string;
121
+ };
122
+ /**
123
+ * Amounts are decimal strings in the docs' envelopes ("100.00") — never numbers.
124
+ * Named to pair with `CryptoAmount`: every use site is a `fiat:` field.
125
+ */
126
+ export interface FiatAmount {
127
+ currency: string;
128
+ amount: string;
129
+ }
130
+ export interface CryptoAmount {
131
+ currency: string;
132
+ network: string;
133
+ amount: string;
134
+ }
135
+ /** `id` is the client's localization lookup key; `labelFallback` renders for unknown ids. */
136
+ export interface FeeLine {
137
+ id: string;
138
+ labelFallback: string;
139
+ amount: string;
140
+ }
141
+ /**
142
+ * `expiresAt` is BE-stamped, not a provider passthrough (contract §Quote semantics).
143
+ * All v1 quotes are estimates as a global fact, not a field.
144
+ * `paymentMethod` is an agnostic category (`card`, `apple_pay`, `bank_transfer`, …) — the docs
145
+ * leave the set open, so it stays `string` rather than a fabricated enum.
146
+ */
147
+ export interface Quote {
148
+ quoteRef: string;
149
+ fiat: FiatAmount;
150
+ crypto: CryptoAmount;
151
+ rate: string;
152
+ totalFee: string;
153
+ feeBreakdown: FeeLine[];
154
+ paymentMethod: string;
155
+ expiresAt: string;
156
+ }
157
+ /** Deferred to the credit-card iteration (FE doc port ledger) — optional wherever it appears. */
158
+ export interface Instrument {
159
+ brandLabel: string;
160
+ last4: string;
161
+ }
162
+ /** Payload of `PENDING_ORDER` (contract: `OrderRef`). */
163
+ export interface OrderRef {
164
+ orderId: string;
165
+ fiat: FiatAmount;
166
+ crypto: CryptoAmount;
167
+ method: string;
168
+ createdAt: string;
169
+ status: OrderStatus;
170
+ }
171
+ /**
172
+ * Payload of `ORDER{CREATED}` (FE doc Screens 7 · 13 · 14 · 15).
173
+ * `crypto` is optional because the Screen 14/15 envelopes omit it (fiat-only failure/refund views).
174
+ */
175
+ export interface OrderSummary {
176
+ orderId: string;
177
+ fiat: FiatAmount;
178
+ crypto?: CryptoAmount;
179
+ method: string;
180
+ createdAt: string;
181
+ instrument?: Instrument;
182
+ }
183
+ /**
184
+ * TODO(open-decision): the element shape of `statusHistory` is not defined in either doc
185
+ * (FE doc Screen 7 lists the key; the FE sketch renders `<StatusTimeline history={…}/>`).
186
+ * Encoding the documented minimum — the status only — rather than inventing timestamps.
187
+ */
188
+ export interface StatusHistoryEntry {
189
+ status: OrderStatus;
190
+ }
191
+ export interface Tx {
192
+ chainId: number;
193
+ hash: string;
194
+ }
195
+ export interface Refund {
196
+ currency: string;
197
+ amount: string;
198
+ expectedBy: string;
199
+ }
200
+ /**
201
+ * Carried as *data* in CAPTURE phases only (`PAYMENT{CAPTURE}` · `KYC{CAPTURE}`).
202
+ * Δ2: the CLIENT_SURFACE transition carries just `report` — single-carry, no duplication.
203
+ * `session` values are secrets: never log them (FE doc §Decisions, redaction).
204
+ */
205
+ export type Surface = {
206
+ kind: 'URL_REDIRECT';
207
+ url: string;
208
+ } | {
209
+ kind: 'URL_EMBED';
210
+ url: string;
211
+ } | {
212
+ kind: 'PCI_COMPONENT';
213
+ session: string;
214
+ } | {
215
+ kind: 'PAY_SHEET';
216
+ session: string;
217
+ } | {
218
+ kind: 'ACH_COMPONENT';
219
+ session: string;
220
+ };
221
+ export interface InstructionField {
222
+ id: string;
223
+ labelFallback: string;
224
+ value: string;
225
+ }
226
+ /**
227
+ * Payload of `PAYMENT{INSTRUCT}` — push rails. Values are PII-adjacent: never logged.
228
+ * TODO(open-decision): contract OQ1 lists `QR_IMAGE` as *deliberately not pre-declared* in the
229
+ * v1 vocabulary while §The envelope's Instructions bullet enumerates it. Kept in the union per
230
+ * the envelope section (and the conformance-package spec); handshake-gate it if OQ1 lands the
231
+ * other way.
232
+ * TODO(open-decision): contract OQ7 — expired instructions have no documented recovery and no
233
+ * re-issue endpoint exists.
234
+ */
235
+ export type Instructions = {
236
+ kind: 'DEEPLINK';
237
+ url: string;
238
+ /** TODO(open-decision): element shape undocumented (contract: "UPI deeplink ×4 app variants"). */
239
+ appVariants: string[];
240
+ } | {
241
+ kind: 'QR_IMAGE';
242
+ url: string;
243
+ } | {
244
+ kind: 'BANK_FIELDS';
245
+ fields: InstructionField[];
246
+ expiresAt?: string;
247
+ } | {
248
+ kind: 'REFERENCE';
249
+ code: string;
250
+ expiresAt: string;
251
+ };
252
+ export interface SelectOption {
253
+ id: string;
254
+ labelFallback: string;
255
+ }
256
+ /** Field types inside a KYC form round. A new field TYPE is a client release (handshake-gated). */
257
+ export type FormFieldType = 'TEXT' | 'DATE' | 'SELECT' | 'COUNTRY' | 'FILE' | 'HOSTED_LINK';
258
+ export interface FormField {
259
+ id: string;
260
+ type: FormFieldType;
261
+ labelFallback: string;
262
+ required: boolean;
263
+ regex?: string;
264
+ /** SELECT only. */
265
+ options?: SelectOption[];
266
+ /** HOSTED_LINK only. */
267
+ url?: string;
268
+ }
269
+ /** Payload of `KYC{INPUT_REQUIRED}`. One uniform renderer walks `fields`. */
270
+ export interface FormDescriptor {
271
+ formId: string;
272
+ fields: FormField[];
273
+ }
274
+ /**
275
+ * A `FieldSpec` describes something the **client collects**. It never carries a value.
276
+ * `FORM_VALUES` = "collect the values of the form already carried in `state.form`" — the
277
+ * transition never re-carries the descriptor (the double-carry disease Δ2 killed).
278
+ *
279
+ * Note: the contract's illustrative list reads `'TEXT' | 'NUMBER' | …`; the conformance-package
280
+ * spec closes the set at the seven below, so `NUMBER` is not declared here.
281
+ */
282
+ export type FieldSpec = {
283
+ type: 'TEXT';
284
+ length?: number;
285
+ regex?: string;
286
+ labelFallback?: string;
287
+ } | {
288
+ type: 'DATE';
289
+ labelFallback?: string;
290
+ } | {
291
+ type: 'COUNTRY';
292
+ labelFallback?: string;
293
+ } | {
294
+ type: 'FILE';
295
+ labelFallback?: string;
296
+ } | {
297
+ type: 'SELECT';
298
+ options?: SelectOption[];
299
+ labelFallback?: string;
300
+ } | {
301
+ type: 'HOSTED_LINK';
302
+ url: string;
303
+ labelFallback?: string;
304
+ } | {
305
+ type: 'FORM_VALUES';
306
+ };
307
+ export type FieldSpecType = FieldSpec['type'];
308
+ /** Server literals — spread into the body verbatim, opaque to the client. */
309
+ export type TransitionParams = Record<string, JsonValue>;
310
+ /** Client-collected specs — the harness renders + locally validates, then merges values in. */
311
+ export type TransitionInputs = Record<string, FieldSpec>;
312
+ export interface PollSpec {
313
+ endpoint: FiatEndpoint;
314
+ params?: TransitionParams;
315
+ intervalMs: number;
316
+ backoffFactor?: number;
317
+ maxIntervalMs?: number;
318
+ giveUpAfterMs?: number;
319
+ }
320
+ export interface ReportSpec {
321
+ endpoint: FiatEndpoint;
322
+ params?: TransitionParams;
323
+ /**
324
+ * Names the harness copies out of the surface result into the body (e.g. `["requestId"]`).
325
+ * Declared so conformance can assert the surface actually yields them.
326
+ */
327
+ expects?: string[];
328
+ }
329
+ export type TransitionMode = 'SUBMIT' | 'AWAIT' | 'CLIENT_SURFACE';
330
+ /**
331
+ * Body assembly, harness-owned, one rule:
332
+ * `body = {…params, …collected(inputs), …injected(expects)}`
333
+ * A key collision across the three sources is a contract violation (see `assertLegalEmission`),
334
+ * never last-write-wins.
335
+ *
336
+ * `id` keys localization; `labelFallback` renders for ids the client doesn't recognize and rides
337
+ * SUBMIT entries only — the user-visible ones.
338
+ */
339
+ export type Transition = {
340
+ id: string;
341
+ mode: 'SUBMIT';
342
+ endpoint: FiatEndpoint;
343
+ params?: TransitionParams;
344
+ inputs?: TransitionInputs;
345
+ labelFallback?: string;
346
+ } | {
347
+ id: string;
348
+ mode: 'AWAIT';
349
+ poll: PollSpec;
350
+ } | {
351
+ id: string;
352
+ mode: 'CLIENT_SURFACE';
353
+ report: ReportSpec;
354
+ };
355
+ /**
356
+ * TODO(open-decision): shape owned by the auth spike (contract §Auth & ref binding, item 5).
357
+ * `FUN_AUTH` never renders in fomo (identity is ambient), so nothing in either doc constrains
358
+ * this beyond the key name. Charles's review (2026-08-14) questions whether the kind is needed at
359
+ * all, pending the Transak Auth Reliance answer — if reliance lands, provider-side session auth is
360
+ * unreachable for fomo users and this kind may go with it. Kept for now: removing a kind is
361
+ * cheap, adding one back costs a client release (handshake-gated).
362
+ */
363
+ export type AuthChallenge = Record<string, JsonValue>;
364
+ /**
365
+ * The semantic state. Three rules keep this union honest:
366
+ * 1. sub-states are payload discriminants, never new kinds
367
+ * 2. terminal outcomes are OrderStatus, never kinds
368
+ * 3. every fallible state carries `error?: FailureReason`
369
+ */
370
+ export type FlowState = {
371
+ kind: 'QUOTE';
372
+ quote: Quote;
373
+ error?: FailureReason;
374
+ } | {
375
+ kind: 'FUN_AUTH';
376
+ challenge: AuthChallenge;
377
+ error?: FailureReason;
378
+ } | {
379
+ kind: 'SESSION_AUTH';
380
+ channel: 'EMAIL_OTP';
381
+ error?: FailureReason;
382
+ }
383
+ /** Dropped from v1 (no UK module coverage) — kind retained, handshake-gated. */
384
+ | {
385
+ kind: 'KYC';
386
+ phase: 'CAPTURE';
387
+ surface: Surface;
388
+ error?: FailureReason;
389
+ } | {
390
+ kind: 'KYC';
391
+ phase: 'INPUT_REQUIRED';
392
+ form: FormDescriptor;
393
+ error?: FailureReason;
394
+ } | {
395
+ kind: 'KYC';
396
+ phase: 'NO_ACTION_REQUIRED';
397
+ /**
398
+ * Note the deliberate name collision (Charles's nit, review 2026-08-14): `ON_HOLD` appears
399
+ * here as a KYC reason *and* in `OrderStatus`. They are different things and both names come
400
+ * from their own doc, so neither is renamed:
401
+ * - `KYC{NO_ACTION_REQUIRED, ON_HOLD}` = FCA cooling-off, pre-order, `retryAfter` set,
402
+ * empty transitions, NOT terminal.
403
+ * - `OrderStatus.ON_HOLD` = post-order compliance or amount mismatch, resolves to
404
+ * `PROCESSING` or `REFUNDED`.
405
+ * They cannot be confused in code — different types, different positions in the envelope —
406
+ * but they can be confused in conversation, so say which one you mean.
407
+ */
408
+ reason: 'IN_REVIEW' | 'ON_HOLD' | 'REJECTED';
409
+ retryAfter?: string;
410
+ /**
411
+ * Terminal REJECTED variant carries this (FE doc Screen 11: `KYC_REJECTED`,
412
+ * `retryable: false`, `recovery: CONTACT_SUPPORT`) and renders in place — it never routes
413
+ * through `ORDER{CREATED, FAILED}`.
414
+ * TODO(open-decision): contract OQ7 — which rejections escalate vs terminate.
415
+ */
416
+ failureReason?: FailureReason;
417
+ error?: FailureReason;
418
+ }
419
+ /**
420
+ * TODO(open-decision): removal proposed (contract §"Proposal: remove PENDING_ORDER").
421
+ * Shipped in 0.1.0 so the current shape is representable; FE Screen 12 is frozen until the
422
+ * decision lands. The removal targets the wizard-interstitial *kind*, not the pending-order
423
+ * concept (which may re-land as an out-of-wizard PENDING_ORDERS view).
424
+ */
425
+ | {
426
+ kind: 'PENDING_ORDER';
427
+ order: OrderRef;
428
+ error?: FailureReason;
429
+ } | {
430
+ kind: 'BLOCKED';
431
+ /**
432
+ * Observed value: `REGION_UNSUPPORTED` (FE doc Screen 16). The contract types this as
433
+ * `string`; no enum is published, so none is invented here.
434
+ */
435
+ reason: string;
436
+ /** `null` renders "no comeback date" (FE doc Screen 16 emits it explicitly). */
437
+ retryAfter?: string | null;
438
+ error?: FailureReason;
439
+ } | {
440
+ kind: 'PAYMENT';
441
+ phase: 'CAPTURE';
442
+ surface: Surface;
443
+ error?: FailureReason;
444
+ } | {
445
+ kind: 'PAYMENT';
446
+ phase: 'INSTRUCT';
447
+ /**
448
+ * TODO(open-decision): `orderId` placement — envelope sibling (contract §Worked example,
449
+ * steps 4–5) vs inside state (FE doc v0, Screen 6). Both are accepted here; the FE doc's
450
+ * in-state placement is the current shape, `StepResponse.orderId` covers the other.
451
+ */
452
+ orderId?: string;
453
+ instructions: Instructions;
454
+ error?: FailureReason;
455
+ } | {
456
+ kind: 'ORDER';
457
+ phase: 'AWAITING_CONFIRMATION';
458
+ quote: Quote;
459
+ error?: FailureReason;
460
+ } | {
461
+ kind: 'ORDER';
462
+ phase: 'CREATED';
463
+ /** Absent in the contract's terse worked-example envelopes, present in every FE screen. */
464
+ order?: OrderSummary;
465
+ status: OrderStatus;
466
+ statusHistory?: StatusHistoryEntry[];
467
+ tx?: Tx;
468
+ refund?: Refund;
469
+ failureReason?: FailureReason;
470
+ error?: FailureReason;
471
+ };
472
+ export type FlowStateKind = FlowState['kind'];
473
+ /**
474
+ * Every `/fiat/*` response. `transitions: []` = nothing the wizard can do; whether the *flow*
475
+ * is over is read from the table (`terminal`), never from array emptiness (ON_HOLD is the proof).
476
+ */
477
+ export interface StepResponse {
478
+ state: FlowState;
479
+ provider: FiatProvider;
480
+ transitions: Transition[];
481
+ /** TODO(open-decision): `orderId` placement — see `PAYMENT{INSTRUCT}.orderId` above. */
482
+ orderId?: string;
483
+ }
484
+ /** The envelope as it appears in the docs' JSON — alias kept because both names are in use. */
485
+ export type Envelope = StepResponse;
486
+ //# sourceMappingURL=types.d.ts.map
package/dist/types.js ADDED
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __copyProps = (to, from, except, desc) => {
7
+ if (from && typeof from === "object" || typeof from === "function") {
8
+ for (let key of __getOwnPropNames(from))
9
+ if (!__hasOwnProp.call(to, key) && key !== except)
10
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
11
+ }
12
+ return to;
13
+ };
14
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
15
+
16
+ // src/types.ts
17
+ var types_exports = {};
18
+ module.exports = __toCommonJS(types_exports);
19
+ //# sourceMappingURL=types.js.map
package/dist/types.mjs ADDED
@@ -0,0 +1,2 @@
1
+ import "./chunk-KIQOUUEZ.mjs";
2
+ //# sourceMappingURL=types.mjs.map
package/package.json ADDED
@@ -0,0 +1,81 @@
1
+ {
2
+ "name": "@fun-xyz/fiat-contract",
3
+ "version": "0.2.1",
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
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/fun-xyz/fiat-contract.git"
9
+ },
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "main": "./dist/index.js",
14
+ "types": "./dist/index.d.ts",
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/index.d.ts",
18
+ "import": "./dist/index.mjs",
19
+ "require": "./dist/index.js"
20
+ },
21
+ "./types": {
22
+ "types": "./dist/types.d.ts",
23
+ "import": "./dist/types.mjs",
24
+ "require": "./dist/types.js"
25
+ },
26
+ "./table": {
27
+ "types": "./dist/table.d.ts",
28
+ "import": "./dist/table.mjs",
29
+ "require": "./dist/table.js"
30
+ },
31
+ "./package.json": "./package.json"
32
+ },
33
+ "files": [
34
+ "dist",
35
+ "table.js",
36
+ "table.d.ts",
37
+ "types.js",
38
+ "types.d.ts",
39
+ "README.md",
40
+ "!dist/**/*.map"
41
+ ],
42
+ "engines": {
43
+ "node": ">=18"
44
+ },
45
+ "scripts": {
46
+ "generate:fixtures": "node scripts/generate-fixture-data.mjs",
47
+ "typegen": "tsc -p tsconfig.build.json --emitDeclarationOnly",
48
+ "build": "rm -rf dist && npm run typegen && node build.mjs",
49
+ "prepublishOnly": "npm run build",
50
+ "typecheck": "tsc -p tsconfig.json --noEmit",
51
+ "test": "vitest run",
52
+ "check": "npm run typecheck && npm run test"
53
+ },
54
+ "peerDependencies": {
55
+ "zod": "^3.22.0 || ^4.0.0"
56
+ },
57
+ "peerDependenciesMeta": {
58
+ "zod": {
59
+ "optional": true
60
+ }
61
+ },
62
+ "devDependencies": {
63
+ "@types/node": "^20.19.0",
64
+ "esbuild": "^0.25.0",
65
+ "typescript": "^5.9.3",
66
+ "vitest": "^3.2.4",
67
+ "zod": "^4.4.3"
68
+ },
69
+ "module": "./dist/index.mjs",
70
+ "sideEffects": false,
71
+ "typesVersions": {
72
+ "*": {
73
+ "types": [
74
+ "./dist/types.d.ts"
75
+ ],
76
+ "table": [
77
+ "./dist/table.d.ts"
78
+ ]
79
+ }
80
+ }
81
+ }
package/table.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './dist/table';
package/table.js ADDED
@@ -0,0 +1,5 @@
1
+ // Compat stub: resolvers that do not read `package.json#exports` (Metro before RN 0.79, where
2
+ // `unstable_enablePackageExports` defaults to false) resolve `@fun-xyz/fiat-contract/table` to this
3
+ // file by path instead. Verified: without it, Metro fails with "Unable to resolve module".
4
+ // Anything that does read `exports` never sees this file — it goes straight to dist/.
5
+ module.exports = require('./dist/table.js');
package/types.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './dist/types';
package/types.js ADDED
@@ -0,0 +1,3 @@
1
+ // Compat stub — see table.js. The types entry has no runtime exports; this exists so a
2
+ // path-based resolver does not fail on `@fun-xyz/fiat-contract/types`.
3
+ module.exports = require('./dist/types.js');