@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/dist/index.js
CHANGED
|
@@ -51,6 +51,7 @@ __export(index_exports, {
|
|
|
51
51
|
InstructionsSchema: () => InstructionsSchema,
|
|
52
52
|
InstrumentSchema: () => InstrumentSchema,
|
|
53
53
|
JsonSchema: () => JsonSchema,
|
|
54
|
+
KYCProviderSchema: () => KYCProviderSchema,
|
|
54
55
|
KycCaptureStateSchema: () => KycCaptureStateSchema,
|
|
55
56
|
KycInputRequiredStateSchema: () => KycInputRequiredStateSchema,
|
|
56
57
|
KycNoActionRequiredStateSchema: () => KycNoActionRequiredStateSchema,
|
|
@@ -73,6 +74,8 @@ __export(index_exports, {
|
|
|
73
74
|
SelectOptionSchema: () => SelectOptionSchema,
|
|
74
75
|
SessionAuthStateSchema: () => SessionAuthStateSchema,
|
|
75
76
|
StatusHistoryEntrySchema: () => StatusHistoryEntrySchema,
|
|
77
|
+
SurfaceKindSchema: () => SurfaceKindSchema,
|
|
78
|
+
SurfaceProviderSchema: () => SurfaceProviderSchema,
|
|
76
79
|
SurfaceSchema: () => SurfaceSchema,
|
|
77
80
|
TABLE_VERSION: () => TABLE_VERSION,
|
|
78
81
|
TERMINAL_ORDER_STATUSES: () => TERMINAL_ORDER_STATUSES,
|
|
@@ -81,6 +84,8 @@ __export(index_exports, {
|
|
|
81
84
|
TransitionParamsSchema: () => TransitionParamsSchema,
|
|
82
85
|
TransitionSchema: () => TransitionSchema,
|
|
83
86
|
TxSchema: () => TxSchema,
|
|
87
|
+
UNOFFERED_ENDPOINTS: () => UNOFFERED_ENDPOINTS,
|
|
88
|
+
UNREACHABLE_STATES: () => UNREACHABLE_STATES,
|
|
84
89
|
assertAllFixtures: () => assertAllFixtures,
|
|
85
90
|
assertFiatStepResponse: () => assertFiatStepResponse,
|
|
86
91
|
assertFixture: () => assertFixture,
|
|
@@ -95,6 +100,8 @@ __export(index_exports, {
|
|
|
95
100
|
looksLikeFieldSpec: () => looksLikeFieldSpec,
|
|
96
101
|
stateKey: () => stateKey,
|
|
97
102
|
tableEntry: () => tableEntry,
|
|
103
|
+
unofferedEndpoints: () => unofferedEndpoints,
|
|
104
|
+
unreachableStates: () => unreachableStates,
|
|
98
105
|
walkTable: () => walkTable
|
|
99
106
|
});
|
|
100
107
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -380,8 +387,12 @@ var HTTP_VERBS = ["GET", "POST"];
|
|
|
380
387
|
var ENDPOINT_PATTERN = /^(GET|POST) \/fiat\/[^\s]*$/;
|
|
381
388
|
var $endpoint = import_zod.z.string().regex(ENDPOINT_PATTERN, 'endpoint must read "<GET|POST> /fiat/\u2026"');
|
|
382
389
|
var FiatEndpointSchema = $endpoint;
|
|
383
|
-
var $provider = import_zod.z.enum(["TRANSAK", "SWAPPED"]);
|
|
390
|
+
var $provider = import_zod.z.enum(["TRANSAK", "SWAPPED", "BANXA", "MOONPAY", "STRIPE"]);
|
|
384
391
|
var FiatProviderSchema = $provider;
|
|
392
|
+
var $kycProvider = import_zod.z.enum(["SUMSUB"]);
|
|
393
|
+
var KYCProviderSchema = $kycProvider;
|
|
394
|
+
var $surfaceProvider = import_zod.z.enum([...$provider.options, ...$kycProvider.options]);
|
|
395
|
+
var SurfaceProviderSchema = $surfaceProvider;
|
|
385
396
|
var $orderStatus = import_zod.z.enum([
|
|
386
397
|
"CREATING",
|
|
387
398
|
"AWAITING_PAYMENT",
|
|
@@ -473,13 +484,22 @@ var $refund = obj({
|
|
|
473
484
|
expectedBy: import_zod.z.string()
|
|
474
485
|
});
|
|
475
486
|
var RefundSchema = $refund;
|
|
476
|
-
var $
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
487
|
+
var $surfaceKind = import_zod.z.enum(["URL_REDIRECT", "KYC_SDK", "PCI_COMPONENT", "PAY_SHEET", "ACH_COMPONENT"]);
|
|
488
|
+
var SurfaceKindSchema = $surfaceKind;
|
|
489
|
+
var $surface = obj({
|
|
490
|
+
kind: $surfaceKind,
|
|
491
|
+
provider: $surfaceProvider,
|
|
492
|
+
/**
|
|
493
|
+
* The one deliberate non-strict leaf in the contract. Provider inputs are the adapter's
|
|
494
|
+
* vocabulary, so typing them here would drag every provider field change through a contract
|
|
495
|
+
* release and both consumers — and a normalised `environment` could only be lossy across
|
|
496
|
+
* Transak's four values and Banxa's two. Both ends narrow instead: fun-backend emits from a
|
|
497
|
+
* typed provider session, the client adapter validates with its own schema, and a recorded
|
|
498
|
+
* fixture per `(provider, kind)` keeps the two honest.
|
|
499
|
+
*/
|
|
500
|
+
params: import_zod.z.record(import_zod.z.string(), JsonSchema),
|
|
501
|
+
expiresAt: import_zod.z.string().optional()
|
|
502
|
+
});
|
|
483
503
|
var SurfaceSchema = $surface;
|
|
484
504
|
var $instructionField = obj({
|
|
485
505
|
id: import_zod.z.string(),
|
|
@@ -609,6 +629,8 @@ var AuthChallengeSchema = $authChallenge;
|
|
|
609
629
|
var $quoteState = obj({
|
|
610
630
|
kind: import_zod.z.literal("QUOTE"),
|
|
611
631
|
quote: $quote,
|
|
632
|
+
/** Present when the provider's component is the primary CTA, inline on the entry screen. */
|
|
633
|
+
surface: $surface.optional(),
|
|
612
634
|
error: $failureReason.optional()
|
|
613
635
|
});
|
|
614
636
|
var QuoteStateSchema = $quoteState;
|
|
@@ -627,6 +649,7 @@ var SessionAuthStateSchema = $sessionAuthState;
|
|
|
627
649
|
var $kycCaptureState = obj({
|
|
628
650
|
kind: import_zod.z.literal("KYC"),
|
|
629
651
|
phase: import_zod.z.literal("CAPTURE"),
|
|
652
|
+
quote: $quote,
|
|
630
653
|
surface: $surface,
|
|
631
654
|
error: $failureReason.optional()
|
|
632
655
|
});
|
|
@@ -634,6 +657,7 @@ var KycCaptureStateSchema = $kycCaptureState;
|
|
|
634
657
|
var $kycInputRequiredState = obj({
|
|
635
658
|
kind: import_zod.z.literal("KYC"),
|
|
636
659
|
phase: import_zod.z.literal("INPUT_REQUIRED"),
|
|
660
|
+
quote: $quote,
|
|
637
661
|
form: $formDescriptor,
|
|
638
662
|
error: $failureReason.optional()
|
|
639
663
|
});
|
|
@@ -641,6 +665,7 @@ var KycInputRequiredStateSchema = $kycInputRequiredState;
|
|
|
641
665
|
var $kycNoActionRequiredState = obj({
|
|
642
666
|
kind: import_zod.z.literal("KYC"),
|
|
643
667
|
phase: import_zod.z.literal("NO_ACTION_REQUIRED"),
|
|
668
|
+
quote: $quote,
|
|
644
669
|
reason: import_zod.z.enum(["IN_REVIEW", "ON_HOLD", "REJECTED"]),
|
|
645
670
|
retryAfter: import_zod.z.string().optional(),
|
|
646
671
|
failureReason: $failureReason.optional(),
|
|
@@ -718,7 +743,7 @@ var $stepResponse = obj({
|
|
|
718
743
|
var FiatStepResponseSchema = $stepResponse;
|
|
719
744
|
|
|
720
745
|
// package.json
|
|
721
|
-
var version = "0.
|
|
746
|
+
var version = "0.8.0";
|
|
722
747
|
|
|
723
748
|
// src/table.ts
|
|
724
749
|
var TABLE_VERSION = version;
|
|
@@ -749,22 +774,46 @@ var TRANSITION_TABLE = {
|
|
|
749
774
|
endpoint: "POST /fiat/session",
|
|
750
775
|
when: "no valid provider session token (own-tables check)"
|
|
751
776
|
},
|
|
777
|
+
{
|
|
778
|
+
id: "open_session",
|
|
779
|
+
mode: "SUBMIT",
|
|
780
|
+
endpoint: "POST /fiat/payment-session",
|
|
781
|
+
when: "returning user, session + KYC valid, capture-first rail \u2014 the method authorises before any order exists",
|
|
782
|
+
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."
|
|
783
|
+
},
|
|
752
784
|
{
|
|
753
785
|
id: "continue",
|
|
754
786
|
mode: "SUBMIT",
|
|
755
787
|
endpoint: "POST /fiat/orders",
|
|
756
|
-
when: "returning user, session + KYC valid \u2014 the quote screen doubles as review"
|
|
788
|
+
when: "returning user, session + KYC valid, order-first rail \u2014 the quote screen doubles as review",
|
|
789
|
+
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."
|
|
757
790
|
},
|
|
758
791
|
{
|
|
759
792
|
id: "continue",
|
|
760
793
|
mode: "SUBMIT",
|
|
761
794
|
endpoint: "POST /fiat/auth",
|
|
762
795
|
when: "no ambient Fun identity (non-fomo surfaces only)"
|
|
796
|
+
},
|
|
797
|
+
{
|
|
798
|
+
id: "capture",
|
|
799
|
+
mode: "CLIENT_SURFACE",
|
|
800
|
+
endpoint: "POST /fiat/orders",
|
|
801
|
+
when: "capture-then-order rail whose session is minted at quote time (inline pay sheet)",
|
|
802
|
+
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)."
|
|
803
|
+
},
|
|
804
|
+
{
|
|
805
|
+
id: "capture",
|
|
806
|
+
mode: "CLIENT_SURFACE",
|
|
807
|
+
endpoint: "POST /fiat/orders/:id/surface-result",
|
|
808
|
+
when: "order-then-capture rail whose provider order was created with the quote-time session",
|
|
809
|
+
note: "Same quote-time placement, other topology: the order already exists, so the surface reports against it rather than creating one."
|
|
763
810
|
}
|
|
764
811
|
],
|
|
765
812
|
mayReturn: [
|
|
766
813
|
"SESSION_AUTH",
|
|
767
814
|
...KYC_ANY,
|
|
815
|
+
"PAYMENT/CAPTURE",
|
|
816
|
+
"PAYMENT/INSTRUCT",
|
|
768
817
|
"PENDING_ORDER",
|
|
769
818
|
"ORDER/AWAITING_CONFIRMATION",
|
|
770
819
|
"ORDER/CREATED",
|
|
@@ -772,8 +821,10 @@ var TRANSITION_TABLE = {
|
|
|
772
821
|
],
|
|
773
822
|
terminal: false,
|
|
774
823
|
notes: [
|
|
775
|
-
"Re-quote on amount change is a client-local event, not a transition.",
|
|
776
|
-
|
|
824
|
+
"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.",
|
|
825
|
+
"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.",
|
|
826
|
+
`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.`,
|
|
827
|
+
'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.'
|
|
777
828
|
]
|
|
778
829
|
},
|
|
779
830
|
FUN_AUTH: {
|
|
@@ -818,21 +869,23 @@ var TRANSITION_TABLE = {
|
|
|
818
869
|
{
|
|
819
870
|
id: "capture",
|
|
820
871
|
mode: "CLIENT_SURFACE",
|
|
821
|
-
endpoint: "
|
|
822
|
-
note:
|
|
872
|
+
endpoint: "POST /fiat/kyc/capture",
|
|
873
|
+
note: "Reports SDK submission; provider eligibility is re-evaluated through quote orchestration."
|
|
823
874
|
}
|
|
824
875
|
],
|
|
825
876
|
mayReturn: [
|
|
877
|
+
"KYC/CAPTURE",
|
|
826
878
|
"KYC/INPUT_REQUIRED",
|
|
827
879
|
"KYC/NO_ACTION_REQUIRED:IN_REVIEW",
|
|
828
880
|
"KYC/NO_ACTION_REQUIRED:ON_HOLD",
|
|
829
881
|
"KYC/NO_ACTION_REQUIRED:REJECTED",
|
|
882
|
+
"QUOTE",
|
|
883
|
+
"PAYMENT/CAPTURE",
|
|
884
|
+
"PAYMENT/INSTRUCT",
|
|
830
885
|
"ORDER/AWAITING_CONFIRMATION"
|
|
831
886
|
],
|
|
832
887
|
terminal: false,
|
|
833
|
-
notes: [
|
|
834
|
-
"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."
|
|
835
|
-
]
|
|
888
|
+
notes: ["Carries the handshake-gated KYC_SDK surface; provider selects the identity-vendor adapter."]
|
|
836
889
|
},
|
|
837
890
|
"KYC/INPUT_REQUIRED": {
|
|
838
891
|
key: "KYC/INPUT_REQUIRED",
|
|
@@ -844,16 +897,20 @@ var TRANSITION_TABLE = {
|
|
|
844
897
|
{ id: "submit_round", mode: "SUBMIT", endpoint: "POST /fiat/kyc/form" }
|
|
845
898
|
],
|
|
846
899
|
mayReturn: [
|
|
900
|
+
"KYC/CAPTURE",
|
|
847
901
|
"KYC/INPUT_REQUIRED",
|
|
848
902
|
"KYC/NO_ACTION_REQUIRED:IN_REVIEW",
|
|
849
903
|
"KYC/NO_ACTION_REQUIRED:ON_HOLD",
|
|
850
904
|
"KYC/NO_ACTION_REQUIRED:REJECTED",
|
|
905
|
+
"QUOTE",
|
|
906
|
+
"PAYMENT/CAPTURE",
|
|
907
|
+
"PAYMENT/INSTRUCT",
|
|
851
908
|
"ORDER/AWAITING_CONFIRMATION"
|
|
852
909
|
],
|
|
853
910
|
terminal: false,
|
|
854
911
|
notes: [
|
|
855
912
|
"One POST per round; conditional requirements may open another round.",
|
|
856
|
-
"FILE fields
|
|
913
|
+
"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.",
|
|
857
914
|
"Hosted KYC links arrive as HOSTED_LINK form fields, not as a Surface."
|
|
858
915
|
]
|
|
859
916
|
},
|
|
@@ -864,12 +921,16 @@ var TRANSITION_TABLE = {
|
|
|
864
921
|
reason: "IN_REVIEW",
|
|
865
922
|
screens: [9],
|
|
866
923
|
docStatus: "SPECIFIED",
|
|
867
|
-
allowedTransitions: [{ id: "poll", mode: "AWAIT", endpoint: "GET /fiat/
|
|
924
|
+
allowedTransitions: [{ id: "poll", mode: "AWAIT", endpoint: "GET /fiat/quotes/:quoteRef" }],
|
|
868
925
|
mayReturn: [
|
|
869
926
|
"KYC/NO_ACTION_REQUIRED:IN_REVIEW",
|
|
927
|
+
"KYC/CAPTURE",
|
|
870
928
|
"KYC/INPUT_REQUIRED",
|
|
871
929
|
"KYC/NO_ACTION_REQUIRED:ON_HOLD",
|
|
872
930
|
"KYC/NO_ACTION_REQUIRED:REJECTED",
|
|
931
|
+
"QUOTE",
|
|
932
|
+
"PAYMENT/CAPTURE",
|
|
933
|
+
"PAYMENT/INSTRUCT",
|
|
873
934
|
"ORDER/AWAITING_CONFIRMATION"
|
|
874
935
|
],
|
|
875
936
|
terminal: false,
|
|
@@ -899,20 +960,11 @@ var TRANSITION_TABLE = {
|
|
|
899
960
|
reason: "REJECTED",
|
|
900
961
|
screens: [11],
|
|
901
962
|
docStatus: "SPECIFIED",
|
|
902
|
-
allowedTransitions: [
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
mode: "SUBMIT",
|
|
906
|
-
endpoint: "GET /fiat/kyc",
|
|
907
|
-
when: "an escalation round is offered",
|
|
908
|
-
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."
|
|
909
|
-
}
|
|
910
|
-
],
|
|
911
|
-
mayReturn: KYC_ANY,
|
|
912
|
-
terminal: false,
|
|
963
|
+
allowedTransitions: [],
|
|
964
|
+
mayReturn: [],
|
|
965
|
+
terminal: true,
|
|
913
966
|
notes: [
|
|
914
|
-
"
|
|
915
|
-
"TODO(open-decision): contract OQ7 \u2014 terminal vs escalating rejections are not enumerated."
|
|
967
|
+
"Only explicit non-retryable outcomes reach REJECTED. Correctable failures return CAPTURE or INPUT_REQUIRED; provider-specific declines re-route before becoming user rejection."
|
|
916
968
|
]
|
|
917
969
|
},
|
|
918
970
|
PENDING_ORDER: {
|
|
@@ -1071,10 +1123,13 @@ var DOCUMENTED_ENDPOINTS = [
|
|
|
1071
1123
|
"GET /fiat/payment-methods",
|
|
1072
1124
|
"POST /fiat/quote",
|
|
1073
1125
|
"POST /fiat/auth",
|
|
1126
|
+
"POST /fiat/payment-session",
|
|
1074
1127
|
"POST /fiat/session",
|
|
1075
1128
|
"POST /fiat/session/verify",
|
|
1076
|
-
"GET /fiat/
|
|
1129
|
+
"GET /fiat/quotes/:quoteRef",
|
|
1077
1130
|
"POST /fiat/kyc/form",
|
|
1131
|
+
"POST /fiat/kyc/capture",
|
|
1132
|
+
"POST /fiat/kyc/sdk-token",
|
|
1078
1133
|
"POST /fiat/kyc/document",
|
|
1079
1134
|
"POST /fiat/instruments",
|
|
1080
1135
|
"POST /fiat/orders",
|
|
@@ -1085,6 +1140,58 @@ var DOCUMENTED_ENDPOINTS = [
|
|
|
1085
1140
|
"POST /fiat/orders/:id/cancel",
|
|
1086
1141
|
"POST /fiat/orders/:id/surface-result"
|
|
1087
1142
|
];
|
|
1143
|
+
var UNOFFERED_ENDPOINTS = [
|
|
1144
|
+
{
|
|
1145
|
+
endpoint: "POST /fiat/kyc/sdk-token",
|
|
1146
|
+
reason: "SDK token refresh is adapter-driven rather than a flow transition."
|
|
1147
|
+
},
|
|
1148
|
+
{
|
|
1149
|
+
endpoint: "GET /fiat/payment-methods",
|
|
1150
|
+
reason: "Discovery, called before any flow state exists \u2014 there is no state to hang it off."
|
|
1151
|
+
},
|
|
1152
|
+
{
|
|
1153
|
+
endpoint: "POST /fiat/kyc/document",
|
|
1154
|
+
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)."
|
|
1155
|
+
},
|
|
1156
|
+
{
|
|
1157
|
+
endpoint: "POST /fiat/instruments",
|
|
1158
|
+
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."
|
|
1159
|
+
},
|
|
1160
|
+
{
|
|
1161
|
+
endpoint: "GET /fiat/orders/:id/instructions",
|
|
1162
|
+
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."
|
|
1163
|
+
},
|
|
1164
|
+
{
|
|
1165
|
+
endpoint: "POST /fiat/orders/:id/reference",
|
|
1166
|
+
reason: "The bank-reference submission is offered by no state. TODO(open-decision): PAYMENT{INSTRUCT} is the plausible home \u2014 confirm or wire it."
|
|
1167
|
+
}
|
|
1168
|
+
];
|
|
1169
|
+
var UNREACHABLE_STATES = [
|
|
1170
|
+
{
|
|
1171
|
+
stateKey: "FUN_AUTH",
|
|
1172
|
+
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."
|
|
1173
|
+
}
|
|
1174
|
+
];
|
|
1175
|
+
function unofferedEndpoints() {
|
|
1176
|
+
const offered = /* @__PURE__ */ new Set();
|
|
1177
|
+
for (const key of STATE_KEYS) {
|
|
1178
|
+
for (const allowed of TRANSITION_TABLE[key].allowedTransitions) offered.add(allowed.endpoint);
|
|
1179
|
+
}
|
|
1180
|
+
return DOCUMENTED_ENDPOINTS.filter((endpoint) => !offered.has(endpoint));
|
|
1181
|
+
}
|
|
1182
|
+
function unreachableStates(from = "QUOTE") {
|
|
1183
|
+
const seen = /* @__PURE__ */ new Set([from]);
|
|
1184
|
+
const queue = [from];
|
|
1185
|
+
while (queue.length > 0) {
|
|
1186
|
+
for (const target of TRANSITION_TABLE[queue.pop()].mayReturn ?? []) {
|
|
1187
|
+
if (!seen.has(target)) {
|
|
1188
|
+
seen.add(target);
|
|
1189
|
+
queue.push(target);
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
return STATE_KEYS.filter((key) => !seen.has(key));
|
|
1194
|
+
}
|
|
1088
1195
|
function stateKey(state) {
|
|
1089
1196
|
switch (state.kind) {
|
|
1090
1197
|
case "QUOTE":
|
|
@@ -1167,6 +1274,64 @@ var FIXTURE_DATA = {
|
|
|
1167
1274
|
}
|
|
1168
1275
|
]
|
|
1169
1276
|
},
|
|
1277
|
+
"screen-01b-quote-with-pay-sheet": {
|
|
1278
|
+
"state": {
|
|
1279
|
+
"kind": "QUOTE",
|
|
1280
|
+
"quote": {
|
|
1281
|
+
"quoteRef": "q_8f2",
|
|
1282
|
+
"fiat": {
|
|
1283
|
+
"currency": "GBP",
|
|
1284
|
+
"amount": "100.00"
|
|
1285
|
+
},
|
|
1286
|
+
"crypto": {
|
|
1287
|
+
"currency": "USDC",
|
|
1288
|
+
"network": "polygon",
|
|
1289
|
+
"amount": "98.41"
|
|
1290
|
+
},
|
|
1291
|
+
"rate": "0.9841",
|
|
1292
|
+
"totalFee": "1.59",
|
|
1293
|
+
"feeBreakdown": [
|
|
1294
|
+
{
|
|
1295
|
+
"id": "provider_fee",
|
|
1296
|
+
"labelFallback": "Processing fee",
|
|
1297
|
+
"amount": "1.09"
|
|
1298
|
+
},
|
|
1299
|
+
{
|
|
1300
|
+
"id": "network_fee",
|
|
1301
|
+
"labelFallback": "Network fee",
|
|
1302
|
+
"amount": "0.50"
|
|
1303
|
+
}
|
|
1304
|
+
],
|
|
1305
|
+
"paymentMethod": "bank_transfer",
|
|
1306
|
+
"expiresAt": "\u2026"
|
|
1307
|
+
},
|
|
1308
|
+
"surface": {
|
|
1309
|
+
"kind": "PAY_SHEET",
|
|
1310
|
+
"provider": "TRANSAK",
|
|
1311
|
+
"params": {
|
|
1312
|
+
"sessionId": "txs_\u2026",
|
|
1313
|
+
"environment": "STAGING"
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
},
|
|
1317
|
+
"provider": "TRANSAK",
|
|
1318
|
+
"transitions": [
|
|
1319
|
+
{
|
|
1320
|
+
"id": "capture",
|
|
1321
|
+
"mode": "CLIENT_SURFACE",
|
|
1322
|
+
"report": {
|
|
1323
|
+
"endpoint": "POST /fiat/orders",
|
|
1324
|
+
"params": {
|
|
1325
|
+
"quoteRef": "q_8f2",
|
|
1326
|
+
"depositAddress": "\u2026"
|
|
1327
|
+
},
|
|
1328
|
+
"expects": [
|
|
1329
|
+
"paymentAuthorisationId"
|
|
1330
|
+
]
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
]
|
|
1334
|
+
},
|
|
1170
1335
|
"screen-02-session-auth": {
|
|
1171
1336
|
"state": {
|
|
1172
1337
|
"kind": "SESSION_AUTH",
|
|
@@ -1194,45 +1359,43 @@ var FIXTURE_DATA = {
|
|
|
1194
1359
|
"state": {
|
|
1195
1360
|
"kind": "KYC",
|
|
1196
1361
|
"phase": "INPUT_REQUIRED",
|
|
1362
|
+
"quote": {
|
|
1363
|
+
"quoteRef": "q_8f2",
|
|
1364
|
+
"fiat": {
|
|
1365
|
+
"currency": "GBP",
|
|
1366
|
+
"amount": "100.00"
|
|
1367
|
+
},
|
|
1368
|
+
"crypto": {
|
|
1369
|
+
"currency": "USDC",
|
|
1370
|
+
"network": "polygon",
|
|
1371
|
+
"amount": "98.41"
|
|
1372
|
+
},
|
|
1373
|
+
"rate": "0.9841",
|
|
1374
|
+
"totalFee": "1.59",
|
|
1375
|
+
"feeBreakdown": [
|
|
1376
|
+
{
|
|
1377
|
+
"id": "provider_fee",
|
|
1378
|
+
"labelFallback": "Processing fee",
|
|
1379
|
+
"amount": "1.59"
|
|
1380
|
+
}
|
|
1381
|
+
],
|
|
1382
|
+
"paymentMethod": "bank_transfer",
|
|
1383
|
+
"expiresAt": "\u2026"
|
|
1384
|
+
},
|
|
1197
1385
|
"form": {
|
|
1198
|
-
"formId": "
|
|
1386
|
+
"formId": "kyc_q_8f2_1",
|
|
1199
1387
|
"fields": [
|
|
1200
1388
|
{
|
|
1201
|
-
"id": "
|
|
1389
|
+
"id": "tin",
|
|
1202
1390
|
"type": "TEXT",
|
|
1203
|
-
"labelFallback": "
|
|
1204
|
-
"required": true
|
|
1205
|
-
},
|
|
1206
|
-
{
|
|
1207
|
-
"id": "dob",
|
|
1208
|
-
"type": "DATE",
|
|
1209
|
-
"labelFallback": "Date of birth",
|
|
1391
|
+
"labelFallback": "Tax ID",
|
|
1210
1392
|
"required": true
|
|
1211
1393
|
},
|
|
1212
1394
|
{
|
|
1213
|
-
"id": "
|
|
1214
|
-
"type": "
|
|
1215
|
-
"labelFallback": "
|
|
1395
|
+
"id": "address",
|
|
1396
|
+
"type": "TEXT",
|
|
1397
|
+
"labelFallback": "Home address",
|
|
1216
1398
|
"required": true
|
|
1217
|
-
},
|
|
1218
|
-
{
|
|
1219
|
-
"id": "sourceOfFunds",
|
|
1220
|
-
"type": "SELECT",
|
|
1221
|
-
"labelFallback": "Source of funds",
|
|
1222
|
-
"required": true,
|
|
1223
|
-
"options": [
|
|
1224
|
-
{
|
|
1225
|
-
"id": "salary",
|
|
1226
|
-
"labelFallback": "Salary"
|
|
1227
|
-
}
|
|
1228
|
-
]
|
|
1229
|
-
},
|
|
1230
|
-
{
|
|
1231
|
-
"id": "idProof",
|
|
1232
|
-
"type": "HOSTED_LINK",
|
|
1233
|
-
"labelFallback": "Verify your ID",
|
|
1234
|
-
"required": true,
|
|
1235
|
-
"url": "\u2026"
|
|
1236
1399
|
}
|
|
1237
1400
|
]
|
|
1238
1401
|
}
|
|
@@ -1244,7 +1407,8 @@ var FIXTURE_DATA = {
|
|
|
1244
1407
|
"mode": "SUBMIT",
|
|
1245
1408
|
"endpoint": "POST /fiat/kyc/form",
|
|
1246
1409
|
"params": {
|
|
1247
|
-
"
|
|
1410
|
+
"quoteRef": "q_8f2",
|
|
1411
|
+
"formId": "kyc_q_8f2_1"
|
|
1248
1412
|
},
|
|
1249
1413
|
"inputs": {
|
|
1250
1414
|
"values": {
|
|
@@ -1305,7 +1469,11 @@ var FIXTURE_DATA = {
|
|
|
1305
1469
|
"phase": "CAPTURE",
|
|
1306
1470
|
"surface": {
|
|
1307
1471
|
"kind": "PCI_COMPONENT",
|
|
1308
|
-
"
|
|
1472
|
+
"provider": "TRANSAK",
|
|
1473
|
+
"params": {
|
|
1474
|
+
"sessionId": "eyJ\u2026",
|
|
1475
|
+
"environment": "STAGING"
|
|
1476
|
+
}
|
|
1309
1477
|
}
|
|
1310
1478
|
},
|
|
1311
1479
|
"provider": "TRANSAK",
|
|
@@ -1325,11 +1493,84 @@ var FIXTURE_DATA = {
|
|
|
1325
1493
|
}
|
|
1326
1494
|
]
|
|
1327
1495
|
},
|
|
1496
|
+
"screen-08-kyc-capture": {
|
|
1497
|
+
"state": {
|
|
1498
|
+
"kind": "KYC",
|
|
1499
|
+
"phase": "CAPTURE",
|
|
1500
|
+
"quote": {
|
|
1501
|
+
"quoteRef": "q_8f2",
|
|
1502
|
+
"fiat": {
|
|
1503
|
+
"currency": "GBP",
|
|
1504
|
+
"amount": "100.00"
|
|
1505
|
+
},
|
|
1506
|
+
"crypto": {
|
|
1507
|
+
"currency": "USDC",
|
|
1508
|
+
"network": "polygon",
|
|
1509
|
+
"amount": "98.41"
|
|
1510
|
+
},
|
|
1511
|
+
"rate": "0.9841",
|
|
1512
|
+
"totalFee": "1.59",
|
|
1513
|
+
"feeBreakdown": [
|
|
1514
|
+
{
|
|
1515
|
+
"id": "provider_fee",
|
|
1516
|
+
"labelFallback": "Processing fee",
|
|
1517
|
+
"amount": "1.59"
|
|
1518
|
+
}
|
|
1519
|
+
],
|
|
1520
|
+
"paymentMethod": "bank_transfer",
|
|
1521
|
+
"expiresAt": "\u2026"
|
|
1522
|
+
},
|
|
1523
|
+
"surface": {
|
|
1524
|
+
"kind": "KYC_SDK",
|
|
1525
|
+
"provider": "SUMSUB",
|
|
1526
|
+
"params": {
|
|
1527
|
+
"accessToken": "eyJ\u2026"
|
|
1528
|
+
},
|
|
1529
|
+
"expiresAt": "\u2026"
|
|
1530
|
+
}
|
|
1531
|
+
},
|
|
1532
|
+
"provider": "TRANSAK",
|
|
1533
|
+
"transitions": [
|
|
1534
|
+
{
|
|
1535
|
+
"id": "capture",
|
|
1536
|
+
"mode": "CLIENT_SURFACE",
|
|
1537
|
+
"report": {
|
|
1538
|
+
"endpoint": "POST /fiat/kyc/capture",
|
|
1539
|
+
"params": {
|
|
1540
|
+
"quoteRef": "q_8f2"
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
]
|
|
1545
|
+
},
|
|
1328
1546
|
"screen-09-kyc-in-review": {
|
|
1329
1547
|
"state": {
|
|
1330
1548
|
"kind": "KYC",
|
|
1331
1549
|
"phase": "NO_ACTION_REQUIRED",
|
|
1332
|
-
"reason": "IN_REVIEW"
|
|
1550
|
+
"reason": "IN_REVIEW",
|
|
1551
|
+
"quote": {
|
|
1552
|
+
"quoteRef": "q_8f2",
|
|
1553
|
+
"fiat": {
|
|
1554
|
+
"currency": "GBP",
|
|
1555
|
+
"amount": "100.00"
|
|
1556
|
+
},
|
|
1557
|
+
"crypto": {
|
|
1558
|
+
"currency": "USDC",
|
|
1559
|
+
"network": "polygon",
|
|
1560
|
+
"amount": "98.41"
|
|
1561
|
+
},
|
|
1562
|
+
"rate": "0.9841",
|
|
1563
|
+
"totalFee": "1.59",
|
|
1564
|
+
"feeBreakdown": [
|
|
1565
|
+
{
|
|
1566
|
+
"id": "provider_fee",
|
|
1567
|
+
"labelFallback": "Processing fee",
|
|
1568
|
+
"amount": "1.59"
|
|
1569
|
+
}
|
|
1570
|
+
],
|
|
1571
|
+
"paymentMethod": "bank_transfer",
|
|
1572
|
+
"expiresAt": "\u2026"
|
|
1573
|
+
}
|
|
1333
1574
|
},
|
|
1334
1575
|
"provider": "TRANSAK",
|
|
1335
1576
|
"transitions": [
|
|
@@ -1337,10 +1578,7 @@ var FIXTURE_DATA = {
|
|
|
1337
1578
|
"id": "poll",
|
|
1338
1579
|
"mode": "AWAIT",
|
|
1339
1580
|
"poll": {
|
|
1340
|
-
"endpoint": "GET /fiat/
|
|
1341
|
-
"params": {
|
|
1342
|
-
"quoteRef": "q_8f2"
|
|
1343
|
-
},
|
|
1581
|
+
"endpoint": "GET /fiat/quotes/q_8f2",
|
|
1344
1582
|
"intervalMs": 3e3,
|
|
1345
1583
|
"backoffFactor": 1.5,
|
|
1346
1584
|
"maxIntervalMs": 1e4,
|
|
@@ -1354,28 +1592,70 @@ var FIXTURE_DATA = {
|
|
|
1354
1592
|
"kind": "KYC",
|
|
1355
1593
|
"phase": "NO_ACTION_REQUIRED",
|
|
1356
1594
|
"reason": "ON_HOLD",
|
|
1357
|
-
"retryAfter": "2026-08-20T09:00:00Z"
|
|
1595
|
+
"retryAfter": "2026-08-20T09:00:00Z",
|
|
1596
|
+
"quote": {
|
|
1597
|
+
"quoteRef": "q_8f2",
|
|
1598
|
+
"fiat": {
|
|
1599
|
+
"currency": "GBP",
|
|
1600
|
+
"amount": "100.00"
|
|
1601
|
+
},
|
|
1602
|
+
"crypto": {
|
|
1603
|
+
"currency": "USDC",
|
|
1604
|
+
"network": "polygon",
|
|
1605
|
+
"amount": "98.41"
|
|
1606
|
+
},
|
|
1607
|
+
"rate": "0.9841",
|
|
1608
|
+
"totalFee": "1.59",
|
|
1609
|
+
"feeBreakdown": [
|
|
1610
|
+
{
|
|
1611
|
+
"id": "provider_fee",
|
|
1612
|
+
"labelFallback": "Processing fee",
|
|
1613
|
+
"amount": "1.59"
|
|
1614
|
+
}
|
|
1615
|
+
],
|
|
1616
|
+
"paymentMethod": "bank_transfer",
|
|
1617
|
+
"expiresAt": "\u2026"
|
|
1618
|
+
}
|
|
1358
1619
|
},
|
|
1359
1620
|
"provider": "TRANSAK",
|
|
1360
1621
|
"transitions": []
|
|
1361
1622
|
},
|
|
1362
|
-
"screen-11-kyc-rejected
|
|
1623
|
+
"screen-11-kyc-rejected": {
|
|
1363
1624
|
"state": {
|
|
1364
1625
|
"kind": "KYC",
|
|
1365
1626
|
"phase": "NO_ACTION_REQUIRED",
|
|
1366
|
-
"reason": "REJECTED"
|
|
1627
|
+
"reason": "REJECTED",
|
|
1628
|
+
"quote": {
|
|
1629
|
+
"quoteRef": "q_8f2",
|
|
1630
|
+
"fiat": {
|
|
1631
|
+
"currency": "GBP",
|
|
1632
|
+
"amount": "100.00"
|
|
1633
|
+
},
|
|
1634
|
+
"crypto": {
|
|
1635
|
+
"currency": "USDC",
|
|
1636
|
+
"network": "polygon",
|
|
1637
|
+
"amount": "98.41"
|
|
1638
|
+
},
|
|
1639
|
+
"rate": "0.9841",
|
|
1640
|
+
"totalFee": "1.59",
|
|
1641
|
+
"feeBreakdown": [
|
|
1642
|
+
{
|
|
1643
|
+
"id": "provider_fee",
|
|
1644
|
+
"labelFallback": "Processing fee",
|
|
1645
|
+
"amount": "1.59"
|
|
1646
|
+
}
|
|
1647
|
+
],
|
|
1648
|
+
"paymentMethod": "bank_transfer",
|
|
1649
|
+
"expiresAt": "\u2026"
|
|
1650
|
+
},
|
|
1651
|
+
"failureReason": {
|
|
1652
|
+
"category": "KYC_REJECTED",
|
|
1653
|
+
"retryable": false,
|
|
1654
|
+
"recovery": "CONTACT_SUPPORT"
|
|
1655
|
+
}
|
|
1367
1656
|
},
|
|
1368
1657
|
"provider": "TRANSAK",
|
|
1369
|
-
"transitions": [
|
|
1370
|
-
{
|
|
1371
|
-
"id": "escalate",
|
|
1372
|
-
"mode": "SUBMIT",
|
|
1373
|
-
"endpoint": "GET /fiat/kyc",
|
|
1374
|
-
"params": {
|
|
1375
|
-
"tier": "escalated"
|
|
1376
|
-
}
|
|
1377
|
-
}
|
|
1378
|
-
]
|
|
1658
|
+
"transitions": []
|
|
1379
1659
|
},
|
|
1380
1660
|
"screen-12-pending-order": {
|
|
1381
1661
|
"state": {
|
|
@@ -1630,6 +1910,18 @@ var FIXTURES = [
|
|
|
1630
1910
|
screen: 1,
|
|
1631
1911
|
stateKey: "QUOTE"
|
|
1632
1912
|
},
|
|
1913
|
+
{
|
|
1914
|
+
id: "screen-01b-quote-with-pay-sheet",
|
|
1915
|
+
file: "screen-01b-quote-with-pay-sheet.json",
|
|
1916
|
+
source: "CONTRACT_DOC",
|
|
1917
|
+
docRef: "Fiat Contract 0.7 \xA7The contract diff \xB7 example envelopes",
|
|
1918
|
+
screen: 1,
|
|
1919
|
+
stateKey: "QUOTE",
|
|
1920
|
+
notes: [
|
|
1921
|
+
"The 0.7 single-screen shape: the quote carries its own PAY_SHEET, so the provider component is the primary CTA rather than something a later state reveals.",
|
|
1922
|
+
"Quote payload is screen-01-quote verbatim; only the surface and the transition differ."
|
|
1923
|
+
]
|
|
1924
|
+
},
|
|
1633
1925
|
{
|
|
1634
1926
|
id: "screen-02-session-auth",
|
|
1635
1927
|
file: "screen-02-session-auth.json",
|
|
@@ -1641,8 +1933,8 @@ var FIXTURES = [
|
|
|
1641
1933
|
{
|
|
1642
1934
|
id: "screen-03-kyc-input-required",
|
|
1643
1935
|
file: "screen-03-kyc-input-required.json",
|
|
1644
|
-
source: "
|
|
1645
|
-
docRef: "
|
|
1936
|
+
source: "KYC_DOC",
|
|
1937
|
+
docRef: "Fiat KYC \xB7 L1 input round",
|
|
1646
1938
|
screen: 3,
|
|
1647
1939
|
stateKey: "KYC/INPUT_REQUIRED",
|
|
1648
1940
|
notes: ["Carries the FORM_VALUES input spec \u2014 the no-double-carry case of the accepted split."]
|
|
@@ -1670,34 +1962,40 @@ var FIXTURES = [
|
|
|
1670
1962
|
"TODO(open-decision): report target per card topology is a Transak ask (contract Flow A)."
|
|
1671
1963
|
]
|
|
1672
1964
|
},
|
|
1965
|
+
{
|
|
1966
|
+
id: "screen-08-kyc-capture",
|
|
1967
|
+
file: "screen-08-kyc-capture.json",
|
|
1968
|
+
source: "KYC_DOC",
|
|
1969
|
+
docRef: "Fiat KYC \xB7 embedded Sumsub WebSDK",
|
|
1970
|
+
screen: 8,
|
|
1971
|
+
stateKey: "KYC/CAPTURE",
|
|
1972
|
+
notes: ["The client mounts the Sumsub adapter; no URL or iframe is emitted."]
|
|
1973
|
+
},
|
|
1673
1974
|
{
|
|
1674
1975
|
id: "screen-09-kyc-in-review",
|
|
1675
1976
|
file: "screen-09-kyc-in-review.json",
|
|
1676
|
-
source: "
|
|
1677
|
-
docRef: "
|
|
1977
|
+
source: "KYC_DOC",
|
|
1978
|
+
docRef: "Fiat KYC \xB7 pending quote",
|
|
1678
1979
|
screen: 9,
|
|
1679
1980
|
stateKey: "KYC/NO_ACTION_REQUIRED:IN_REVIEW"
|
|
1680
1981
|
},
|
|
1681
1982
|
{
|
|
1682
1983
|
id: "screen-10-kyc-on-hold",
|
|
1683
1984
|
file: "screen-10-kyc-on-hold.json",
|
|
1684
|
-
source: "
|
|
1685
|
-
docRef: "
|
|
1985
|
+
source: "KYC_DOC",
|
|
1986
|
+
docRef: "Fiat KYC \xB7 verification on hold",
|
|
1686
1987
|
screen: 10,
|
|
1687
1988
|
stateKey: "KYC/NO_ACTION_REQUIRED:ON_HOLD",
|
|
1688
1989
|
notes: ["transitions: [] and NOT terminal \u2014 the fixture that proves terminality is table data."]
|
|
1689
1990
|
},
|
|
1690
1991
|
{
|
|
1691
|
-
id: "screen-11-kyc-rejected
|
|
1692
|
-
file: "screen-11-kyc-rejected
|
|
1693
|
-
source: "
|
|
1694
|
-
docRef: "
|
|
1992
|
+
id: "screen-11-kyc-rejected",
|
|
1993
|
+
file: "screen-11-kyc-rejected.json",
|
|
1994
|
+
source: "KYC_DOC",
|
|
1995
|
+
docRef: "Fiat KYC \xB7 non-retryable rejection",
|
|
1695
1996
|
screen: 11,
|
|
1696
1997
|
stateKey: "KYC/NO_ACTION_REQUIRED:REJECTED",
|
|
1697
|
-
notes: [
|
|
1698
|
-
"TODO(open-decision): the escalation transition is a flagged placeholder \u2014 a GET carrying params is not a real request shape (FE doc Screen 11 + open question 7).",
|
|
1699
|
-
"The terminal REJECTED variant (failureReason + transitions []) is described in prose, not published as JSON \u2014 no fixture."
|
|
1700
|
-
]
|
|
1998
|
+
notes: ["Only an explicit non-retryable provider or identity-vendor outcome reaches this state."]
|
|
1701
1999
|
},
|
|
1702
2000
|
{
|
|
1703
2001
|
id: "screen-12-pending-order",
|
|
@@ -1742,7 +2040,7 @@ var FIXTURES = [
|
|
|
1742
2040
|
docRef: "Screen 16 \xB7 Not available",
|
|
1743
2041
|
screen: 16,
|
|
1744
2042
|
stateKey: "BLOCKED",
|
|
1745
|
-
notes: ["
|
|
2043
|
+
notes: ["A flatly terminal availability state; explicit non-retryable KYC rejection is terminal too."]
|
|
1746
2044
|
},
|
|
1747
2045
|
{
|
|
1748
2046
|
id: "worked-example-01-quote",
|
|
@@ -1774,10 +2072,6 @@ var FIXTURE_COVERAGE_GAPS = [
|
|
|
1774
2072
|
{
|
|
1775
2073
|
stateKey: "FUN_AUTH",
|
|
1776
2074
|
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
|
-
},
|
|
1778
|
-
{
|
|
1779
|
-
stateKey: "KYC/CAPTURE",
|
|
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
2075
|
}
|
|
1782
2076
|
];
|
|
1783
2077
|
var fixtureMeta = (id) => {
|
|
@@ -1867,6 +2161,16 @@ function checkLegalEmission(state, transitions) {
|
|
|
1867
2161
|
`${entry.key} is terminal (table) and must carry transitions: [] \u2014 got ${transitions.length}`
|
|
1868
2162
|
);
|
|
1869
2163
|
}
|
|
2164
|
+
const surfaceTargets = new Set(
|
|
2165
|
+
transitions.filter((t) => t.mode === "CLIENT_SURFACE").map((t) => t.report.endpoint)
|
|
2166
|
+
);
|
|
2167
|
+
for (const transition of transitions) {
|
|
2168
|
+
if (transition.mode === "SUBMIT" && surfaceTargets.has(transition.endpoint)) {
|
|
2169
|
+
issues.push(
|
|
2170
|
+
`transition "${transition.id}": SUBMIT ${transition.endpoint} coexists with a CLIENT_SURFACE reporting to it`
|
|
2171
|
+
);
|
|
2172
|
+
}
|
|
2173
|
+
}
|
|
1870
2174
|
for (const transition of transitions) {
|
|
1871
2175
|
issues.push(...bodyKeyIssues(transition));
|
|
1872
2176
|
if (transition.mode === "CLIENT_SURFACE" && !("surface" in state)) {
|