@cimplify/sdk 0.104.0 → 0.104.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.
- package/dist/advanced.d.mts +3 -7
- package/dist/advanced.d.ts +3 -7
- package/dist/advanced.js +23 -23
- package/dist/advanced.mjs +4 -4
- package/dist/{client-BuLcOWLv.d.ts → catalogue-BlQ85Uma.d.mts} +415 -763
- package/dist/{client-D5AoobFc.d.mts → catalogue-C8TqOj9K.d.ts} +415 -763
- package/dist/{payment-g0HFkEDU.d.mts → checkout-BqK9XUz5.d.mts} +3824 -1446
- package/dist/{payment-g0HFkEDU.d.ts → checkout-BqK9XUz5.d.ts} +3824 -1446
- package/dist/{chunk-GOHSXBKW.mjs → chunk-33VCYWVL.mjs} +25 -1
- package/dist/{chunk-2WPVUIHX.mjs → chunk-6G3ANKQA.mjs} +1030 -360
- package/dist/{chunk-E4K2VQDD.js → chunk-BWEIMKLF.js} +541 -16
- package/dist/{chunk-IQFFSKE2.js → chunk-DQEXY542.js} +1345 -746
- package/dist/{chunk-CNN7Z2IP.js → chunk-EV2EVOYS.js} +27 -0
- package/dist/{chunk-YVAEY3VC.js → chunk-HYFA67A7.js} +1034 -364
- package/dist/{chunk-5RT4HYMP.mjs → chunk-L5SNEU2O.mjs} +524 -15
- package/dist/{chunk-4BKOKHE3.js → chunk-RBNBSQI7.mjs} +217 -19
- package/dist/{chunk-H3RATH4B.mjs → chunk-SFUJMS5E.js} +261 -2
- package/dist/{chunk-DYIAX3WI.mjs → chunk-U54CWUUT.mjs} +11 -2
- package/dist/{chunk-OKADBZAI.mjs → chunk-UFOWPN3D.mjs} +808 -574
- package/dist/{chunk-OCCNG6MO.mjs → chunk-V4457OKB.mjs} +1310 -722
- package/dist/{chunk-5BDYDZ4U.js → chunk-WXREIBFW.js} +892 -657
- package/dist/{chunk-7PDEX54K.js → chunk-XAUSDMMZ.js} +13 -3
- package/dist/{client-BVNWjk6W.d.ts → client-DhXeiUrf.d.ts} +2 -2
- package/dist/{client-fM716k7G.d.mts → client-mXiUrV4X.d.mts} +2 -2
- package/dist/{index-BtcoqrY1.d.ts → index-B0AdZvrM.d.mts} +9 -4
- package/dist/{index-D7Tf3v7c.d.mts → index-CK8Nk5uQ.d.ts} +9 -4
- package/dist/index-DPWI5XcU.d.ts +8 -0
- package/dist/index-DVfUh-ci.d.mts +8 -0
- package/dist/index.d.mts +31 -14
- package/dist/index.d.ts +31 -14
- package/dist/index.js +305 -133
- package/dist/index.mjs +6 -6
- package/dist/mock/cli.mjs +1442 -386
- package/dist/mock/library.js +1168 -352
- package/dist/mock/library.mjs +1168 -352
- package/dist/mock/msw.js +1168 -352
- package/dist/mock/msw.mjs +1168 -352
- package/dist/{price-BLIeAb8K.d.ts → price-6hQIIac9.d.ts} +1 -1
- package/dist/{price-Czq_cTC_.d.mts → price-BUDv1rI7.d.mts} +1 -1
- package/dist/react.d.mts +10 -5
- package/dist/react.d.ts +10 -5
- package/dist/react.js +227 -197
- package/dist/react.mjs +90 -60
- package/dist/{server-DKK_D-PP.d.ts → server-DJ0mSs9i.d.mts} +31 -57
- package/dist/{server-BkVhOIwn.d.mts → server-DSTX76Ki.d.ts} +31 -57
- package/dist/server.d.mts +3 -4
- package/dist/server.d.ts +3 -4
- package/dist/server.js +11 -11
- package/dist/server.mjs +5 -5
- package/dist/styles.css +2 -2
- package/dist/testing/msw.d.mts +2 -2
- package/dist/testing/msw.d.ts +2 -2
- package/dist/testing/msw.js +4 -4
- package/dist/testing/msw.mjs +3 -3
- package/dist/testing/suite.d.mts +5 -6
- package/dist/testing/suite.d.ts +5 -6
- package/dist/testing/suite.js +29 -28
- package/dist/testing/suite.mjs +10 -9
- package/dist/testing.d.mts +2177 -14
- package/dist/testing.d.ts +2177 -14
- package/dist/testing.js +167 -89
- package/dist/testing.mjs +24 -10
- package/dist/utils.d.mts +3 -4
- package/dist/utils.d.ts +3 -4
- package/dist/utils.js +35 -31
- package/dist/utils.mjs +2 -2
- package/package.json +9 -9
- package/registry/order-history.json +1 -1
- package/registry/order-summary.json +1 -1
- package/dist/link-D9iSVxt5.d.mts +0 -308
- package/dist/link-D9iSVxt5.d.ts +0 -308
|
@@ -1,6 +1,333 @@
|
|
|
1
1
|
import { messagePreview } from './chunk-U4MW6PYU.mjs';
|
|
2
|
-
import { safe, generateIdempotencyKey,
|
|
3
|
-
import { CimplifyError, ErrorCode, IdempotencyMismatchError, enrichError } from './chunk-
|
|
2
|
+
import { ok, err, safe, generateIdempotencyKey, createDefaultPaymentAuthorizationAdapterRegistry, CatalogueQueries, CartOperations, CheckoutService, OrderQueries, LinkService, AuthService, BusinessService, InventoryService, SchedulingService, LiteService, FxService, createElements } from './chunk-V4457OKB.mjs';
|
|
3
|
+
import { PURCHASE_INTENT_RESOLVE_STATUS, PURCHASE_INTENT_CONFIRM_STATUS, PURCHASE_INTENT_RESOLUTION_KIND, PURCHASE_INTENT_ACTION_DECISION, CimplifyError, PURCHASE_INTENT_ERROR_CODE, ErrorCode, IdempotencyMismatchError, enrichError } from './chunk-RBNBSQI7.mjs';
|
|
4
|
+
|
|
5
|
+
// src/types/observability.ts
|
|
6
|
+
var SESSION_CHANGE_SOURCE = {
|
|
7
|
+
RESPONSE: "response",
|
|
8
|
+
MANUAL: "manual",
|
|
9
|
+
CLEAR: "clear"
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// src/utils/purchase-intent-resolver.ts
|
|
13
|
+
var DEFAULT_POLL_INTERVAL_MS = 3e3;
|
|
14
|
+
var DEFAULT_MAX_POLL_ATTEMPTS = 60;
|
|
15
|
+
var DEFAULT_MAX_ACTION_STEPS = 4;
|
|
16
|
+
var DEFAULT_MAX_TERMS_REVIEW_STEPS = 4;
|
|
17
|
+
function nonnegativeInteger(value, fallback) {
|
|
18
|
+
if (value === void 0 || !Number.isFinite(value)) return fallback;
|
|
19
|
+
return Math.max(0, Math.floor(value));
|
|
20
|
+
}
|
|
21
|
+
function abortError() {
|
|
22
|
+
const error = new Error("Purchase-intent resolution was cancelled.");
|
|
23
|
+
error.name = "AbortError";
|
|
24
|
+
return error;
|
|
25
|
+
}
|
|
26
|
+
async function abortableWait(milliseconds, signal) {
|
|
27
|
+
if (signal?.aborted) throw abortError();
|
|
28
|
+
if (milliseconds <= 0) return;
|
|
29
|
+
await new Promise((resolve, reject) => {
|
|
30
|
+
const timer = setTimeout(() => {
|
|
31
|
+
signal?.removeEventListener("abort", onAbort);
|
|
32
|
+
resolve();
|
|
33
|
+
}, milliseconds);
|
|
34
|
+
const onAbort = () => {
|
|
35
|
+
clearTimeout(timer);
|
|
36
|
+
reject(abortError());
|
|
37
|
+
};
|
|
38
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
var PurchaseIntentResolver = class {
|
|
42
|
+
constructor(dependencies) {
|
|
43
|
+
this.confirmation = dependencies.confirmation;
|
|
44
|
+
this.wait = dependencies.wait ?? abortableWait;
|
|
45
|
+
}
|
|
46
|
+
async resolve(intentId, options) {
|
|
47
|
+
const pollIntervalMs = nonnegativeInteger(
|
|
48
|
+
options.pollIntervalMs,
|
|
49
|
+
DEFAULT_POLL_INTERVAL_MS
|
|
50
|
+
);
|
|
51
|
+
const maxPollAttempts = nonnegativeInteger(
|
|
52
|
+
options.maxPollAttempts,
|
|
53
|
+
DEFAULT_MAX_POLL_ATTEMPTS
|
|
54
|
+
);
|
|
55
|
+
const maxActionSteps = nonnegativeInteger(options.maxActionSteps, DEFAULT_MAX_ACTION_STEPS);
|
|
56
|
+
const maxTermsReviewSteps = nonnegativeInteger(
|
|
57
|
+
options.maxTermsReviewSteps,
|
|
58
|
+
DEFAULT_MAX_TERMS_REVIEW_STEPS
|
|
59
|
+
);
|
|
60
|
+
let request = options.confirmation;
|
|
61
|
+
let pollAttempts = 0;
|
|
62
|
+
let actionSteps = 0;
|
|
63
|
+
let termsReviewSteps = 0;
|
|
64
|
+
this.ensureActive(options.signal);
|
|
65
|
+
this.notify(options, PURCHASE_INTENT_RESOLVE_STATUS.CONFIRMING);
|
|
66
|
+
let responseResult = options.initialResponse ? ok(options.initialResponse) : await this.confirmation.confirm(intentId, request, { signal: options.signal });
|
|
67
|
+
while (responseResult.ok) {
|
|
68
|
+
this.ensureActive(options.signal);
|
|
69
|
+
const response = responseResult.value;
|
|
70
|
+
switch (response.status) {
|
|
71
|
+
case PURCHASE_INTENT_CONFIRM_STATUS.ORDER_CREATED:
|
|
72
|
+
this.notify(options, PURCHASE_INTENT_RESOLVE_STATUS.COMPLETED, response);
|
|
73
|
+
return ok({ kind: PURCHASE_INTENT_RESOLUTION_KIND.COMPLETED, response });
|
|
74
|
+
case PURCHASE_INTENT_CONFIRM_STATUS.PAYMENT_FAILED:
|
|
75
|
+
this.notify(options, PURCHASE_INTENT_RESOLVE_STATUS.FAILED, response);
|
|
76
|
+
return ok({ kind: PURCHASE_INTENT_RESOLUTION_KIND.FAILED, response });
|
|
77
|
+
case PURCHASE_INTENT_CONFIRM_STATUS.PAYMENT_OUTCOME_UNKNOWN:
|
|
78
|
+
this.notify(options, PURCHASE_INTENT_RESOLVE_STATUS.OUTCOME_UNKNOWN, response);
|
|
79
|
+
return ok({ kind: PURCHASE_INTENT_RESOLUTION_KIND.OUTCOME_UNKNOWN, response });
|
|
80
|
+
case PURCHASE_INTENT_CONFIRM_STATUS.COLLECTION_PROCESSING:
|
|
81
|
+
if (pollAttempts >= maxPollAttempts) {
|
|
82
|
+
return ok({ kind: PURCHASE_INTENT_RESOLUTION_KIND.TIMED_OUT, response });
|
|
83
|
+
}
|
|
84
|
+
pollAttempts += 1;
|
|
85
|
+
this.notify(options, PURCHASE_INTENT_RESOLVE_STATUS.POLLING, response);
|
|
86
|
+
await this.wait(pollIntervalMs, options.signal);
|
|
87
|
+
responseResult = await this.confirmation.confirm(intentId, request, {
|
|
88
|
+
signal: options.signal
|
|
89
|
+
});
|
|
90
|
+
continue;
|
|
91
|
+
case PURCHASE_INTENT_CONFIRM_STATUS.REDIRECT_REQUIRED:
|
|
92
|
+
case PURCHASE_INTENT_CONFIRM_STATUS.CARD_POPUP_REQUIRED:
|
|
93
|
+
case PURCHASE_INTENT_CONFIRM_STATUS.AUTHORIZATION_REQUIRED: {
|
|
94
|
+
this.notify(options, PURCHASE_INTENT_RESOLVE_STATUS.AWAITING_ACTION, response);
|
|
95
|
+
if (!options.onActionRequired || actionSteps >= maxActionSteps) {
|
|
96
|
+
return ok({ kind: PURCHASE_INTENT_RESOLUTION_KIND.ACTION_REQUIRED, response });
|
|
97
|
+
}
|
|
98
|
+
actionSteps += 1;
|
|
99
|
+
const decision = await options.onActionRequired(response);
|
|
100
|
+
if (decision === PURCHASE_INTENT_ACTION_DECISION.STOP) {
|
|
101
|
+
return ok({ kind: PURCHASE_INTENT_RESOLUTION_KIND.ACTION_REQUIRED, response });
|
|
102
|
+
}
|
|
103
|
+
if (typeof decision === "object" && decision.kind === PURCHASE_INTENT_ACTION_DECISION.SUBMIT) {
|
|
104
|
+
if (!this.confirmation.submitAction) {
|
|
105
|
+
return err(
|
|
106
|
+
new CimplifyError(
|
|
107
|
+
PURCHASE_INTENT_ERROR_CODE.ACTION_UNSUPPORTED,
|
|
108
|
+
"This confirmation transport cannot submit purchase-intent actions.",
|
|
109
|
+
false
|
|
110
|
+
)
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
responseResult = await this.confirmation.submitAction(
|
|
114
|
+
intentId,
|
|
115
|
+
response.attempt_id,
|
|
116
|
+
decision.request,
|
|
117
|
+
{ signal: options.signal }
|
|
118
|
+
);
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
if (decision !== PURCHASE_INTENT_ACTION_DECISION.CONTINUE) {
|
|
122
|
+
return err(
|
|
123
|
+
new CimplifyError(
|
|
124
|
+
PURCHASE_INTENT_ERROR_CODE.ACTION_UNSUPPORTED,
|
|
125
|
+
"The action callback returned an unsupported decision.",
|
|
126
|
+
false
|
|
127
|
+
)
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
await this.wait(pollIntervalMs, options.signal);
|
|
131
|
+
responseResult = await this.confirmation.confirm(intentId, request, {
|
|
132
|
+
signal: options.signal
|
|
133
|
+
});
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
case PURCHASE_INTENT_CONFIRM_STATUS.TERMS_CHANGED: {
|
|
137
|
+
this.notify(options, PURCHASE_INTENT_RESOLVE_STATUS.REVIEWING_TERMS, response);
|
|
138
|
+
if (!options.onTermsChanged || termsReviewSteps >= maxTermsReviewSteps) {
|
|
139
|
+
return ok({ kind: PURCHASE_INTENT_RESOLUTION_KIND.REVIEW_REQUIRED, response });
|
|
140
|
+
}
|
|
141
|
+
termsReviewSteps += 1;
|
|
142
|
+
const replacementRequest = await options.onTermsChanged(response);
|
|
143
|
+
if (!replacementRequest) {
|
|
144
|
+
return ok({ kind: PURCHASE_INTENT_RESOLUTION_KIND.REVIEW_REQUIRED, response });
|
|
145
|
+
}
|
|
146
|
+
request = replacementRequest;
|
|
147
|
+
responseResult = await this.confirmation.confirm(intentId, request, {
|
|
148
|
+
signal: options.signal
|
|
149
|
+
});
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
case PURCHASE_INTENT_CONFIRM_STATUS.SELECTION_UNAVAILABLE:
|
|
153
|
+
case PURCHASE_INTENT_CONFIRM_STATUS.OFFER_ENDED:
|
|
154
|
+
case PURCHASE_INTENT_CONFIRM_STATUS.SOURCE_UNAVAILABLE:
|
|
155
|
+
return ok({ kind: PURCHASE_INTENT_RESOLUTION_KIND.REVIEW_REQUIRED, response });
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return responseResult;
|
|
159
|
+
}
|
|
160
|
+
ensureActive(signal) {
|
|
161
|
+
if (signal?.aborted) throw abortError();
|
|
162
|
+
}
|
|
163
|
+
notify(options, status, response) {
|
|
164
|
+
try {
|
|
165
|
+
options.onStatusChange?.(status, response);
|
|
166
|
+
} catch {
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
// src/purchase-intent.ts
|
|
172
|
+
var PURCHASE_INTENT_ROUTE = {
|
|
173
|
+
DIRECT_COLLECTION: "/api/v1/purchase-intents",
|
|
174
|
+
HOSTED_SESSION_COLLECTION: "/v1/checkout/sessions",
|
|
175
|
+
PREPARE: "prepare",
|
|
176
|
+
REFRESH: "refresh",
|
|
177
|
+
CONFIRM: "confirm",
|
|
178
|
+
REPLAY: "replay",
|
|
179
|
+
ATTEMPTS: "attempts",
|
|
180
|
+
ACTIONS: "actions"
|
|
181
|
+
};
|
|
182
|
+
function directIntentPath(intentId) {
|
|
183
|
+
const normalized = intentId.trim();
|
|
184
|
+
if (!normalized) throw new TypeError("intentId must not be empty");
|
|
185
|
+
return `${PURCHASE_INTENT_ROUTE.DIRECT_COLLECTION}/${encodeURIComponent(normalized)}`;
|
|
186
|
+
}
|
|
187
|
+
function hostedSessionPath(sessionId) {
|
|
188
|
+
const normalized = sessionId.trim();
|
|
189
|
+
if (!normalized) throw new TypeError("sessionId must not be empty");
|
|
190
|
+
return `${PURCHASE_INTENT_ROUTE.HOSTED_SESSION_COLLECTION}/${encodeURIComponent(normalized)}`;
|
|
191
|
+
}
|
|
192
|
+
var PurchaseIntentService = class {
|
|
193
|
+
constructor(transport) {
|
|
194
|
+
this.transport = transport;
|
|
195
|
+
this.resolver = new PurchaseIntentResolver({ confirmation: this });
|
|
196
|
+
}
|
|
197
|
+
mint(request, options) {
|
|
198
|
+
return safe(
|
|
199
|
+
this.transport.post(PURCHASE_INTENT_ROUTE.DIRECT_COLLECTION, request, {
|
|
200
|
+
idempotencyKey: request.client_intent_key,
|
|
201
|
+
signal: options?.signal,
|
|
202
|
+
timeoutMs: options?.timeoutMs
|
|
203
|
+
})
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
get(intentId, options) {
|
|
207
|
+
return safe(
|
|
208
|
+
this.transport.get(this.intentPath(intentId), {
|
|
209
|
+
signal: options?.signal,
|
|
210
|
+
timeoutMs: options?.timeoutMs
|
|
211
|
+
})
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
refresh(intentId, expectedRevision, options) {
|
|
215
|
+
const request = {
|
|
216
|
+
expected_intent_revision: expectedRevision
|
|
217
|
+
};
|
|
218
|
+
return safe(
|
|
219
|
+
this.transport.post(
|
|
220
|
+
`${this.intentPath(intentId)}/${PURCHASE_INTENT_ROUTE.REFRESH}`,
|
|
221
|
+
request,
|
|
222
|
+
{
|
|
223
|
+
signal: options?.signal,
|
|
224
|
+
timeoutMs: options?.timeoutMs
|
|
225
|
+
}
|
|
226
|
+
)
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
confirm(intentId, request, options) {
|
|
230
|
+
return safe(
|
|
231
|
+
this.transport.post(
|
|
232
|
+
`${this.intentPath(intentId)}/${PURCHASE_INTENT_ROUTE.CONFIRM}`,
|
|
233
|
+
request,
|
|
234
|
+
{
|
|
235
|
+
idempotencyKey: request.idempotency_key,
|
|
236
|
+
signal: options?.signal,
|
|
237
|
+
timeoutMs: options?.timeoutMs
|
|
238
|
+
}
|
|
239
|
+
)
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
submitAction(intentId, attemptId, request, options) {
|
|
243
|
+
const normalizedAttemptId = attemptId.trim();
|
|
244
|
+
if (!normalizedAttemptId) throw new TypeError("attemptId must not be empty");
|
|
245
|
+
return safe(
|
|
246
|
+
this.transport.post(
|
|
247
|
+
`${this.intentPath(intentId)}/${PURCHASE_INTENT_ROUTE.ATTEMPTS}/${encodeURIComponent(normalizedAttemptId)}/${PURCHASE_INTENT_ROUTE.ACTIONS}`,
|
|
248
|
+
request,
|
|
249
|
+
{
|
|
250
|
+
signal: options?.signal,
|
|
251
|
+
timeoutMs: options?.timeoutMs
|
|
252
|
+
}
|
|
253
|
+
)
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
resolve(intentId, options) {
|
|
257
|
+
return this.resolver.resolve(intentId, options);
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Bind subsequent preparation/confirmation to one hosted checkout-session
|
|
261
|
+
* owner. The session ID is a route authority, not an intent-owner rewrite.
|
|
262
|
+
*/
|
|
263
|
+
forHostedSession(sessionId) {
|
|
264
|
+
return new HostedPurchaseIntentService(this.transport, sessionId);
|
|
265
|
+
}
|
|
266
|
+
intentPath(intentId) {
|
|
267
|
+
return directIntentPath(intentId);
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
var HostedPurchaseIntentService = class {
|
|
271
|
+
constructor(transport, sessionId) {
|
|
272
|
+
this.transport = transport;
|
|
273
|
+
this.sessionPath = hostedSessionPath(sessionId);
|
|
274
|
+
this.resolver = new PurchaseIntentResolver({ confirmation: this });
|
|
275
|
+
}
|
|
276
|
+
prepare(request, options) {
|
|
277
|
+
return safe(
|
|
278
|
+
this.transport.post(
|
|
279
|
+
`${this.sessionPath}/${PURCHASE_INTENT_ROUTE.PREPARE}`,
|
|
280
|
+
request,
|
|
281
|
+
{
|
|
282
|
+
idempotencyKey: request.idempotency_key,
|
|
283
|
+
signal: options?.signal,
|
|
284
|
+
timeoutMs: options?.timeoutMs
|
|
285
|
+
}
|
|
286
|
+
)
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
confirm(_intentId, request, options) {
|
|
290
|
+
return safe(
|
|
291
|
+
this.transport.post(
|
|
292
|
+
`${this.sessionPath}/${PURCHASE_INTENT_ROUTE.CONFIRM}`,
|
|
293
|
+
request,
|
|
294
|
+
{
|
|
295
|
+
idempotencyKey: request.idempotency_key,
|
|
296
|
+
signal: options?.signal,
|
|
297
|
+
timeoutMs: options?.timeoutMs
|
|
298
|
+
}
|
|
299
|
+
)
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
replay(options) {
|
|
303
|
+
return safe(
|
|
304
|
+
this.transport.get(
|
|
305
|
+
`${this.sessionPath}/${PURCHASE_INTENT_ROUTE.REPLAY}`,
|
|
306
|
+
{
|
|
307
|
+
signal: options?.signal,
|
|
308
|
+
timeoutMs: options?.timeoutMs
|
|
309
|
+
}
|
|
310
|
+
)
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
submitAction(_intentId, attemptId, request, options) {
|
|
314
|
+
const normalized = attemptId.trim();
|
|
315
|
+
if (!normalized) throw new TypeError("attemptId must not be empty");
|
|
316
|
+
return safe(
|
|
317
|
+
this.transport.post(
|
|
318
|
+
`${this.sessionPath}/${PURCHASE_INTENT_ROUTE.ATTEMPTS}/${encodeURIComponent(normalized)}/${PURCHASE_INTENT_ROUTE.ACTIONS}`,
|
|
319
|
+
request,
|
|
320
|
+
{
|
|
321
|
+
signal: options?.signal,
|
|
322
|
+
timeoutMs: options?.timeoutMs
|
|
323
|
+
}
|
|
324
|
+
)
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
resolve(intentId, options) {
|
|
328
|
+
return this.resolver.resolve(intentId, options);
|
|
329
|
+
}
|
|
330
|
+
};
|
|
4
331
|
|
|
5
332
|
// src/store-credit.ts
|
|
6
333
|
var StoreCreditQueries = class {
|
|
@@ -18,12 +345,13 @@ var StoreCreditQueries = class {
|
|
|
18
345
|
|
|
19
346
|
// src/internal/analytics-session.ts
|
|
20
347
|
var ANALYTICS_SESSION_IDLE_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
21
|
-
var
|
|
22
|
-
var ANALYTICS_SESSION_STATE_VERSION =
|
|
348
|
+
var ANALYTICS_SESSION_STORAGE_KEY_PREFIX = "cimplify_analytics_session:v2:";
|
|
349
|
+
var ANALYTICS_SESSION_STATE_VERSION = 2;
|
|
23
350
|
var MICROSECONDS_PER_MILLISECOND = 1e3;
|
|
24
351
|
var MICROSECONDS_PER_SECOND = 1e6;
|
|
25
352
|
var AnalyticsSessionCoordinator = class {
|
|
26
|
-
constructor() {
|
|
353
|
+
constructor(identity) {
|
|
354
|
+
this.identity = identity;
|
|
27
355
|
this.fallbackState = null;
|
|
28
356
|
}
|
|
29
357
|
beginEvent(nowMs = Date.now()) {
|
|
@@ -61,7 +389,7 @@ var AnalyticsSessionCoordinator = class {
|
|
|
61
389
|
reset() {
|
|
62
390
|
this.fallbackState = null;
|
|
63
391
|
try {
|
|
64
|
-
sessionStorage.removeItem(
|
|
392
|
+
sessionStorage.removeItem(this.storageKey());
|
|
65
393
|
} catch {
|
|
66
394
|
}
|
|
67
395
|
}
|
|
@@ -71,11 +399,11 @@ var AnalyticsSessionCoordinator = class {
|
|
|
71
399
|
}
|
|
72
400
|
read() {
|
|
73
401
|
try {
|
|
74
|
-
const serialized = sessionStorage.getItem(
|
|
402
|
+
const serialized = sessionStorage.getItem(this.storageKey());
|
|
75
403
|
if (!serialized) return this.fallbackState;
|
|
76
404
|
const state = JSON.parse(serialized);
|
|
77
405
|
if (!isPersistedSession(state)) {
|
|
78
|
-
sessionStorage.removeItem(
|
|
406
|
+
sessionStorage.removeItem(this.storageKey());
|
|
79
407
|
return this.fallbackState;
|
|
80
408
|
}
|
|
81
409
|
this.fallbackState = state;
|
|
@@ -87,10 +415,13 @@ var AnalyticsSessionCoordinator = class {
|
|
|
87
415
|
write(state) {
|
|
88
416
|
this.fallbackState = state;
|
|
89
417
|
try {
|
|
90
|
-
sessionStorage.setItem(
|
|
418
|
+
sessionStorage.setItem(this.storageKey(), JSON.stringify(state));
|
|
91
419
|
} catch {
|
|
92
420
|
}
|
|
93
421
|
}
|
|
422
|
+
storageKey() {
|
|
423
|
+
return `${ANALYTICS_SESSION_STORAGE_KEY_PREFIX}${encodeURIComponent(this.identity)}`;
|
|
424
|
+
}
|
|
94
425
|
};
|
|
95
426
|
function newSession(nowMs) {
|
|
96
427
|
return {
|
|
@@ -157,11 +488,127 @@ function generateSessionId() {
|
|
|
157
488
|
|
|
158
489
|
// src/activity.ts
|
|
159
490
|
var VISITOR_ID_STORAGE_KEY = "cimplify_visitor_id";
|
|
491
|
+
var ACTIVITY_RETRY_POLICY = {
|
|
492
|
+
maxRetries: 1,
|
|
493
|
+
jitterRatio: 0.2
|
|
494
|
+
};
|
|
495
|
+
var PAGE_VIEW_TRACKER_KEY = /* @__PURE__ */ Symbol.for("@cimplify/sdk.page-view-tracker-hub");
|
|
496
|
+
var PAGE_VIEW_TRACKER_VERSION = 2;
|
|
497
|
+
function trackerHost() {
|
|
498
|
+
return window;
|
|
499
|
+
}
|
|
500
|
+
function currentWindowTrackerHub() {
|
|
501
|
+
const candidate = trackerHost()[PAGE_VIEW_TRACKER_KEY];
|
|
502
|
+
return candidate?.version === PAGE_VIEW_TRACKER_VERSION && candidate.registrations instanceof Map && typeof candidate.recordAll === "function" ? candidate : null;
|
|
503
|
+
}
|
|
504
|
+
function weakReference(target) {
|
|
505
|
+
const WeakReference = globalThis.WeakRef;
|
|
506
|
+
if (WeakReference) return new WeakReference(target);
|
|
507
|
+
return { deref: () => target };
|
|
508
|
+
}
|
|
509
|
+
function latestLiveOwner(registration) {
|
|
510
|
+
let latest = null;
|
|
511
|
+
for (const [token, reference] of registration.owners) {
|
|
512
|
+
const owner = reference.deref();
|
|
513
|
+
if (!owner || owner.released) {
|
|
514
|
+
registration.owners.delete(token);
|
|
515
|
+
} else {
|
|
516
|
+
latest = owner;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
return latest;
|
|
520
|
+
}
|
|
521
|
+
function recordRegistration(registration, path) {
|
|
522
|
+
if (path === registration.lastPath) return;
|
|
523
|
+
const owner = registration.activeOwner && !registration.activeOwner.released ? registration.activeOwner : latestLiveOwner(registration);
|
|
524
|
+
if (!owner) return;
|
|
525
|
+
registration.activeOwner = owner;
|
|
526
|
+
registration.lastPath = path;
|
|
527
|
+
owner.recorder(path);
|
|
528
|
+
}
|
|
529
|
+
function createWindowTrackerHub() {
|
|
530
|
+
const originalPushState = window.history.pushState;
|
|
531
|
+
const originalReplaceState = window.history.replaceState;
|
|
532
|
+
const hub = {
|
|
533
|
+
version: PAGE_VIEW_TRACKER_VERSION,
|
|
534
|
+
registrations: /* @__PURE__ */ new Map(),
|
|
535
|
+
originalPushState,
|
|
536
|
+
originalReplaceState
|
|
537
|
+
};
|
|
538
|
+
hub.recordAll = () => {
|
|
539
|
+
const path = window.location.pathname;
|
|
540
|
+
for (const registration of hub.registrations.values()) {
|
|
541
|
+
recordRegistration(registration, path);
|
|
542
|
+
}
|
|
543
|
+
};
|
|
544
|
+
hub.patchedPushState = function(...args) {
|
|
545
|
+
originalPushState.apply(this, args);
|
|
546
|
+
hub.recordAll();
|
|
547
|
+
};
|
|
548
|
+
hub.patchedReplaceState = function(...args) {
|
|
549
|
+
originalReplaceState.apply(this, args);
|
|
550
|
+
hub.recordAll();
|
|
551
|
+
};
|
|
552
|
+
hub.popstateListener = () => hub.recordAll();
|
|
553
|
+
window.history.pushState = hub.patchedPushState;
|
|
554
|
+
window.history.replaceState = hub.patchedReplaceState;
|
|
555
|
+
window.addEventListener("popstate", hub.popstateListener);
|
|
556
|
+
trackerHost()[PAGE_VIEW_TRACKER_KEY] = hub;
|
|
557
|
+
return hub;
|
|
558
|
+
}
|
|
559
|
+
function pageViewTrackerIdentity(client) {
|
|
560
|
+
return JSON.stringify([client.getBaseUrl(), client.getPublicKey().trim()]);
|
|
561
|
+
}
|
|
562
|
+
function acquireWindowPageViewTracker(identity, recorder) {
|
|
563
|
+
const hub = currentWindowTrackerHub() ?? createWindowTrackerHub();
|
|
564
|
+
let registration = hub.registrations.get(identity);
|
|
565
|
+
const isNewIdentity = !registration;
|
|
566
|
+
if (!registration) {
|
|
567
|
+
registration = { owners: /* @__PURE__ */ new Map(), activeOwner: null, lastPath: null };
|
|
568
|
+
hub.registrations.set(identity, registration);
|
|
569
|
+
}
|
|
570
|
+
const owner = {
|
|
571
|
+
token: Symbol(identity),
|
|
572
|
+
recorder,
|
|
573
|
+
released: false
|
|
574
|
+
};
|
|
575
|
+
registration.owners.set(owner.token, weakReference(owner));
|
|
576
|
+
registration.activeOwner = owner;
|
|
577
|
+
if (isNewIdentity) {
|
|
578
|
+
recordRegistration(registration, window.location.pathname);
|
|
579
|
+
}
|
|
580
|
+
let released = false;
|
|
581
|
+
return () => {
|
|
582
|
+
if (released) return;
|
|
583
|
+
released = true;
|
|
584
|
+
owner.released = true;
|
|
585
|
+
registration.owners.delete(owner.token);
|
|
586
|
+
if (registration.activeOwner === owner) {
|
|
587
|
+
registration.activeOwner = latestLiveOwner(registration);
|
|
588
|
+
}
|
|
589
|
+
if (registration.activeOwner) return;
|
|
590
|
+
if (hub.registrations.get(identity) === registration) {
|
|
591
|
+
hub.registrations.delete(identity);
|
|
592
|
+
}
|
|
593
|
+
if (hub.registrations.size !== 0) return;
|
|
594
|
+
window.removeEventListener("popstate", hub.popstateListener);
|
|
595
|
+
if (window.history.pushState === hub.patchedPushState) {
|
|
596
|
+
window.history.pushState = hub.originalPushState;
|
|
597
|
+
}
|
|
598
|
+
if (window.history.replaceState === hub.patchedReplaceState) {
|
|
599
|
+
window.history.replaceState = hub.originalReplaceState;
|
|
600
|
+
}
|
|
601
|
+
if (currentWindowTrackerHub() === hub) {
|
|
602
|
+
delete trackerHost()[PAGE_VIEW_TRACKER_KEY];
|
|
603
|
+
}
|
|
604
|
+
};
|
|
605
|
+
}
|
|
160
606
|
var ActivityService = class {
|
|
161
607
|
constructor(client) {
|
|
162
608
|
this.client = client;
|
|
163
|
-
this.
|
|
609
|
+
this.localGeneration = 0;
|
|
164
610
|
this.bootstrapRequest = null;
|
|
611
|
+
this.sessions = this.createSessionCoordinator();
|
|
165
612
|
}
|
|
166
613
|
/**
|
|
167
614
|
* Record session start (landing, referrer, UTM, device). Fire-and-forget,
|
|
@@ -171,7 +618,7 @@ var ActivityService = class {
|
|
|
171
618
|
startSession() {
|
|
172
619
|
if (typeof window === "undefined") return;
|
|
173
620
|
const session = this.sessions.beginEvent();
|
|
174
|
-
void this.bootstrapSession(session).catch(() => {
|
|
621
|
+
void this.bootstrapSession(session, false, this.captureGeneration()).catch(() => {
|
|
175
622
|
});
|
|
176
623
|
}
|
|
177
624
|
/** Rebind an existing analytics session after the server authenticates a customer. */
|
|
@@ -179,13 +626,19 @@ var ActivityService = class {
|
|
|
179
626
|
if (typeof window === "undefined") return;
|
|
180
627
|
const session = this.sessions.current();
|
|
181
628
|
if (!session) return;
|
|
182
|
-
void this.bootstrapSession(session, true).catch(() => {
|
|
629
|
+
void this.bootstrapSession(session, true, this.captureGeneration()).catch(() => {
|
|
183
630
|
});
|
|
184
631
|
}
|
|
185
632
|
/** A signed-out browser starts a fresh anonymous analytics session. */
|
|
186
633
|
resetSession() {
|
|
634
|
+
this.invalidatePendingActivity();
|
|
187
635
|
this.sessions.reset();
|
|
188
|
-
|
|
636
|
+
}
|
|
637
|
+
/** Drop the old storefront's analytics state before binding this service to the new one. */
|
|
638
|
+
resetForStorefrontIdentityChange() {
|
|
639
|
+
this.invalidatePendingActivity();
|
|
640
|
+
this.sessions.reset();
|
|
641
|
+
this.sessions = this.createSessionCoordinator();
|
|
189
642
|
}
|
|
190
643
|
buildSessionPayload(session) {
|
|
191
644
|
const params = new URLSearchParams(window.location.search);
|
|
@@ -281,32 +734,12 @@ var ActivityService = class {
|
|
|
281
734
|
autoTrackPageViews() {
|
|
282
735
|
if (typeof window === "undefined") return () => {
|
|
283
736
|
};
|
|
284
|
-
|
|
285
|
-
const record = () => {
|
|
286
|
-
const path = window.location.pathname;
|
|
287
|
-
if (path === lastPath) return;
|
|
288
|
-
lastPath = path;
|
|
737
|
+
return acquireWindowPageViewTracker(pageViewTrackerIdentity(this.client), (path) => {
|
|
289
738
|
try {
|
|
290
739
|
this.trackPageView({ pagePath: path });
|
|
291
740
|
} catch {
|
|
292
741
|
}
|
|
293
|
-
};
|
|
294
|
-
const { pushState, replaceState } = window.history;
|
|
295
|
-
window.history.pushState = function(...args) {
|
|
296
|
-
pushState.apply(this, args);
|
|
297
|
-
record();
|
|
298
|
-
};
|
|
299
|
-
window.history.replaceState = function(...args) {
|
|
300
|
-
replaceState.apply(this, args);
|
|
301
|
-
record();
|
|
302
|
-
};
|
|
303
|
-
window.addEventListener("popstate", record);
|
|
304
|
-
record();
|
|
305
|
-
return () => {
|
|
306
|
-
window.history.pushState = pushState;
|
|
307
|
-
window.history.replaceState = replaceState;
|
|
308
|
-
window.removeEventListener("popstate", record);
|
|
309
|
-
};
|
|
742
|
+
});
|
|
310
743
|
}
|
|
311
744
|
sendEvents(events, session) {
|
|
312
745
|
if (typeof window === "undefined") return;
|
|
@@ -314,29 +747,59 @@ var ActivityService = class {
|
|
|
314
747
|
});
|
|
315
748
|
}
|
|
316
749
|
async postEvents(events, session) {
|
|
317
|
-
|
|
318
|
-
await this.
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
events
|
|
322
|
-
|
|
750
|
+
const generation = this.captureGeneration();
|
|
751
|
+
await this.bootstrapSession(session, false, generation);
|
|
752
|
+
if (!this.isCurrentGeneration(generation)) return;
|
|
753
|
+
await this.client.post(
|
|
754
|
+
"/api/v1/activity/events",
|
|
755
|
+
{
|
|
756
|
+
analytics_session_id: session.id,
|
|
757
|
+
occurred_at: session.occurredAt,
|
|
758
|
+
events
|
|
759
|
+
},
|
|
760
|
+
{ retryPolicy: ACTIVITY_RETRY_POLICY }
|
|
761
|
+
);
|
|
323
762
|
}
|
|
324
|
-
bootstrapSession(session, force = false) {
|
|
763
|
+
bootstrapSession(session, force = false, generation = this.captureGeneration()) {
|
|
325
764
|
if (!force && !session.requiresBootstrap) return Promise.resolve();
|
|
326
|
-
if (!force && this.bootstrapRequest?.sessionId === session.id) {
|
|
765
|
+
if (!force && this.bootstrapRequest?.sessionId === session.id && sameActivityGeneration(this.bootstrapRequest.generation, generation)) {
|
|
327
766
|
return this.bootstrapRequest.promise;
|
|
328
767
|
}
|
|
329
|
-
const promise = this.client.post("/api/v1/activity/session", this.buildSessionPayload(session)
|
|
330
|
-
|
|
768
|
+
const promise = this.client.post("/api/v1/activity/session", this.buildSessionPayload(session), {
|
|
769
|
+
retryPolicy: ACTIVITY_RETRY_POLICY
|
|
770
|
+
}).then(() => {
|
|
771
|
+
if (this.isCurrentGeneration(generation)) {
|
|
772
|
+
this.sessions.markBootstrapped(session.id);
|
|
773
|
+
}
|
|
331
774
|
}).finally(() => {
|
|
332
775
|
if (this.bootstrapRequest?.promise === promise) {
|
|
333
776
|
this.bootstrapRequest = null;
|
|
334
777
|
}
|
|
335
778
|
});
|
|
336
|
-
this.bootstrapRequest = { sessionId: session.id, promise };
|
|
779
|
+
this.bootstrapRequest = { sessionId: session.id, generation, promise };
|
|
337
780
|
return promise;
|
|
338
781
|
}
|
|
782
|
+
captureGeneration() {
|
|
783
|
+
return {
|
|
784
|
+
local: this.localGeneration,
|
|
785
|
+
publicKey: this.client.getPublicKeyEpoch(),
|
|
786
|
+
session: this.client.getSessionEpoch()
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
isCurrentGeneration(generation) {
|
|
790
|
+
return sameActivityGeneration(generation, this.captureGeneration());
|
|
791
|
+
}
|
|
792
|
+
invalidatePendingActivity() {
|
|
793
|
+
this.localGeneration += 1;
|
|
794
|
+
this.bootstrapRequest = null;
|
|
795
|
+
}
|
|
796
|
+
createSessionCoordinator() {
|
|
797
|
+
return new AnalyticsSessionCoordinator(pageViewTrackerIdentity(this.client));
|
|
798
|
+
}
|
|
339
799
|
};
|
|
800
|
+
function sameActivityGeneration(left, right) {
|
|
801
|
+
return left.local === right.local && left.publicKey === right.publicKey && left.session === right.session;
|
|
802
|
+
}
|
|
340
803
|
function currentPath() {
|
|
341
804
|
if (typeof window !== "undefined") {
|
|
342
805
|
return window.location.pathname;
|
|
@@ -849,459 +1312,108 @@ var SupportService = class {
|
|
|
849
1312
|
}
|
|
850
1313
|
};
|
|
851
1314
|
|
|
852
|
-
// src/
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
try {
|
|
859
|
-
const imported = await import('@paystack/inline-js');
|
|
860
|
-
PaystackPop = imported.default;
|
|
861
|
-
} catch {
|
|
862
|
-
return { success: false, error: "PROVIDER_UNAVAILABLE" };
|
|
863
|
-
}
|
|
864
|
-
return new Promise((resolve) => {
|
|
865
|
-
let settled = false;
|
|
866
|
-
let providerStarted = false;
|
|
867
|
-
const resolveOnce = (result) => {
|
|
868
|
-
if (settled) {
|
|
869
|
-
return;
|
|
870
|
-
}
|
|
871
|
-
settled = true;
|
|
872
|
-
if (signal) {
|
|
873
|
-
signal.removeEventListener("abort", onAbort);
|
|
874
|
-
}
|
|
875
|
-
resolve(result);
|
|
876
|
-
};
|
|
877
|
-
const onAbort = () => {
|
|
878
|
-
resolveOnce({
|
|
879
|
-
success: false,
|
|
880
|
-
// Once Paystack owns the transaction, cancellation is ambiguous. The
|
|
881
|
-
// adapter converts this into submitted so Cimplify polling decides.
|
|
882
|
-
error: providerStarted ? "SUBMISSION_UNKNOWN" : "CANCELLED"
|
|
883
|
-
});
|
|
884
|
-
};
|
|
885
|
-
if (signal?.aborted) {
|
|
886
|
-
resolveOnce({ success: false, error: "CANCELLED" });
|
|
887
|
-
return;
|
|
888
|
-
}
|
|
889
|
-
if (signal) {
|
|
890
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
891
|
-
}
|
|
892
|
-
try {
|
|
893
|
-
const popup = new PaystackPop();
|
|
894
|
-
popup.newTransaction({
|
|
895
|
-
key: options.key,
|
|
896
|
-
email: options.email,
|
|
897
|
-
amount: options.amount,
|
|
898
|
-
currency: options.currency,
|
|
899
|
-
reference: options.reference,
|
|
900
|
-
accessCode: options.accessCode,
|
|
901
|
-
onSuccess: (transaction) => {
|
|
902
|
-
resolveOnce({
|
|
903
|
-
success: true,
|
|
904
|
-
reference: transaction.reference ?? options.reference
|
|
905
|
-
});
|
|
906
|
-
},
|
|
907
|
-
onCancel: () => {
|
|
908
|
-
resolveOnce({ success: false, error: "PAYMENT_CANCELLED" });
|
|
909
|
-
},
|
|
910
|
-
onError: (error) => {
|
|
911
|
-
resolveOnce({
|
|
912
|
-
success: false,
|
|
913
|
-
error: error?.message || "PAYMENT_FAILED"
|
|
914
|
-
});
|
|
915
|
-
}
|
|
916
|
-
});
|
|
917
|
-
providerStarted = true;
|
|
918
|
-
} catch {
|
|
919
|
-
resolveOnce({ success: false, error: "POPUP_BLOCKED" });
|
|
920
|
-
}
|
|
921
|
-
});
|
|
1315
|
+
// src/internal/cross-tab-session.ts
|
|
1316
|
+
var CROSS_TAB_SESSION_HUB_KEY = /* @__PURE__ */ Symbol.for("@cimplify/sdk.cross-tab-session-hub");
|
|
1317
|
+
var SESSION_SYNC_STORAGE_KEY = "cimplify_session_sync";
|
|
1318
|
+
var MAX_RECENT_ENVELOPES = 64;
|
|
1319
|
+
function hubHost() {
|
|
1320
|
+
return window;
|
|
922
1321
|
}
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
currency: context.currency
|
|
936
|
-
},
|
|
937
|
-
context.signal
|
|
938
|
-
);
|
|
939
|
-
if (result.success) {
|
|
940
|
-
return { status: "submitted", reference: result.reference || context.reference };
|
|
941
|
-
}
|
|
942
|
-
if (result.error === "SUBMISSION_UNKNOWN") {
|
|
943
|
-
return { status: "submitted", reference: context.reference };
|
|
944
|
-
}
|
|
945
|
-
if (!result.error || result.error === "CANCELLED" || result.error === "PAYMENT_CANCELLED") {
|
|
946
|
-
return { status: "cancelled" };
|
|
947
|
-
}
|
|
948
|
-
if (result.error === "POPUP_BLOCKED") {
|
|
949
|
-
return {
|
|
950
|
-
status: "failed",
|
|
951
|
-
code: "POPUP_BLOCKED",
|
|
952
|
-
message: "Unable to open the Paystack payment popup. Please allow popups and try again.",
|
|
953
|
-
recoverable: true
|
|
954
|
-
};
|
|
955
|
-
}
|
|
956
|
-
if (result.error === "PROVIDER_UNAVAILABLE") {
|
|
957
|
-
return {
|
|
958
|
-
status: "failed",
|
|
959
|
-
code: "PROVIDER_UNAVAILABLE",
|
|
960
|
-
message: "Paystack is unavailable in this environment.",
|
|
961
|
-
recoverable: false
|
|
962
|
-
};
|
|
963
|
-
}
|
|
964
|
-
return {
|
|
965
|
-
status: "failed",
|
|
966
|
-
code: "PAYMENT_FAILED",
|
|
967
|
-
message: result.error || "Paystack payment could not be submitted.",
|
|
968
|
-
recoverable: true
|
|
969
|
-
};
|
|
1322
|
+
function currentHub() {
|
|
1323
|
+
const candidate = hubHost()[CROSS_TAB_SESSION_HUB_KEY];
|
|
1324
|
+
return candidate?.subscribers instanceof Map && candidate.recentEnvelopes instanceof Set && typeof candidate.storageListener === "function" && typeof candidate.dispatch === "function" ? candidate : null;
|
|
1325
|
+
}
|
|
1326
|
+
function subscriberReference(target) {
|
|
1327
|
+
const WeakReference = globalThis.WeakRef;
|
|
1328
|
+
if (WeakReference) return new WeakReference(target);
|
|
1329
|
+
return { deref: () => target };
|
|
1330
|
+
}
|
|
1331
|
+
function pruneSubscribers(hub) {
|
|
1332
|
+
for (const [token, reference] of hub.subscribers) {
|
|
1333
|
+
if (!reference.deref()) hub.subscribers.delete(token);
|
|
970
1334
|
}
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
async function loadStripeFromPeer(publicKey, options) {
|
|
1335
|
+
}
|
|
1336
|
+
function parseSessionSyncPayload(serialized) {
|
|
1337
|
+
if (!serialized) return null;
|
|
975
1338
|
try {
|
|
976
|
-
const
|
|
977
|
-
return
|
|
1339
|
+
const payload = JSON.parse(serialized);
|
|
1340
|
+
return (payload.type === "claimed" || payload.type === "cleared") && typeof payload.token === "string" && payload.token.length > 0 && typeof payload.at === "number" && Number.isFinite(payload.at) && (payload.name === void 0 || payload.name === null || typeof payload.name === "string") ? payload : null;
|
|
978
1341
|
} catch {
|
|
979
1342
|
return null;
|
|
980
1343
|
}
|
|
981
1344
|
}
|
|
982
|
-
function
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
const root = document.createElement("div");
|
|
986
|
-
root.setAttribute("role", "dialog");
|
|
987
|
-
root.setAttribute("aria-modal", "true");
|
|
988
|
-
root.setAttribute("aria-label", "Secure card payment");
|
|
989
|
-
Object.assign(root.style, {
|
|
990
|
-
position: "fixed",
|
|
991
|
-
inset: "0",
|
|
992
|
-
zIndex: "2147483647",
|
|
993
|
-
display: "grid",
|
|
994
|
-
placeItems: "center",
|
|
995
|
-
padding: "16px",
|
|
996
|
-
background: "rgba(15, 23, 42, 0.58)"
|
|
997
|
-
});
|
|
998
|
-
const form = document.createElement("form");
|
|
999
|
-
Object.assign(form.style, {
|
|
1000
|
-
width: "min(100%, 480px)",
|
|
1001
|
-
maxHeight: "calc(100vh - 32px)",
|
|
1002
|
-
overflow: "auto",
|
|
1003
|
-
borderRadius: "12px",
|
|
1004
|
-
padding: "24px",
|
|
1005
|
-
background: "#fff",
|
|
1006
|
-
boxShadow: "0 24px 80px rgba(15, 23, 42, 0.32)"
|
|
1007
|
-
});
|
|
1008
|
-
const title = document.createElement("h2");
|
|
1009
|
-
title.textContent = "Complete card payment";
|
|
1010
|
-
Object.assign(title.style, { margin: "0 0 18px", font: "600 18px/1.4 system-ui" });
|
|
1011
|
-
const paymentMount = document.createElement("div");
|
|
1012
|
-
paymentMount.setAttribute("data-cimplify-stripe-payment-element", "");
|
|
1013
|
-
const error = document.createElement("p");
|
|
1014
|
-
error.setAttribute("role", "alert");
|
|
1015
|
-
Object.assign(error.style, {
|
|
1016
|
-
minHeight: "20px",
|
|
1017
|
-
margin: "14px 0 0",
|
|
1018
|
-
color: "#b91c1c",
|
|
1019
|
-
font: "14px/1.4 system-ui"
|
|
1020
|
-
});
|
|
1021
|
-
const actions = document.createElement("div");
|
|
1022
|
-
Object.assign(actions.style, {
|
|
1023
|
-
display: "flex",
|
|
1024
|
-
justifyContent: "flex-end",
|
|
1025
|
-
gap: "10px",
|
|
1026
|
-
marginTop: "18px"
|
|
1027
|
-
});
|
|
1028
|
-
const cancelButton = document.createElement("button");
|
|
1029
|
-
cancelButton.type = "button";
|
|
1030
|
-
cancelButton.textContent = "Cancel";
|
|
1031
|
-
Object.assign(cancelButton.style, {
|
|
1032
|
-
border: "1px solid #cbd5e1",
|
|
1033
|
-
borderRadius: "8px",
|
|
1034
|
-
padding: "10px 16px",
|
|
1035
|
-
background: "#fff",
|
|
1036
|
-
color: "#0f172a",
|
|
1037
|
-
cursor: "pointer"
|
|
1038
|
-
});
|
|
1039
|
-
const submitButton = document.createElement("button");
|
|
1040
|
-
submitButton.type = "submit";
|
|
1041
|
-
submitButton.textContent = "Pay securely";
|
|
1042
|
-
Object.assign(submitButton.style, {
|
|
1043
|
-
border: "0",
|
|
1044
|
-
borderRadius: "8px",
|
|
1045
|
-
padding: "10px 16px",
|
|
1046
|
-
background: "#0f172a",
|
|
1047
|
-
color: "#fff",
|
|
1048
|
-
cursor: "pointer"
|
|
1049
|
-
});
|
|
1050
|
-
actions.append(cancelButton, submitButton);
|
|
1051
|
-
form.append(title, paymentMount, error, actions);
|
|
1052
|
-
root.append(form);
|
|
1053
|
-
document.body.append(root);
|
|
1054
|
-
cancelButton.focus();
|
|
1055
|
-
return { root, form, paymentMount, submitButton, cancelButton, error, previousFocus };
|
|
1345
|
+
function sessionSyncEnvelopeKey(payload) {
|
|
1346
|
+
const name = payload.name === void 0 ? ["missing"] : ["present", payload.name];
|
|
1347
|
+
return JSON.stringify([payload.type, payload.token, name, payload.at]);
|
|
1056
1348
|
}
|
|
1057
|
-
function
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
};
|
|
1066
|
-
}
|
|
1067
|
-
return { status: "failed", code: "PAYMENT_FAILED", message, recoverable: true };
|
|
1349
|
+
function rememberEnvelope(hub, envelope) {
|
|
1350
|
+
if (hub.recentEnvelopes.has(envelope)) return false;
|
|
1351
|
+
hub.recentEnvelopes.add(envelope);
|
|
1352
|
+
if (hub.recentEnvelopes.size > MAX_RECENT_ENVELOPES) {
|
|
1353
|
+
const oldest = hub.recentEnvelopes.values().next().value;
|
|
1354
|
+
if (oldest) hub.recentEnvelopes.delete(oldest);
|
|
1355
|
+
}
|
|
1356
|
+
return true;
|
|
1068
1357
|
}
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
if (
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
context.action.public_key,
|
|
1087
|
-
context.action.provider_account_id ? { stripeAccount: context.action.provider_account_id } : void 0
|
|
1088
|
-
);
|
|
1089
|
-
} catch (error) {
|
|
1090
|
-
return {
|
|
1091
|
-
status: "failed",
|
|
1092
|
-
code: "PROVIDER_UNAVAILABLE",
|
|
1093
|
-
message: error instanceof Error ? error.message : "Stripe.js could not be loaded.",
|
|
1094
|
-
recoverable: true
|
|
1095
|
-
};
|
|
1096
|
-
}
|
|
1097
|
-
if (context.signal?.aborted) {
|
|
1098
|
-
return { status: "cancelled" };
|
|
1099
|
-
}
|
|
1100
|
-
if (!stripe) {
|
|
1101
|
-
return {
|
|
1102
|
-
status: "failed",
|
|
1103
|
-
code: "PROVIDER_UNAVAILABLE",
|
|
1104
|
-
message: "Stripe.js could not be loaded.",
|
|
1105
|
-
recoverable: false
|
|
1106
|
-
};
|
|
1107
|
-
}
|
|
1108
|
-
const modal = createStripeModal();
|
|
1109
|
-
if (!modal) {
|
|
1110
|
-
return {
|
|
1111
|
-
status: "failed",
|
|
1112
|
-
code: "PROVIDER_UNAVAILABLE",
|
|
1113
|
-
message: "Stripe Payment Element could not be mounted.",
|
|
1114
|
-
recoverable: false
|
|
1115
|
-
};
|
|
1116
|
-
}
|
|
1117
|
-
let paymentElement;
|
|
1118
|
-
let elements;
|
|
1119
|
-
try {
|
|
1120
|
-
elements = stripe.elements({ clientSecret: context.action.client_secret });
|
|
1121
|
-
paymentElement = elements.create("payment");
|
|
1122
|
-
paymentElement.mount(modal.paymentMount);
|
|
1123
|
-
} catch {
|
|
1124
|
-
modal.root.remove();
|
|
1125
|
-
modal.previousFocus?.focus();
|
|
1126
|
-
return {
|
|
1127
|
-
status: "failed",
|
|
1128
|
-
code: "PROVIDER_UNAVAILABLE",
|
|
1129
|
-
message: "Stripe Payment Element could not be initialized.",
|
|
1130
|
-
recoverable: false
|
|
1131
|
-
};
|
|
1358
|
+
function createHub() {
|
|
1359
|
+
const hub = {
|
|
1360
|
+
subscribers: /* @__PURE__ */ new Map(),
|
|
1361
|
+
recentEnvelopes: /* @__PURE__ */ new Set()
|
|
1362
|
+
};
|
|
1363
|
+
hub.dispatch = (payload) => {
|
|
1364
|
+
if (!rememberEnvelope(hub, sessionSyncEnvelopeKey(payload))) return;
|
|
1365
|
+
for (const [token, reference] of hub.subscribers) {
|
|
1366
|
+
const subscriber = reference.deref();
|
|
1367
|
+
if (!subscriber) {
|
|
1368
|
+
hub.subscribers.delete(token);
|
|
1369
|
+
continue;
|
|
1370
|
+
}
|
|
1371
|
+
try {
|
|
1372
|
+
subscriber.notify(payload);
|
|
1373
|
+
} catch {
|
|
1374
|
+
}
|
|
1132
1375
|
}
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
let confirmationStarted = false;
|
|
1144
|
-
const cleanup = () => {
|
|
1145
|
-
context.signal?.removeEventListener("abort", onAbort);
|
|
1146
|
-
modal.form.removeEventListener("submit", onSubmit);
|
|
1147
|
-
modal.cancelButton.removeEventListener("click", onCancel);
|
|
1148
|
-
if (paymentElement.destroy) paymentElement.destroy();
|
|
1149
|
-
else paymentElement.unmount?.();
|
|
1150
|
-
modal.root.remove();
|
|
1151
|
-
modal.previousFocus?.focus();
|
|
1152
|
-
};
|
|
1153
|
-
const settle = (outcome) => {
|
|
1154
|
-
if (settled) return;
|
|
1155
|
-
settled = true;
|
|
1156
|
-
cleanup();
|
|
1157
|
-
resolve(outcome);
|
|
1158
|
-
};
|
|
1159
|
-
const onAbort = () => {
|
|
1160
|
-
if (confirmationStarted) {
|
|
1161
|
-
settle({ status: "submitted", reference: context.reference });
|
|
1162
|
-
return;
|
|
1163
|
-
}
|
|
1164
|
-
settle({ status: "cancelled" });
|
|
1165
|
-
};
|
|
1166
|
-
const onCancel = () => {
|
|
1167
|
-
if (!confirmationStarted) settle({ status: "cancelled" });
|
|
1168
|
-
};
|
|
1169
|
-
const onSubmit = async (event) => {
|
|
1170
|
-
event.preventDefault();
|
|
1171
|
-
if (submitting || settled) return;
|
|
1172
|
-
submitting = true;
|
|
1173
|
-
modal.submitButton.disabled = true;
|
|
1174
|
-
modal.error.textContent = "";
|
|
1175
|
-
try {
|
|
1176
|
-
const validation = elements.submit ? await elements.submit() : {};
|
|
1177
|
-
if (validation.error) {
|
|
1178
|
-
modal.error.textContent = validation.error.message || "Check your payment details.";
|
|
1179
|
-
submitting = false;
|
|
1180
|
-
modal.submitButton.disabled = false;
|
|
1181
|
-
return;
|
|
1182
|
-
}
|
|
1183
|
-
if (settled || context.signal?.aborted) {
|
|
1184
|
-
settle({ status: "cancelled" });
|
|
1185
|
-
return;
|
|
1186
|
-
}
|
|
1187
|
-
confirmationStarted = true;
|
|
1188
|
-
modal.cancelButton.disabled = true;
|
|
1189
|
-
const confirmation = await stripe.confirmPayment({
|
|
1190
|
-
elements,
|
|
1191
|
-
confirmParams: { return_url: context.returnUrl || window.location.href },
|
|
1192
|
-
redirect: "if_required"
|
|
1193
|
-
});
|
|
1194
|
-
if (settled) return;
|
|
1195
|
-
if (confirmation.error) {
|
|
1196
|
-
settle(stripeFailure(confirmation.error));
|
|
1197
|
-
return;
|
|
1198
|
-
}
|
|
1199
|
-
settle({
|
|
1200
|
-
status: "submitted",
|
|
1201
|
-
reference: confirmation.paymentIntent?.id || context.reference
|
|
1202
|
-
});
|
|
1203
|
-
} catch (error) {
|
|
1204
|
-
if (confirmationStarted) {
|
|
1205
|
-
settle({ status: "submitted", reference: context.reference });
|
|
1206
|
-
return;
|
|
1207
|
-
}
|
|
1208
|
-
settle(
|
|
1209
|
-
stripeFailure({
|
|
1210
|
-
message: error instanceof Error ? error.message : "Stripe confirmation failed."
|
|
1211
|
-
})
|
|
1212
|
-
);
|
|
1213
|
-
}
|
|
1214
|
-
};
|
|
1215
|
-
modal.form.addEventListener("submit", onSubmit);
|
|
1216
|
-
modal.cancelButton.addEventListener("click", onCancel);
|
|
1217
|
-
context.signal?.addEventListener("abort", onAbort, { once: true });
|
|
1218
|
-
paymentElement.on?.("escape", onCancel);
|
|
1219
|
-
paymentElement.on?.("loaderror", (event) => {
|
|
1220
|
-
if (confirmationStarted) return;
|
|
1221
|
-
settle({
|
|
1222
|
-
status: "failed",
|
|
1223
|
-
code: "PROVIDER_UNAVAILABLE",
|
|
1224
|
-
message: event?.error?.message || "Stripe Payment Element failed to load.",
|
|
1225
|
-
recoverable: true
|
|
1226
|
-
});
|
|
1227
|
-
});
|
|
1228
|
-
});
|
|
1229
|
-
}
|
|
1230
|
-
};
|
|
1231
|
-
|
|
1232
|
-
// src/payment-authorization/registry.ts
|
|
1233
|
-
function normalizeProvider(provider) {
|
|
1234
|
-
return typeof provider === "string" ? provider.trim().toLowerCase() : "";
|
|
1376
|
+
};
|
|
1377
|
+
hub.storageListener = (event) => {
|
|
1378
|
+
const storageEvent = event;
|
|
1379
|
+
if (storageEvent.key !== SESSION_SYNC_STORAGE_KEY) return;
|
|
1380
|
+
const payload = parseSessionSyncPayload(storageEvent.newValue);
|
|
1381
|
+
if (payload) hub.dispatch(payload);
|
|
1382
|
+
};
|
|
1383
|
+
window.addEventListener("storage", hub.storageListener);
|
|
1384
|
+
hubHost()[CROSS_TAB_SESSION_HUB_KEY] = hub;
|
|
1385
|
+
return hub;
|
|
1235
1386
|
}
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
const provider = normalizeProvider(adapter.provider);
|
|
1245
|
-
if (!provider) {
|
|
1246
|
-
throw new TypeError("Payment authorization adapter provider cannot be empty.");
|
|
1247
|
-
}
|
|
1248
|
-
this.adapters.set(provider, adapter);
|
|
1249
|
-
return this;
|
|
1250
|
-
}
|
|
1251
|
-
unregister(provider) {
|
|
1252
|
-
return this.adapters.delete(normalizeProvider(provider));
|
|
1253
|
-
}
|
|
1254
|
-
get(provider) {
|
|
1255
|
-
return this.adapters.get(normalizeProvider(provider));
|
|
1256
|
-
}
|
|
1257
|
-
supports(provider) {
|
|
1258
|
-
return this.adapters.has(normalizeProvider(provider));
|
|
1259
|
-
}
|
|
1260
|
-
providers() {
|
|
1261
|
-
return [...this.adapters.keys()];
|
|
1262
|
-
}
|
|
1263
|
-
async authorize(action, options) {
|
|
1264
|
-
const provider = normalizeProvider(action.provider);
|
|
1265
|
-
if (!provider) {
|
|
1266
|
-
return {
|
|
1267
|
-
status: "failed",
|
|
1268
|
-
code: "PROVIDER_UNAVAILABLE",
|
|
1269
|
-
message: "Card authorization response did not identify a payment provider.",
|
|
1270
|
-
recoverable: false
|
|
1271
|
-
};
|
|
1272
|
-
}
|
|
1273
|
-
const adapter = this.adapters.get(provider);
|
|
1274
|
-
if (!adapter) {
|
|
1275
|
-
return {
|
|
1276
|
-
status: "failed",
|
|
1277
|
-
code: "PROVIDER_UNAVAILABLE",
|
|
1278
|
-
message: `Card authorization provider '${provider}' is not supported by this SDK client.`,
|
|
1279
|
-
recoverable: false
|
|
1280
|
-
};
|
|
1281
|
-
}
|
|
1282
|
-
try {
|
|
1283
|
-
const normalizedAction = action.provider === provider ? action : { ...action, provider };
|
|
1284
|
-
return await adapter.authorize({ ...options, action: normalizedAction });
|
|
1285
|
-
} catch (error) {
|
|
1286
|
-
return {
|
|
1287
|
-
status: "failed",
|
|
1288
|
-
code: "PAYMENT_FAILED",
|
|
1289
|
-
message: error instanceof Error ? error.message : `Card authorization provider '${provider}' failed unexpectedly.`,
|
|
1290
|
-
recoverable: true
|
|
1291
|
-
};
|
|
1292
|
-
}
|
|
1387
|
+
function publishLocalSessionSync(payload) {
|
|
1388
|
+
if (typeof window === "undefined" || typeof window.addEventListener !== "function") return;
|
|
1389
|
+
(currentHub() ?? createHub()).dispatch(payload);
|
|
1390
|
+
}
|
|
1391
|
+
function subscribeCrossTabSession(notify) {
|
|
1392
|
+
if (typeof window === "undefined" || typeof window.addEventListener !== "function") {
|
|
1393
|
+
return () => {
|
|
1394
|
+
};
|
|
1293
1395
|
}
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1396
|
+
const hub = currentHub() ?? createHub();
|
|
1397
|
+
pruneSubscribers(hub);
|
|
1398
|
+
const token = /* @__PURE__ */ Symbol("cross-tab-session-subscriber");
|
|
1399
|
+
const subscriber = { notify };
|
|
1400
|
+
hub.subscribers.set(token, subscriberReference(subscriber));
|
|
1401
|
+
let unsubscribed = false;
|
|
1402
|
+
return () => {
|
|
1403
|
+
if (unsubscribed) return;
|
|
1404
|
+
unsubscribed = true;
|
|
1405
|
+
subscriber.notify = () => {
|
|
1406
|
+
};
|
|
1407
|
+
hub.subscribers.delete(token);
|
|
1408
|
+
pruneSubscribers(hub);
|
|
1409
|
+
if (hub.subscribers.size !== 0) return;
|
|
1410
|
+
window.removeEventListener("storage", hub.storageListener);
|
|
1411
|
+
if (currentHub() === hub) delete hubHost()[CROSS_TAB_SESSION_HUB_KEY];
|
|
1412
|
+
};
|
|
1300
1413
|
}
|
|
1301
1414
|
|
|
1302
1415
|
// src/client.ts
|
|
1303
1416
|
var SESSION_TOKEN_STORAGE_KEY = "cimplify_session_token";
|
|
1304
|
-
var SESSION_SYNC_STORAGE_KEY = "cimplify_session_sync";
|
|
1305
1417
|
var ORDER_TOKEN_PREFIX = "cimplify_ot_";
|
|
1306
1418
|
var SESSION_TOKEN_HEADER = "x-session-token";
|
|
1307
1419
|
var LOCATION_ID_QUERY_PARAMETER = "location_id";
|
|
@@ -1309,8 +1421,37 @@ var ORDER_TOKEN_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
|
1309
1421
|
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
1310
1422
|
var DEFAULT_MAX_RETRIES = 3;
|
|
1311
1423
|
var DEFAULT_RETRY_DELAY_MS = 1e3;
|
|
1312
|
-
|
|
1313
|
-
|
|
1424
|
+
var MAX_RETRY_AFTER_MS = 3e4;
|
|
1425
|
+
function sleep(ms, signal) {
|
|
1426
|
+
if (signal?.aborted) {
|
|
1427
|
+
return Promise.reject(new CimplifyError(ErrorCode.CANCELLED, "Request was cancelled.", true));
|
|
1428
|
+
}
|
|
1429
|
+
return new Promise((resolve, reject) => {
|
|
1430
|
+
const timer = setTimeout(() => {
|
|
1431
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1432
|
+
resolve();
|
|
1433
|
+
}, ms);
|
|
1434
|
+
const onAbort = () => {
|
|
1435
|
+
clearTimeout(timer);
|
|
1436
|
+
reject(new CimplifyError(ErrorCode.CANCELLED, "Request was cancelled.", true));
|
|
1437
|
+
};
|
|
1438
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1439
|
+
});
|
|
1440
|
+
}
|
|
1441
|
+
function parseRetryAfterMs(value) {
|
|
1442
|
+
if (!value) return null;
|
|
1443
|
+
const seconds = Number(value);
|
|
1444
|
+
if (Number.isFinite(seconds) && seconds >= 0) {
|
|
1445
|
+
return Math.min(seconds * 1e3, MAX_RETRY_AFTER_MS);
|
|
1446
|
+
}
|
|
1447
|
+
const retryAt = Date.parse(value);
|
|
1448
|
+
if (!Number.isFinite(retryAt)) return null;
|
|
1449
|
+
return Math.min(Math.max(0, retryAt - Date.now()), MAX_RETRY_AFTER_MS);
|
|
1450
|
+
}
|
|
1451
|
+
function withPositiveJitter(delayMs, jitterRatio = 0) {
|
|
1452
|
+
const boundedRatio = Math.min(Math.max(jitterRatio, 0), 1);
|
|
1453
|
+
if (boundedRatio === 0 || delayMs === 0) return delayMs;
|
|
1454
|
+
return Math.round(delayMs + delayMs * boundedRatio * Math.random());
|
|
1314
1455
|
}
|
|
1315
1456
|
function nextInit(cacheOptions) {
|
|
1316
1457
|
if (!cacheOptions) return {};
|
|
@@ -1424,6 +1565,9 @@ var CimplifyClient = class {
|
|
|
1424
1565
|
this.businessId = null;
|
|
1425
1566
|
this.businessIdResolvePromise = null;
|
|
1426
1567
|
this.publicKeyEpoch = 0;
|
|
1568
|
+
this.stopCrossTabSessionWatch = null;
|
|
1569
|
+
this.latestSessionSyncAt = Number.NEGATIVE_INFINITY;
|
|
1570
|
+
this.latestSessionSyncOrderKey = null;
|
|
1427
1571
|
this.inflightRequests = /* @__PURE__ */ new Map();
|
|
1428
1572
|
/**
|
|
1429
1573
|
* App-level event channel. Subscribe to surface mutation rollbacks, outbox
|
|
@@ -1445,7 +1589,11 @@ var CimplifyClient = class {
|
|
|
1445
1589
|
this.authorizationAdapters = config.authorizationAdapterRegistry ?? createDefaultPaymentAuthorizationAdapterRegistry();
|
|
1446
1590
|
this.fetchImpl = (config.fetch ?? globalThis.fetch).bind(globalThis);
|
|
1447
1591
|
this.sessionToken = this.loadSessionToken() ?? this.mintSessionToken();
|
|
1448
|
-
this.
|
|
1592
|
+
const persistedSessionSync = this.loadSessionSyncPayload();
|
|
1593
|
+
if (persistedSessionSync) this.observeSessionSync(persistedSessionSync);
|
|
1594
|
+
this.stopCrossTabSessionWatch = subscribeCrossTabSession((payload) => {
|
|
1595
|
+
this.applyCrossTabSession(payload);
|
|
1596
|
+
});
|
|
1449
1597
|
if (!this.publicKey && !config.suppressPublicKeyWarning) {
|
|
1450
1598
|
console.warn(
|
|
1451
1599
|
'[Cimplify] No public key found. Set NEXT_PUBLIC_CIMPLIFY_PUBLIC_KEY in your environment, or pass { publicKey: "cpk_..." } to createCimplifyClient().'
|
|
@@ -1472,6 +1620,12 @@ var CimplifyClient = class {
|
|
|
1472
1620
|
this.stopPageViewTracking?.();
|
|
1473
1621
|
this.stopPageViewTracking = null;
|
|
1474
1622
|
}
|
|
1623
|
+
/** Release window-level subscriptions and automatic tracking owned by this client. */
|
|
1624
|
+
destroy() {
|
|
1625
|
+
this.stopTrackingPageViews();
|
|
1626
|
+
this.stopCrossTabSessionWatch?.();
|
|
1627
|
+
this.stopCrossTabSessionWatch = null;
|
|
1628
|
+
}
|
|
1475
1629
|
getAccessToken() {
|
|
1476
1630
|
return this.accessToken;
|
|
1477
1631
|
}
|
|
@@ -1499,7 +1653,7 @@ var CimplifyClient = class {
|
|
|
1499
1653
|
this.hooks.onSessionChange?.({
|
|
1500
1654
|
previousToken: previous,
|
|
1501
1655
|
newToken: token,
|
|
1502
|
-
source:
|
|
1656
|
+
source: SESSION_CHANGE_SOURCE.MANUAL
|
|
1503
1657
|
});
|
|
1504
1658
|
this.events.emit(AUTH_TOKEN_CHANGED, { token });
|
|
1505
1659
|
}
|
|
@@ -1543,7 +1697,7 @@ var CimplifyClient = class {
|
|
|
1543
1697
|
this.hooks.onSessionChange?.({
|
|
1544
1698
|
previousToken: previous,
|
|
1545
1699
|
newToken: null,
|
|
1546
|
-
source:
|
|
1700
|
+
source: SESSION_CHANGE_SOURCE.CLEAR
|
|
1547
1701
|
});
|
|
1548
1702
|
this.events.emit(AUTH_TOKEN_CHANGED, { token: null });
|
|
1549
1703
|
this.events.emit(SESSION_CLEARED, {});
|
|
@@ -1565,9 +1719,10 @@ var CimplifyClient = class {
|
|
|
1565
1719
|
hasClaimedSessionAuthority() {
|
|
1566
1720
|
if (!this.sessionToken) return false;
|
|
1567
1721
|
try {
|
|
1568
|
-
const
|
|
1569
|
-
|
|
1570
|
-
|
|
1722
|
+
const payload = parseSessionSyncPayload(
|
|
1723
|
+
this.safeStorage()?.getItem(SESSION_SYNC_STORAGE_KEY) ?? null
|
|
1724
|
+
);
|
|
1725
|
+
if (!payload) return false;
|
|
1571
1726
|
return payload.type === "claimed" && payload.token === this.sessionToken;
|
|
1572
1727
|
} catch {
|
|
1573
1728
|
return false;
|
|
@@ -1582,41 +1737,49 @@ var CimplifyClient = class {
|
|
|
1582
1737
|
});
|
|
1583
1738
|
}
|
|
1584
1739
|
broadcastSessionSync(payload) {
|
|
1740
|
+
const envelope = {
|
|
1741
|
+
...payload,
|
|
1742
|
+
at: this.nextSessionSyncTimestamp()
|
|
1743
|
+
};
|
|
1585
1744
|
try {
|
|
1586
|
-
this.safeStorage()?.setItem(
|
|
1587
|
-
SESSION_SYNC_STORAGE_KEY,
|
|
1588
|
-
JSON.stringify({ ...payload, at: Date.now() })
|
|
1589
|
-
);
|
|
1745
|
+
this.safeStorage()?.setItem(SESSION_SYNC_STORAGE_KEY, JSON.stringify(envelope));
|
|
1590
1746
|
} catch {
|
|
1591
1747
|
}
|
|
1748
|
+
publishLocalSessionSync(envelope);
|
|
1592
1749
|
}
|
|
1593
|
-
|
|
1594
|
-
if (
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1750
|
+
applyCrossTabSession(payload) {
|
|
1751
|
+
if (!this.observeSessionSync(payload)) return;
|
|
1752
|
+
if (payload.token === this.sessionToken) return;
|
|
1753
|
+
if (payload.type === "claimed") {
|
|
1754
|
+
this.sessionToken = payload.token;
|
|
1755
|
+
this.sessionEpoch++;
|
|
1756
|
+
this.events.emit(SESSION_CLAIMED, {
|
|
1757
|
+
sessionToken: payload.token,
|
|
1758
|
+
threadId: null,
|
|
1759
|
+
customerName: payload.name ?? ""
|
|
1760
|
+
});
|
|
1761
|
+
} else {
|
|
1762
|
+
this.accessToken = null;
|
|
1763
|
+
this._activity?.resetSession();
|
|
1764
|
+
this.sessionToken = payload.token;
|
|
1765
|
+
this.sessionEpoch++;
|
|
1766
|
+
this.events.emit(AUTH_TOKEN_CHANGED, { token: null });
|
|
1767
|
+
this.events.emit(SESSION_CLEARED, {});
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
observeSessionSync(payload) {
|
|
1771
|
+
if (payload.at < this.latestSessionSyncAt) return false;
|
|
1772
|
+
const envelope = sessionSyncEnvelopeKey(payload);
|
|
1773
|
+
if (payload.at === this.latestSessionSyncAt && this.latestSessionSyncOrderKey !== null && envelope <= this.latestSessionSyncOrderKey) {
|
|
1774
|
+
return false;
|
|
1775
|
+
}
|
|
1776
|
+
this.latestSessionSyncAt = payload.at;
|
|
1777
|
+
this.latestSessionSyncOrderKey = envelope;
|
|
1778
|
+
return true;
|
|
1779
|
+
}
|
|
1780
|
+
nextSessionSyncTimestamp() {
|
|
1781
|
+
const now = Date.now();
|
|
1782
|
+
return Number.isFinite(this.latestSessionSyncAt) ? Math.max(now, this.latestSessionSyncAt + 1) : now;
|
|
1620
1783
|
}
|
|
1621
1784
|
setOrderToken(orderId, token) {
|
|
1622
1785
|
try {
|
|
@@ -1680,17 +1843,24 @@ var CimplifyClient = class {
|
|
|
1680
1843
|
setPublicKey(publicKey) {
|
|
1681
1844
|
const normalized = publicKey.trim();
|
|
1682
1845
|
if (normalized === this.publicKey) return;
|
|
1846
|
+
const restartPageViewTracking = this.stopPageViewTracking !== null;
|
|
1847
|
+
if (restartPageViewTracking) {
|
|
1848
|
+
this.stopTrackingPageViews();
|
|
1849
|
+
}
|
|
1683
1850
|
this.publicKey = normalized;
|
|
1684
1851
|
this.publicKeyEpoch += 1;
|
|
1685
1852
|
this.businessId = null;
|
|
1686
1853
|
this.businessIdResolvePromise = null;
|
|
1687
1854
|
this.inflightRequests.clear();
|
|
1688
|
-
this._activity?.
|
|
1855
|
+
this._activity?.resetForStorefrontIdentityChange();
|
|
1689
1856
|
delete this.context.location_id;
|
|
1690
1857
|
if (this.accessToken !== null) {
|
|
1691
1858
|
this.setAccessToken(null);
|
|
1692
1859
|
}
|
|
1693
1860
|
this.events.emit(SESSION_CLEARED, {});
|
|
1861
|
+
if (restartPageViewTracking) {
|
|
1862
|
+
this.stopPageViewTracking = this.activity.autoTrackPageViews();
|
|
1863
|
+
}
|
|
1694
1864
|
}
|
|
1695
1865
|
getBusinessId() {
|
|
1696
1866
|
return this.businessId;
|
|
@@ -1731,8 +1901,8 @@ var CimplifyClient = class {
|
|
|
1731
1901
|
}
|
|
1732
1902
|
}
|
|
1733
1903
|
}
|
|
1734
|
-
async authedFetch(url, options, timeoutMs) {
|
|
1735
|
-
return this.resilientFetch(url, options, timeoutMs);
|
|
1904
|
+
async authedFetch(url, options, timeoutMs, retryPolicy, signal) {
|
|
1905
|
+
return this.resilientFetch(url, options, timeoutMs, retryPolicy, signal);
|
|
1736
1906
|
}
|
|
1737
1907
|
// Storage guards: even the `window.localStorage` property access throws in
|
|
1738
1908
|
// some sandboxed iframes and strict private-browsing modes. These run in the
|
|
@@ -1754,6 +1924,13 @@ var CimplifyClient = class {
|
|
|
1754
1924
|
return null;
|
|
1755
1925
|
}
|
|
1756
1926
|
}
|
|
1927
|
+
loadSessionSyncPayload() {
|
|
1928
|
+
try {
|
|
1929
|
+
return parseSessionSyncPayload(this.safeStorage()?.getItem(SESSION_SYNC_STORAGE_KEY) ?? null);
|
|
1930
|
+
} catch {
|
|
1931
|
+
return null;
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1757
1934
|
/**
|
|
1758
1935
|
* Mint the session token client-side so the very first request already
|
|
1759
1936
|
* carries it. Without this, parallel first-load requests each get a
|
|
@@ -1809,7 +1986,7 @@ var CimplifyClient = class {
|
|
|
1809
1986
|
}
|
|
1810
1987
|
return headers;
|
|
1811
1988
|
}
|
|
1812
|
-
async resilientFetch(url, options, timeoutMs) {
|
|
1989
|
+
async resilientFetch(url, options, timeoutMs, retryPolicy, signal) {
|
|
1813
1990
|
const method = options.method || "GET";
|
|
1814
1991
|
const path = url.replace(this.baseUrl, "").replace(this.linkApiUrl, "");
|
|
1815
1992
|
const startTime = Date.now();
|
|
@@ -1823,15 +2000,31 @@ var CimplifyClient = class {
|
|
|
1823
2000
|
};
|
|
1824
2001
|
this.hooks.onRequestStart?.(context);
|
|
1825
2002
|
let lastError;
|
|
1826
|
-
|
|
2003
|
+
const configuredMaxRetries = Math.max(0, Math.floor(this.maxRetries));
|
|
2004
|
+
const maxRetries = Math.min(
|
|
2005
|
+
configuredMaxRetries,
|
|
2006
|
+
Math.max(0, Math.floor(retryPolicy?.maxRetries ?? configuredMaxRetries))
|
|
2007
|
+
);
|
|
2008
|
+
const jitterRatio = retryPolicy?.jitterRatio ?? 0;
|
|
2009
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
1827
2010
|
try {
|
|
2011
|
+
if (signal?.aborted) {
|
|
2012
|
+
throw new CimplifyError(ErrorCode.CANCELLED, "Request was cancelled.", true);
|
|
2013
|
+
}
|
|
1828
2014
|
const controller = new AbortController();
|
|
1829
2015
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs ?? this.timeout);
|
|
1830
|
-
const
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
2016
|
+
const onAbort = () => controller.abort();
|
|
2017
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
2018
|
+
let response;
|
|
2019
|
+
try {
|
|
2020
|
+
response = await this.fetchImpl(url, {
|
|
2021
|
+
...options,
|
|
2022
|
+
signal: controller.signal
|
|
2023
|
+
});
|
|
2024
|
+
} finally {
|
|
2025
|
+
clearTimeout(timeoutId);
|
|
2026
|
+
signal?.removeEventListener("abort", onAbort);
|
|
2027
|
+
}
|
|
1835
2028
|
this.captureSessionToken(response);
|
|
1836
2029
|
if (response.ok) {
|
|
1837
2030
|
this.hooks.onRequestSuccess?.({
|
|
@@ -1841,17 +2034,19 @@ var CimplifyClient = class {
|
|
|
1841
2034
|
});
|
|
1842
2035
|
return response;
|
|
1843
2036
|
}
|
|
1844
|
-
if ((response.status === 429 || response.status === 503) && attempt <
|
|
2037
|
+
if ((response.status === 429 || response.status === 503) && attempt < maxRetries) {
|
|
1845
2038
|
retryCount++;
|
|
1846
|
-
const
|
|
1847
|
-
const
|
|
2039
|
+
const retryAfterMs = parseRetryAfterMs(response.headers.get("Retry-After"));
|
|
2040
|
+
const retryBaseMs = retryAfterMs ?? this.retryDelay * Math.pow(2, attempt);
|
|
2041
|
+
const jitteredDelayMs = withPositiveJitter(retryBaseMs, jitterRatio);
|
|
2042
|
+
const delay = retryAfterMs === null ? jitteredDelayMs : Math.min(jitteredDelayMs, MAX_RETRY_AFTER_MS);
|
|
1848
2043
|
this.hooks.onRetry?.({
|
|
1849
2044
|
...context,
|
|
1850
2045
|
attempt: retryCount,
|
|
1851
2046
|
delayMs: delay,
|
|
1852
2047
|
error: new Error(`Retryable: ${response.status}`)
|
|
1853
2048
|
});
|
|
1854
|
-
await sleep(delay);
|
|
2049
|
+
await sleep(delay, signal);
|
|
1855
2050
|
continue;
|
|
1856
2051
|
}
|
|
1857
2052
|
if (response.status >= 400 && response.status < 500) {
|
|
@@ -1869,22 +2064,22 @@ var CimplifyClient = class {
|
|
|
1869
2064
|
});
|
|
1870
2065
|
return response;
|
|
1871
2066
|
}
|
|
1872
|
-
if (response.status >= 500 && attempt <
|
|
2067
|
+
if (response.status >= 500 && attempt < maxRetries) {
|
|
1873
2068
|
retryCount++;
|
|
1874
|
-
const delay = this.retryDelay * Math.pow(2, attempt);
|
|
2069
|
+
const delay = withPositiveJitter(this.retryDelay * Math.pow(2, attempt), jitterRatio);
|
|
1875
2070
|
this.hooks.onRetry?.({
|
|
1876
2071
|
...context,
|
|
1877
2072
|
attempt: retryCount,
|
|
1878
2073
|
delayMs: delay,
|
|
1879
2074
|
error: new Error(`Server error: ${response.status}`)
|
|
1880
2075
|
});
|
|
1881
|
-
await sleep(delay);
|
|
2076
|
+
await sleep(delay, signal);
|
|
1882
2077
|
continue;
|
|
1883
2078
|
}
|
|
1884
2079
|
this.hooks.onRequestError?.({
|
|
1885
2080
|
...context,
|
|
1886
2081
|
error: new CimplifyError(
|
|
1887
|
-
|
|
2082
|
+
ErrorCode.SERVER_ERROR,
|
|
1888
2083
|
`Server error ${response.status} after ${retryCount} retries`,
|
|
1889
2084
|
false
|
|
1890
2085
|
),
|
|
@@ -1896,9 +2091,12 @@ var CimplifyClient = class {
|
|
|
1896
2091
|
return response;
|
|
1897
2092
|
} catch (error) {
|
|
1898
2093
|
lastError = error;
|
|
2094
|
+
if (signal?.aborted) {
|
|
2095
|
+
throw new CimplifyError(ErrorCode.CANCELLED, "Request was cancelled.", true);
|
|
2096
|
+
}
|
|
1899
2097
|
const networkError = toNetworkError(error, this.isTestMode());
|
|
1900
2098
|
const errorRetryable = isRetryable(error);
|
|
1901
|
-
if (!errorRetryable || attempt >=
|
|
2099
|
+
if (!errorRetryable || attempt >= maxRetries) {
|
|
1902
2100
|
this.hooks.onRequestError?.({
|
|
1903
2101
|
...context,
|
|
1904
2102
|
error: networkError,
|
|
@@ -1909,14 +2107,14 @@ var CimplifyClient = class {
|
|
|
1909
2107
|
throw networkError;
|
|
1910
2108
|
}
|
|
1911
2109
|
retryCount++;
|
|
1912
|
-
const delay = this.retryDelay * Math.pow(2, attempt);
|
|
2110
|
+
const delay = withPositiveJitter(this.retryDelay * Math.pow(2, attempt), jitterRatio);
|
|
1913
2111
|
this.hooks.onRetry?.({
|
|
1914
2112
|
...context,
|
|
1915
2113
|
attempt: retryCount,
|
|
1916
2114
|
delayMs: delay,
|
|
1917
2115
|
error: networkError
|
|
1918
2116
|
});
|
|
1919
|
-
await sleep(delay);
|
|
2117
|
+
await sleep(delay, signal);
|
|
1920
2118
|
}
|
|
1921
2119
|
}
|
|
1922
2120
|
const finalError = toNetworkError(lastError, this.isTestMode());
|
|
@@ -1953,16 +2151,24 @@ var CimplifyClient = class {
|
|
|
1953
2151
|
return request;
|
|
1954
2152
|
}
|
|
1955
2153
|
async get(path, opts) {
|
|
1956
|
-
const
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
2154
|
+
const request = async () => {
|
|
2155
|
+
const response = await this.authedFetch(
|
|
2156
|
+
this.buildUrl(this.baseUrl, path),
|
|
2157
|
+
{
|
|
2158
|
+
method: "GET",
|
|
2159
|
+
credentials: this.credentials,
|
|
2160
|
+
headers: this.getHeaders(),
|
|
2161
|
+
...nextInit(opts?.cacheOptions)
|
|
2162
|
+
},
|
|
2163
|
+
opts?.timeoutMs,
|
|
2164
|
+
opts?.retryPolicy,
|
|
2165
|
+
opts?.signal
|
|
2166
|
+
);
|
|
1964
2167
|
return this.handleRestResponse(response);
|
|
1965
|
-
}
|
|
2168
|
+
};
|
|
2169
|
+
if (opts?.signal) return request();
|
|
2170
|
+
const key = this.getDedupeKey("get", path, opts?.cacheOptions);
|
|
2171
|
+
return this.deduplicatedRequest(key, request);
|
|
1966
2172
|
}
|
|
1967
2173
|
async post(path, body, opts) {
|
|
1968
2174
|
const response = await this.authedFetch(
|
|
@@ -1973,7 +2179,9 @@ var CimplifyClient = class {
|
|
|
1973
2179
|
headers: mergeOptionHeaders(this.getHeaders(), opts),
|
|
1974
2180
|
body: body ? JSON.stringify(body) : void 0
|
|
1975
2181
|
},
|
|
1976
|
-
opts?.timeoutMs
|
|
2182
|
+
opts?.timeoutMs,
|
|
2183
|
+
opts?.retryPolicy,
|
|
2184
|
+
opts?.signal
|
|
1977
2185
|
);
|
|
1978
2186
|
return this.handleRestResponse(response);
|
|
1979
2187
|
}
|
|
@@ -1986,7 +2194,9 @@ var CimplifyClient = class {
|
|
|
1986
2194
|
headers: mergeOptionHeaders(this.getHeaders(), opts),
|
|
1987
2195
|
body: body ? JSON.stringify(body) : void 0
|
|
1988
2196
|
},
|
|
1989
|
-
opts?.timeoutMs
|
|
2197
|
+
opts?.timeoutMs,
|
|
2198
|
+
opts?.retryPolicy,
|
|
2199
|
+
opts?.signal
|
|
1990
2200
|
);
|
|
1991
2201
|
return this.handleRestResponse(response);
|
|
1992
2202
|
}
|
|
@@ -1998,21 +2208,31 @@ var CimplifyClient = class {
|
|
|
1998
2208
|
credentials: this.credentials,
|
|
1999
2209
|
headers: mergeOptionHeaders(this.getHeaders(), opts)
|
|
2000
2210
|
},
|
|
2001
|
-
opts?.timeoutMs
|
|
2211
|
+
opts?.timeoutMs,
|
|
2212
|
+
opts?.retryPolicy,
|
|
2213
|
+
opts?.signal
|
|
2002
2214
|
);
|
|
2003
2215
|
return this.handleRestResponse(response);
|
|
2004
2216
|
}
|
|
2005
2217
|
async linkGet(path, opts) {
|
|
2006
|
-
const
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2218
|
+
const request = async () => {
|
|
2219
|
+
const response = await this.authedFetch(
|
|
2220
|
+
`${this.linkApiUrl}${path}`,
|
|
2221
|
+
{
|
|
2222
|
+
method: "GET",
|
|
2223
|
+
credentials: this.credentials,
|
|
2224
|
+
headers: this.getHeaders(),
|
|
2225
|
+
...nextInit(opts?.cacheOptions)
|
|
2226
|
+
},
|
|
2227
|
+
opts?.timeoutMs,
|
|
2228
|
+
opts?.retryPolicy,
|
|
2229
|
+
opts?.signal
|
|
2230
|
+
);
|
|
2014
2231
|
return this.handleRestResponse(response);
|
|
2015
|
-
}
|
|
2232
|
+
};
|
|
2233
|
+
if (opts?.signal) return request();
|
|
2234
|
+
const key = this.getDedupeKey("linkGet", path, opts?.cacheOptions);
|
|
2235
|
+
return this.deduplicatedRequest(key, request);
|
|
2016
2236
|
}
|
|
2017
2237
|
async linkPost(path, body, opts) {
|
|
2018
2238
|
const response = await this.authedFetch(
|
|
@@ -2023,25 +2243,33 @@ var CimplifyClient = class {
|
|
|
2023
2243
|
headers: mergeOptionHeaders(this.getHeaders(), opts),
|
|
2024
2244
|
body: body ? JSON.stringify(body) : void 0
|
|
2025
2245
|
},
|
|
2026
|
-
opts?.timeoutMs
|
|
2246
|
+
opts?.timeoutMs,
|
|
2247
|
+
opts?.retryPolicy,
|
|
2248
|
+
opts?.signal
|
|
2027
2249
|
);
|
|
2028
2250
|
return this.handleRestResponse(response);
|
|
2029
2251
|
}
|
|
2030
2252
|
async linkDelete(path, opts) {
|
|
2031
|
-
const response = await this.authedFetch(
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2253
|
+
const response = await this.authedFetch(
|
|
2254
|
+
`${this.linkApiUrl}${path}`,
|
|
2255
|
+
{
|
|
2256
|
+
method: "DELETE",
|
|
2257
|
+
credentials: this.credentials,
|
|
2258
|
+
headers: mergeOptionHeaders(this.getHeaders(), opts)
|
|
2259
|
+
},
|
|
2260
|
+
opts?.timeoutMs,
|
|
2261
|
+
opts?.retryPolicy,
|
|
2262
|
+
opts?.signal
|
|
2263
|
+
);
|
|
2036
2264
|
return this.handleRestResponse(response);
|
|
2037
2265
|
}
|
|
2038
2266
|
async handleRestResponse(response) {
|
|
2039
2267
|
const json = await response.json();
|
|
2040
2268
|
if (!response.ok) {
|
|
2041
|
-
const errorCode = typeof json.error === "object" && json.error?.error_code || typeof json.error === "object" && json.error?.code ||
|
|
2269
|
+
const errorCode = typeof json.error === "object" && json.error?.error_code || typeof json.error === "object" && json.error?.code || ErrorCode.API_ERROR;
|
|
2042
2270
|
const errorMessage = typeof json.error === "object" && json.error?.error_message || typeof json.error === "object" && json.error?.message || typeof json.error === "string" && json.error || typeof json.message === "string" && json.message || "An error occurred";
|
|
2043
2271
|
const retryable = typeof json.error === "object" && typeof json.error?.retryable === "boolean" ? json.error.retryable : false;
|
|
2044
|
-
if (errorCode ===
|
|
2272
|
+
if (errorCode === ErrorCode.IDEMPOTENCY_MISMATCH) {
|
|
2045
2273
|
const originalCreatedAt = typeof json.error === "object" && json.error?.original_created_at || "";
|
|
2046
2274
|
throw new IdempotencyMismatchError(errorMessage, originalCreatedAt);
|
|
2047
2275
|
}
|
|
@@ -2058,7 +2286,7 @@ var CimplifyClient = class {
|
|
|
2058
2286
|
if (json?.success === false || json?.error?.code || json?.error?.message) {
|
|
2059
2287
|
const error = enrichError(
|
|
2060
2288
|
new CimplifyError(
|
|
2061
|
-
json.error?.code ||
|
|
2289
|
+
json.error?.code || ErrorCode.API_ERROR,
|
|
2062
2290
|
json.error?.message || "An error occurred",
|
|
2063
2291
|
json.error?.retryable || false
|
|
2064
2292
|
),
|
|
@@ -2089,6 +2317,12 @@ var CimplifyClient = class {
|
|
|
2089
2317
|
}
|
|
2090
2318
|
return this._checkout;
|
|
2091
2319
|
}
|
|
2320
|
+
get purchaseIntents() {
|
|
2321
|
+
if (!this._purchaseIntents) {
|
|
2322
|
+
this._purchaseIntents = new PurchaseIntentService(this);
|
|
2323
|
+
}
|
|
2324
|
+
return this._purchaseIntents;
|
|
2325
|
+
}
|
|
2092
2326
|
get orders() {
|
|
2093
2327
|
if (!this._orders) {
|
|
2094
2328
|
this._orders = new OrderQueries(this);
|
|
@@ -2199,4 +2433,4 @@ function createCimplifyClient(config = {}) {
|
|
|
2199
2433
|
return new CimplifyClient(config);
|
|
2200
2434
|
}
|
|
2201
2435
|
|
|
2202
|
-
export { AUTH_TOKEN_CHANGED, ActivityService, CimplifyClient, DeliveryService,
|
|
2436
|
+
export { AUTH_TOKEN_CHANGED, ActivityService, CimplifyClient, DeliveryService, HostedPurchaseIntentService, MUTATION_ERROR, PURCHASE_INTENT_ROUTE, PlacesService, PurchaseIntentResolver, PurchaseIntentService, SESSION_CHANGE_SOURCE, SESSION_CLAIMED, SESSION_CLEARED, StoreCreditQueries, SubscriptionService, SupportService, UploadService, chatRealtimeUpdateFromWire, createCimplifyClient };
|