@cimplify/sdk 0.103.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-DHBwvh7U.d.ts → catalogue-BlQ85Uma.d.mts} +492 -748
- package/dist/{client-BfpNjJ_a.d.mts → catalogue-C8TqOj9K.d.ts} +492 -748
- package/dist/{payment-H5LoIvXU.d.mts → checkout-BqK9XUz5.d.mts} +4338 -1495
- package/dist/{payment-H5LoIvXU.d.ts → checkout-BqK9XUz5.d.ts} +4338 -1495
- package/dist/{chunk-GOHSXBKW.mjs → chunk-33VCYWVL.mjs} +25 -1
- package/dist/{chunk-C2JSNJUC.mjs → chunk-6G3ANKQA.mjs} +1080 -369
- package/dist/{chunk-QOWPGS7Z.js → chunk-BWEIMKLF.js} +572 -17
- package/dist/{chunk-JSWJ4XDE.js → chunk-DQEXY542.js} +1630 -824
- package/dist/{chunk-CNN7Z2IP.js → chunk-EV2EVOYS.js} +27 -0
- package/dist/{chunk-VIL6IQCG.js → chunk-HYFA67A7.js} +1084 -373
- package/dist/{chunk-H7ACMHM3.mjs → chunk-L5SNEU2O.mjs} +553 -16
- package/dist/{chunk-JG5CQNCB.js → chunk-RBNBSQI7.mjs} +221 -19
- package/dist/{chunk-VNPEYLB2.mjs → chunk-SFUJMS5E.js} +265 -2
- package/dist/{chunk-M4URVKHE.mjs → chunk-U54CWUUT.mjs} +11 -2
- package/dist/{chunk-GERXZQSX.mjs → chunk-UFOWPN3D.mjs} +967 -582
- package/dist/{chunk-J6PNHDHL.mjs → chunk-V4457OKB.mjs} +1580 -785
- package/dist/{chunk-R6OLQ33I.js → chunk-WXREIBFW.js} +1041 -654
- package/dist/{chunk-NNG6IFFX.js → chunk-XAUSDMMZ.js} +13 -3
- package/dist/{client-DpqRobY9.d.mts → client-DhXeiUrf.d.ts} +2 -2
- package/dist/{client-Dqg2mc-7.d.ts → client-mXiUrV4X.d.mts} +2 -2
- package/dist/{index-C8uDqLwJ.d.ts → index-B0AdZvrM.d.mts} +9 -4
- package/dist/{index-e2y6AK70.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 +34 -15
- package/dist/index.d.ts +34 -15
- package/dist/index.js +306 -133
- package/dist/index.mjs +7 -6
- package/dist/mock/cli.mjs +1494 -395
- package/dist/mock/library.js +1220 -361
- package/dist/mock/library.mjs +1220 -361
- package/dist/mock/msw.js +1220 -361
- package/dist/mock/msw.mjs +1220 -361
- package/dist/{price-C2RqscMY.d.ts → price-6hQIIac9.d.ts} +1 -1
- package/dist/{price-BvePgDXe.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 +279 -225
- package/dist/react.mjs +140 -86
- package/dist/{server-DoXIpSd7.d.ts → server-DJ0mSs9i.d.mts} +33 -57
- package/dist/{server-C7FyLdrz.d.mts → server-DSTX76Ki.d.ts} +33 -57
- package/dist/server.d.mts +3 -4
- package/dist/server.d.ts +3 -4
- package/dist/server.js +12 -11
- package/dist/server.mjs +6 -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 +2217 -14
- package/dist/testing.d.ts +2217 -14
- package/dist/testing.js +173 -83
- package/dist/testing.mjs +28 -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/chat-widget.json +1 -1
- package/registry/order-history.json +1 -1
- package/registry/order-summary.json +1 -1
- package/dist/link-cuNVYc2s.d.mts +0 -297
- package/dist/link-cuNVYc2s.d.ts +0 -297
|
@@ -1,7 +1,335 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var
|
|
3
|
+
var chunkXREIZ56K_js = require('./chunk-XREIZ56K.js');
|
|
4
|
+
var chunkDQEXY542_js = require('./chunk-DQEXY542.js');
|
|
5
|
+
var chunkSFUJMS5E_js = require('./chunk-SFUJMS5E.js');
|
|
6
|
+
|
|
7
|
+
// src/types/observability.ts
|
|
8
|
+
var SESSION_CHANGE_SOURCE = {
|
|
9
|
+
RESPONSE: "response",
|
|
10
|
+
MANUAL: "manual",
|
|
11
|
+
CLEAR: "clear"
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// src/utils/purchase-intent-resolver.ts
|
|
15
|
+
var DEFAULT_POLL_INTERVAL_MS = 3e3;
|
|
16
|
+
var DEFAULT_MAX_POLL_ATTEMPTS = 60;
|
|
17
|
+
var DEFAULT_MAX_ACTION_STEPS = 4;
|
|
18
|
+
var DEFAULT_MAX_TERMS_REVIEW_STEPS = 4;
|
|
19
|
+
function nonnegativeInteger(value, fallback) {
|
|
20
|
+
if (value === void 0 || !Number.isFinite(value)) return fallback;
|
|
21
|
+
return Math.max(0, Math.floor(value));
|
|
22
|
+
}
|
|
23
|
+
function abortError() {
|
|
24
|
+
const error = new Error("Purchase-intent resolution was cancelled.");
|
|
25
|
+
error.name = "AbortError";
|
|
26
|
+
return error;
|
|
27
|
+
}
|
|
28
|
+
async function abortableWait(milliseconds, signal) {
|
|
29
|
+
if (signal?.aborted) throw abortError();
|
|
30
|
+
if (milliseconds <= 0) return;
|
|
31
|
+
await new Promise((resolve, reject) => {
|
|
32
|
+
const timer = setTimeout(() => {
|
|
33
|
+
signal?.removeEventListener("abort", onAbort);
|
|
34
|
+
resolve();
|
|
35
|
+
}, milliseconds);
|
|
36
|
+
const onAbort = () => {
|
|
37
|
+
clearTimeout(timer);
|
|
38
|
+
reject(abortError());
|
|
39
|
+
};
|
|
40
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
var PurchaseIntentResolver = class {
|
|
44
|
+
constructor(dependencies) {
|
|
45
|
+
this.confirmation = dependencies.confirmation;
|
|
46
|
+
this.wait = dependencies.wait ?? abortableWait;
|
|
47
|
+
}
|
|
48
|
+
async resolve(intentId, options) {
|
|
49
|
+
const pollIntervalMs = nonnegativeInteger(
|
|
50
|
+
options.pollIntervalMs,
|
|
51
|
+
DEFAULT_POLL_INTERVAL_MS
|
|
52
|
+
);
|
|
53
|
+
const maxPollAttempts = nonnegativeInteger(
|
|
54
|
+
options.maxPollAttempts,
|
|
55
|
+
DEFAULT_MAX_POLL_ATTEMPTS
|
|
56
|
+
);
|
|
57
|
+
const maxActionSteps = nonnegativeInteger(options.maxActionSteps, DEFAULT_MAX_ACTION_STEPS);
|
|
58
|
+
const maxTermsReviewSteps = nonnegativeInteger(
|
|
59
|
+
options.maxTermsReviewSteps,
|
|
60
|
+
DEFAULT_MAX_TERMS_REVIEW_STEPS
|
|
61
|
+
);
|
|
62
|
+
let request = options.confirmation;
|
|
63
|
+
let pollAttempts = 0;
|
|
64
|
+
let actionSteps = 0;
|
|
65
|
+
let termsReviewSteps = 0;
|
|
66
|
+
this.ensureActive(options.signal);
|
|
67
|
+
this.notify(options, chunkSFUJMS5E_js.PURCHASE_INTENT_RESOLVE_STATUS.CONFIRMING);
|
|
68
|
+
let responseResult = options.initialResponse ? chunkDQEXY542_js.ok(options.initialResponse) : await this.confirmation.confirm(intentId, request, { signal: options.signal });
|
|
69
|
+
while (responseResult.ok) {
|
|
70
|
+
this.ensureActive(options.signal);
|
|
71
|
+
const response = responseResult.value;
|
|
72
|
+
switch (response.status) {
|
|
73
|
+
case chunkSFUJMS5E_js.PURCHASE_INTENT_CONFIRM_STATUS.ORDER_CREATED:
|
|
74
|
+
this.notify(options, chunkSFUJMS5E_js.PURCHASE_INTENT_RESOLVE_STATUS.COMPLETED, response);
|
|
75
|
+
return chunkDQEXY542_js.ok({ kind: chunkSFUJMS5E_js.PURCHASE_INTENT_RESOLUTION_KIND.COMPLETED, response });
|
|
76
|
+
case chunkSFUJMS5E_js.PURCHASE_INTENT_CONFIRM_STATUS.PAYMENT_FAILED:
|
|
77
|
+
this.notify(options, chunkSFUJMS5E_js.PURCHASE_INTENT_RESOLVE_STATUS.FAILED, response);
|
|
78
|
+
return chunkDQEXY542_js.ok({ kind: chunkSFUJMS5E_js.PURCHASE_INTENT_RESOLUTION_KIND.FAILED, response });
|
|
79
|
+
case chunkSFUJMS5E_js.PURCHASE_INTENT_CONFIRM_STATUS.PAYMENT_OUTCOME_UNKNOWN:
|
|
80
|
+
this.notify(options, chunkSFUJMS5E_js.PURCHASE_INTENT_RESOLVE_STATUS.OUTCOME_UNKNOWN, response);
|
|
81
|
+
return chunkDQEXY542_js.ok({ kind: chunkSFUJMS5E_js.PURCHASE_INTENT_RESOLUTION_KIND.OUTCOME_UNKNOWN, response });
|
|
82
|
+
case chunkSFUJMS5E_js.PURCHASE_INTENT_CONFIRM_STATUS.COLLECTION_PROCESSING:
|
|
83
|
+
if (pollAttempts >= maxPollAttempts) {
|
|
84
|
+
return chunkDQEXY542_js.ok({ kind: chunkSFUJMS5E_js.PURCHASE_INTENT_RESOLUTION_KIND.TIMED_OUT, response });
|
|
85
|
+
}
|
|
86
|
+
pollAttempts += 1;
|
|
87
|
+
this.notify(options, chunkSFUJMS5E_js.PURCHASE_INTENT_RESOLVE_STATUS.POLLING, response);
|
|
88
|
+
await this.wait(pollIntervalMs, options.signal);
|
|
89
|
+
responseResult = await this.confirmation.confirm(intentId, request, {
|
|
90
|
+
signal: options.signal
|
|
91
|
+
});
|
|
92
|
+
continue;
|
|
93
|
+
case chunkSFUJMS5E_js.PURCHASE_INTENT_CONFIRM_STATUS.REDIRECT_REQUIRED:
|
|
94
|
+
case chunkSFUJMS5E_js.PURCHASE_INTENT_CONFIRM_STATUS.CARD_POPUP_REQUIRED:
|
|
95
|
+
case chunkSFUJMS5E_js.PURCHASE_INTENT_CONFIRM_STATUS.AUTHORIZATION_REQUIRED: {
|
|
96
|
+
this.notify(options, chunkSFUJMS5E_js.PURCHASE_INTENT_RESOLVE_STATUS.AWAITING_ACTION, response);
|
|
97
|
+
if (!options.onActionRequired || actionSteps >= maxActionSteps) {
|
|
98
|
+
return chunkDQEXY542_js.ok({ kind: chunkSFUJMS5E_js.PURCHASE_INTENT_RESOLUTION_KIND.ACTION_REQUIRED, response });
|
|
99
|
+
}
|
|
100
|
+
actionSteps += 1;
|
|
101
|
+
const decision = await options.onActionRequired(response);
|
|
102
|
+
if (decision === chunkSFUJMS5E_js.PURCHASE_INTENT_ACTION_DECISION.STOP) {
|
|
103
|
+
return chunkDQEXY542_js.ok({ kind: chunkSFUJMS5E_js.PURCHASE_INTENT_RESOLUTION_KIND.ACTION_REQUIRED, response });
|
|
104
|
+
}
|
|
105
|
+
if (typeof decision === "object" && decision.kind === chunkSFUJMS5E_js.PURCHASE_INTENT_ACTION_DECISION.SUBMIT) {
|
|
106
|
+
if (!this.confirmation.submitAction) {
|
|
107
|
+
return chunkDQEXY542_js.err(
|
|
108
|
+
new chunkSFUJMS5E_js.CimplifyError(
|
|
109
|
+
chunkSFUJMS5E_js.PURCHASE_INTENT_ERROR_CODE.ACTION_UNSUPPORTED,
|
|
110
|
+
"This confirmation transport cannot submit purchase-intent actions.",
|
|
111
|
+
false
|
|
112
|
+
)
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
responseResult = await this.confirmation.submitAction(
|
|
116
|
+
intentId,
|
|
117
|
+
response.attempt_id,
|
|
118
|
+
decision.request,
|
|
119
|
+
{ signal: options.signal }
|
|
120
|
+
);
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (decision !== chunkSFUJMS5E_js.PURCHASE_INTENT_ACTION_DECISION.CONTINUE) {
|
|
124
|
+
return chunkDQEXY542_js.err(
|
|
125
|
+
new chunkSFUJMS5E_js.CimplifyError(
|
|
126
|
+
chunkSFUJMS5E_js.PURCHASE_INTENT_ERROR_CODE.ACTION_UNSUPPORTED,
|
|
127
|
+
"The action callback returned an unsupported decision.",
|
|
128
|
+
false
|
|
129
|
+
)
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
await this.wait(pollIntervalMs, options.signal);
|
|
133
|
+
responseResult = await this.confirmation.confirm(intentId, request, {
|
|
134
|
+
signal: options.signal
|
|
135
|
+
});
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
case chunkSFUJMS5E_js.PURCHASE_INTENT_CONFIRM_STATUS.TERMS_CHANGED: {
|
|
139
|
+
this.notify(options, chunkSFUJMS5E_js.PURCHASE_INTENT_RESOLVE_STATUS.REVIEWING_TERMS, response);
|
|
140
|
+
if (!options.onTermsChanged || termsReviewSteps >= maxTermsReviewSteps) {
|
|
141
|
+
return chunkDQEXY542_js.ok({ kind: chunkSFUJMS5E_js.PURCHASE_INTENT_RESOLUTION_KIND.REVIEW_REQUIRED, response });
|
|
142
|
+
}
|
|
143
|
+
termsReviewSteps += 1;
|
|
144
|
+
const replacementRequest = await options.onTermsChanged(response);
|
|
145
|
+
if (!replacementRequest) {
|
|
146
|
+
return chunkDQEXY542_js.ok({ kind: chunkSFUJMS5E_js.PURCHASE_INTENT_RESOLUTION_KIND.REVIEW_REQUIRED, response });
|
|
147
|
+
}
|
|
148
|
+
request = replacementRequest;
|
|
149
|
+
responseResult = await this.confirmation.confirm(intentId, request, {
|
|
150
|
+
signal: options.signal
|
|
151
|
+
});
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
case chunkSFUJMS5E_js.PURCHASE_INTENT_CONFIRM_STATUS.SELECTION_UNAVAILABLE:
|
|
155
|
+
case chunkSFUJMS5E_js.PURCHASE_INTENT_CONFIRM_STATUS.OFFER_ENDED:
|
|
156
|
+
case chunkSFUJMS5E_js.PURCHASE_INTENT_CONFIRM_STATUS.SOURCE_UNAVAILABLE:
|
|
157
|
+
return chunkDQEXY542_js.ok({ kind: chunkSFUJMS5E_js.PURCHASE_INTENT_RESOLUTION_KIND.REVIEW_REQUIRED, response });
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return responseResult;
|
|
161
|
+
}
|
|
162
|
+
ensureActive(signal) {
|
|
163
|
+
if (signal?.aborted) throw abortError();
|
|
164
|
+
}
|
|
165
|
+
notify(options, status, response) {
|
|
166
|
+
try {
|
|
167
|
+
options.onStatusChange?.(status, response);
|
|
168
|
+
} catch {
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// src/purchase-intent.ts
|
|
174
|
+
var PURCHASE_INTENT_ROUTE = {
|
|
175
|
+
DIRECT_COLLECTION: "/api/v1/purchase-intents",
|
|
176
|
+
HOSTED_SESSION_COLLECTION: "/v1/checkout/sessions",
|
|
177
|
+
PREPARE: "prepare",
|
|
178
|
+
REFRESH: "refresh",
|
|
179
|
+
CONFIRM: "confirm",
|
|
180
|
+
REPLAY: "replay",
|
|
181
|
+
ATTEMPTS: "attempts",
|
|
182
|
+
ACTIONS: "actions"
|
|
183
|
+
};
|
|
184
|
+
function directIntentPath(intentId) {
|
|
185
|
+
const normalized = intentId.trim();
|
|
186
|
+
if (!normalized) throw new TypeError("intentId must not be empty");
|
|
187
|
+
return `${PURCHASE_INTENT_ROUTE.DIRECT_COLLECTION}/${encodeURIComponent(normalized)}`;
|
|
188
|
+
}
|
|
189
|
+
function hostedSessionPath(sessionId) {
|
|
190
|
+
const normalized = sessionId.trim();
|
|
191
|
+
if (!normalized) throw new TypeError("sessionId must not be empty");
|
|
192
|
+
return `${PURCHASE_INTENT_ROUTE.HOSTED_SESSION_COLLECTION}/${encodeURIComponent(normalized)}`;
|
|
193
|
+
}
|
|
194
|
+
var PurchaseIntentService = class {
|
|
195
|
+
constructor(transport) {
|
|
196
|
+
this.transport = transport;
|
|
197
|
+
this.resolver = new PurchaseIntentResolver({ confirmation: this });
|
|
198
|
+
}
|
|
199
|
+
mint(request, options) {
|
|
200
|
+
return chunkDQEXY542_js.safe(
|
|
201
|
+
this.transport.post(PURCHASE_INTENT_ROUTE.DIRECT_COLLECTION, request, {
|
|
202
|
+
idempotencyKey: request.client_intent_key,
|
|
203
|
+
signal: options?.signal,
|
|
204
|
+
timeoutMs: options?.timeoutMs
|
|
205
|
+
})
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
get(intentId, options) {
|
|
209
|
+
return chunkDQEXY542_js.safe(
|
|
210
|
+
this.transport.get(this.intentPath(intentId), {
|
|
211
|
+
signal: options?.signal,
|
|
212
|
+
timeoutMs: options?.timeoutMs
|
|
213
|
+
})
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
refresh(intentId, expectedRevision, options) {
|
|
217
|
+
const request = {
|
|
218
|
+
expected_intent_revision: expectedRevision
|
|
219
|
+
};
|
|
220
|
+
return chunkDQEXY542_js.safe(
|
|
221
|
+
this.transport.post(
|
|
222
|
+
`${this.intentPath(intentId)}/${PURCHASE_INTENT_ROUTE.REFRESH}`,
|
|
223
|
+
request,
|
|
224
|
+
{
|
|
225
|
+
signal: options?.signal,
|
|
226
|
+
timeoutMs: options?.timeoutMs
|
|
227
|
+
}
|
|
228
|
+
)
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
confirm(intentId, request, options) {
|
|
232
|
+
return chunkDQEXY542_js.safe(
|
|
233
|
+
this.transport.post(
|
|
234
|
+
`${this.intentPath(intentId)}/${PURCHASE_INTENT_ROUTE.CONFIRM}`,
|
|
235
|
+
request,
|
|
236
|
+
{
|
|
237
|
+
idempotencyKey: request.idempotency_key,
|
|
238
|
+
signal: options?.signal,
|
|
239
|
+
timeoutMs: options?.timeoutMs
|
|
240
|
+
}
|
|
241
|
+
)
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
submitAction(intentId, attemptId, request, options) {
|
|
245
|
+
const normalizedAttemptId = attemptId.trim();
|
|
246
|
+
if (!normalizedAttemptId) throw new TypeError("attemptId must not be empty");
|
|
247
|
+
return chunkDQEXY542_js.safe(
|
|
248
|
+
this.transport.post(
|
|
249
|
+
`${this.intentPath(intentId)}/${PURCHASE_INTENT_ROUTE.ATTEMPTS}/${encodeURIComponent(normalizedAttemptId)}/${PURCHASE_INTENT_ROUTE.ACTIONS}`,
|
|
250
|
+
request,
|
|
251
|
+
{
|
|
252
|
+
signal: options?.signal,
|
|
253
|
+
timeoutMs: options?.timeoutMs
|
|
254
|
+
}
|
|
255
|
+
)
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
resolve(intentId, options) {
|
|
259
|
+
return this.resolver.resolve(intentId, options);
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Bind subsequent preparation/confirmation to one hosted checkout-session
|
|
263
|
+
* owner. The session ID is a route authority, not an intent-owner rewrite.
|
|
264
|
+
*/
|
|
265
|
+
forHostedSession(sessionId) {
|
|
266
|
+
return new HostedPurchaseIntentService(this.transport, sessionId);
|
|
267
|
+
}
|
|
268
|
+
intentPath(intentId) {
|
|
269
|
+
return directIntentPath(intentId);
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
var HostedPurchaseIntentService = class {
|
|
273
|
+
constructor(transport, sessionId) {
|
|
274
|
+
this.transport = transport;
|
|
275
|
+
this.sessionPath = hostedSessionPath(sessionId);
|
|
276
|
+
this.resolver = new PurchaseIntentResolver({ confirmation: this });
|
|
277
|
+
}
|
|
278
|
+
prepare(request, options) {
|
|
279
|
+
return chunkDQEXY542_js.safe(
|
|
280
|
+
this.transport.post(
|
|
281
|
+
`${this.sessionPath}/${PURCHASE_INTENT_ROUTE.PREPARE}`,
|
|
282
|
+
request,
|
|
283
|
+
{
|
|
284
|
+
idempotencyKey: request.idempotency_key,
|
|
285
|
+
signal: options?.signal,
|
|
286
|
+
timeoutMs: options?.timeoutMs
|
|
287
|
+
}
|
|
288
|
+
)
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
confirm(_intentId, request, options) {
|
|
292
|
+
return chunkDQEXY542_js.safe(
|
|
293
|
+
this.transport.post(
|
|
294
|
+
`${this.sessionPath}/${PURCHASE_INTENT_ROUTE.CONFIRM}`,
|
|
295
|
+
request,
|
|
296
|
+
{
|
|
297
|
+
idempotencyKey: request.idempotency_key,
|
|
298
|
+
signal: options?.signal,
|
|
299
|
+
timeoutMs: options?.timeoutMs
|
|
300
|
+
}
|
|
301
|
+
)
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
replay(options) {
|
|
305
|
+
return chunkDQEXY542_js.safe(
|
|
306
|
+
this.transport.get(
|
|
307
|
+
`${this.sessionPath}/${PURCHASE_INTENT_ROUTE.REPLAY}`,
|
|
308
|
+
{
|
|
309
|
+
signal: options?.signal,
|
|
310
|
+
timeoutMs: options?.timeoutMs
|
|
311
|
+
}
|
|
312
|
+
)
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
submitAction(_intentId, attemptId, request, options) {
|
|
316
|
+
const normalized = attemptId.trim();
|
|
317
|
+
if (!normalized) throw new TypeError("attemptId must not be empty");
|
|
318
|
+
return chunkDQEXY542_js.safe(
|
|
319
|
+
this.transport.post(
|
|
320
|
+
`${this.sessionPath}/${PURCHASE_INTENT_ROUTE.ATTEMPTS}/${encodeURIComponent(normalized)}/${PURCHASE_INTENT_ROUTE.ACTIONS}`,
|
|
321
|
+
request,
|
|
322
|
+
{
|
|
323
|
+
signal: options?.signal,
|
|
324
|
+
timeoutMs: options?.timeoutMs
|
|
325
|
+
}
|
|
326
|
+
)
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
resolve(intentId, options) {
|
|
330
|
+
return this.resolver.resolve(intentId, options);
|
|
331
|
+
}
|
|
332
|
+
};
|
|
5
333
|
|
|
6
334
|
// src/store-credit.ts
|
|
7
335
|
var StoreCreditQueries = class {
|
|
@@ -13,18 +341,19 @@ var StoreCreditQueries = class {
|
|
|
13
341
|
* currency. Requires a signed-in customer; returns an error otherwise.
|
|
14
342
|
*/
|
|
15
343
|
async balance() {
|
|
16
|
-
return
|
|
344
|
+
return chunkDQEXY542_js.safe(this.client.get("/api/v1/store-credit/balance"));
|
|
17
345
|
}
|
|
18
346
|
};
|
|
19
347
|
|
|
20
348
|
// src/internal/analytics-session.ts
|
|
21
349
|
var ANALYTICS_SESSION_IDLE_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
22
|
-
var
|
|
23
|
-
var ANALYTICS_SESSION_STATE_VERSION =
|
|
350
|
+
var ANALYTICS_SESSION_STORAGE_KEY_PREFIX = "cimplify_analytics_session:v2:";
|
|
351
|
+
var ANALYTICS_SESSION_STATE_VERSION = 2;
|
|
24
352
|
var MICROSECONDS_PER_MILLISECOND = 1e3;
|
|
25
353
|
var MICROSECONDS_PER_SECOND = 1e6;
|
|
26
354
|
var AnalyticsSessionCoordinator = class {
|
|
27
|
-
constructor() {
|
|
355
|
+
constructor(identity) {
|
|
356
|
+
this.identity = identity;
|
|
28
357
|
this.fallbackState = null;
|
|
29
358
|
}
|
|
30
359
|
beginEvent(nowMs = Date.now()) {
|
|
@@ -62,7 +391,7 @@ var AnalyticsSessionCoordinator = class {
|
|
|
62
391
|
reset() {
|
|
63
392
|
this.fallbackState = null;
|
|
64
393
|
try {
|
|
65
|
-
sessionStorage.removeItem(
|
|
394
|
+
sessionStorage.removeItem(this.storageKey());
|
|
66
395
|
} catch {
|
|
67
396
|
}
|
|
68
397
|
}
|
|
@@ -72,11 +401,11 @@ var AnalyticsSessionCoordinator = class {
|
|
|
72
401
|
}
|
|
73
402
|
read() {
|
|
74
403
|
try {
|
|
75
|
-
const serialized = sessionStorage.getItem(
|
|
404
|
+
const serialized = sessionStorage.getItem(this.storageKey());
|
|
76
405
|
if (!serialized) return this.fallbackState;
|
|
77
406
|
const state = JSON.parse(serialized);
|
|
78
407
|
if (!isPersistedSession(state)) {
|
|
79
|
-
sessionStorage.removeItem(
|
|
408
|
+
sessionStorage.removeItem(this.storageKey());
|
|
80
409
|
return this.fallbackState;
|
|
81
410
|
}
|
|
82
411
|
this.fallbackState = state;
|
|
@@ -88,10 +417,13 @@ var AnalyticsSessionCoordinator = class {
|
|
|
88
417
|
write(state) {
|
|
89
418
|
this.fallbackState = state;
|
|
90
419
|
try {
|
|
91
|
-
sessionStorage.setItem(
|
|
420
|
+
sessionStorage.setItem(this.storageKey(), JSON.stringify(state));
|
|
92
421
|
} catch {
|
|
93
422
|
}
|
|
94
423
|
}
|
|
424
|
+
storageKey() {
|
|
425
|
+
return `${ANALYTICS_SESSION_STORAGE_KEY_PREFIX}${encodeURIComponent(this.identity)}`;
|
|
426
|
+
}
|
|
95
427
|
};
|
|
96
428
|
function newSession(nowMs) {
|
|
97
429
|
return {
|
|
@@ -158,11 +490,127 @@ function generateSessionId() {
|
|
|
158
490
|
|
|
159
491
|
// src/activity.ts
|
|
160
492
|
var VISITOR_ID_STORAGE_KEY = "cimplify_visitor_id";
|
|
493
|
+
var ACTIVITY_RETRY_POLICY = {
|
|
494
|
+
maxRetries: 1,
|
|
495
|
+
jitterRatio: 0.2
|
|
496
|
+
};
|
|
497
|
+
var PAGE_VIEW_TRACKER_KEY = /* @__PURE__ */ Symbol.for("@cimplify/sdk.page-view-tracker-hub");
|
|
498
|
+
var PAGE_VIEW_TRACKER_VERSION = 2;
|
|
499
|
+
function trackerHost() {
|
|
500
|
+
return window;
|
|
501
|
+
}
|
|
502
|
+
function currentWindowTrackerHub() {
|
|
503
|
+
const candidate = trackerHost()[PAGE_VIEW_TRACKER_KEY];
|
|
504
|
+
return candidate?.version === PAGE_VIEW_TRACKER_VERSION && candidate.registrations instanceof Map && typeof candidate.recordAll === "function" ? candidate : null;
|
|
505
|
+
}
|
|
506
|
+
function weakReference(target) {
|
|
507
|
+
const WeakReference = globalThis.WeakRef;
|
|
508
|
+
if (WeakReference) return new WeakReference(target);
|
|
509
|
+
return { deref: () => target };
|
|
510
|
+
}
|
|
511
|
+
function latestLiveOwner(registration) {
|
|
512
|
+
let latest = null;
|
|
513
|
+
for (const [token, reference] of registration.owners) {
|
|
514
|
+
const owner = reference.deref();
|
|
515
|
+
if (!owner || owner.released) {
|
|
516
|
+
registration.owners.delete(token);
|
|
517
|
+
} else {
|
|
518
|
+
latest = owner;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
return latest;
|
|
522
|
+
}
|
|
523
|
+
function recordRegistration(registration, path) {
|
|
524
|
+
if (path === registration.lastPath) return;
|
|
525
|
+
const owner = registration.activeOwner && !registration.activeOwner.released ? registration.activeOwner : latestLiveOwner(registration);
|
|
526
|
+
if (!owner) return;
|
|
527
|
+
registration.activeOwner = owner;
|
|
528
|
+
registration.lastPath = path;
|
|
529
|
+
owner.recorder(path);
|
|
530
|
+
}
|
|
531
|
+
function createWindowTrackerHub() {
|
|
532
|
+
const originalPushState = window.history.pushState;
|
|
533
|
+
const originalReplaceState = window.history.replaceState;
|
|
534
|
+
const hub = {
|
|
535
|
+
version: PAGE_VIEW_TRACKER_VERSION,
|
|
536
|
+
registrations: /* @__PURE__ */ new Map(),
|
|
537
|
+
originalPushState,
|
|
538
|
+
originalReplaceState
|
|
539
|
+
};
|
|
540
|
+
hub.recordAll = () => {
|
|
541
|
+
const path = window.location.pathname;
|
|
542
|
+
for (const registration of hub.registrations.values()) {
|
|
543
|
+
recordRegistration(registration, path);
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
hub.patchedPushState = function(...args) {
|
|
547
|
+
originalPushState.apply(this, args);
|
|
548
|
+
hub.recordAll();
|
|
549
|
+
};
|
|
550
|
+
hub.patchedReplaceState = function(...args) {
|
|
551
|
+
originalReplaceState.apply(this, args);
|
|
552
|
+
hub.recordAll();
|
|
553
|
+
};
|
|
554
|
+
hub.popstateListener = () => hub.recordAll();
|
|
555
|
+
window.history.pushState = hub.patchedPushState;
|
|
556
|
+
window.history.replaceState = hub.patchedReplaceState;
|
|
557
|
+
window.addEventListener("popstate", hub.popstateListener);
|
|
558
|
+
trackerHost()[PAGE_VIEW_TRACKER_KEY] = hub;
|
|
559
|
+
return hub;
|
|
560
|
+
}
|
|
561
|
+
function pageViewTrackerIdentity(client) {
|
|
562
|
+
return JSON.stringify([client.getBaseUrl(), client.getPublicKey().trim()]);
|
|
563
|
+
}
|
|
564
|
+
function acquireWindowPageViewTracker(identity, recorder) {
|
|
565
|
+
const hub = currentWindowTrackerHub() ?? createWindowTrackerHub();
|
|
566
|
+
let registration = hub.registrations.get(identity);
|
|
567
|
+
const isNewIdentity = !registration;
|
|
568
|
+
if (!registration) {
|
|
569
|
+
registration = { owners: /* @__PURE__ */ new Map(), activeOwner: null, lastPath: null };
|
|
570
|
+
hub.registrations.set(identity, registration);
|
|
571
|
+
}
|
|
572
|
+
const owner = {
|
|
573
|
+
token: Symbol(identity),
|
|
574
|
+
recorder,
|
|
575
|
+
released: false
|
|
576
|
+
};
|
|
577
|
+
registration.owners.set(owner.token, weakReference(owner));
|
|
578
|
+
registration.activeOwner = owner;
|
|
579
|
+
if (isNewIdentity) {
|
|
580
|
+
recordRegistration(registration, window.location.pathname);
|
|
581
|
+
}
|
|
582
|
+
let released = false;
|
|
583
|
+
return () => {
|
|
584
|
+
if (released) return;
|
|
585
|
+
released = true;
|
|
586
|
+
owner.released = true;
|
|
587
|
+
registration.owners.delete(owner.token);
|
|
588
|
+
if (registration.activeOwner === owner) {
|
|
589
|
+
registration.activeOwner = latestLiveOwner(registration);
|
|
590
|
+
}
|
|
591
|
+
if (registration.activeOwner) return;
|
|
592
|
+
if (hub.registrations.get(identity) === registration) {
|
|
593
|
+
hub.registrations.delete(identity);
|
|
594
|
+
}
|
|
595
|
+
if (hub.registrations.size !== 0) return;
|
|
596
|
+
window.removeEventListener("popstate", hub.popstateListener);
|
|
597
|
+
if (window.history.pushState === hub.patchedPushState) {
|
|
598
|
+
window.history.pushState = hub.originalPushState;
|
|
599
|
+
}
|
|
600
|
+
if (window.history.replaceState === hub.patchedReplaceState) {
|
|
601
|
+
window.history.replaceState = hub.originalReplaceState;
|
|
602
|
+
}
|
|
603
|
+
if (currentWindowTrackerHub() === hub) {
|
|
604
|
+
delete trackerHost()[PAGE_VIEW_TRACKER_KEY];
|
|
605
|
+
}
|
|
606
|
+
};
|
|
607
|
+
}
|
|
161
608
|
var ActivityService = class {
|
|
162
609
|
constructor(client) {
|
|
163
610
|
this.client = client;
|
|
164
|
-
this.
|
|
611
|
+
this.localGeneration = 0;
|
|
165
612
|
this.bootstrapRequest = null;
|
|
613
|
+
this.sessions = this.createSessionCoordinator();
|
|
166
614
|
}
|
|
167
615
|
/**
|
|
168
616
|
* Record session start (landing, referrer, UTM, device). Fire-and-forget,
|
|
@@ -172,7 +620,7 @@ var ActivityService = class {
|
|
|
172
620
|
startSession() {
|
|
173
621
|
if (typeof window === "undefined") return;
|
|
174
622
|
const session = this.sessions.beginEvent();
|
|
175
|
-
void this.bootstrapSession(session).catch(() => {
|
|
623
|
+
void this.bootstrapSession(session, false, this.captureGeneration()).catch(() => {
|
|
176
624
|
});
|
|
177
625
|
}
|
|
178
626
|
/** Rebind an existing analytics session after the server authenticates a customer. */
|
|
@@ -180,13 +628,19 @@ var ActivityService = class {
|
|
|
180
628
|
if (typeof window === "undefined") return;
|
|
181
629
|
const session = this.sessions.current();
|
|
182
630
|
if (!session) return;
|
|
183
|
-
void this.bootstrapSession(session, true).catch(() => {
|
|
631
|
+
void this.bootstrapSession(session, true, this.captureGeneration()).catch(() => {
|
|
184
632
|
});
|
|
185
633
|
}
|
|
186
634
|
/** A signed-out browser starts a fresh anonymous analytics session. */
|
|
187
635
|
resetSession() {
|
|
636
|
+
this.invalidatePendingActivity();
|
|
188
637
|
this.sessions.reset();
|
|
189
|
-
|
|
638
|
+
}
|
|
639
|
+
/** Drop the old storefront's analytics state before binding this service to the new one. */
|
|
640
|
+
resetForStorefrontIdentityChange() {
|
|
641
|
+
this.invalidatePendingActivity();
|
|
642
|
+
this.sessions.reset();
|
|
643
|
+
this.sessions = this.createSessionCoordinator();
|
|
190
644
|
}
|
|
191
645
|
buildSessionPayload(session) {
|
|
192
646
|
const params = new URLSearchParams(window.location.search);
|
|
@@ -208,6 +662,7 @@ var ActivityService = class {
|
|
|
208
662
|
utm_campaign: utm("campaign"),
|
|
209
663
|
utm_term: utm("term"),
|
|
210
664
|
utm_content: utm("content"),
|
|
665
|
+
social_commerce_ref: params.get("social_commerce_ref") || void 0,
|
|
211
666
|
screen_width: window.screen?.width,
|
|
212
667
|
screen_height: window.screen?.height,
|
|
213
668
|
viewport_width: window.innerWidth,
|
|
@@ -262,7 +717,7 @@ var ActivityService = class {
|
|
|
262
717
|
this.sendEvents([event]);
|
|
263
718
|
}
|
|
264
719
|
async getState() {
|
|
265
|
-
return
|
|
720
|
+
return chunkDQEXY542_js.safe(this.client.get("/api/v1/activity/state"));
|
|
266
721
|
}
|
|
267
722
|
async getRecommendations(options) {
|
|
268
723
|
const params = new URLSearchParams();
|
|
@@ -270,10 +725,10 @@ var ActivityService = class {
|
|
|
270
725
|
if (options?.limit) params.set("limit", String(options.limit));
|
|
271
726
|
const query = params.toString();
|
|
272
727
|
const path = query ? `/api/v1/activity/recommendations?${query}` : "/api/v1/activity/recommendations";
|
|
273
|
-
return
|
|
728
|
+
return chunkDQEXY542_js.safe(this.client.get(path));
|
|
274
729
|
}
|
|
275
730
|
async dismissMessage(code) {
|
|
276
|
-
return
|
|
731
|
+
return chunkDQEXY542_js.safe(
|
|
277
732
|
this.client.post("/api/v1/activity/messages/dismiss", { code })
|
|
278
733
|
);
|
|
279
734
|
}
|
|
@@ -281,32 +736,12 @@ var ActivityService = class {
|
|
|
281
736
|
autoTrackPageViews() {
|
|
282
737
|
if (typeof window === "undefined") return () => {
|
|
283
738
|
};
|
|
284
|
-
|
|
285
|
-
const record = () => {
|
|
286
|
-
const path = window.location.pathname;
|
|
287
|
-
if (path === lastPath) return;
|
|
288
|
-
lastPath = path;
|
|
739
|
+
return acquireWindowPageViewTracker(pageViewTrackerIdentity(this.client), (path) => {
|
|
289
740
|
try {
|
|
290
741
|
this.trackPageView({ pagePath: path });
|
|
291
742
|
} catch {
|
|
292
743
|
}
|
|
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
|
-
};
|
|
744
|
+
});
|
|
310
745
|
}
|
|
311
746
|
sendEvents(events, session) {
|
|
312
747
|
if (typeof window === "undefined") return;
|
|
@@ -314,29 +749,59 @@ var ActivityService = class {
|
|
|
314
749
|
});
|
|
315
750
|
}
|
|
316
751
|
async postEvents(events, session) {
|
|
317
|
-
|
|
318
|
-
await this.
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
events
|
|
322
|
-
|
|
752
|
+
const generation = this.captureGeneration();
|
|
753
|
+
await this.bootstrapSession(session, false, generation);
|
|
754
|
+
if (!this.isCurrentGeneration(generation)) return;
|
|
755
|
+
await this.client.post(
|
|
756
|
+
"/api/v1/activity/events",
|
|
757
|
+
{
|
|
758
|
+
analytics_session_id: session.id,
|
|
759
|
+
occurred_at: session.occurredAt,
|
|
760
|
+
events
|
|
761
|
+
},
|
|
762
|
+
{ retryPolicy: ACTIVITY_RETRY_POLICY }
|
|
763
|
+
);
|
|
323
764
|
}
|
|
324
|
-
bootstrapSession(session, force = false) {
|
|
765
|
+
bootstrapSession(session, force = false, generation = this.captureGeneration()) {
|
|
325
766
|
if (!force && !session.requiresBootstrap) return Promise.resolve();
|
|
326
|
-
if (!force && this.bootstrapRequest?.sessionId === session.id) {
|
|
767
|
+
if (!force && this.bootstrapRequest?.sessionId === session.id && sameActivityGeneration(this.bootstrapRequest.generation, generation)) {
|
|
327
768
|
return this.bootstrapRequest.promise;
|
|
328
769
|
}
|
|
329
|
-
const promise = this.client.post("/api/v1/activity/session", this.buildSessionPayload(session)
|
|
330
|
-
|
|
770
|
+
const promise = this.client.post("/api/v1/activity/session", this.buildSessionPayload(session), {
|
|
771
|
+
retryPolicy: ACTIVITY_RETRY_POLICY
|
|
772
|
+
}).then(() => {
|
|
773
|
+
if (this.isCurrentGeneration(generation)) {
|
|
774
|
+
this.sessions.markBootstrapped(session.id);
|
|
775
|
+
}
|
|
331
776
|
}).finally(() => {
|
|
332
777
|
if (this.bootstrapRequest?.promise === promise) {
|
|
333
778
|
this.bootstrapRequest = null;
|
|
334
779
|
}
|
|
335
780
|
});
|
|
336
|
-
this.bootstrapRequest = { sessionId: session.id, promise };
|
|
781
|
+
this.bootstrapRequest = { sessionId: session.id, generation, promise };
|
|
337
782
|
return promise;
|
|
338
783
|
}
|
|
784
|
+
captureGeneration() {
|
|
785
|
+
return {
|
|
786
|
+
local: this.localGeneration,
|
|
787
|
+
publicKey: this.client.getPublicKeyEpoch(),
|
|
788
|
+
session: this.client.getSessionEpoch()
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
isCurrentGeneration(generation) {
|
|
792
|
+
return sameActivityGeneration(generation, this.captureGeneration());
|
|
793
|
+
}
|
|
794
|
+
invalidatePendingActivity() {
|
|
795
|
+
this.localGeneration += 1;
|
|
796
|
+
this.bootstrapRequest = null;
|
|
797
|
+
}
|
|
798
|
+
createSessionCoordinator() {
|
|
799
|
+
return new AnalyticsSessionCoordinator(pageViewTrackerIdentity(this.client));
|
|
800
|
+
}
|
|
339
801
|
};
|
|
802
|
+
function sameActivityGeneration(left, right) {
|
|
803
|
+
return left.local === right.local && left.publicKey === right.publicKey && left.session === right.session;
|
|
804
|
+
}
|
|
340
805
|
function currentPath() {
|
|
341
806
|
if (typeof window !== "undefined") {
|
|
342
807
|
return window.location.pathname;
|
|
@@ -369,16 +834,16 @@ var SubscriptionService = class {
|
|
|
369
834
|
this.client = client;
|
|
370
835
|
}
|
|
371
836
|
async list() {
|
|
372
|
-
return
|
|
837
|
+
return chunkDQEXY542_js.safe(this.client.get("/api/v1/subscriptions"));
|
|
373
838
|
}
|
|
374
839
|
async get(id) {
|
|
375
840
|
const encodedId = encodeURIComponent(id);
|
|
376
|
-
return
|
|
841
|
+
return chunkDQEXY542_js.safe(this.client.get(`/api/v1/subscriptions/${encodedId}`));
|
|
377
842
|
}
|
|
378
843
|
async cancel(id, reason, opts) {
|
|
379
844
|
const encodedId = encodeURIComponent(id);
|
|
380
|
-
const idempotencyKey = opts?.idempotencyKey ??
|
|
381
|
-
return
|
|
845
|
+
const idempotencyKey = opts?.idempotencyKey ?? chunkDQEXY542_js.generateIdempotencyKey();
|
|
846
|
+
return chunkDQEXY542_js.safe(
|
|
382
847
|
this.client.post(
|
|
383
848
|
`/api/v1/subscriptions/${encodedId}/cancel`,
|
|
384
849
|
{ reason },
|
|
@@ -388,20 +853,20 @@ var SubscriptionService = class {
|
|
|
388
853
|
}
|
|
389
854
|
async pause(id) {
|
|
390
855
|
const encodedId = encodeURIComponent(id);
|
|
391
|
-
return
|
|
856
|
+
return chunkDQEXY542_js.safe(
|
|
392
857
|
this.client.post(`/api/v1/subscriptions/${encodedId}/pause`)
|
|
393
858
|
);
|
|
394
859
|
}
|
|
395
860
|
async resume(id) {
|
|
396
861
|
const encodedId = encodeURIComponent(id);
|
|
397
|
-
return
|
|
862
|
+
return chunkDQEXY542_js.safe(
|
|
398
863
|
this.client.post(`/api/v1/subscriptions/${encodedId}/resume`)
|
|
399
864
|
);
|
|
400
865
|
}
|
|
401
866
|
async skipNextRenewal(id, opts) {
|
|
402
867
|
const encodedId = encodeURIComponent(id);
|
|
403
|
-
const idempotencyKey = opts?.idempotencyKey ??
|
|
404
|
-
return
|
|
868
|
+
const idempotencyKey = opts?.idempotencyKey ?? chunkDQEXY542_js.generateIdempotencyKey();
|
|
869
|
+
return chunkDQEXY542_js.safe(
|
|
405
870
|
this.client.post(
|
|
406
871
|
`/api/v1/subscriptions/${encodedId}/skip`,
|
|
407
872
|
void 0,
|
|
@@ -416,14 +881,14 @@ var UploadService = class {
|
|
|
416
881
|
constructor(client) {
|
|
417
882
|
this.client = client;
|
|
418
883
|
}
|
|
419
|
-
async init(filename,
|
|
420
|
-
const idempotencyKey = opts?.idempotencyKey ??
|
|
421
|
-
return
|
|
884
|
+
async init(filename, contentType2, sizeBytes, opts) {
|
|
885
|
+
const idempotencyKey = opts?.idempotencyKey ?? chunkDQEXY542_js.generateIdempotencyKey();
|
|
886
|
+
return chunkDQEXY542_js.safe(
|
|
422
887
|
this.client.post(
|
|
423
888
|
"/api/v1/uploads/init",
|
|
424
889
|
{
|
|
425
890
|
filename,
|
|
426
|
-
content_type:
|
|
891
|
+
content_type: contentType2,
|
|
427
892
|
size_bytes: sizeBytes
|
|
428
893
|
},
|
|
429
894
|
{ idempotencyKey }
|
|
@@ -431,7 +896,7 @@ var UploadService = class {
|
|
|
431
896
|
);
|
|
432
897
|
}
|
|
433
898
|
async confirm(uploadId) {
|
|
434
|
-
return
|
|
899
|
+
return chunkDQEXY542_js.safe(
|
|
435
900
|
this.client.post("/api/v1/uploads/confirm", {
|
|
436
901
|
upload_id: uploadId
|
|
437
902
|
})
|
|
@@ -450,7 +915,7 @@ var UploadService = class {
|
|
|
450
915
|
if (!uploadResponse.ok) {
|
|
451
916
|
return {
|
|
452
917
|
ok: false,
|
|
453
|
-
error: new
|
|
918
|
+
error: new chunkSFUJMS5E_js.CimplifyError(
|
|
454
919
|
"UPLOAD_FAILED",
|
|
455
920
|
`Upload failed with status ${uploadResponse.status}`,
|
|
456
921
|
true
|
|
@@ -460,7 +925,7 @@ var UploadService = class {
|
|
|
460
925
|
} catch (error) {
|
|
461
926
|
return {
|
|
462
927
|
ok: false,
|
|
463
|
-
error: new
|
|
928
|
+
error: new chunkSFUJMS5E_js.CimplifyError(
|
|
464
929
|
"UPLOAD_FAILED",
|
|
465
930
|
error instanceof Error ? error.message : "Upload failed",
|
|
466
931
|
true
|
|
@@ -477,7 +942,7 @@ var PlacesService = class {
|
|
|
477
942
|
this.client = client;
|
|
478
943
|
}
|
|
479
944
|
async autocomplete(input, sessionToken) {
|
|
480
|
-
return
|
|
945
|
+
return chunkDQEXY542_js.safe(
|
|
481
946
|
this.client.post("/api/v1/places/autocomplete", {
|
|
482
947
|
input,
|
|
483
948
|
sessionToken
|
|
@@ -485,7 +950,7 @@ var PlacesService = class {
|
|
|
485
950
|
);
|
|
486
951
|
}
|
|
487
952
|
async details(placeId, sessionToken) {
|
|
488
|
-
return
|
|
953
|
+
return chunkDQEXY542_js.safe(
|
|
489
954
|
this.client.post("/api/v1/places/details", {
|
|
490
955
|
place_id: placeId,
|
|
491
956
|
sessionToken
|
|
@@ -504,7 +969,7 @@ var DeliveryService = class {
|
|
|
504
969
|
params.set("dropoff_lat", String(dropoffLat));
|
|
505
970
|
params.set("dropoff_lng", String(dropoffLng));
|
|
506
971
|
if (country) params.set("country", country);
|
|
507
|
-
return
|
|
972
|
+
return chunkDQEXY542_js.safe(this.client.get(`/api/v1/delivery/fee?${params.toString()}`));
|
|
508
973
|
}
|
|
509
974
|
async getOptions(queryOrLatitude, longitude, legacyOptions) {
|
|
510
975
|
const query = typeof queryOrLatitude === "number" ? {
|
|
@@ -518,12 +983,105 @@ var DeliveryService = class {
|
|
|
518
983
|
if (query.country) params.set("country", query.country);
|
|
519
984
|
if (query.currency) params.set("currency", query.currency);
|
|
520
985
|
if (query.orderValue != null) params.set("order_value", String(query.orderValue));
|
|
521
|
-
return
|
|
986
|
+
return chunkDQEXY542_js.safe(
|
|
522
987
|
this.client.get(`/api/v1/delivery/options?${params.toString()}`)
|
|
523
988
|
);
|
|
524
989
|
}
|
|
525
990
|
};
|
|
526
991
|
|
|
992
|
+
// src/chat-wire.ts
|
|
993
|
+
function senderType(message) {
|
|
994
|
+
if (message.sender_side === "account") return "customer";
|
|
995
|
+
if (message.sender_side === "system") return "system";
|
|
996
|
+
return message.authorship === "ai" ? "bot" : "agent";
|
|
997
|
+
}
|
|
998
|
+
function contentType(message) {
|
|
999
|
+
if (message.content_type === "rich") {
|
|
1000
|
+
return message.structured_content?.["kind"] === "reaction" ? "reaction" : "interactive";
|
|
1001
|
+
}
|
|
1002
|
+
if (message.content_type === "media") {
|
|
1003
|
+
const mime = message.media[0]?.mime_type ?? "";
|
|
1004
|
+
if (mime.startsWith("image/")) return "image";
|
|
1005
|
+
if (mime.startsWith("video/")) return "video";
|
|
1006
|
+
if (mime.startsWith("audio/")) return "audio";
|
|
1007
|
+
return "document";
|
|
1008
|
+
}
|
|
1009
|
+
return "text";
|
|
1010
|
+
}
|
|
1011
|
+
function unresolvedReply(id) {
|
|
1012
|
+
return { id, sender_type: "system", preview: "Earlier message" };
|
|
1013
|
+
}
|
|
1014
|
+
function chatMessagesFromWire(messages) {
|
|
1015
|
+
const converted = messages.map((message) => ({
|
|
1016
|
+
id: message.id,
|
|
1017
|
+
sequence: message.sequence,
|
|
1018
|
+
sender_type: senderType(message),
|
|
1019
|
+
content: message.content,
|
|
1020
|
+
content_type: contentType(message),
|
|
1021
|
+
attachments: Array.isArray(message.media) ? message.media : [],
|
|
1022
|
+
metadata: message.structured_content ?? {},
|
|
1023
|
+
reply_to: message.reply_to_message_id ? unresolvedReply(message.reply_to_message_id) : void 0,
|
|
1024
|
+
edited_at: message.edited_at,
|
|
1025
|
+
deleted_at: message.deleted_at,
|
|
1026
|
+
created_at: message.created_at
|
|
1027
|
+
}));
|
|
1028
|
+
const byId = new Map(converted.map((message) => [message.id, message]));
|
|
1029
|
+
return converted.map((message) => {
|
|
1030
|
+
const targetId = message.reply_to?.id;
|
|
1031
|
+
const target = targetId ? byId.get(targetId) : void 0;
|
|
1032
|
+
if (!targetId || !target) return message;
|
|
1033
|
+
return {
|
|
1034
|
+
...message,
|
|
1035
|
+
reply_to: {
|
|
1036
|
+
id: targetId,
|
|
1037
|
+
sender_type: target.sender_type,
|
|
1038
|
+
preview: chunkXREIZ56K_js.messagePreview(target.content)
|
|
1039
|
+
}
|
|
1040
|
+
};
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
1043
|
+
function chatMessageFromWire(message) {
|
|
1044
|
+
return chatMessagesFromWire([message])[0];
|
|
1045
|
+
}
|
|
1046
|
+
function chatRealtimeUpdateFromWire(envelope) {
|
|
1047
|
+
if (envelope.type === "message_created" && envelope.data) {
|
|
1048
|
+
return {
|
|
1049
|
+
kind: "message",
|
|
1050
|
+
message: chatMessageFromWire(envelope.data)
|
|
1051
|
+
};
|
|
1052
|
+
}
|
|
1053
|
+
if (envelope.type === "message_updated" && envelope.data) {
|
|
1054
|
+
return {
|
|
1055
|
+
kind: "message_updated",
|
|
1056
|
+
message: chatMessageFromWire(envelope.data)
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
1059
|
+
if (envelope.type === "read" && envelope.data) {
|
|
1060
|
+
const read = envelope.data;
|
|
1061
|
+
if (read.reader_side === "business" && typeof read.occurred_at === "string") {
|
|
1062
|
+
return { kind: "business_read", occurredAt: read.occurred_at };
|
|
1063
|
+
}
|
|
1064
|
+
return { kind: "none" };
|
|
1065
|
+
}
|
|
1066
|
+
if (envelope.type === "history_imported") {
|
|
1067
|
+
return { kind: "reload" };
|
|
1068
|
+
}
|
|
1069
|
+
if (envelope.type === "ai_mode_changed" && envelope.data) {
|
|
1070
|
+
const mode = envelope.data.mode;
|
|
1071
|
+
if (mode === "auto" || mode === "assist" || mode === "off") {
|
|
1072
|
+
return { kind: "ai_mode", mode };
|
|
1073
|
+
}
|
|
1074
|
+
return { kind: "none" };
|
|
1075
|
+
}
|
|
1076
|
+
if (envelope.type === "handoff_requested") {
|
|
1077
|
+
return { kind: "handoff" };
|
|
1078
|
+
}
|
|
1079
|
+
if (envelope.type === "resync") {
|
|
1080
|
+
return { kind: "resync", latestSequence: envelope.latest_sequence };
|
|
1081
|
+
}
|
|
1082
|
+
return { kind: "none" };
|
|
1083
|
+
}
|
|
1084
|
+
|
|
527
1085
|
// src/internal/events.ts
|
|
528
1086
|
var MUTATION_ERROR = "mutation:error";
|
|
529
1087
|
var AUTH_TOKEN_CHANGED = "auth:token-changed";
|
|
@@ -586,16 +1144,18 @@ var SupportService = class {
|
|
|
586
1144
|
* Claim the anonymous session for the verified Link identity: the server
|
|
587
1145
|
* verifies the access token, rotates the session token (adopted here, so
|
|
588
1146
|
* every subsequent request rides the bound session), folds the guest cart
|
|
589
|
-
* in, and
|
|
1147
|
+
* in, and opens or reuses the account's canonical direct Chat thread.
|
|
590
1148
|
*
|
|
591
1149
|
* Idempotent and single-flight: concurrent calls share one request, and a
|
|
592
1150
|
* token already claimed by this instance short-circuits without a network
|
|
593
|
-
* round trip.
|
|
1151
|
+
* round trip. A session already bound to another Link account is rejected
|
|
1152
|
+
* rather than transferring that account's cart. Emits `session:claimed` on
|
|
1153
|
+
* the client's event channel.
|
|
594
1154
|
*/
|
|
595
1155
|
async claimConversation() {
|
|
596
1156
|
const accessToken = this.client.getAccessToken();
|
|
597
1157
|
if (!accessToken) {
|
|
598
|
-
return
|
|
1158
|
+
return chunkDQEXY542_js.err(new chunkSFUJMS5E_js.CimplifyError("not_authenticated", "No access token to claim with"));
|
|
599
1159
|
}
|
|
600
1160
|
if (this.claimInFlight) {
|
|
601
1161
|
if (this.claimInFlightToken === accessToken) return this.claimInFlight;
|
|
@@ -603,12 +1163,12 @@ var SupportService = class {
|
|
|
603
1163
|
return this.claimConversation();
|
|
604
1164
|
}
|
|
605
1165
|
if (this.lastClaimedAccessToken === accessToken) {
|
|
606
|
-
return
|
|
1166
|
+
return chunkDQEXY542_js.err(new chunkSFUJMS5E_js.CimplifyError("already_claimed", "Session already claimed for this token"));
|
|
607
1167
|
}
|
|
608
1168
|
const epoch = this.client.getSessionEpoch();
|
|
609
1169
|
this.claimInFlightToken = accessToken;
|
|
610
1170
|
this.claimInFlight = (async () => {
|
|
611
|
-
const result = await
|
|
1171
|
+
const result = await chunkDQEXY542_js.safe(
|
|
612
1172
|
this.client.post(
|
|
613
1173
|
"/api/v1/support/conversation/claim",
|
|
614
1174
|
{ access_token: accessToken },
|
|
@@ -617,8 +1177,8 @@ var SupportService = class {
|
|
|
617
1177
|
);
|
|
618
1178
|
if (result.ok) {
|
|
619
1179
|
if (this.client.getSessionEpoch() !== epoch) {
|
|
620
|
-
return
|
|
621
|
-
new
|
|
1180
|
+
return chunkDQEXY542_js.err(
|
|
1181
|
+
new chunkSFUJMS5E_js.CimplifyError("session_cleared", "Session was cleared during the claim")
|
|
622
1182
|
);
|
|
623
1183
|
}
|
|
624
1184
|
this.lastClaimedAccessToken = accessToken;
|
|
@@ -627,7 +1187,7 @@ var SupportService = class {
|
|
|
627
1187
|
this.client.publishSessionClaimed(this.claimedName);
|
|
628
1188
|
this.client.events.emit(SESSION_CLAIMED, {
|
|
629
1189
|
sessionToken: result.value.session_token,
|
|
630
|
-
threadId: result.value.
|
|
1190
|
+
threadId: result.value.thread.id,
|
|
631
1191
|
customerName: result.value.customer_name
|
|
632
1192
|
});
|
|
633
1193
|
}
|
|
@@ -640,30 +1200,54 @@ var SupportService = class {
|
|
|
640
1200
|
this.claimInFlightToken = null;
|
|
641
1201
|
}
|
|
642
1202
|
}
|
|
1203
|
+
/**
|
|
1204
|
+
* Retry verified external-history discovery for the current storefront.
|
|
1205
|
+
* Launch scope is intentionally WhatsApp-only because it is the registered
|
|
1206
|
+
* bidirectional transport. Results are per envelope and omit provider ids,
|
|
1207
|
+
* contact values, account ids, and message bodies.
|
|
1208
|
+
*/
|
|
1209
|
+
async claimExternalChats() {
|
|
1210
|
+
if (!this.client.getAccessToken()) {
|
|
1211
|
+
return chunkDQEXY542_js.err(new chunkSFUJMS5E_js.CimplifyError("not_authenticated", "No access token to claim with"));
|
|
1212
|
+
}
|
|
1213
|
+
return chunkDQEXY542_js.safe(
|
|
1214
|
+
(async () => {
|
|
1215
|
+
const businessId = await this.client.resolveBusinessId();
|
|
1216
|
+
return this.client.post("/v1/me/chats/external-claims", {
|
|
1217
|
+
business_id: businessId
|
|
1218
|
+
});
|
|
1219
|
+
})()
|
|
1220
|
+
);
|
|
1221
|
+
}
|
|
643
1222
|
/**
|
|
644
1223
|
* Peek at the conversation without creating one: resolves the existing
|
|
645
1224
|
* thread (or null) so a page load can hydrate history without littering
|
|
646
1225
|
* the inbox with empty threads.
|
|
647
1226
|
*/
|
|
648
1227
|
async peekConversation() {
|
|
649
|
-
const result = await
|
|
1228
|
+
const result = await chunkDQEXY542_js.safe(
|
|
650
1229
|
this.client.get("/api/v1/support/conversation")
|
|
651
1230
|
);
|
|
652
1231
|
if (!result.ok) return result;
|
|
653
|
-
if (!result.value.conversation) return
|
|
654
|
-
return
|
|
1232
|
+
if (!result.value.conversation) return chunkDQEXY542_js.ok(null);
|
|
1233
|
+
return chunkDQEXY542_js.ok({
|
|
655
1234
|
conversation: result.value.conversation,
|
|
656
|
-
messages: result.value.messages
|
|
1235
|
+
messages: chatMessagesFromWire(result.value.messages)
|
|
657
1236
|
});
|
|
658
1237
|
}
|
|
659
1238
|
/** Open (or resume) the customer's widget conversation. */
|
|
660
1239
|
async openConversation(opts) {
|
|
661
|
-
const idempotencyKey = opts?.idempotencyKey ??
|
|
662
|
-
|
|
1240
|
+
const idempotencyKey = opts?.idempotencyKey ?? chunkDQEXY542_js.generateIdempotencyKey();
|
|
1241
|
+
const result = await chunkDQEXY542_js.safe(
|
|
663
1242
|
this.client.post("/api/v1/support/conversation", void 0, {
|
|
664
1243
|
idempotencyKey
|
|
665
1244
|
})
|
|
666
1245
|
);
|
|
1246
|
+
if (!result.ok) return result;
|
|
1247
|
+
return chunkDQEXY542_js.ok({
|
|
1248
|
+
conversation: result.value.conversation,
|
|
1249
|
+
messages: chatMessagesFromWire(result.value.messages)
|
|
1250
|
+
});
|
|
667
1251
|
}
|
|
668
1252
|
/**
|
|
669
1253
|
* Send a message in the active conversation.
|
|
@@ -674,8 +1258,8 @@ var SupportService = class {
|
|
|
674
1258
|
* server resolves it thread-scoped and echoes the quote as `reply_to`.
|
|
675
1259
|
*/
|
|
676
1260
|
async sendMessage(content, opts) {
|
|
677
|
-
const idempotencyKey = opts?.idempotencyKey ??
|
|
678
|
-
|
|
1261
|
+
const idempotencyKey = opts?.idempotencyKey ?? chunkDQEXY542_js.generateIdempotencyKey();
|
|
1262
|
+
const result = await chunkDQEXY542_js.safe(
|
|
679
1263
|
this.client.post(
|
|
680
1264
|
"/api/v1/support/conversation/messages",
|
|
681
1265
|
{
|
|
@@ -687,6 +1271,7 @@ var SupportService = class {
|
|
|
687
1271
|
{ idempotencyKey }
|
|
688
1272
|
)
|
|
689
1273
|
);
|
|
1274
|
+
return result.ok ? chunkDQEXY542_js.ok(chatMessageFromWire(result.value.message)) : result;
|
|
690
1275
|
}
|
|
691
1276
|
/**
|
|
692
1277
|
* Poll for messages. `after` pages forward (live updates); `before` pages
|
|
@@ -694,26 +1279,32 @@ var SupportService = class {
|
|
|
694
1279
|
*/
|
|
695
1280
|
async getMessages(options = {}) {
|
|
696
1281
|
const params = new URLSearchParams();
|
|
697
|
-
if (options.
|
|
698
|
-
|
|
1282
|
+
if (options.afterSequence !== void 0)
|
|
1283
|
+
params.set("after_sequence", String(options.afterSequence));
|
|
1284
|
+
if (options.beforeSequence !== void 0)
|
|
1285
|
+
params.set("before_sequence", String(options.beforeSequence));
|
|
699
1286
|
if (options.limit) params.set("limit", String(options.limit));
|
|
700
1287
|
const query = params.toString();
|
|
701
1288
|
const path = query ? `/api/v1/support/conversation/messages?${query}` : "/api/v1/support/conversation/messages";
|
|
702
|
-
|
|
1289
|
+
const result = await chunkDQEXY542_js.safe(this.client.get(path));
|
|
1290
|
+
return result.ok ? chunkDQEXY542_js.ok(chatMessagesFromWire(result.value)) : result;
|
|
703
1291
|
}
|
|
704
1292
|
/** React to a message in the active conversation by its `ChatMessage.id`. */
|
|
705
1293
|
async reactToMessage(messageId, emoji) {
|
|
706
|
-
|
|
1294
|
+
const clientId = chunkDQEXY542_js.generateIdempotencyKey();
|
|
1295
|
+
const result = await chunkDQEXY542_js.safe(
|
|
707
1296
|
this.client.post(
|
|
708
1297
|
`/api/v1/support/conversation/messages/${messageId}/reactions`,
|
|
709
|
-
{ emoji }
|
|
1298
|
+
{ emoji, client_id: clientId },
|
|
1299
|
+
{ idempotencyKey: clientId }
|
|
710
1300
|
)
|
|
711
1301
|
);
|
|
1302
|
+
return result.ok ? chunkDQEXY542_js.ok(chatMessageFromWire(result.value.message)) : result;
|
|
712
1303
|
}
|
|
713
1304
|
/** Ask for a person: flags the thread for the merchant's inbox. */
|
|
714
1305
|
async requestHuman(opts) {
|
|
715
|
-
const idempotencyKey = opts?.idempotencyKey ??
|
|
716
|
-
return
|
|
1306
|
+
const idempotencyKey = opts?.idempotencyKey ?? chunkDQEXY542_js.generateIdempotencyKey();
|
|
1307
|
+
return chunkDQEXY542_js.safe(
|
|
717
1308
|
this.client.post(
|
|
718
1309
|
"/api/v1/support/conversation/handoff",
|
|
719
1310
|
void 0,
|
|
@@ -723,456 +1314,108 @@ var SupportService = class {
|
|
|
723
1314
|
}
|
|
724
1315
|
};
|
|
725
1316
|
|
|
726
|
-
// src/
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
try {
|
|
733
|
-
const imported = await import('@paystack/inline-js');
|
|
734
|
-
PaystackPop = imported.default;
|
|
735
|
-
} catch {
|
|
736
|
-
return { success: false, error: "PROVIDER_UNAVAILABLE" };
|
|
737
|
-
}
|
|
738
|
-
return new Promise((resolve) => {
|
|
739
|
-
let settled = false;
|
|
740
|
-
let providerStarted = false;
|
|
741
|
-
const resolveOnce = (result) => {
|
|
742
|
-
if (settled) {
|
|
743
|
-
return;
|
|
744
|
-
}
|
|
745
|
-
settled = true;
|
|
746
|
-
if (signal) {
|
|
747
|
-
signal.removeEventListener("abort", onAbort);
|
|
748
|
-
}
|
|
749
|
-
resolve(result);
|
|
750
|
-
};
|
|
751
|
-
const onAbort = () => {
|
|
752
|
-
resolveOnce({
|
|
753
|
-
success: false,
|
|
754
|
-
// Once Paystack owns the transaction, cancellation is ambiguous. The
|
|
755
|
-
// adapter converts this into submitted so Cimplify polling decides.
|
|
756
|
-
error: providerStarted ? "SUBMISSION_UNKNOWN" : "CANCELLED"
|
|
757
|
-
});
|
|
758
|
-
};
|
|
759
|
-
if (signal?.aborted) {
|
|
760
|
-
resolveOnce({ success: false, error: "CANCELLED" });
|
|
761
|
-
return;
|
|
762
|
-
}
|
|
763
|
-
if (signal) {
|
|
764
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
765
|
-
}
|
|
766
|
-
try {
|
|
767
|
-
const popup = new PaystackPop();
|
|
768
|
-
popup.newTransaction({
|
|
769
|
-
key: options.key,
|
|
770
|
-
email: options.email,
|
|
771
|
-
amount: options.amount,
|
|
772
|
-
currency: options.currency,
|
|
773
|
-
reference: options.reference,
|
|
774
|
-
accessCode: options.accessCode,
|
|
775
|
-
onSuccess: (transaction) => {
|
|
776
|
-
resolveOnce({
|
|
777
|
-
success: true,
|
|
778
|
-
reference: transaction.reference ?? options.reference
|
|
779
|
-
});
|
|
780
|
-
},
|
|
781
|
-
onCancel: () => {
|
|
782
|
-
resolveOnce({ success: false, error: "PAYMENT_CANCELLED" });
|
|
783
|
-
},
|
|
784
|
-
onError: (error) => {
|
|
785
|
-
resolveOnce({
|
|
786
|
-
success: false,
|
|
787
|
-
error: error?.message || "PAYMENT_FAILED"
|
|
788
|
-
});
|
|
789
|
-
}
|
|
790
|
-
});
|
|
791
|
-
providerStarted = true;
|
|
792
|
-
} catch {
|
|
793
|
-
resolveOnce({ success: false, error: "POPUP_BLOCKED" });
|
|
794
|
-
}
|
|
795
|
-
});
|
|
1317
|
+
// src/internal/cross-tab-session.ts
|
|
1318
|
+
var CROSS_TAB_SESSION_HUB_KEY = /* @__PURE__ */ Symbol.for("@cimplify/sdk.cross-tab-session-hub");
|
|
1319
|
+
var SESSION_SYNC_STORAGE_KEY = "cimplify_session_sync";
|
|
1320
|
+
var MAX_RECENT_ENVELOPES = 64;
|
|
1321
|
+
function hubHost() {
|
|
1322
|
+
return window;
|
|
796
1323
|
}
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
currency: context.currency
|
|
810
|
-
},
|
|
811
|
-
context.signal
|
|
812
|
-
);
|
|
813
|
-
if (result.success) {
|
|
814
|
-
return { status: "submitted", reference: result.reference || context.reference };
|
|
815
|
-
}
|
|
816
|
-
if (result.error === "SUBMISSION_UNKNOWN") {
|
|
817
|
-
return { status: "submitted", reference: context.reference };
|
|
818
|
-
}
|
|
819
|
-
if (!result.error || result.error === "CANCELLED" || result.error === "PAYMENT_CANCELLED") {
|
|
820
|
-
return { status: "cancelled" };
|
|
821
|
-
}
|
|
822
|
-
if (result.error === "POPUP_BLOCKED") {
|
|
823
|
-
return {
|
|
824
|
-
status: "failed",
|
|
825
|
-
code: "POPUP_BLOCKED",
|
|
826
|
-
message: "Unable to open the Paystack payment popup. Please allow popups and try again.",
|
|
827
|
-
recoverable: true
|
|
828
|
-
};
|
|
829
|
-
}
|
|
830
|
-
if (result.error === "PROVIDER_UNAVAILABLE") {
|
|
831
|
-
return {
|
|
832
|
-
status: "failed",
|
|
833
|
-
code: "PROVIDER_UNAVAILABLE",
|
|
834
|
-
message: "Paystack is unavailable in this environment.",
|
|
835
|
-
recoverable: false
|
|
836
|
-
};
|
|
837
|
-
}
|
|
838
|
-
return {
|
|
839
|
-
status: "failed",
|
|
840
|
-
code: "PAYMENT_FAILED",
|
|
841
|
-
message: result.error || "Paystack payment could not be submitted.",
|
|
842
|
-
recoverable: true
|
|
843
|
-
};
|
|
1324
|
+
function currentHub() {
|
|
1325
|
+
const candidate = hubHost()[CROSS_TAB_SESSION_HUB_KEY];
|
|
1326
|
+
return candidate?.subscribers instanceof Map && candidate.recentEnvelopes instanceof Set && typeof candidate.storageListener === "function" && typeof candidate.dispatch === "function" ? candidate : null;
|
|
1327
|
+
}
|
|
1328
|
+
function subscriberReference(target) {
|
|
1329
|
+
const WeakReference = globalThis.WeakRef;
|
|
1330
|
+
if (WeakReference) return new WeakReference(target);
|
|
1331
|
+
return { deref: () => target };
|
|
1332
|
+
}
|
|
1333
|
+
function pruneSubscribers(hub) {
|
|
1334
|
+
for (const [token, reference] of hub.subscribers) {
|
|
1335
|
+
if (!reference.deref()) hub.subscribers.delete(token);
|
|
844
1336
|
}
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
async function loadStripeFromPeer(publicKey) {
|
|
1337
|
+
}
|
|
1338
|
+
function parseSessionSyncPayload(serialized) {
|
|
1339
|
+
if (!serialized) return null;
|
|
849
1340
|
try {
|
|
850
|
-
const
|
|
851
|
-
return
|
|
1341
|
+
const payload = JSON.parse(serialized);
|
|
1342
|
+
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;
|
|
852
1343
|
} catch {
|
|
853
1344
|
return null;
|
|
854
1345
|
}
|
|
855
1346
|
}
|
|
856
|
-
function
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
const root = document.createElement("div");
|
|
860
|
-
root.setAttribute("role", "dialog");
|
|
861
|
-
root.setAttribute("aria-modal", "true");
|
|
862
|
-
root.setAttribute("aria-label", "Secure card payment");
|
|
863
|
-
Object.assign(root.style, {
|
|
864
|
-
position: "fixed",
|
|
865
|
-
inset: "0",
|
|
866
|
-
zIndex: "2147483647",
|
|
867
|
-
display: "grid",
|
|
868
|
-
placeItems: "center",
|
|
869
|
-
padding: "16px",
|
|
870
|
-
background: "rgba(15, 23, 42, 0.58)"
|
|
871
|
-
});
|
|
872
|
-
const form = document.createElement("form");
|
|
873
|
-
Object.assign(form.style, {
|
|
874
|
-
width: "min(100%, 480px)",
|
|
875
|
-
maxHeight: "calc(100vh - 32px)",
|
|
876
|
-
overflow: "auto",
|
|
877
|
-
borderRadius: "12px",
|
|
878
|
-
padding: "24px",
|
|
879
|
-
background: "#fff",
|
|
880
|
-
boxShadow: "0 24px 80px rgba(15, 23, 42, 0.32)"
|
|
881
|
-
});
|
|
882
|
-
const title = document.createElement("h2");
|
|
883
|
-
title.textContent = "Complete card payment";
|
|
884
|
-
Object.assign(title.style, { margin: "0 0 18px", font: "600 18px/1.4 system-ui" });
|
|
885
|
-
const paymentMount = document.createElement("div");
|
|
886
|
-
paymentMount.setAttribute("data-cimplify-stripe-payment-element", "");
|
|
887
|
-
const error = document.createElement("p");
|
|
888
|
-
error.setAttribute("role", "alert");
|
|
889
|
-
Object.assign(error.style, {
|
|
890
|
-
minHeight: "20px",
|
|
891
|
-
margin: "14px 0 0",
|
|
892
|
-
color: "#b91c1c",
|
|
893
|
-
font: "14px/1.4 system-ui"
|
|
894
|
-
});
|
|
895
|
-
const actions = document.createElement("div");
|
|
896
|
-
Object.assign(actions.style, {
|
|
897
|
-
display: "flex",
|
|
898
|
-
justifyContent: "flex-end",
|
|
899
|
-
gap: "10px",
|
|
900
|
-
marginTop: "18px"
|
|
901
|
-
});
|
|
902
|
-
const cancelButton = document.createElement("button");
|
|
903
|
-
cancelButton.type = "button";
|
|
904
|
-
cancelButton.textContent = "Cancel";
|
|
905
|
-
Object.assign(cancelButton.style, {
|
|
906
|
-
border: "1px solid #cbd5e1",
|
|
907
|
-
borderRadius: "8px",
|
|
908
|
-
padding: "10px 16px",
|
|
909
|
-
background: "#fff",
|
|
910
|
-
color: "#0f172a",
|
|
911
|
-
cursor: "pointer"
|
|
912
|
-
});
|
|
913
|
-
const submitButton = document.createElement("button");
|
|
914
|
-
submitButton.type = "submit";
|
|
915
|
-
submitButton.textContent = "Pay securely";
|
|
916
|
-
Object.assign(submitButton.style, {
|
|
917
|
-
border: "0",
|
|
918
|
-
borderRadius: "8px",
|
|
919
|
-
padding: "10px 16px",
|
|
920
|
-
background: "#0f172a",
|
|
921
|
-
color: "#fff",
|
|
922
|
-
cursor: "pointer"
|
|
923
|
-
});
|
|
924
|
-
actions.append(cancelButton, submitButton);
|
|
925
|
-
form.append(title, paymentMount, error, actions);
|
|
926
|
-
root.append(form);
|
|
927
|
-
document.body.append(root);
|
|
928
|
-
cancelButton.focus();
|
|
929
|
-
return { root, form, paymentMount, submitButton, cancelButton, error, previousFocus };
|
|
1347
|
+
function sessionSyncEnvelopeKey(payload) {
|
|
1348
|
+
const name = payload.name === void 0 ? ["missing"] : ["present", payload.name];
|
|
1349
|
+
return JSON.stringify([payload.type, payload.token, name, payload.at]);
|
|
930
1350
|
}
|
|
931
|
-
function
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
};
|
|
940
|
-
}
|
|
941
|
-
return { status: "failed", code: "PAYMENT_FAILED", message, recoverable: true };
|
|
1351
|
+
function rememberEnvelope(hub, envelope) {
|
|
1352
|
+
if (hub.recentEnvelopes.has(envelope)) return false;
|
|
1353
|
+
hub.recentEnvelopes.add(envelope);
|
|
1354
|
+
if (hub.recentEnvelopes.size > MAX_RECENT_ENVELOPES) {
|
|
1355
|
+
const oldest = hub.recentEnvelopes.values().next().value;
|
|
1356
|
+
if (oldest) hub.recentEnvelopes.delete(oldest);
|
|
1357
|
+
}
|
|
1358
|
+
return true;
|
|
942
1359
|
}
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
}
|
|
948
|
-
|
|
949
|
-
if (
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
}
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
} catch (error) {
|
|
961
|
-
return {
|
|
962
|
-
status: "failed",
|
|
963
|
-
code: "PROVIDER_UNAVAILABLE",
|
|
964
|
-
message: error instanceof Error ? error.message : "Stripe.js could not be loaded.",
|
|
965
|
-
recoverable: true
|
|
966
|
-
};
|
|
967
|
-
}
|
|
968
|
-
if (context.signal?.aborted) {
|
|
969
|
-
return { status: "cancelled" };
|
|
970
|
-
}
|
|
971
|
-
if (!stripe) {
|
|
972
|
-
return {
|
|
973
|
-
status: "failed",
|
|
974
|
-
code: "PROVIDER_UNAVAILABLE",
|
|
975
|
-
message: "Stripe.js could not be loaded.",
|
|
976
|
-
recoverable: false
|
|
977
|
-
};
|
|
978
|
-
}
|
|
979
|
-
const modal = createStripeModal();
|
|
980
|
-
if (!modal) {
|
|
981
|
-
return {
|
|
982
|
-
status: "failed",
|
|
983
|
-
code: "PROVIDER_UNAVAILABLE",
|
|
984
|
-
message: "Stripe Payment Element could not be mounted.",
|
|
985
|
-
recoverable: false
|
|
986
|
-
};
|
|
987
|
-
}
|
|
988
|
-
let paymentElement;
|
|
989
|
-
let elements;
|
|
990
|
-
try {
|
|
991
|
-
elements = stripe.elements({ clientSecret: context.action.client_secret });
|
|
992
|
-
paymentElement = elements.create("payment");
|
|
993
|
-
paymentElement.mount(modal.paymentMount);
|
|
994
|
-
} catch {
|
|
995
|
-
modal.root.remove();
|
|
996
|
-
modal.previousFocus?.focus();
|
|
997
|
-
return {
|
|
998
|
-
status: "failed",
|
|
999
|
-
code: "PROVIDER_UNAVAILABLE",
|
|
1000
|
-
message: "Stripe Payment Element could not be initialized.",
|
|
1001
|
-
recoverable: false
|
|
1002
|
-
};
|
|
1360
|
+
function createHub() {
|
|
1361
|
+
const hub = {
|
|
1362
|
+
subscribers: /* @__PURE__ */ new Map(),
|
|
1363
|
+
recentEnvelopes: /* @__PURE__ */ new Set()
|
|
1364
|
+
};
|
|
1365
|
+
hub.dispatch = (payload) => {
|
|
1366
|
+
if (!rememberEnvelope(hub, sessionSyncEnvelopeKey(payload))) return;
|
|
1367
|
+
for (const [token, reference] of hub.subscribers) {
|
|
1368
|
+
const subscriber = reference.deref();
|
|
1369
|
+
if (!subscriber) {
|
|
1370
|
+
hub.subscribers.delete(token);
|
|
1371
|
+
continue;
|
|
1372
|
+
}
|
|
1373
|
+
try {
|
|
1374
|
+
subscriber.notify(payload);
|
|
1375
|
+
} catch {
|
|
1376
|
+
}
|
|
1003
1377
|
}
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
let confirmationStarted = false;
|
|
1015
|
-
const cleanup = () => {
|
|
1016
|
-
context.signal?.removeEventListener("abort", onAbort);
|
|
1017
|
-
modal.form.removeEventListener("submit", onSubmit);
|
|
1018
|
-
modal.cancelButton.removeEventListener("click", onCancel);
|
|
1019
|
-
if (paymentElement.destroy) paymentElement.destroy();
|
|
1020
|
-
else paymentElement.unmount?.();
|
|
1021
|
-
modal.root.remove();
|
|
1022
|
-
modal.previousFocus?.focus();
|
|
1023
|
-
};
|
|
1024
|
-
const settle = (outcome) => {
|
|
1025
|
-
if (settled) return;
|
|
1026
|
-
settled = true;
|
|
1027
|
-
cleanup();
|
|
1028
|
-
resolve(outcome);
|
|
1029
|
-
};
|
|
1030
|
-
const onAbort = () => {
|
|
1031
|
-
if (confirmationStarted) {
|
|
1032
|
-
settle({ status: "submitted", reference: context.reference });
|
|
1033
|
-
return;
|
|
1034
|
-
}
|
|
1035
|
-
settle({ status: "cancelled" });
|
|
1036
|
-
};
|
|
1037
|
-
const onCancel = () => {
|
|
1038
|
-
if (!confirmationStarted) settle({ status: "cancelled" });
|
|
1039
|
-
};
|
|
1040
|
-
const onSubmit = async (event) => {
|
|
1041
|
-
event.preventDefault();
|
|
1042
|
-
if (submitting || settled) return;
|
|
1043
|
-
submitting = true;
|
|
1044
|
-
modal.submitButton.disabled = true;
|
|
1045
|
-
modal.error.textContent = "";
|
|
1046
|
-
try {
|
|
1047
|
-
const validation = elements.submit ? await elements.submit() : {};
|
|
1048
|
-
if (validation.error) {
|
|
1049
|
-
modal.error.textContent = validation.error.message || "Check your payment details.";
|
|
1050
|
-
submitting = false;
|
|
1051
|
-
modal.submitButton.disabled = false;
|
|
1052
|
-
return;
|
|
1053
|
-
}
|
|
1054
|
-
if (settled || context.signal?.aborted) {
|
|
1055
|
-
settle({ status: "cancelled" });
|
|
1056
|
-
return;
|
|
1057
|
-
}
|
|
1058
|
-
confirmationStarted = true;
|
|
1059
|
-
modal.cancelButton.disabled = true;
|
|
1060
|
-
const confirmation = await stripe.confirmPayment({
|
|
1061
|
-
elements,
|
|
1062
|
-
confirmParams: { return_url: context.returnUrl || window.location.href },
|
|
1063
|
-
redirect: "if_required"
|
|
1064
|
-
});
|
|
1065
|
-
if (settled) return;
|
|
1066
|
-
if (confirmation.error) {
|
|
1067
|
-
settle(stripeFailure(confirmation.error));
|
|
1068
|
-
return;
|
|
1069
|
-
}
|
|
1070
|
-
settle({
|
|
1071
|
-
status: "submitted",
|
|
1072
|
-
reference: confirmation.paymentIntent?.id || context.reference
|
|
1073
|
-
});
|
|
1074
|
-
} catch (error) {
|
|
1075
|
-
if (confirmationStarted) {
|
|
1076
|
-
settle({ status: "submitted", reference: context.reference });
|
|
1077
|
-
return;
|
|
1078
|
-
}
|
|
1079
|
-
settle(
|
|
1080
|
-
stripeFailure({
|
|
1081
|
-
message: error instanceof Error ? error.message : "Stripe confirmation failed."
|
|
1082
|
-
})
|
|
1083
|
-
);
|
|
1084
|
-
}
|
|
1085
|
-
};
|
|
1086
|
-
modal.form.addEventListener("submit", onSubmit);
|
|
1087
|
-
modal.cancelButton.addEventListener("click", onCancel);
|
|
1088
|
-
context.signal?.addEventListener("abort", onAbort, { once: true });
|
|
1089
|
-
paymentElement.on?.("escape", onCancel);
|
|
1090
|
-
paymentElement.on?.("loaderror", (event) => {
|
|
1091
|
-
if (confirmationStarted) return;
|
|
1092
|
-
settle({
|
|
1093
|
-
status: "failed",
|
|
1094
|
-
code: "PROVIDER_UNAVAILABLE",
|
|
1095
|
-
message: event?.error?.message || "Stripe Payment Element failed to load.",
|
|
1096
|
-
recoverable: true
|
|
1097
|
-
});
|
|
1098
|
-
});
|
|
1099
|
-
});
|
|
1100
|
-
}
|
|
1101
|
-
};
|
|
1102
|
-
|
|
1103
|
-
// src/payment-authorization/registry.ts
|
|
1104
|
-
function normalizeProvider(provider) {
|
|
1105
|
-
return typeof provider === "string" ? provider.trim().toLowerCase() : "";
|
|
1378
|
+
};
|
|
1379
|
+
hub.storageListener = (event) => {
|
|
1380
|
+
const storageEvent = event;
|
|
1381
|
+
if (storageEvent.key !== SESSION_SYNC_STORAGE_KEY) return;
|
|
1382
|
+
const payload = parseSessionSyncPayload(storageEvent.newValue);
|
|
1383
|
+
if (payload) hub.dispatch(payload);
|
|
1384
|
+
};
|
|
1385
|
+
window.addEventListener("storage", hub.storageListener);
|
|
1386
|
+
hubHost()[CROSS_TAB_SESSION_HUB_KEY] = hub;
|
|
1387
|
+
return hub;
|
|
1106
1388
|
}
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
const provider = normalizeProvider(adapter.provider);
|
|
1116
|
-
if (!provider) {
|
|
1117
|
-
throw new TypeError("Payment authorization adapter provider cannot be empty.");
|
|
1118
|
-
}
|
|
1119
|
-
this.adapters.set(provider, adapter);
|
|
1120
|
-
return this;
|
|
1121
|
-
}
|
|
1122
|
-
unregister(provider) {
|
|
1123
|
-
return this.adapters.delete(normalizeProvider(provider));
|
|
1124
|
-
}
|
|
1125
|
-
get(provider) {
|
|
1126
|
-
return this.adapters.get(normalizeProvider(provider));
|
|
1127
|
-
}
|
|
1128
|
-
supports(provider) {
|
|
1129
|
-
return this.adapters.has(normalizeProvider(provider));
|
|
1130
|
-
}
|
|
1131
|
-
providers() {
|
|
1132
|
-
return [...this.adapters.keys()];
|
|
1133
|
-
}
|
|
1134
|
-
async authorize(action, options) {
|
|
1135
|
-
const provider = normalizeProvider(action.provider);
|
|
1136
|
-
if (!provider) {
|
|
1137
|
-
return {
|
|
1138
|
-
status: "failed",
|
|
1139
|
-
code: "PROVIDER_UNAVAILABLE",
|
|
1140
|
-
message: "Card authorization response did not identify a payment provider.",
|
|
1141
|
-
recoverable: false
|
|
1142
|
-
};
|
|
1143
|
-
}
|
|
1144
|
-
const adapter = this.adapters.get(provider);
|
|
1145
|
-
if (!adapter) {
|
|
1146
|
-
return {
|
|
1147
|
-
status: "failed",
|
|
1148
|
-
code: "PROVIDER_UNAVAILABLE",
|
|
1149
|
-
message: `Card authorization provider '${provider}' is not supported by this SDK client.`,
|
|
1150
|
-
recoverable: false
|
|
1151
|
-
};
|
|
1152
|
-
}
|
|
1153
|
-
try {
|
|
1154
|
-
const normalizedAction = action.provider === provider ? action : { ...action, provider };
|
|
1155
|
-
return await adapter.authorize({ ...options, action: normalizedAction });
|
|
1156
|
-
} catch (error) {
|
|
1157
|
-
return {
|
|
1158
|
-
status: "failed",
|
|
1159
|
-
code: "PAYMENT_FAILED",
|
|
1160
|
-
message: error instanceof Error ? error.message : `Card authorization provider '${provider}' failed unexpectedly.`,
|
|
1161
|
-
recoverable: true
|
|
1162
|
-
};
|
|
1163
|
-
}
|
|
1389
|
+
function publishLocalSessionSync(payload) {
|
|
1390
|
+
if (typeof window === "undefined" || typeof window.addEventListener !== "function") return;
|
|
1391
|
+
(currentHub() ?? createHub()).dispatch(payload);
|
|
1392
|
+
}
|
|
1393
|
+
function subscribeCrossTabSession(notify) {
|
|
1394
|
+
if (typeof window === "undefined" || typeof window.addEventListener !== "function") {
|
|
1395
|
+
return () => {
|
|
1396
|
+
};
|
|
1164
1397
|
}
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1398
|
+
const hub = currentHub() ?? createHub();
|
|
1399
|
+
pruneSubscribers(hub);
|
|
1400
|
+
const token = /* @__PURE__ */ Symbol("cross-tab-session-subscriber");
|
|
1401
|
+
const subscriber = { notify };
|
|
1402
|
+
hub.subscribers.set(token, subscriberReference(subscriber));
|
|
1403
|
+
let unsubscribed = false;
|
|
1404
|
+
return () => {
|
|
1405
|
+
if (unsubscribed) return;
|
|
1406
|
+
unsubscribed = true;
|
|
1407
|
+
subscriber.notify = () => {
|
|
1408
|
+
};
|
|
1409
|
+
hub.subscribers.delete(token);
|
|
1410
|
+
pruneSubscribers(hub);
|
|
1411
|
+
if (hub.subscribers.size !== 0) return;
|
|
1412
|
+
window.removeEventListener("storage", hub.storageListener);
|
|
1413
|
+
if (currentHub() === hub) delete hubHost()[CROSS_TAB_SESSION_HUB_KEY];
|
|
1414
|
+
};
|
|
1171
1415
|
}
|
|
1172
1416
|
|
|
1173
1417
|
// src/client.ts
|
|
1174
1418
|
var SESSION_TOKEN_STORAGE_KEY = "cimplify_session_token";
|
|
1175
|
-
var SESSION_SYNC_STORAGE_KEY = "cimplify_session_sync";
|
|
1176
1419
|
var ORDER_TOKEN_PREFIX = "cimplify_ot_";
|
|
1177
1420
|
var SESSION_TOKEN_HEADER = "x-session-token";
|
|
1178
1421
|
var LOCATION_ID_QUERY_PARAMETER = "location_id";
|
|
@@ -1180,8 +1423,37 @@ var ORDER_TOKEN_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
|
1180
1423
|
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
1181
1424
|
var DEFAULT_MAX_RETRIES = 3;
|
|
1182
1425
|
var DEFAULT_RETRY_DELAY_MS = 1e3;
|
|
1183
|
-
|
|
1184
|
-
|
|
1426
|
+
var MAX_RETRY_AFTER_MS = 3e4;
|
|
1427
|
+
function sleep(ms, signal) {
|
|
1428
|
+
if (signal?.aborted) {
|
|
1429
|
+
return Promise.reject(new chunkSFUJMS5E_js.CimplifyError(chunkSFUJMS5E_js.ErrorCode.CANCELLED, "Request was cancelled.", true));
|
|
1430
|
+
}
|
|
1431
|
+
return new Promise((resolve, reject) => {
|
|
1432
|
+
const timer = setTimeout(() => {
|
|
1433
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1434
|
+
resolve();
|
|
1435
|
+
}, ms);
|
|
1436
|
+
const onAbort = () => {
|
|
1437
|
+
clearTimeout(timer);
|
|
1438
|
+
reject(new chunkSFUJMS5E_js.CimplifyError(chunkSFUJMS5E_js.ErrorCode.CANCELLED, "Request was cancelled.", true));
|
|
1439
|
+
};
|
|
1440
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1441
|
+
});
|
|
1442
|
+
}
|
|
1443
|
+
function parseRetryAfterMs(value) {
|
|
1444
|
+
if (!value) return null;
|
|
1445
|
+
const seconds = Number(value);
|
|
1446
|
+
if (Number.isFinite(seconds) && seconds >= 0) {
|
|
1447
|
+
return Math.min(seconds * 1e3, MAX_RETRY_AFTER_MS);
|
|
1448
|
+
}
|
|
1449
|
+
const retryAt = Date.parse(value);
|
|
1450
|
+
if (!Number.isFinite(retryAt)) return null;
|
|
1451
|
+
return Math.min(Math.max(0, retryAt - Date.now()), MAX_RETRY_AFTER_MS);
|
|
1452
|
+
}
|
|
1453
|
+
function withPositiveJitter(delayMs, jitterRatio = 0) {
|
|
1454
|
+
const boundedRatio = Math.min(Math.max(jitterRatio, 0), 1);
|
|
1455
|
+
if (boundedRatio === 0 || delayMs === 0) return delayMs;
|
|
1456
|
+
return Math.round(delayMs + delayMs * boundedRatio * Math.random());
|
|
1185
1457
|
}
|
|
1186
1458
|
function nextInit(cacheOptions) {
|
|
1187
1459
|
if (!cacheOptions) return {};
|
|
@@ -1221,16 +1493,16 @@ function isRetryable(error) {
|
|
|
1221
1493
|
if (error instanceof DOMException && error.name === "AbortError") {
|
|
1222
1494
|
return true;
|
|
1223
1495
|
}
|
|
1224
|
-
if (error instanceof
|
|
1496
|
+
if (error instanceof chunkSFUJMS5E_js.CimplifyError) {
|
|
1225
1497
|
return error.retryable;
|
|
1226
1498
|
}
|
|
1227
1499
|
return false;
|
|
1228
1500
|
}
|
|
1229
1501
|
function toNetworkError(error, isTestMode) {
|
|
1230
1502
|
if (error instanceof DOMException && error.name === "AbortError") {
|
|
1231
|
-
return
|
|
1232
|
-
new
|
|
1233
|
-
|
|
1503
|
+
return chunkSFUJMS5E_js.enrichError(
|
|
1504
|
+
new chunkSFUJMS5E_js.CimplifyError(
|
|
1505
|
+
chunkSFUJMS5E_js.ErrorCode.TIMEOUT,
|
|
1234
1506
|
"Request timed out. Please check your connection and try again.",
|
|
1235
1507
|
true
|
|
1236
1508
|
),
|
|
@@ -1238,21 +1510,21 @@ function toNetworkError(error, isTestMode) {
|
|
|
1238
1510
|
);
|
|
1239
1511
|
}
|
|
1240
1512
|
if (error instanceof TypeError && error.message.includes("fetch")) {
|
|
1241
|
-
return
|
|
1242
|
-
new
|
|
1243
|
-
|
|
1513
|
+
return chunkSFUJMS5E_js.enrichError(
|
|
1514
|
+
new chunkSFUJMS5E_js.CimplifyError(
|
|
1515
|
+
chunkSFUJMS5E_js.ErrorCode.NETWORK_ERROR,
|
|
1244
1516
|
"Network error. Please check your internet connection.",
|
|
1245
1517
|
true
|
|
1246
1518
|
),
|
|
1247
1519
|
{ isTestMode }
|
|
1248
1520
|
);
|
|
1249
1521
|
}
|
|
1250
|
-
if (error instanceof
|
|
1251
|
-
return
|
|
1522
|
+
if (error instanceof chunkSFUJMS5E_js.CimplifyError) {
|
|
1523
|
+
return chunkSFUJMS5E_js.enrichError(error, { isTestMode });
|
|
1252
1524
|
}
|
|
1253
|
-
return
|
|
1254
|
-
new
|
|
1255
|
-
|
|
1525
|
+
return chunkSFUJMS5E_js.enrichError(
|
|
1526
|
+
new chunkSFUJMS5E_js.CimplifyError(
|
|
1527
|
+
chunkSFUJMS5E_js.ErrorCode.UNKNOWN_ERROR,
|
|
1256
1528
|
error instanceof Error ? error.message : "An unknown error occurred",
|
|
1257
1529
|
false
|
|
1258
1530
|
),
|
|
@@ -1295,6 +1567,9 @@ var CimplifyClient = class {
|
|
|
1295
1567
|
this.businessId = null;
|
|
1296
1568
|
this.businessIdResolvePromise = null;
|
|
1297
1569
|
this.publicKeyEpoch = 0;
|
|
1570
|
+
this.stopCrossTabSessionWatch = null;
|
|
1571
|
+
this.latestSessionSyncAt = Number.NEGATIVE_INFINITY;
|
|
1572
|
+
this.latestSessionSyncOrderKey = null;
|
|
1298
1573
|
this.inflightRequests = /* @__PURE__ */ new Map();
|
|
1299
1574
|
/**
|
|
1300
1575
|
* App-level event channel. Subscribe to surface mutation rollbacks, outbox
|
|
@@ -1313,10 +1588,14 @@ var CimplifyClient = class {
|
|
|
1313
1588
|
this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
1314
1589
|
this.retryDelay = config.retryDelay ?? DEFAULT_RETRY_DELAY_MS;
|
|
1315
1590
|
this.hooks = config.hooks ?? {};
|
|
1316
|
-
this.authorizationAdapters = config.authorizationAdapterRegistry ?? createDefaultPaymentAuthorizationAdapterRegistry();
|
|
1591
|
+
this.authorizationAdapters = config.authorizationAdapterRegistry ?? chunkDQEXY542_js.createDefaultPaymentAuthorizationAdapterRegistry();
|
|
1317
1592
|
this.fetchImpl = (config.fetch ?? globalThis.fetch).bind(globalThis);
|
|
1318
1593
|
this.sessionToken = this.loadSessionToken() ?? this.mintSessionToken();
|
|
1319
|
-
this.
|
|
1594
|
+
const persistedSessionSync = this.loadSessionSyncPayload();
|
|
1595
|
+
if (persistedSessionSync) this.observeSessionSync(persistedSessionSync);
|
|
1596
|
+
this.stopCrossTabSessionWatch = subscribeCrossTabSession((payload) => {
|
|
1597
|
+
this.applyCrossTabSession(payload);
|
|
1598
|
+
});
|
|
1320
1599
|
if (!this.publicKey && !config.suppressPublicKeyWarning) {
|
|
1321
1600
|
console.warn(
|
|
1322
1601
|
'[Cimplify] No public key found. Set NEXT_PUBLIC_CIMPLIFY_PUBLIC_KEY in your environment, or pass { publicKey: "cpk_..." } to createCimplifyClient().'
|
|
@@ -1343,6 +1622,12 @@ var CimplifyClient = class {
|
|
|
1343
1622
|
this.stopPageViewTracking?.();
|
|
1344
1623
|
this.stopPageViewTracking = null;
|
|
1345
1624
|
}
|
|
1625
|
+
/** Release window-level subscriptions and automatic tracking owned by this client. */
|
|
1626
|
+
destroy() {
|
|
1627
|
+
this.stopTrackingPageViews();
|
|
1628
|
+
this.stopCrossTabSessionWatch?.();
|
|
1629
|
+
this.stopCrossTabSessionWatch = null;
|
|
1630
|
+
}
|
|
1346
1631
|
getAccessToken() {
|
|
1347
1632
|
return this.accessToken;
|
|
1348
1633
|
}
|
|
@@ -1370,7 +1655,7 @@ var CimplifyClient = class {
|
|
|
1370
1655
|
this.hooks.onSessionChange?.({
|
|
1371
1656
|
previousToken: previous,
|
|
1372
1657
|
newToken: token,
|
|
1373
|
-
source:
|
|
1658
|
+
source: SESSION_CHANGE_SOURCE.MANUAL
|
|
1374
1659
|
});
|
|
1375
1660
|
this.events.emit(AUTH_TOKEN_CHANGED, { token });
|
|
1376
1661
|
}
|
|
@@ -1379,7 +1664,8 @@ var CimplifyClient = class {
|
|
|
1379
1664
|
* refresh restore) binds the anonymous session so the conversation, cart
|
|
1380
1665
|
* and orders survive a cleared browser. Best-effort by design — a failed
|
|
1381
1666
|
* claim leaves the session anonymous, exactly as it was. Called by the
|
|
1382
|
-
* auth flows, never by the raw token setter.
|
|
1667
|
+
* merchant auth flows, never by the raw token setter. Global Link clients
|
|
1668
|
+
* have no storefront tenant and therefore activate without this step.
|
|
1383
1669
|
*/
|
|
1384
1670
|
autoClaimChatIdentity() {
|
|
1385
1671
|
if (typeof window === "undefined" || !this.sessionToken || !this.accessToken) return;
|
|
@@ -1413,7 +1699,7 @@ var CimplifyClient = class {
|
|
|
1413
1699
|
this.hooks.onSessionChange?.({
|
|
1414
1700
|
previousToken: previous,
|
|
1415
1701
|
newToken: null,
|
|
1416
|
-
source:
|
|
1702
|
+
source: SESSION_CHANGE_SOURCE.CLEAR
|
|
1417
1703
|
});
|
|
1418
1704
|
this.events.emit(AUTH_TOKEN_CHANGED, { token: null });
|
|
1419
1705
|
this.events.emit(SESSION_CLEARED, {});
|
|
@@ -1421,9 +1707,29 @@ var CimplifyClient = class {
|
|
|
1421
1707
|
getSessionEpoch() {
|
|
1422
1708
|
return this.sessionEpoch;
|
|
1423
1709
|
}
|
|
1710
|
+
/** Monotonic fence for async work scoped to the current storefront tenant. */
|
|
1711
|
+
getPublicKeyEpoch() {
|
|
1712
|
+
return this.publicKeyEpoch;
|
|
1713
|
+
}
|
|
1424
1714
|
getSessionToken() {
|
|
1425
1715
|
return this.sessionToken;
|
|
1426
1716
|
}
|
|
1717
|
+
/** Whether the current transport session is known to have been claimed for
|
|
1718
|
+
* a Link identity, even when its in-memory bearer is absent after reload or
|
|
1719
|
+
* a failed refresh. The sync marker is written only after canonical claim
|
|
1720
|
+
* rotation and is replaced with `cleared` when a fresh session is minted. */
|
|
1721
|
+
hasClaimedSessionAuthority() {
|
|
1722
|
+
if (!this.sessionToken) return false;
|
|
1723
|
+
try {
|
|
1724
|
+
const payload = parseSessionSyncPayload(
|
|
1725
|
+
this.safeStorage()?.getItem(SESSION_SYNC_STORAGE_KEY) ?? null
|
|
1726
|
+
);
|
|
1727
|
+
if (!payload) return false;
|
|
1728
|
+
return payload.type === "claimed" && payload.token === this.sessionToken;
|
|
1729
|
+
} catch {
|
|
1730
|
+
return false;
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1427
1733
|
publishSessionClaimed(customerName) {
|
|
1428
1734
|
if (!this.sessionToken) return;
|
|
1429
1735
|
this.broadcastSessionSync({
|
|
@@ -1433,41 +1739,49 @@ var CimplifyClient = class {
|
|
|
1433
1739
|
});
|
|
1434
1740
|
}
|
|
1435
1741
|
broadcastSessionSync(payload) {
|
|
1742
|
+
const envelope = {
|
|
1743
|
+
...payload,
|
|
1744
|
+
at: this.nextSessionSyncTimestamp()
|
|
1745
|
+
};
|
|
1436
1746
|
try {
|
|
1437
|
-
this.safeStorage()?.setItem(
|
|
1438
|
-
SESSION_SYNC_STORAGE_KEY,
|
|
1439
|
-
JSON.stringify({ ...payload, at: Date.now() })
|
|
1440
|
-
);
|
|
1747
|
+
this.safeStorage()?.setItem(SESSION_SYNC_STORAGE_KEY, JSON.stringify(envelope));
|
|
1441
1748
|
} catch {
|
|
1442
1749
|
}
|
|
1750
|
+
publishLocalSessionSync(envelope);
|
|
1443
1751
|
}
|
|
1444
|
-
|
|
1445
|
-
if (
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1752
|
+
applyCrossTabSession(payload) {
|
|
1753
|
+
if (!this.observeSessionSync(payload)) return;
|
|
1754
|
+
if (payload.token === this.sessionToken) return;
|
|
1755
|
+
if (payload.type === "claimed") {
|
|
1756
|
+
this.sessionToken = payload.token;
|
|
1757
|
+
this.sessionEpoch++;
|
|
1758
|
+
this.events.emit(SESSION_CLAIMED, {
|
|
1759
|
+
sessionToken: payload.token,
|
|
1760
|
+
threadId: null,
|
|
1761
|
+
customerName: payload.name ?? ""
|
|
1762
|
+
});
|
|
1763
|
+
} else {
|
|
1764
|
+
this.accessToken = null;
|
|
1765
|
+
this._activity?.resetSession();
|
|
1766
|
+
this.sessionToken = payload.token;
|
|
1767
|
+
this.sessionEpoch++;
|
|
1768
|
+
this.events.emit(AUTH_TOKEN_CHANGED, { token: null });
|
|
1769
|
+
this.events.emit(SESSION_CLEARED, {});
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
observeSessionSync(payload) {
|
|
1773
|
+
if (payload.at < this.latestSessionSyncAt) return false;
|
|
1774
|
+
const envelope = sessionSyncEnvelopeKey(payload);
|
|
1775
|
+
if (payload.at === this.latestSessionSyncAt && this.latestSessionSyncOrderKey !== null && envelope <= this.latestSessionSyncOrderKey) {
|
|
1776
|
+
return false;
|
|
1777
|
+
}
|
|
1778
|
+
this.latestSessionSyncAt = payload.at;
|
|
1779
|
+
this.latestSessionSyncOrderKey = envelope;
|
|
1780
|
+
return true;
|
|
1781
|
+
}
|
|
1782
|
+
nextSessionSyncTimestamp() {
|
|
1783
|
+
const now = Date.now();
|
|
1784
|
+
return Number.isFinite(this.latestSessionSyncAt) ? Math.max(now, this.latestSessionSyncAt + 1) : now;
|
|
1471
1785
|
}
|
|
1472
1786
|
setOrderToken(orderId, token) {
|
|
1473
1787
|
try {
|
|
@@ -1531,17 +1845,24 @@ var CimplifyClient = class {
|
|
|
1531
1845
|
setPublicKey(publicKey) {
|
|
1532
1846
|
const normalized = publicKey.trim();
|
|
1533
1847
|
if (normalized === this.publicKey) return;
|
|
1848
|
+
const restartPageViewTracking = this.stopPageViewTracking !== null;
|
|
1849
|
+
if (restartPageViewTracking) {
|
|
1850
|
+
this.stopTrackingPageViews();
|
|
1851
|
+
}
|
|
1534
1852
|
this.publicKey = normalized;
|
|
1535
1853
|
this.publicKeyEpoch += 1;
|
|
1536
1854
|
this.businessId = null;
|
|
1537
1855
|
this.businessIdResolvePromise = null;
|
|
1538
1856
|
this.inflightRequests.clear();
|
|
1539
|
-
this._activity?.
|
|
1857
|
+
this._activity?.resetForStorefrontIdentityChange();
|
|
1540
1858
|
delete this.context.location_id;
|
|
1541
1859
|
if (this.accessToken !== null) {
|
|
1542
1860
|
this.setAccessToken(null);
|
|
1543
1861
|
}
|
|
1544
1862
|
this.events.emit(SESSION_CLEARED, {});
|
|
1863
|
+
if (restartPageViewTracking) {
|
|
1864
|
+
this.stopPageViewTracking = this.activity.autoTrackPageViews();
|
|
1865
|
+
}
|
|
1545
1866
|
}
|
|
1546
1867
|
getBusinessId() {
|
|
1547
1868
|
return this.businessId;
|
|
@@ -1557,15 +1878,15 @@ var CimplifyClient = class {
|
|
|
1557
1878
|
const resolution = (async () => {
|
|
1558
1879
|
const result = await this.business.getInfo();
|
|
1559
1880
|
if (!result.ok || !result.value?.id) {
|
|
1560
|
-
throw new
|
|
1561
|
-
|
|
1881
|
+
throw new chunkSFUJMS5E_js.CimplifyError(
|
|
1882
|
+
chunkSFUJMS5E_js.ErrorCode.NOT_FOUND,
|
|
1562
1883
|
"Unable to resolve business ID from the current public key.",
|
|
1563
1884
|
true
|
|
1564
1885
|
);
|
|
1565
1886
|
}
|
|
1566
1887
|
if (this.publicKeyEpoch !== publicKeyEpoch) {
|
|
1567
|
-
throw new
|
|
1568
|
-
|
|
1888
|
+
throw new chunkSFUJMS5E_js.CimplifyError(
|
|
1889
|
+
chunkSFUJMS5E_js.ErrorCode.TENANT_CONTEXT_CHANGED,
|
|
1569
1890
|
"The storefront tenant changed while resolving its business identity.",
|
|
1570
1891
|
true
|
|
1571
1892
|
);
|
|
@@ -1582,8 +1903,8 @@ var CimplifyClient = class {
|
|
|
1582
1903
|
}
|
|
1583
1904
|
}
|
|
1584
1905
|
}
|
|
1585
|
-
async authedFetch(url, options, timeoutMs) {
|
|
1586
|
-
return this.resilientFetch(url, options, timeoutMs);
|
|
1906
|
+
async authedFetch(url, options, timeoutMs, retryPolicy, signal) {
|
|
1907
|
+
return this.resilientFetch(url, options, timeoutMs, retryPolicy, signal);
|
|
1587
1908
|
}
|
|
1588
1909
|
// Storage guards: even the `window.localStorage` property access throws in
|
|
1589
1910
|
// some sandboxed iframes and strict private-browsing modes. These run in the
|
|
@@ -1605,6 +1926,13 @@ var CimplifyClient = class {
|
|
|
1605
1926
|
return null;
|
|
1606
1927
|
}
|
|
1607
1928
|
}
|
|
1929
|
+
loadSessionSyncPayload() {
|
|
1930
|
+
try {
|
|
1931
|
+
return parseSessionSyncPayload(this.safeStorage()?.getItem(SESSION_SYNC_STORAGE_KEY) ?? null);
|
|
1932
|
+
} catch {
|
|
1933
|
+
return null;
|
|
1934
|
+
}
|
|
1935
|
+
}
|
|
1608
1936
|
/**
|
|
1609
1937
|
* Mint the session token client-side so the very first request already
|
|
1610
1938
|
* carries it. Without this, parallel first-load requests each get a
|
|
@@ -1660,7 +1988,7 @@ var CimplifyClient = class {
|
|
|
1660
1988
|
}
|
|
1661
1989
|
return headers;
|
|
1662
1990
|
}
|
|
1663
|
-
async resilientFetch(url, options, timeoutMs) {
|
|
1991
|
+
async resilientFetch(url, options, timeoutMs, retryPolicy, signal) {
|
|
1664
1992
|
const method = options.method || "GET";
|
|
1665
1993
|
const path = url.replace(this.baseUrl, "").replace(this.linkApiUrl, "");
|
|
1666
1994
|
const startTime = Date.now();
|
|
@@ -1674,15 +2002,31 @@ var CimplifyClient = class {
|
|
|
1674
2002
|
};
|
|
1675
2003
|
this.hooks.onRequestStart?.(context);
|
|
1676
2004
|
let lastError;
|
|
1677
|
-
|
|
2005
|
+
const configuredMaxRetries = Math.max(0, Math.floor(this.maxRetries));
|
|
2006
|
+
const maxRetries = Math.min(
|
|
2007
|
+
configuredMaxRetries,
|
|
2008
|
+
Math.max(0, Math.floor(retryPolicy?.maxRetries ?? configuredMaxRetries))
|
|
2009
|
+
);
|
|
2010
|
+
const jitterRatio = retryPolicy?.jitterRatio ?? 0;
|
|
2011
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
1678
2012
|
try {
|
|
2013
|
+
if (signal?.aborted) {
|
|
2014
|
+
throw new chunkSFUJMS5E_js.CimplifyError(chunkSFUJMS5E_js.ErrorCode.CANCELLED, "Request was cancelled.", true);
|
|
2015
|
+
}
|
|
1679
2016
|
const controller = new AbortController();
|
|
1680
2017
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs ?? this.timeout);
|
|
1681
|
-
const
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
2018
|
+
const onAbort = () => controller.abort();
|
|
2019
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
2020
|
+
let response;
|
|
2021
|
+
try {
|
|
2022
|
+
response = await this.fetchImpl(url, {
|
|
2023
|
+
...options,
|
|
2024
|
+
signal: controller.signal
|
|
2025
|
+
});
|
|
2026
|
+
} finally {
|
|
2027
|
+
clearTimeout(timeoutId);
|
|
2028
|
+
signal?.removeEventListener("abort", onAbort);
|
|
2029
|
+
}
|
|
1686
2030
|
this.captureSessionToken(response);
|
|
1687
2031
|
if (response.ok) {
|
|
1688
2032
|
this.hooks.onRequestSuccess?.({
|
|
@@ -1692,23 +2036,25 @@ var CimplifyClient = class {
|
|
|
1692
2036
|
});
|
|
1693
2037
|
return response;
|
|
1694
2038
|
}
|
|
1695
|
-
if ((response.status === 429 || response.status === 503) && attempt <
|
|
2039
|
+
if ((response.status === 429 || response.status === 503) && attempt < maxRetries) {
|
|
1696
2040
|
retryCount++;
|
|
1697
|
-
const
|
|
1698
|
-
const
|
|
2041
|
+
const retryAfterMs = parseRetryAfterMs(response.headers.get("Retry-After"));
|
|
2042
|
+
const retryBaseMs = retryAfterMs ?? this.retryDelay * Math.pow(2, attempt);
|
|
2043
|
+
const jitteredDelayMs = withPositiveJitter(retryBaseMs, jitterRatio);
|
|
2044
|
+
const delay = retryAfterMs === null ? jitteredDelayMs : Math.min(jitteredDelayMs, MAX_RETRY_AFTER_MS);
|
|
1699
2045
|
this.hooks.onRetry?.({
|
|
1700
2046
|
...context,
|
|
1701
2047
|
attempt: retryCount,
|
|
1702
2048
|
delayMs: delay,
|
|
1703
2049
|
error: new Error(`Retryable: ${response.status}`)
|
|
1704
2050
|
});
|
|
1705
|
-
await sleep(delay);
|
|
2051
|
+
await sleep(delay, signal);
|
|
1706
2052
|
continue;
|
|
1707
2053
|
}
|
|
1708
2054
|
if (response.status >= 400 && response.status < 500) {
|
|
1709
2055
|
this.hooks.onRequestError?.({
|
|
1710
2056
|
...context,
|
|
1711
|
-
error: new
|
|
2057
|
+
error: new chunkSFUJMS5E_js.CimplifyError(
|
|
1712
2058
|
`HTTP_${response.status}`,
|
|
1713
2059
|
`Request failed with status ${response.status}`,
|
|
1714
2060
|
false
|
|
@@ -1720,22 +2066,22 @@ var CimplifyClient = class {
|
|
|
1720
2066
|
});
|
|
1721
2067
|
return response;
|
|
1722
2068
|
}
|
|
1723
|
-
if (response.status >= 500 && attempt <
|
|
2069
|
+
if (response.status >= 500 && attempt < maxRetries) {
|
|
1724
2070
|
retryCount++;
|
|
1725
|
-
const delay = this.retryDelay * Math.pow(2, attempt);
|
|
2071
|
+
const delay = withPositiveJitter(this.retryDelay * Math.pow(2, attempt), jitterRatio);
|
|
1726
2072
|
this.hooks.onRetry?.({
|
|
1727
2073
|
...context,
|
|
1728
2074
|
attempt: retryCount,
|
|
1729
2075
|
delayMs: delay,
|
|
1730
2076
|
error: new Error(`Server error: ${response.status}`)
|
|
1731
2077
|
});
|
|
1732
|
-
await sleep(delay);
|
|
2078
|
+
await sleep(delay, signal);
|
|
1733
2079
|
continue;
|
|
1734
2080
|
}
|
|
1735
2081
|
this.hooks.onRequestError?.({
|
|
1736
2082
|
...context,
|
|
1737
|
-
error: new
|
|
1738
|
-
|
|
2083
|
+
error: new chunkSFUJMS5E_js.CimplifyError(
|
|
2084
|
+
chunkSFUJMS5E_js.ErrorCode.SERVER_ERROR,
|
|
1739
2085
|
`Server error ${response.status} after ${retryCount} retries`,
|
|
1740
2086
|
false
|
|
1741
2087
|
),
|
|
@@ -1747,9 +2093,12 @@ var CimplifyClient = class {
|
|
|
1747
2093
|
return response;
|
|
1748
2094
|
} catch (error) {
|
|
1749
2095
|
lastError = error;
|
|
2096
|
+
if (signal?.aborted) {
|
|
2097
|
+
throw new chunkSFUJMS5E_js.CimplifyError(chunkSFUJMS5E_js.ErrorCode.CANCELLED, "Request was cancelled.", true);
|
|
2098
|
+
}
|
|
1750
2099
|
const networkError = toNetworkError(error, this.isTestMode());
|
|
1751
2100
|
const errorRetryable = isRetryable(error);
|
|
1752
|
-
if (!errorRetryable || attempt >=
|
|
2101
|
+
if (!errorRetryable || attempt >= maxRetries) {
|
|
1753
2102
|
this.hooks.onRequestError?.({
|
|
1754
2103
|
...context,
|
|
1755
2104
|
error: networkError,
|
|
@@ -1760,14 +2109,14 @@ var CimplifyClient = class {
|
|
|
1760
2109
|
throw networkError;
|
|
1761
2110
|
}
|
|
1762
2111
|
retryCount++;
|
|
1763
|
-
const delay = this.retryDelay * Math.pow(2, attempt);
|
|
2112
|
+
const delay = withPositiveJitter(this.retryDelay * Math.pow(2, attempt), jitterRatio);
|
|
1764
2113
|
this.hooks.onRetry?.({
|
|
1765
2114
|
...context,
|
|
1766
2115
|
attempt: retryCount,
|
|
1767
2116
|
delayMs: delay,
|
|
1768
2117
|
error: networkError
|
|
1769
2118
|
});
|
|
1770
|
-
await sleep(delay);
|
|
2119
|
+
await sleep(delay, signal);
|
|
1771
2120
|
}
|
|
1772
2121
|
}
|
|
1773
2122
|
const finalError = toNetworkError(lastError, this.isTestMode());
|
|
@@ -1804,16 +2153,24 @@ var CimplifyClient = class {
|
|
|
1804
2153
|
return request;
|
|
1805
2154
|
}
|
|
1806
2155
|
async get(path, opts) {
|
|
1807
|
-
const
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
2156
|
+
const request = async () => {
|
|
2157
|
+
const response = await this.authedFetch(
|
|
2158
|
+
this.buildUrl(this.baseUrl, path),
|
|
2159
|
+
{
|
|
2160
|
+
method: "GET",
|
|
2161
|
+
credentials: this.credentials,
|
|
2162
|
+
headers: this.getHeaders(),
|
|
2163
|
+
...nextInit(opts?.cacheOptions)
|
|
2164
|
+
},
|
|
2165
|
+
opts?.timeoutMs,
|
|
2166
|
+
opts?.retryPolicy,
|
|
2167
|
+
opts?.signal
|
|
2168
|
+
);
|
|
1815
2169
|
return this.handleRestResponse(response);
|
|
1816
|
-
}
|
|
2170
|
+
};
|
|
2171
|
+
if (opts?.signal) return request();
|
|
2172
|
+
const key = this.getDedupeKey("get", path, opts?.cacheOptions);
|
|
2173
|
+
return this.deduplicatedRequest(key, request);
|
|
1817
2174
|
}
|
|
1818
2175
|
async post(path, body, opts) {
|
|
1819
2176
|
const response = await this.authedFetch(
|
|
@@ -1824,7 +2181,9 @@ var CimplifyClient = class {
|
|
|
1824
2181
|
headers: mergeOptionHeaders(this.getHeaders(), opts),
|
|
1825
2182
|
body: body ? JSON.stringify(body) : void 0
|
|
1826
2183
|
},
|
|
1827
|
-
opts?.timeoutMs
|
|
2184
|
+
opts?.timeoutMs,
|
|
2185
|
+
opts?.retryPolicy,
|
|
2186
|
+
opts?.signal
|
|
1828
2187
|
);
|
|
1829
2188
|
return this.handleRestResponse(response);
|
|
1830
2189
|
}
|
|
@@ -1837,7 +2196,9 @@ var CimplifyClient = class {
|
|
|
1837
2196
|
headers: mergeOptionHeaders(this.getHeaders(), opts),
|
|
1838
2197
|
body: body ? JSON.stringify(body) : void 0
|
|
1839
2198
|
},
|
|
1840
|
-
opts?.timeoutMs
|
|
2199
|
+
opts?.timeoutMs,
|
|
2200
|
+
opts?.retryPolicy,
|
|
2201
|
+
opts?.signal
|
|
1841
2202
|
);
|
|
1842
2203
|
return this.handleRestResponse(response);
|
|
1843
2204
|
}
|
|
@@ -1849,21 +2210,31 @@ var CimplifyClient = class {
|
|
|
1849
2210
|
credentials: this.credentials,
|
|
1850
2211
|
headers: mergeOptionHeaders(this.getHeaders(), opts)
|
|
1851
2212
|
},
|
|
1852
|
-
opts?.timeoutMs
|
|
2213
|
+
opts?.timeoutMs,
|
|
2214
|
+
opts?.retryPolicy,
|
|
2215
|
+
opts?.signal
|
|
1853
2216
|
);
|
|
1854
2217
|
return this.handleRestResponse(response);
|
|
1855
2218
|
}
|
|
1856
2219
|
async linkGet(path, opts) {
|
|
1857
|
-
const
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
2220
|
+
const request = async () => {
|
|
2221
|
+
const response = await this.authedFetch(
|
|
2222
|
+
`${this.linkApiUrl}${path}`,
|
|
2223
|
+
{
|
|
2224
|
+
method: "GET",
|
|
2225
|
+
credentials: this.credentials,
|
|
2226
|
+
headers: this.getHeaders(),
|
|
2227
|
+
...nextInit(opts?.cacheOptions)
|
|
2228
|
+
},
|
|
2229
|
+
opts?.timeoutMs,
|
|
2230
|
+
opts?.retryPolicy,
|
|
2231
|
+
opts?.signal
|
|
2232
|
+
);
|
|
1865
2233
|
return this.handleRestResponse(response);
|
|
1866
|
-
}
|
|
2234
|
+
};
|
|
2235
|
+
if (opts?.signal) return request();
|
|
2236
|
+
const key = this.getDedupeKey("linkGet", path, opts?.cacheOptions);
|
|
2237
|
+
return this.deduplicatedRequest(key, request);
|
|
1867
2238
|
}
|
|
1868
2239
|
async linkPost(path, body, opts) {
|
|
1869
2240
|
const response = await this.authedFetch(
|
|
@@ -1874,32 +2245,40 @@ var CimplifyClient = class {
|
|
|
1874
2245
|
headers: mergeOptionHeaders(this.getHeaders(), opts),
|
|
1875
2246
|
body: body ? JSON.stringify(body) : void 0
|
|
1876
2247
|
},
|
|
1877
|
-
opts?.timeoutMs
|
|
2248
|
+
opts?.timeoutMs,
|
|
2249
|
+
opts?.retryPolicy,
|
|
2250
|
+
opts?.signal
|
|
1878
2251
|
);
|
|
1879
2252
|
return this.handleRestResponse(response);
|
|
1880
2253
|
}
|
|
1881
2254
|
async linkDelete(path, opts) {
|
|
1882
|
-
const response = await this.authedFetch(
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
2255
|
+
const response = await this.authedFetch(
|
|
2256
|
+
`${this.linkApiUrl}${path}`,
|
|
2257
|
+
{
|
|
2258
|
+
method: "DELETE",
|
|
2259
|
+
credentials: this.credentials,
|
|
2260
|
+
headers: mergeOptionHeaders(this.getHeaders(), opts)
|
|
2261
|
+
},
|
|
2262
|
+
opts?.timeoutMs,
|
|
2263
|
+
opts?.retryPolicy,
|
|
2264
|
+
opts?.signal
|
|
2265
|
+
);
|
|
1887
2266
|
return this.handleRestResponse(response);
|
|
1888
2267
|
}
|
|
1889
2268
|
async handleRestResponse(response) {
|
|
1890
2269
|
const json = await response.json();
|
|
1891
2270
|
if (!response.ok) {
|
|
1892
|
-
const errorCode = typeof json.error === "object" && json.error?.error_code || typeof json.error === "object" && json.error?.code ||
|
|
2271
|
+
const errorCode = typeof json.error === "object" && json.error?.error_code || typeof json.error === "object" && json.error?.code || chunkSFUJMS5E_js.ErrorCode.API_ERROR;
|
|
1893
2272
|
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";
|
|
1894
2273
|
const retryable = typeof json.error === "object" && typeof json.error?.retryable === "boolean" ? json.error.retryable : false;
|
|
1895
|
-
if (errorCode ===
|
|
2274
|
+
if (errorCode === chunkSFUJMS5E_js.ErrorCode.IDEMPOTENCY_MISMATCH) {
|
|
1896
2275
|
const originalCreatedAt = typeof json.error === "object" && json.error?.original_created_at || "";
|
|
1897
|
-
throw new
|
|
2276
|
+
throw new chunkSFUJMS5E_js.IdempotencyMismatchError(errorMessage, originalCreatedAt);
|
|
1898
2277
|
}
|
|
1899
|
-
const error =
|
|
2278
|
+
const error = chunkSFUJMS5E_js.enrichError(new chunkSFUJMS5E_js.CimplifyError(errorCode, errorMessage, retryable), {
|
|
1900
2279
|
isTestMode: this.isTestMode()
|
|
1901
2280
|
});
|
|
1902
|
-
if ((response.status === 401 || error.code ===
|
|
2281
|
+
if ((response.status === 401 || error.code === chunkSFUJMS5E_js.ErrorCode.UNAUTHORIZED) && this.accessToken) {
|
|
1903
2282
|
console.warn(
|
|
1904
2283
|
"[Cimplify] Received 401 Unauthorized. Access token may be missing/expired. Refresh authentication and retry the request."
|
|
1905
2284
|
);
|
|
@@ -1907,9 +2286,9 @@ var CimplifyClient = class {
|
|
|
1907
2286
|
throw error;
|
|
1908
2287
|
}
|
|
1909
2288
|
if (json?.success === false || json?.error?.code || json?.error?.message) {
|
|
1910
|
-
const error =
|
|
1911
|
-
new
|
|
1912
|
-
json.error?.code ||
|
|
2289
|
+
const error = chunkSFUJMS5E_js.enrichError(
|
|
2290
|
+
new chunkSFUJMS5E_js.CimplifyError(
|
|
2291
|
+
json.error?.code || chunkSFUJMS5E_js.ErrorCode.API_ERROR,
|
|
1913
2292
|
json.error?.message || "An error occurred",
|
|
1914
2293
|
json.error?.retryable || false
|
|
1915
2294
|
),
|
|
@@ -1924,25 +2303,31 @@ var CimplifyClient = class {
|
|
|
1924
2303
|
}
|
|
1925
2304
|
get catalogue() {
|
|
1926
2305
|
if (!this._catalogue) {
|
|
1927
|
-
this._catalogue = new
|
|
2306
|
+
this._catalogue = new chunkDQEXY542_js.CatalogueQueries(this);
|
|
1928
2307
|
}
|
|
1929
2308
|
return this._catalogue;
|
|
1930
2309
|
}
|
|
1931
2310
|
get cart() {
|
|
1932
2311
|
if (!this._cart) {
|
|
1933
|
-
this._cart = new
|
|
2312
|
+
this._cart = new chunkDQEXY542_js.CartOperations(this);
|
|
1934
2313
|
}
|
|
1935
2314
|
return this._cart;
|
|
1936
2315
|
}
|
|
1937
2316
|
get checkout() {
|
|
1938
2317
|
if (!this._checkout) {
|
|
1939
|
-
this._checkout = new
|
|
2318
|
+
this._checkout = new chunkDQEXY542_js.CheckoutService(this);
|
|
1940
2319
|
}
|
|
1941
2320
|
return this._checkout;
|
|
1942
2321
|
}
|
|
2322
|
+
get purchaseIntents() {
|
|
2323
|
+
if (!this._purchaseIntents) {
|
|
2324
|
+
this._purchaseIntents = new PurchaseIntentService(this);
|
|
2325
|
+
}
|
|
2326
|
+
return this._purchaseIntents;
|
|
2327
|
+
}
|
|
1943
2328
|
get orders() {
|
|
1944
2329
|
if (!this._orders) {
|
|
1945
|
-
this._orders = new
|
|
2330
|
+
this._orders = new chunkDQEXY542_js.OrderQueries(this);
|
|
1946
2331
|
}
|
|
1947
2332
|
return this._orders;
|
|
1948
2333
|
}
|
|
@@ -1972,43 +2357,43 @@ var CimplifyClient = class {
|
|
|
1972
2357
|
}
|
|
1973
2358
|
get link() {
|
|
1974
2359
|
if (!this._link) {
|
|
1975
|
-
this._link = new
|
|
2360
|
+
this._link = new chunkDQEXY542_js.LinkService(this);
|
|
1976
2361
|
}
|
|
1977
2362
|
return this._link;
|
|
1978
2363
|
}
|
|
1979
2364
|
get auth() {
|
|
1980
2365
|
if (!this._auth) {
|
|
1981
|
-
this._auth = new
|
|
2366
|
+
this._auth = new chunkDQEXY542_js.AuthService(this);
|
|
1982
2367
|
}
|
|
1983
2368
|
return this._auth;
|
|
1984
2369
|
}
|
|
1985
2370
|
get business() {
|
|
1986
2371
|
if (!this._business) {
|
|
1987
|
-
this._business = new
|
|
2372
|
+
this._business = new chunkDQEXY542_js.BusinessService(this);
|
|
1988
2373
|
}
|
|
1989
2374
|
return this._business;
|
|
1990
2375
|
}
|
|
1991
2376
|
get inventory() {
|
|
1992
2377
|
if (!this._inventory) {
|
|
1993
|
-
this._inventory = new
|
|
2378
|
+
this._inventory = new chunkDQEXY542_js.InventoryService(this);
|
|
1994
2379
|
}
|
|
1995
2380
|
return this._inventory;
|
|
1996
2381
|
}
|
|
1997
2382
|
get scheduling() {
|
|
1998
2383
|
if (!this._scheduling) {
|
|
1999
|
-
this._scheduling = new
|
|
2384
|
+
this._scheduling = new chunkDQEXY542_js.SchedulingService(this);
|
|
2000
2385
|
}
|
|
2001
2386
|
return this._scheduling;
|
|
2002
2387
|
}
|
|
2003
2388
|
get lite() {
|
|
2004
2389
|
if (!this._lite) {
|
|
2005
|
-
this._lite = new
|
|
2390
|
+
this._lite = new chunkDQEXY542_js.LiteService(this);
|
|
2006
2391
|
}
|
|
2007
2392
|
return this._lite;
|
|
2008
2393
|
}
|
|
2009
2394
|
get fx() {
|
|
2010
2395
|
if (!this._fx) {
|
|
2011
|
-
this._fx = new
|
|
2396
|
+
this._fx = new chunkDQEXY542_js.FxService(this);
|
|
2012
2397
|
}
|
|
2013
2398
|
return this._fx;
|
|
2014
2399
|
}
|
|
@@ -2043,7 +2428,7 @@ var CimplifyClient = class {
|
|
|
2043
2428
|
if (businessId) {
|
|
2044
2429
|
this.setBusinessId(businessId);
|
|
2045
2430
|
}
|
|
2046
|
-
return
|
|
2431
|
+
return chunkDQEXY542_js.createElements(this, businessId ?? this.businessId ?? void 0, options);
|
|
2047
2432
|
}
|
|
2048
2433
|
};
|
|
2049
2434
|
function createCimplifyClient(config = {}) {
|
|
@@ -2054,16 +2439,18 @@ exports.AUTH_TOKEN_CHANGED = AUTH_TOKEN_CHANGED;
|
|
|
2054
2439
|
exports.ActivityService = ActivityService;
|
|
2055
2440
|
exports.CimplifyClient = CimplifyClient;
|
|
2056
2441
|
exports.DeliveryService = DeliveryService;
|
|
2442
|
+
exports.HostedPurchaseIntentService = HostedPurchaseIntentService;
|
|
2057
2443
|
exports.MUTATION_ERROR = MUTATION_ERROR;
|
|
2058
|
-
exports.
|
|
2059
|
-
exports.PaystackAuthorizationAdapter = PaystackAuthorizationAdapter;
|
|
2444
|
+
exports.PURCHASE_INTENT_ROUTE = PURCHASE_INTENT_ROUTE;
|
|
2060
2445
|
exports.PlacesService = PlacesService;
|
|
2446
|
+
exports.PurchaseIntentResolver = PurchaseIntentResolver;
|
|
2447
|
+
exports.PurchaseIntentService = PurchaseIntentService;
|
|
2448
|
+
exports.SESSION_CHANGE_SOURCE = SESSION_CHANGE_SOURCE;
|
|
2061
2449
|
exports.SESSION_CLAIMED = SESSION_CLAIMED;
|
|
2062
2450
|
exports.SESSION_CLEARED = SESSION_CLEARED;
|
|
2063
2451
|
exports.StoreCreditQueries = StoreCreditQueries;
|
|
2064
|
-
exports.StripeAuthorizationAdapter = StripeAuthorizationAdapter;
|
|
2065
2452
|
exports.SubscriptionService = SubscriptionService;
|
|
2066
2453
|
exports.SupportService = SupportService;
|
|
2067
2454
|
exports.UploadService = UploadService;
|
|
2455
|
+
exports.chatRealtimeUpdateFromWire = chatRealtimeUpdateFromWire;
|
|
2068
2456
|
exports.createCimplifyClient = createCimplifyClient;
|
|
2069
|
-
exports.createDefaultPaymentAuthorizationAdapterRegistry = createDefaultPaymentAuthorizationAdapterRegistry;
|