@juo/blocks 1.14.0 → 1.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{ExtensionRoot-CRJx0Dtr.js → ExtensionRoot-C3lVJEBl.js} +9 -2
- package/dist/{block-VhmfGB3N.js → block-dVP4Rqgz.js} +9 -0
- package/dist/block.d.ts.map +1 -1
- package/dist/block.devtools.test.d.ts +2 -0
- package/dist/block.devtools.test.d.ts.map +1 -0
- package/dist/{bridge-D838SWmN.js → bridge-xiKEzCGR.js} +30 -30
- package/dist/context.d.ts.map +1 -1
- package/dist/context.devtools.test.d.ts +2 -0
- package/dist/context.devtools.test.d.ts.map +1 -0
- package/dist/core.js +2390 -465
- package/dist/{css-runtime-DWytAzZ5.js → css-runtime-ClH0MEYr.js} +151 -113
- package/dist/devtools/env.d.ts +3 -0
- package/dist/devtools/env.d.ts.map +1 -0
- package/dist/devtools/hook.d.ts +40 -0
- package/dist/devtools/hook.d.ts.map +1 -0
- package/dist/devtools/index.d.ts +5 -0
- package/dist/devtools/index.d.ts.map +1 -0
- package/dist/devtools/instrumentation.d.ts +47 -0
- package/dist/devtools/instrumentation.d.ts.map +1 -0
- package/dist/devtools/protocol.d.ts +77 -0
- package/dist/devtools/protocol.d.ts.map +1 -0
- package/dist/devtools.js +11 -0
- package/dist/editor/components.d.ts.map +1 -1
- package/dist/editor/layout-command-emitter.d.ts +6 -0
- package/dist/editor/layout-command-emitter.d.ts.map +1 -1
- package/dist/editor/main.d.ts.map +1 -1
- package/dist/editor/messages.d.ts +6 -0
- package/dist/editor/messages.d.ts.map +1 -1
- package/dist/editor.js +74 -67
- package/dist/{gridstack-adapter-DreG3XnB.js → gridstack-adapter-CDWbSpbt.js} +15 -16
- package/dist/index-JSv6UH_o.js +169 -0
- package/dist/layout/index.d.ts +1 -0
- package/dist/layout/index.d.ts.map +1 -1
- package/dist/layout/insert.d.ts +14 -11
- package/dist/layout/insert.d.ts.map +1 -1
- package/dist/layout/override.d.ts +10 -0
- package/dist/layout/override.d.ts.map +1 -0
- package/dist/layout/override.test.d.ts +2 -0
- package/dist/layout/override.test.d.ts.map +1 -0
- package/dist/layout/project-css.d.ts.map +1 -1
- package/dist/layout/reconcile.d.ts +4 -3
- package/dist/layout/reconcile.d.ts.map +1 -1
- package/dist/{morph-Dj-xvrwt.js → morph-n4LMxOYo.js} +1 -1
- package/dist/preact/block.d.ts.map +1 -1
- package/dist/preact.js +16 -12
- package/dist/{project-gridstack-DYLMKSH1.js → project-gridstack-CM783oKI.js} +1 -1
- package/dist/react/block.d.ts.map +1 -1
- package/dist/react.js +15 -11
- package/dist/registry.devtools.test.d.ts +2 -0
- package/dist/registry.devtools.test.d.ts.map +1 -0
- package/dist/services/customer/adapter.d.ts +8 -0
- package/dist/services/customer/adapter.d.ts.map +1 -0
- package/dist/services/customer/customerService.d.ts +3 -2
- package/dist/services/customer/customerService.d.ts.map +1 -1
- package/dist/services/customer/index.d.ts +1 -0
- package/dist/services/customer/index.d.ts.map +1 -1
- package/dist/services/customer/types.d.ts +4 -1
- package/dist/services/customer/types.d.ts.map +1 -1
- package/dist/services/index.d.ts +3 -0
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/login/adapter.d.ts +33 -0
- package/dist/services/login/adapter.d.ts.map +1 -0
- package/dist/services/login/index.d.ts +3 -1
- package/dist/services/login/index.d.ts.map +1 -1
- package/dist/services/login/loginService.d.ts +6 -2
- package/dist/services/login/loginService.d.ts.map +1 -1
- package/dist/services/order/adapter.d.ts +9 -0
- package/dist/services/order/adapter.d.ts.map +1 -0
- package/dist/services/order/index.d.ts +5 -0
- package/dist/services/order/index.d.ts.map +1 -0
- package/dist/services/order/orderService.d.ts +7 -0
- package/dist/services/order/orderService.d.ts.map +1 -0
- package/dist/services/order/types.d.ts +21 -0
- package/dist/services/order/types.d.ts.map +1 -0
- package/dist/services/orders/adapter.d.ts +2 -0
- package/dist/services/orders/adapter.d.ts.map +1 -1
- package/dist/services/orders/index.d.ts +1 -1
- package/dist/services/orders/index.d.ts.map +1 -1
- package/dist/services/product/adapter.d.ts +27 -2
- package/dist/services/product/adapter.d.ts.map +1 -1
- package/dist/services/product/adapter.test.d.ts +2 -0
- package/dist/services/product/adapter.test.d.ts.map +1 -0
- package/dist/services/product/index.d.ts +1 -1
- package/dist/services/product/index.d.ts.map +1 -1
- package/dist/services/product/productService.d.ts.map +1 -1
- package/dist/services/product/types.d.ts +68 -0
- package/dist/services/product/types.d.ts.map +1 -1
- package/dist/services/schedules/adapter.d.ts +94 -0
- package/dist/services/schedules/adapter.d.ts.map +1 -0
- package/dist/services/schedules/adapter.test.d.ts +2 -0
- package/dist/services/schedules/adapter.test.d.ts.map +1 -0
- package/dist/services/schedules/apiSchedulesAdapter.test.d.ts +2 -0
- package/dist/services/schedules/apiSchedulesAdapter.test.d.ts.map +1 -0
- package/dist/services/schedules/index.d.ts +5 -0
- package/dist/services/schedules/index.d.ts.map +1 -0
- package/dist/services/schedules/schedulesService.d.ts +11 -0
- package/dist/services/schedules/schedulesService.d.ts.map +1 -0
- package/dist/services/schedules/schedulesService.test.d.ts +2 -0
- package/dist/services/schedules/schedulesService.test.d.ts.map +1 -0
- package/dist/services/schedules/types.d.ts +290 -0
- package/dist/services/schedules/types.d.ts.map +1 -0
- package/dist/services/settings/index.d.ts +3 -0
- package/dist/services/settings/index.d.ts.map +1 -0
- package/dist/services/settings/settingsService.d.ts +7 -0
- package/dist/services/settings/settingsService.d.ts.map +1 -0
- package/dist/services/settings/settingsService.test.d.ts +2 -0
- package/dist/services/settings/settingsService.test.d.ts.map +1 -0
- package/dist/services/settings/types.d.ts +35 -0
- package/dist/services/settings/types.d.ts.map +1 -0
- package/dist/services/shop/index.d.ts +3 -0
- package/dist/services/shop/index.d.ts.map +1 -0
- package/dist/services/shop/shopService.d.ts +12 -0
- package/dist/services/shop/shopService.d.ts.map +1 -0
- package/dist/services/shop/shopService.test.d.ts +2 -0
- package/dist/services/shop/shopService.test.d.ts.map +1 -0
- package/dist/services/shop/types.d.ts +7 -0
- package/dist/services/shop/types.d.ts.map +1 -0
- package/dist/services/subscription/adapter.d.ts +77 -2
- package/dist/services/subscription/adapter.d.ts.map +1 -1
- package/dist/services/subscription/adapter.test.d.ts +2 -0
- package/dist/services/subscription/adapter.test.d.ts.map +1 -0
- package/dist/services/subscription/index.d.ts +1 -1
- package/dist/services/subscription/index.d.ts.map +1 -1
- package/dist/services/subscription/subscriptionService.d.ts +1 -6
- package/dist/services/subscription/subscriptionService.d.ts.map +1 -1
- package/dist/services/subscription/types.d.ts +220 -9
- package/dist/services/subscription/types.d.ts.map +1 -1
- package/dist/services/subscription/types.test.d.ts +2 -0
- package/dist/services/subscription/types.test.d.ts.map +1 -0
- package/dist/shared/context.d.ts.map +1 -1
- package/dist/utils/index.d.ts +6 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/vue/block.d.ts.map +1 -1
- package/dist/vue/context.d.ts +1 -0
- package/dist/vue/context.d.ts.map +1 -1
- package/dist/vue.js +34 -6
- package/dist/web-components/ExtensionRoot.d.ts.map +1 -1
- package/dist/web-components/editor/editor-layout-controller.d.ts.map +1 -1
- package/dist/web-components/editor/gridstack-adapter.d.ts.map +1 -1
- package/dist/web-components/layout-mode-adapter.d.ts +6 -0
- package/dist/web-components/layout-mode-adapter.d.ts.map +1 -1
- package/dist/web-components-editor.js +11 -6
- package/dist/web-components-runtime.js +2 -2
- package/package.json +5 -1
package/dist/core.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
3
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
-
import {
|
|
5
|
-
import { B,
|
|
4
|
+
import { f as createContext, g as d, h as w, j as registerBlock, E, u, k as getDefinedBlocks, l as createBlockInstance, m as defineBlock, s as subscribeContext, G as GRID_V1_ENGINE_ID, n as clampRect, p as applyFillExpansion, q as compactItems, t as findFreeRect, D as DEFAULT_GRID } from "./css-runtime-ClH0MEYr.js";
|
|
5
|
+
import { B, v, x, y, z, L, P, S, c, T, A, d as d2, b, F, H, I, J, K, N, O, Q, R, U, V, W, X, Y, Z, i, _, a, $, a0, o, a1, a2, a3, e, a4, r, a5, a6, a7, w as w2 } from "./css-runtime-ClH0MEYr.js";
|
|
6
6
|
import { reportBlockWarning, reportBlockError } from "./observability.js";
|
|
7
|
-
import {
|
|
8
|
-
import { p
|
|
7
|
+
import { a as createEditorBridge, D as createSelectBlockMessage } from "./bridge-xiKEzCGR.js";
|
|
8
|
+
import { p } from "./project-gridstack-CM783oKI.js";
|
|
9
9
|
import { clearLayoutModeAdapter, getLayoutModeAdapter, registerLayoutModeAdapter } from "./layout-mode-adapter-BBd4JRQd.js";
|
|
10
10
|
function createState() {
|
|
11
11
|
const discountCode = d(null);
|
|
@@ -159,13 +159,43 @@ async function loadBlockRegistry(options = {}) {
|
|
|
159
159
|
function createCustomerService(options) {
|
|
160
160
|
const current = d(null);
|
|
161
161
|
const accountOrdersUrl = d(options.getAccountOrdersUrl());
|
|
162
|
-
|
|
162
|
+
return {
|
|
163
163
|
current,
|
|
164
|
-
accountOrdersUrl
|
|
164
|
+
accountOrdersUrl,
|
|
165
|
+
async getCurrent() {
|
|
166
|
+
const result = await options.adapter.getCurrent();
|
|
167
|
+
if (result._tag === "Success") current.value = result.data;
|
|
168
|
+
return result;
|
|
169
|
+
}
|
|
165
170
|
};
|
|
166
|
-
return service;
|
|
167
171
|
}
|
|
168
172
|
const CustomerServiceContext = createContext("services/customer");
|
|
173
|
+
function createMockCustomerAdapter() {
|
|
174
|
+
const customer = { id: "customer-1", name: "Customer 1" };
|
|
175
|
+
return {
|
|
176
|
+
async getCurrent() {
|
|
177
|
+
return { _tag: "Success", data: customer };
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
function createApiCustomerAdapter(fetcher) {
|
|
182
|
+
return {
|
|
183
|
+
async getCurrent() {
|
|
184
|
+
try {
|
|
185
|
+
const res = await fetcher("/customer/info");
|
|
186
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
187
|
+
const data = await res.json();
|
|
188
|
+
const name = [data.firstName, data.lastName].filter(Boolean).join(" ") || data.email || "";
|
|
189
|
+
return {
|
|
190
|
+
_tag: "Success",
|
|
191
|
+
data: { id: data.id, name, email: data.email }
|
|
192
|
+
};
|
|
193
|
+
} catch (e2) {
|
|
194
|
+
return { _tag: "Failure", error: e2 };
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
}
|
|
169
199
|
const PARAMS = {
|
|
170
200
|
DELETGATED_TOKEN: "delegated-token",
|
|
171
201
|
REDIRECT_TO: "redirect-to",
|
|
@@ -179,9 +209,126 @@ const PARAMS = {
|
|
|
179
209
|
function uuid() {
|
|
180
210
|
return ("10000000-1000-4000-8000" + -1e11).replace(
|
|
181
211
|
/[018]/g,
|
|
182
|
-
(
|
|
212
|
+
(c2) => (c2 ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c2 / 4).toString(16)
|
|
183
213
|
);
|
|
184
214
|
}
|
|
215
|
+
function createMockLoginAdapter(opts = {}) {
|
|
216
|
+
const appUrl = opts.appUrl ?? "https://admin.juo.io";
|
|
217
|
+
return {
|
|
218
|
+
async passwordlessStart() {
|
|
219
|
+
return { _tag: "Success", data: void 0 };
|
|
220
|
+
},
|
|
221
|
+
async authenticate() {
|
|
222
|
+
return {
|
|
223
|
+
_tag: "Success",
|
|
224
|
+
data: {
|
|
225
|
+
access_token: "mock-access-token",
|
|
226
|
+
refresh_token: "mock-refresh-token",
|
|
227
|
+
expires_in: 3600
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
},
|
|
231
|
+
getAuthorizeUrl({ connection, state, shop }) {
|
|
232
|
+
const url = new URL(`${appUrl}/auth/customer/authorize`);
|
|
233
|
+
url.searchParams.set("connection", connection);
|
|
234
|
+
url.searchParams.set("state", state);
|
|
235
|
+
url.searchParams.set("shop", shop);
|
|
236
|
+
return url.toString();
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
function createApiLoginAdapter(fetcher, opts) {
|
|
241
|
+
const { shopDomain, appUrl } = opts;
|
|
242
|
+
async function callAuthEndpoint(path, body, lang) {
|
|
243
|
+
const url = lang ? `${path}?lang=${lang}` : path;
|
|
244
|
+
const res = await fetcher(url, {
|
|
245
|
+
method: "POST",
|
|
246
|
+
headers: {
|
|
247
|
+
Accept: "application/json",
|
|
248
|
+
"Content-Type": "application/json",
|
|
249
|
+
"X-Shopify-Shop-Domain": shopDomain
|
|
250
|
+
},
|
|
251
|
+
body: JSON.stringify(body)
|
|
252
|
+
});
|
|
253
|
+
if (res.status === 403) {
|
|
254
|
+
const data = await res.json();
|
|
255
|
+
if ("error_description" in data) {
|
|
256
|
+
if (data.error_description === "Wrong email or verification code.") {
|
|
257
|
+
throw new AuthenticationError(AuthenticationErrorReason.IncorrectCode);
|
|
258
|
+
} else if (data.error_description === "You've reached the maximum number of attempts. Please try to login again.") {
|
|
259
|
+
throw new AuthenticationError(AuthenticationErrorReason.TooManyTries);
|
|
260
|
+
} else if (data.error_description === "The verification code has expired. Please try to login again.") {
|
|
261
|
+
throw new AuthenticationError(AuthenticationErrorReason.Expired);
|
|
262
|
+
}
|
|
263
|
+
} else if ("code" in data && data.code === "AUTH_ERROR") {
|
|
264
|
+
throw new AuthenticationError(AuthenticationErrorReason.NoEmail, {
|
|
265
|
+
connection: data.connection
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
if (!res.ok) {
|
|
270
|
+
throw new Error(`Unexpected response: ${res.statusText}`);
|
|
271
|
+
}
|
|
272
|
+
return { _tag: "Success", data: void 0 };
|
|
273
|
+
}
|
|
274
|
+
return {
|
|
275
|
+
async passwordlessStart({ email, lang }) {
|
|
276
|
+
try {
|
|
277
|
+
await callAuthEndpoint(
|
|
278
|
+
"/auth/customer/passwordless/start",
|
|
279
|
+
{ email },
|
|
280
|
+
lang
|
|
281
|
+
);
|
|
282
|
+
return { _tag: "Success", data: void 0 };
|
|
283
|
+
} catch (e2) {
|
|
284
|
+
return { _tag: "Failure", error: e2 };
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
async authenticate(params) {
|
|
288
|
+
try {
|
|
289
|
+
const res = await fetcher("/auth/customer/token", {
|
|
290
|
+
method: "POST",
|
|
291
|
+
headers: {
|
|
292
|
+
Accept: "application/json",
|
|
293
|
+
"Content-Type": "application/json",
|
|
294
|
+
"X-Shopify-Shop-Domain": shopDomain
|
|
295
|
+
},
|
|
296
|
+
body: JSON.stringify(params)
|
|
297
|
+
});
|
|
298
|
+
if (res.status === 403) {
|
|
299
|
+
const data = await res.json();
|
|
300
|
+
if ("error_description" in data) {
|
|
301
|
+
if (data.error_description === "Wrong email or verification code.") {
|
|
302
|
+
throw new AuthenticationError(AuthenticationErrorReason.IncorrectCode);
|
|
303
|
+
} else if (data.error_description === "You've reached the maximum number of attempts. Please try to login again.") {
|
|
304
|
+
throw new AuthenticationError(AuthenticationErrorReason.TooManyTries);
|
|
305
|
+
} else if (data.error_description === "The verification code has expired. Please try to login again.") {
|
|
306
|
+
throw new AuthenticationError(AuthenticationErrorReason.Expired);
|
|
307
|
+
}
|
|
308
|
+
} else if ("code" in data && data.code === "AUTH_ERROR") {
|
|
309
|
+
throw new AuthenticationError(AuthenticationErrorReason.NoEmail, {
|
|
310
|
+
connection: data.connection
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
if (!res.ok) {
|
|
315
|
+
throw new Error(`Unexpected response: ${res.statusText}`);
|
|
316
|
+
}
|
|
317
|
+
return { _tag: "Success", data: await res.json() };
|
|
318
|
+
} catch (e2) {
|
|
319
|
+
return { _tag: "Failure", error: e2 };
|
|
320
|
+
}
|
|
321
|
+
},
|
|
322
|
+
getAuthorizeUrl({ connection, state, shop }) {
|
|
323
|
+
const url = new URL(`${appUrl}/auth/customer/authorize`);
|
|
324
|
+
url.searchParams.set("connection", connection);
|
|
325
|
+
url.searchParams.set("state", state);
|
|
326
|
+
url.searchParams.set("shop", shop);
|
|
327
|
+
return url.toString();
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
const __vite_import_meta_env__ = {};
|
|
185
332
|
var AuthenticationErrorReason = /* @__PURE__ */ ((AuthenticationErrorReason2) => {
|
|
186
333
|
AuthenticationErrorReason2["Expired"] = "expired";
|
|
187
334
|
AuthenticationErrorReason2["IncorrectCode"] = "wrong-code";
|
|
@@ -200,6 +347,9 @@ class AuthenticationError extends Error {
|
|
|
200
347
|
this.data = data ?? {};
|
|
201
348
|
}
|
|
202
349
|
}
|
|
350
|
+
const STORAGE_DELEGATED_TOKEN = "delegated-token";
|
|
351
|
+
const STORAGE_REFRESH_TOKEN_KEY = "refresh-token";
|
|
352
|
+
const STORAGE_STATE_KEY = "state";
|
|
203
353
|
function storage(key, value) {
|
|
204
354
|
const storageKey = `auth:${key}`;
|
|
205
355
|
try {
|
|
@@ -215,83 +365,33 @@ function storage(key, value) {
|
|
|
215
365
|
}
|
|
216
366
|
return null;
|
|
217
367
|
}
|
|
218
|
-
const STORAGE_DELEGATED_TOKEN = "delegated-token";
|
|
219
|
-
const STORAGE_REFRESH_TOKEN_KEY = "refresh-token";
|
|
220
|
-
const STORAGE_STATE_KEY = "state";
|
|
221
|
-
function createAuthFunctions(basePath, shop) {
|
|
222
|
-
async function start(email, lang) {
|
|
223
|
-
const response = await fetch(
|
|
224
|
-
`${basePath}/auth/customer/passwordless/start?lang=${lang}`,
|
|
225
|
-
{
|
|
226
|
-
method: "POST",
|
|
227
|
-
headers: {
|
|
228
|
-
Accept: "application/json",
|
|
229
|
-
"Content-Type": "application/json",
|
|
230
|
-
"X-Shopify-Shop-Domain": shop
|
|
231
|
-
},
|
|
232
|
-
body: JSON.stringify({ email })
|
|
233
|
-
}
|
|
234
|
-
);
|
|
235
|
-
if (response.status !== 200) {
|
|
236
|
-
throw new Error(`Unexpected response: ${response.statusText}`);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
async function authenticate(data) {
|
|
240
|
-
const response = await fetch(`${basePath}/auth/customer/token`, {
|
|
241
|
-
method: "POST",
|
|
242
|
-
headers: {
|
|
243
|
-
Accept: "application/json",
|
|
244
|
-
"Content-Type": "application/json",
|
|
245
|
-
"X-Shopify-Shop-Domain": shop
|
|
246
|
-
},
|
|
247
|
-
body: JSON.stringify(data)
|
|
248
|
-
});
|
|
249
|
-
if (response.status === 403) {
|
|
250
|
-
const data2 = await response.json();
|
|
251
|
-
if ("error_description" in data2) {
|
|
252
|
-
if (data2.error_description === "Wrong email or verification code.") {
|
|
253
|
-
throw new AuthenticationError(
|
|
254
|
-
"wrong-code"
|
|
255
|
-
/* IncorrectCode */
|
|
256
|
-
);
|
|
257
|
-
} else if (data2.error_description === "You've reached the maximum number of attempts. Please try to login again.") {
|
|
258
|
-
throw new AuthenticationError(
|
|
259
|
-
"too-many-tries"
|
|
260
|
-
/* TooManyTries */
|
|
261
|
-
);
|
|
262
|
-
} else if (data2.error_description === "The verification code has expired. Please try to login again.") {
|
|
263
|
-
throw new AuthenticationError(
|
|
264
|
-
"expired"
|
|
265
|
-
/* Expired */
|
|
266
|
-
);
|
|
267
|
-
}
|
|
268
|
-
} else if ("code" in data2) {
|
|
269
|
-
if (data2.code === "AUTH_ERROR") {
|
|
270
|
-
throw new AuthenticationError("no-email", {
|
|
271
|
-
connection: data2.connection
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
if (response.status !== 200) {
|
|
277
|
-
throw new Error(`Unexpected response: ${response.statusText}`);
|
|
278
|
-
}
|
|
279
|
-
return await response.json();
|
|
280
|
-
}
|
|
281
|
-
return { start, authenticate };
|
|
282
|
-
}
|
|
283
368
|
function nonce(len = 10) {
|
|
284
369
|
return uuid().replace(/-/g, "").slice(0, len);
|
|
285
370
|
}
|
|
286
|
-
function
|
|
287
|
-
const
|
|
371
|
+
function loginService(deps) {
|
|
372
|
+
const apiBase = deps.shop.rootUrl.replace(/\/$/, "") + "/api";
|
|
373
|
+
const appUrl = (typeof import.meta !== "undefined" ? __vite_import_meta_env__ == null ? void 0 : __vite_import_meta_env__.VITE_APP_URL : void 0) ?? "https://admin.juo.io";
|
|
374
|
+
const fetcher = (path, init) => fetch(`${apiBase}${path}`, init);
|
|
375
|
+
const adapter = createApiLoginAdapter(fetcher, {
|
|
376
|
+
shopDomain: deps.shop.domain,
|
|
377
|
+
appUrl
|
|
378
|
+
});
|
|
379
|
+
return createLoginService({
|
|
380
|
+
adapter,
|
|
381
|
+
router: deps.router,
|
|
382
|
+
formatError: deps.formatError,
|
|
383
|
+
shop: {
|
|
384
|
+
domain: deps.shop.domain,
|
|
385
|
+
locale: w(() => deps.getLocale())
|
|
386
|
+
},
|
|
387
|
+
onLoginSuccess: deps.onLoginSuccess
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
function createLoginService(init) {
|
|
391
|
+
const { adapter, formatError, router, shop, onLoginSuccess } = init;
|
|
288
392
|
const accessToken = d(null);
|
|
289
|
-
const delegatedToken = d(
|
|
290
|
-
|
|
291
|
-
);
|
|
292
|
-
const refreshToken = d(
|
|
293
|
-
storage(STORAGE_REFRESH_TOKEN_KEY)
|
|
294
|
-
);
|
|
393
|
+
const delegatedToken = d(storage(STORAGE_DELEGATED_TOKEN));
|
|
394
|
+
const refreshToken = d(storage(STORAGE_REFRESH_TOKEN_KEY));
|
|
295
395
|
const expires = d(null);
|
|
296
396
|
const state = d(storage(STORAGE_STATE_KEY));
|
|
297
397
|
E(() => {
|
|
@@ -303,67 +403,28 @@ function createAuthService(apiUrl, shopDomain) {
|
|
|
303
403
|
E(() => {
|
|
304
404
|
storage(STORAGE_STATE_KEY, state.value);
|
|
305
405
|
});
|
|
306
|
-
function
|
|
406
|
+
function storeTokens(data) {
|
|
307
407
|
const { access_token, refresh_token, expires_in } = data;
|
|
308
408
|
accessToken.value = access_token;
|
|
309
409
|
refreshToken.value = refresh_token;
|
|
310
410
|
expires.value = new Date(Date.now() + expires_in);
|
|
311
411
|
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
},
|
|
320
|
-
getDelegatedToken() {
|
|
321
|
-
return delegatedToken.value;
|
|
322
|
-
},
|
|
323
|
-
setDelegatedToken(token) {
|
|
324
|
-
delegatedToken.value = token;
|
|
325
|
-
},
|
|
326
|
-
async getToken() {
|
|
327
|
-
if (refreshToken.value == null) {
|
|
328
|
-
return null;
|
|
329
|
-
}
|
|
330
|
-
if (accessToken.value == null || typeof accessToken.value === "string" && (expires.value == null || expires.value < /* @__PURE__ */ new Date())) {
|
|
331
|
-
accessToken.value = new Promise(
|
|
332
|
-
(resolve, reject) => authAuthenticate({ refreshToken: refreshToken.value }).then((data) => data).then((data) => {
|
|
333
|
-
store(data);
|
|
334
|
-
resolve(data.access_token);
|
|
412
|
+
async function getToken() {
|
|
413
|
+
if (refreshToken.value == null) return null;
|
|
414
|
+
if (accessToken.value == null || typeof accessToken.value === "string" && (expires.value == null || expires.value < /* @__PURE__ */ new Date())) {
|
|
415
|
+
accessToken.value = new Promise(
|
|
416
|
+
(resolve, reject) => adapter.authenticate({ refreshToken: refreshToken.value }).then((result) => {
|
|
417
|
+
if (result._tag === "Failure") {
|
|
418
|
+
reject(result.error);
|
|
335
419
|
return;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
refreshToken.value = null;
|
|
345
|
-
expires.value = null;
|
|
346
|
-
},
|
|
347
|
-
isAuthenticated: w(
|
|
348
|
-
() => delegatedToken.value != null || refreshToken.value != null
|
|
349
|
-
)
|
|
350
|
-
};
|
|
351
|
-
}
|
|
352
|
-
function loginService(deps) {
|
|
353
|
-
return createLoginService({
|
|
354
|
-
apiUrl: `${deps.shop.rootUrl.replace(/\/$/, "")}/api`,
|
|
355
|
-
router: deps.router,
|
|
356
|
-
formatError: deps.formatError,
|
|
357
|
-
shop: {
|
|
358
|
-
domain: deps.shop.domain,
|
|
359
|
-
locale: w(() => deps.getLocale())
|
|
360
|
-
},
|
|
361
|
-
onLoginSuccess: deps.onLoginSuccess
|
|
362
|
-
});
|
|
363
|
-
}
|
|
364
|
-
function createLoginService(init) {
|
|
365
|
-
const { formatError, router, shop, onLoginSuccess } = init;
|
|
366
|
-
const auth = createAuthService(init.apiUrl.replace(/\/$/, ""), shop.domain);
|
|
420
|
+
}
|
|
421
|
+
storeTokens(result.data);
|
|
422
|
+
resolve(result.data.access_token);
|
|
423
|
+
}).catch(reject)
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
return Promise.resolve(accessToken.value);
|
|
427
|
+
}
|
|
367
428
|
const userEmail = d(null);
|
|
368
429
|
async function onSuccess(query) {
|
|
369
430
|
await (onLoginSuccess == null ? void 0 : onLoginSuccess());
|
|
@@ -376,13 +437,9 @@ function createLoginService(init) {
|
|
|
376
437
|
queryParams[key] = value;
|
|
377
438
|
}
|
|
378
439
|
});
|
|
379
|
-
router.push({
|
|
380
|
-
path: decodeURIComponent(redirect),
|
|
381
|
-
query: queryParams
|
|
382
|
-
});
|
|
440
|
+
router.push({ path: decodeURIComponent(redirect), query: queryParams });
|
|
383
441
|
} else {
|
|
384
|
-
|
|
385
|
-
router.replace(decodeURIComponent(redirect));
|
|
442
|
+
router.replace(decodeURIComponent(query.get(PARAMS.REDIRECT_TO)));
|
|
386
443
|
}
|
|
387
444
|
} else {
|
|
388
445
|
router.replace("/");
|
|
@@ -394,16 +451,17 @@ function createLoginService(init) {
|
|
|
394
451
|
async start(email) {
|
|
395
452
|
try {
|
|
396
453
|
userEmail.value = email;
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
454
|
+
const lang = shop.locale instanceof u ? shop.locale.value : shop.locale;
|
|
455
|
+
const result = await adapter.passwordlessStart({
|
|
456
|
+
email: userEmail.value,
|
|
457
|
+
lang
|
|
458
|
+
});
|
|
459
|
+
if (result._tag === "Failure") {
|
|
460
|
+
return { _tag: "Failure", error: formatError(result.error) };
|
|
461
|
+
}
|
|
401
462
|
return { _tag: "Success", data: void 0 };
|
|
402
463
|
} catch (error) {
|
|
403
|
-
return {
|
|
404
|
-
_tag: "Failure",
|
|
405
|
-
error: formatError(error)
|
|
406
|
-
};
|
|
464
|
+
return { _tag: "Failure", error: formatError(error) };
|
|
407
465
|
}
|
|
408
466
|
},
|
|
409
467
|
async verify(email, code) {
|
|
@@ -414,63 +472,57 @@ function createLoginService(init) {
|
|
|
414
472
|
};
|
|
415
473
|
}
|
|
416
474
|
try {
|
|
417
|
-
const result = await
|
|
475
|
+
const result = await adapter.authenticate({
|
|
418
476
|
email: userEmail.value,
|
|
419
477
|
code
|
|
420
478
|
});
|
|
479
|
+
if (result._tag === "Failure") {
|
|
480
|
+
return { _tag: "Failure", error: formatError(result.error) };
|
|
481
|
+
}
|
|
482
|
+
storeTokens(result.data);
|
|
421
483
|
await onSuccess();
|
|
422
|
-
return { _tag: "Success", data:
|
|
484
|
+
return { _tag: "Success", data: void 0 };
|
|
423
485
|
} catch (error) {
|
|
424
|
-
return {
|
|
425
|
-
_tag: "Failure",
|
|
426
|
-
error: formatError(error)
|
|
427
|
-
};
|
|
486
|
+
return { _tag: "Failure", error: formatError(error) };
|
|
428
487
|
}
|
|
429
488
|
}
|
|
430
489
|
}
|
|
431
490
|
},
|
|
432
491
|
social: {
|
|
433
492
|
async start(provider) {
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
url.searchParams.append("shop", shop2);
|
|
443
|
-
return url.toString();
|
|
444
|
-
}
|
|
445
|
-
window.location.href = socialUrl(
|
|
446
|
-
provider === "Facebook" ? "facebook" : "google-oauth2",
|
|
447
|
-
shop.domain
|
|
448
|
-
);
|
|
449
|
-
return {
|
|
450
|
-
_tag: "Success",
|
|
451
|
-
data: void 0
|
|
452
|
-
};
|
|
493
|
+
state.value = nonce();
|
|
494
|
+
const connection = provider === "Facebook" ? "facebook" : "google-oauth2";
|
|
495
|
+
window.location.href = adapter.getAuthorizeUrl({
|
|
496
|
+
connection,
|
|
497
|
+
state: state.value ?? "",
|
|
498
|
+
shop: shop.domain
|
|
499
|
+
});
|
|
500
|
+
return { _tag: "Success", data: void 0 };
|
|
453
501
|
},
|
|
454
502
|
async verify(code) {
|
|
455
503
|
try {
|
|
456
|
-
const result = await
|
|
504
|
+
const result = await adapter.authenticate({ code });
|
|
505
|
+
if (result._tag === "Failure") {
|
|
506
|
+
return { _tag: "Failure", error: formatError(result.error) };
|
|
507
|
+
}
|
|
508
|
+
storeTokens(result.data);
|
|
457
509
|
await onSuccess();
|
|
458
|
-
return { _tag: "Success", data:
|
|
510
|
+
return { _tag: "Success", data: void 0 };
|
|
459
511
|
} catch (error) {
|
|
460
|
-
return {
|
|
461
|
-
_tag: "Failure",
|
|
462
|
-
error: formatError(error)
|
|
463
|
-
};
|
|
512
|
+
return { _tag: "Failure", error: formatError(error) };
|
|
464
513
|
}
|
|
465
514
|
}
|
|
466
515
|
},
|
|
467
516
|
async logout() {
|
|
468
|
-
|
|
517
|
+
accessToken.value = null;
|
|
518
|
+
delegatedToken.value = null;
|
|
519
|
+
refreshToken.value = null;
|
|
520
|
+
expires.value = null;
|
|
469
521
|
return { _tag: "Success", data: void 0 };
|
|
470
522
|
},
|
|
471
523
|
async handleQueryParams(query) {
|
|
472
524
|
if (query.has(PARAMS.DELETGATED_TOKEN)) {
|
|
473
|
-
|
|
525
|
+
delegatedToken.value = query.get(PARAMS.DELETGATED_TOKEN);
|
|
474
526
|
await onSuccess(query);
|
|
475
527
|
return { _tag: "Success", data: void 0 };
|
|
476
528
|
}
|
|
@@ -482,16 +534,13 @@ function createLoginService(init) {
|
|
|
482
534
|
}
|
|
483
535
|
const code = query.get(PARAMS.CODE);
|
|
484
536
|
if (code != null) {
|
|
485
|
-
if (query.get(PARAMS.STATE) !==
|
|
486
|
-
return {
|
|
487
|
-
_tag: "Failure",
|
|
488
|
-
error: formatError("Invalid state")
|
|
489
|
-
};
|
|
537
|
+
if (query.get(PARAMS.STATE) !== state.value) {
|
|
538
|
+
return { _tag: "Failure", error: formatError("Invalid state") };
|
|
490
539
|
} else {
|
|
491
540
|
return service.social.verify(code);
|
|
492
541
|
}
|
|
493
542
|
}
|
|
494
|
-
if (query.get(PARAMS.REFRESH) === "1" && await
|
|
543
|
+
if (query.get(PARAMS.REFRESH) === "1" && await getToken() != null) {
|
|
495
544
|
await onSuccess();
|
|
496
545
|
return { _tag: "Success", data: void 0 };
|
|
497
546
|
}
|
|
@@ -508,8 +557,20 @@ function createLoginService(init) {
|
|
|
508
557
|
}
|
|
509
558
|
return { _tag: "Success", data: void 0 };
|
|
510
559
|
},
|
|
511
|
-
isAuthenticated:
|
|
512
|
-
|
|
560
|
+
isAuthenticated: w(
|
|
561
|
+
() => delegatedToken.value != null || refreshToken.value != null
|
|
562
|
+
),
|
|
563
|
+
getToken,
|
|
564
|
+
async getAuthHeaders() {
|
|
565
|
+
if (delegatedToken.value) {
|
|
566
|
+
return { "X-Delegated-Token": delegatedToken.value };
|
|
567
|
+
}
|
|
568
|
+
const token = await getToken();
|
|
569
|
+
if (token) {
|
|
570
|
+
return { Authorization: `Bearer ${token}` };
|
|
571
|
+
}
|
|
572
|
+
return {};
|
|
573
|
+
}
|
|
513
574
|
};
|
|
514
575
|
return service;
|
|
515
576
|
}
|
|
@@ -575,6 +636,55 @@ function createMockOrdersAdapter() {
|
|
|
575
636
|
}
|
|
576
637
|
};
|
|
577
638
|
}
|
|
639
|
+
function mapOrderLine(item) {
|
|
640
|
+
var _a;
|
|
641
|
+
return {
|
|
642
|
+
id: item.id,
|
|
643
|
+
title: item.title,
|
|
644
|
+
quantity: item.quantity,
|
|
645
|
+
variantImage: ((_a = item.variantImage) == null ? void 0 : _a.url) ? { url: item.variantImage.url, altText: item.variantImage.altText ?? void 0 } : null,
|
|
646
|
+
customAttributes: item.customAttributes
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
function mapOrder(order) {
|
|
650
|
+
return {
|
|
651
|
+
id: order.id,
|
|
652
|
+
createdAt: order.createdAt,
|
|
653
|
+
placedAt: order.placedAt,
|
|
654
|
+
currencyCode: order.currencyCode,
|
|
655
|
+
totalPrice: order.totalPrice,
|
|
656
|
+
financialStatus: order.financialStatus,
|
|
657
|
+
fulfillmentStatus: order.fulfillmentStatus,
|
|
658
|
+
lines: order.items.map(mapOrderLine),
|
|
659
|
+
customAttributes: order.customAttributes
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
function createApiOrdersAdapter(fetcher) {
|
|
663
|
+
return {
|
|
664
|
+
async getOrders(params) {
|
|
665
|
+
const query = new URLSearchParams();
|
|
666
|
+
if ((params == null ? void 0 : params.limit) != null) query.set("limit", String(params.limit));
|
|
667
|
+
if ((params == null ? void 0 : params.after) != null) query.set("after", params.after);
|
|
668
|
+
if ((params == null ? void 0 : params.before) != null) query.set("before", params.before);
|
|
669
|
+
if ((params == null ? void 0 : params.query) != null) query.set("query", params.query);
|
|
670
|
+
if ((params == null ? void 0 : params.includeOneTime) != null) query.set("includeOneTime", String(params.includeOneTime));
|
|
671
|
+
if ((params == null ? void 0 : params.sort) != null) query.set("sort", params.sort);
|
|
672
|
+
const qs = query.toString();
|
|
673
|
+
const res = await fetcher(`/customer/v1/orders${qs ? `?${qs}` : ""}`);
|
|
674
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
675
|
+
const body = await res.json();
|
|
676
|
+
return {
|
|
677
|
+
items: body.data.map(mapOrder),
|
|
678
|
+
pageInfo: {
|
|
679
|
+
hasNextPage: body.hasNextPage,
|
|
680
|
+
hasPrevPage: body.hasPrevPage,
|
|
681
|
+
startCursor: body.startCursor,
|
|
682
|
+
endCursor: body.endCursor
|
|
683
|
+
}
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
};
|
|
687
|
+
}
|
|
578
688
|
function createOrdersService(adapter) {
|
|
579
689
|
return {
|
|
580
690
|
async getOrders(params) {
|
|
@@ -594,7 +704,7 @@ function createMockProductAdapter() {
|
|
|
594
704
|
id: "sample-1",
|
|
595
705
|
title: "Hydrating Gel Cleanser for Face and Eye Area",
|
|
596
706
|
featuredImage: {
|
|
597
|
-
url: "https://
|
|
707
|
+
url: "https://placehold.co/400x400?text=Product+1"
|
|
598
708
|
},
|
|
599
709
|
hasOnlyDefaultVariant: true,
|
|
600
710
|
variants: [
|
|
@@ -602,7 +712,7 @@ function createMockProductAdapter() {
|
|
|
602
712
|
id: "sample-1-1",
|
|
603
713
|
title: "150 ml bottle",
|
|
604
714
|
image: {
|
|
605
|
-
url: "https://
|
|
715
|
+
url: "https://placehold.co/400x400?text=Product+1"
|
|
606
716
|
},
|
|
607
717
|
price: {
|
|
608
718
|
amount: "12.49",
|
|
@@ -628,7 +738,7 @@ function createMockProductAdapter() {
|
|
|
628
738
|
id: "sample-2",
|
|
629
739
|
title: "More COLLAGEN",
|
|
630
740
|
featuredImage: {
|
|
631
|
-
url: "https://
|
|
741
|
+
url: "https://placehold.co/400x400?text=Product+2"
|
|
632
742
|
},
|
|
633
743
|
hasOnlyDefaultVariant: true,
|
|
634
744
|
variants: [
|
|
@@ -636,7 +746,7 @@ function createMockProductAdapter() {
|
|
|
636
746
|
id: "sample-2-1",
|
|
637
747
|
title: "blackcurrant",
|
|
638
748
|
image: {
|
|
639
|
-
url: "https://
|
|
749
|
+
url: "https://placehold.co/400x400?text=Product+2"
|
|
640
750
|
},
|
|
641
751
|
price: {
|
|
642
752
|
amount: "12.49",
|
|
@@ -657,7 +767,7 @@ function createMockProductAdapter() {
|
|
|
657
767
|
id: "sample-3",
|
|
658
768
|
title: "Gentle deodorant for underarms and bust",
|
|
659
769
|
featuredImage: {
|
|
660
|
-
url: "https://
|
|
770
|
+
url: "https://placehold.co/400x400?text=Product+3"
|
|
661
771
|
},
|
|
662
772
|
hasOnlyDefaultVariant: false,
|
|
663
773
|
variants: [
|
|
@@ -665,7 +775,7 @@ function createMockProductAdapter() {
|
|
|
665
775
|
id: "sample-3-1",
|
|
666
776
|
title: "Green tea and jasmine",
|
|
667
777
|
image: {
|
|
668
|
-
url: "https://
|
|
778
|
+
url: "https://placehold.co/400x400?text=Product+3a"
|
|
669
779
|
},
|
|
670
780
|
price: {
|
|
671
781
|
amount: "6.7",
|
|
@@ -683,7 +793,7 @@ function createMockProductAdapter() {
|
|
|
683
793
|
id: "sample-3-2",
|
|
684
794
|
title: "Lavender and mint",
|
|
685
795
|
image: {
|
|
686
|
-
url: "https://
|
|
796
|
+
url: "https://placehold.co/400x400?text=Product+3b"
|
|
687
797
|
},
|
|
688
798
|
price: {
|
|
689
799
|
amount: "7.5",
|
|
@@ -715,6 +825,48 @@ function createMockProductAdapter() {
|
|
|
715
825
|
}
|
|
716
826
|
};
|
|
717
827
|
}
|
|
828
|
+
function toAvailableProduct(product) {
|
|
829
|
+
return {
|
|
830
|
+
id: product.id,
|
|
831
|
+
title: product.title,
|
|
832
|
+
description: `Sample description for ${product.title}.`,
|
|
833
|
+
featuredImage: product.featuredImage ?? void 0,
|
|
834
|
+
hasOnlyDefaultVariant: product.hasOnlyDefaultVariant,
|
|
835
|
+
variants: product.variants.map((variant) => {
|
|
836
|
+
var _a, _b, _c;
|
|
837
|
+
const base = Number.parseFloat(variant.price.amount);
|
|
838
|
+
const sub = (_b = (_a = variant.purchaseOptions) == null ? void 0 : _a[0]) == null ? void 0 : _b.price.amount;
|
|
839
|
+
const current = sub == null ? base : Number.parseFloat(sub);
|
|
840
|
+
return {
|
|
841
|
+
id: variant.id,
|
|
842
|
+
availableForSale: true,
|
|
843
|
+
title: variant.title,
|
|
844
|
+
currentPrice: { amount: current, currencyCode: variant.price.currencyCode },
|
|
845
|
+
basePrice: { amount: base, currencyCode: variant.price.currencyCode },
|
|
846
|
+
image: variant.image ?? void 0,
|
|
847
|
+
recurring: (((_c = variant.purchaseOptions) == null ? void 0 : _c.length) ?? 0) > 0
|
|
848
|
+
};
|
|
849
|
+
})
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
const sampleBox = {
|
|
853
|
+
id: "sample-box-1",
|
|
854
|
+
title: "Build Your Own Box",
|
|
855
|
+
minProductsCount: 3,
|
|
856
|
+
parentProduct: "sample-1",
|
|
857
|
+
tiers: [
|
|
858
|
+
{ value: 5, threshold: 3 },
|
|
859
|
+
{ value: 10, threshold: 5 }
|
|
860
|
+
],
|
|
861
|
+
variants: products.map((product) => ({
|
|
862
|
+
id: product.id,
|
|
863
|
+
variants: product.variants.map((variant) => ({ id: variant.id }))
|
|
864
|
+
})),
|
|
865
|
+
freeVariants: [],
|
|
866
|
+
thresholdType: "LINES_COUNT",
|
|
867
|
+
type: "PERCENTAGE",
|
|
868
|
+
restrictQuantityToTiers: false
|
|
869
|
+
};
|
|
718
870
|
return {
|
|
719
871
|
async getById(id) {
|
|
720
872
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
@@ -728,6 +880,27 @@ function createMockProductAdapter() {
|
|
|
728
880
|
);
|
|
729
881
|
return product == null ? null : processProduct(product);
|
|
730
882
|
},
|
|
883
|
+
async getProductsByIds({ ids }) {
|
|
884
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
885
|
+
return products.filter((product) => ids.includes(product.id)).map(({ variants: _variants, ...product }) => product);
|
|
886
|
+
},
|
|
887
|
+
async getBoxVariants({ ids }) {
|
|
888
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
889
|
+
return products.filter((product) => ids.includes(product.id)).map(toAvailableProduct);
|
|
890
|
+
},
|
|
891
|
+
async getAvailableProducts({ q }) {
|
|
892
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
893
|
+
const queryLower = q == null ? void 0 : q.toLowerCase();
|
|
894
|
+
return products.filter(
|
|
895
|
+
(product) => !queryLower || product.title.toLowerCase().includes(queryLower) || product.variants.some(
|
|
896
|
+
(variant) => variant.title.toLowerCase().includes(queryLower)
|
|
897
|
+
)
|
|
898
|
+
).map(toAvailableProduct);
|
|
899
|
+
},
|
|
900
|
+
async getBox({ id }) {
|
|
901
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
902
|
+
return id === sampleBox.id ? sampleBox : null;
|
|
903
|
+
},
|
|
731
904
|
async search(opts) {
|
|
732
905
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
733
906
|
let filteredProducts = [...products];
|
|
@@ -768,6 +941,185 @@ function createMockProductAdapter() {
|
|
|
768
941
|
}
|
|
769
942
|
};
|
|
770
943
|
}
|
|
944
|
+
function mapVariant(variant, currencyCode) {
|
|
945
|
+
var _a;
|
|
946
|
+
return {
|
|
947
|
+
id: variant.id,
|
|
948
|
+
title: variant.title,
|
|
949
|
+
image: variant.image == null ? null : { url: variant.image.url, altText: variant.image.altText ?? void 0 },
|
|
950
|
+
price: { amount: variant.price.toFixed(2), currencyCode },
|
|
951
|
+
...((_a = variant.purchaseOptions) == null ? void 0 : _a.length) ? {
|
|
952
|
+
purchaseOptions: variant.purchaseOptions.map((opt) => ({
|
|
953
|
+
name: opt.name,
|
|
954
|
+
price: { amount: opt.price.toFixed(2), currencyCode },
|
|
955
|
+
interval: opt.interval
|
|
956
|
+
}))
|
|
957
|
+
} : {}
|
|
958
|
+
};
|
|
959
|
+
}
|
|
960
|
+
function mapProduct(product, currencyCode) {
|
|
961
|
+
return {
|
|
962
|
+
id: product.id,
|
|
963
|
+
title: product.title,
|
|
964
|
+
featuredImage: product.image == null ? null : { url: product.image.url, altText: product.image.altText ?? void 0 },
|
|
965
|
+
hasOnlyDefaultVariant: product.variants.length <= 1,
|
|
966
|
+
variants: {
|
|
967
|
+
items: product.variants.map((v2) => mapVariant(v2, currencyCode)),
|
|
968
|
+
pageInfo: {
|
|
969
|
+
hasNextPage: false,
|
|
970
|
+
hasPrevPage: false,
|
|
971
|
+
startCursor: null,
|
|
972
|
+
endCursor: null
|
|
973
|
+
}
|
|
974
|
+
},
|
|
975
|
+
collections: product.collections
|
|
976
|
+
};
|
|
977
|
+
}
|
|
978
|
+
function createApiProductAdapter(fetcher, opts = { currencyCode: "USD" }) {
|
|
979
|
+
const { currencyCode } = opts;
|
|
980
|
+
async function fetchProducts(params) {
|
|
981
|
+
const query = params.toString() ? `?${params.toString()}` : "";
|
|
982
|
+
const res = await fetcher(`/customer/v1/products${query}`);
|
|
983
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
984
|
+
return res.json();
|
|
985
|
+
}
|
|
986
|
+
return {
|
|
987
|
+
async getById(id) {
|
|
988
|
+
try {
|
|
989
|
+
const params = new URLSearchParams({ query: `id:"${id}"`, limit: "1" });
|
|
990
|
+
const body = await fetchProducts(params);
|
|
991
|
+
return body.data.length > 0 ? mapProduct(body.data[0], currencyCode) : null;
|
|
992
|
+
} catch {
|
|
993
|
+
return null;
|
|
994
|
+
}
|
|
995
|
+
},
|
|
996
|
+
async getByVariantId(variantId) {
|
|
997
|
+
try {
|
|
998
|
+
const params = new URLSearchParams({
|
|
999
|
+
query: `variants.id:"${variantId}"`,
|
|
1000
|
+
limit: "1"
|
|
1001
|
+
});
|
|
1002
|
+
const body = await fetchProducts(params);
|
|
1003
|
+
return body.data.length > 0 ? mapProduct(body.data[0], currencyCode) : null;
|
|
1004
|
+
} catch {
|
|
1005
|
+
return null;
|
|
1006
|
+
}
|
|
1007
|
+
},
|
|
1008
|
+
async search(opts2) {
|
|
1009
|
+
try {
|
|
1010
|
+
const params = new URLSearchParams();
|
|
1011
|
+
if (opts2 == null ? void 0 : opts2.query) {
|
|
1012
|
+
params.set("query", `"${opts2.query.trim().replace(/"/g, '\\"')}"`);
|
|
1013
|
+
}
|
|
1014
|
+
if (opts2 == null ? void 0 : opts2.pagination) {
|
|
1015
|
+
if ("after" in opts2.pagination)
|
|
1016
|
+
params.set("after", opts2.pagination.after);
|
|
1017
|
+
else if ("before" in opts2.pagination)
|
|
1018
|
+
params.set("before", opts2.pagination.before);
|
|
1019
|
+
}
|
|
1020
|
+
const body = await fetchProducts(params);
|
|
1021
|
+
return {
|
|
1022
|
+
items: body.data.map((p2) => mapProduct(p2, currencyCode)),
|
|
1023
|
+
pageInfo: {
|
|
1024
|
+
hasNextPage: body.hasNextPage,
|
|
1025
|
+
hasPrevPage: body.hasPrevPage,
|
|
1026
|
+
startCursor: body.startCursor,
|
|
1027
|
+
endCursor: body.endCursor
|
|
1028
|
+
}
|
|
1029
|
+
};
|
|
1030
|
+
} catch {
|
|
1031
|
+
return {
|
|
1032
|
+
items: [],
|
|
1033
|
+
pageInfo: {
|
|
1034
|
+
hasNextPage: false,
|
|
1035
|
+
hasPrevPage: false,
|
|
1036
|
+
startCursor: null,
|
|
1037
|
+
endCursor: null
|
|
1038
|
+
}
|
|
1039
|
+
};
|
|
1040
|
+
}
|
|
1041
|
+
},
|
|
1042
|
+
async getProductsByIds({ ids }) {
|
|
1043
|
+
const params = new URLSearchParams();
|
|
1044
|
+
for (const id of ids) params.append("ids", id);
|
|
1045
|
+
const res = await fetcher(`/customer/products/by-ids?${params.toString()}`);
|
|
1046
|
+
if (!res.ok) return [];
|
|
1047
|
+
const data = await res.json();
|
|
1048
|
+
return (data ?? []).map((p2) => ({
|
|
1049
|
+
id: p2.id,
|
|
1050
|
+
title: p2.title ?? "",
|
|
1051
|
+
featuredImage: p2.featuredImage ? { url: p2.featuredImage.url, altText: p2.featuredImage.altText } : null,
|
|
1052
|
+
hasOnlyDefaultVariant: true,
|
|
1053
|
+
collections: []
|
|
1054
|
+
}));
|
|
1055
|
+
},
|
|
1056
|
+
async getBoxVariants({ ids, subscriptionId }) {
|
|
1057
|
+
const params = new URLSearchParams();
|
|
1058
|
+
for (const id of ids) params.append("ids", id);
|
|
1059
|
+
params.set("subscriptionId", subscriptionId);
|
|
1060
|
+
const res = await fetcher(`/customer/products/variants?${params.toString()}`);
|
|
1061
|
+
if (!res.ok) return [];
|
|
1062
|
+
const data = await res.json();
|
|
1063
|
+
return (data ?? []).map((p2) => {
|
|
1064
|
+
var _a;
|
|
1065
|
+
return {
|
|
1066
|
+
id: p2.id,
|
|
1067
|
+
title: p2.title,
|
|
1068
|
+
description: p2.description ?? "",
|
|
1069
|
+
featuredImage: p2.featuredImage,
|
|
1070
|
+
hasOnlyDefaultVariant: p2.hasOnlyDefaultVariant ?? (((_a = p2.variants) == null ? void 0 : _a.length) ?? 0) <= 1,
|
|
1071
|
+
variants: (p2.variants ?? []).map((v2) => ({
|
|
1072
|
+
id: v2.id,
|
|
1073
|
+
availableForSale: v2.availableForSale,
|
|
1074
|
+
title: v2.title,
|
|
1075
|
+
currentPrice: v2.currentPrice,
|
|
1076
|
+
basePrice: v2.basePrice,
|
|
1077
|
+
image: v2.image,
|
|
1078
|
+
recurring: v2.recurring
|
|
1079
|
+
}))
|
|
1080
|
+
};
|
|
1081
|
+
});
|
|
1082
|
+
},
|
|
1083
|
+
async getAvailableProducts({ subscriptionId, lineId, q, mode, action, pagination }) {
|
|
1084
|
+
const params = new URLSearchParams();
|
|
1085
|
+
params.set("action", action);
|
|
1086
|
+
if (q) params.set("q", q);
|
|
1087
|
+
if (mode) params.set("mode", mode);
|
|
1088
|
+
if (pagination && "after" in pagination) params.set("after", pagination.after);
|
|
1089
|
+
if (pagination && "before" in pagination) params.set("before", pagination.before);
|
|
1090
|
+
const res = await fetcher(
|
|
1091
|
+
`/customer/subscriptions/${subscriptionId}/lines/${lineId}/available-products?${params.toString()}`
|
|
1092
|
+
);
|
|
1093
|
+
if (!res.ok) return [];
|
|
1094
|
+
const data = await res.json();
|
|
1095
|
+
return (data ?? []).map((p2) => {
|
|
1096
|
+
var _a;
|
|
1097
|
+
return {
|
|
1098
|
+
id: p2.id,
|
|
1099
|
+
title: p2.title,
|
|
1100
|
+
description: p2.description ?? "",
|
|
1101
|
+
featuredImage: p2.featuredImage,
|
|
1102
|
+
hasOnlyDefaultVariant: p2.hasOnlyDefaultVariant ?? (((_a = p2.variants) == null ? void 0 : _a.length) ?? 0) <= 1,
|
|
1103
|
+
variants: (p2.variants ?? []).map((v2) => ({
|
|
1104
|
+
id: v2.id,
|
|
1105
|
+
availableForSale: v2.availableForSale,
|
|
1106
|
+
title: v2.title,
|
|
1107
|
+
currentPrice: v2.currentPrice,
|
|
1108
|
+
basePrice: v2.basePrice,
|
|
1109
|
+
image: v2.image,
|
|
1110
|
+
recurring: v2.recurring
|
|
1111
|
+
}))
|
|
1112
|
+
};
|
|
1113
|
+
});
|
|
1114
|
+
},
|
|
1115
|
+
async getBox({ id }) {
|
|
1116
|
+
const res = await fetcher(`/customer/boxes/${id}`);
|
|
1117
|
+
if (!res.ok) return null;
|
|
1118
|
+
const data = await res.json();
|
|
1119
|
+
return { id, ...data };
|
|
1120
|
+
}
|
|
1121
|
+
};
|
|
1122
|
+
}
|
|
771
1123
|
const ProductServiceContext = createContext("services/product");
|
|
772
1124
|
function createProductService(adapter) {
|
|
773
1125
|
return {
|
|
@@ -779,7 +1131,11 @@ function createProductService(adapter) {
|
|
|
779
1131
|
},
|
|
780
1132
|
async search(opts) {
|
|
781
1133
|
return adapter.search(opts);
|
|
782
|
-
}
|
|
1134
|
+
},
|
|
1135
|
+
getProductsByIds: (ids) => adapter.getProductsByIds({ ids }),
|
|
1136
|
+
getBoxVariants: (ids, subscriptionId) => adapter.getBoxVariants({ ids, subscriptionId }),
|
|
1137
|
+
getAvailableProducts: (params) => adapter.getAvailableProducts(params),
|
|
1138
|
+
getBox: (id) => adapter.getBox({ id })
|
|
783
1139
|
};
|
|
784
1140
|
}
|
|
785
1141
|
function routeNameTableFromPages(pages) {
|
|
@@ -796,7 +1152,7 @@ function routeNameTableFromPages(pages) {
|
|
|
796
1152
|
function createRouterService(options) {
|
|
797
1153
|
const { pages, pushImplementation } = options;
|
|
798
1154
|
const routeNameToPagePath = routeNameTableFromPages(pages);
|
|
799
|
-
const pathSet = new Set(pages.map((
|
|
1155
|
+
const pathSet = new Set(pages.map((p2) => p2.path));
|
|
800
1156
|
const navigationSubscribers = [];
|
|
801
1157
|
function notifySubscribers(logicalPath) {
|
|
802
1158
|
for (const cb of navigationSubscribers) cb({ path: logicalPath });
|
|
@@ -833,22 +1189,113 @@ function createMockRouterService() {
|
|
|
833
1189
|
}
|
|
834
1190
|
}).routerService;
|
|
835
1191
|
}
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
1192
|
+
const DISCOUNT_REJECTION_REASONS = [
|
|
1193
|
+
"INVALID_CODE",
|
|
1194
|
+
"INACTIVE",
|
|
1195
|
+
"NOT_APPLICABLE_TO_CUSTOMER",
|
|
1196
|
+
"ALREADY_ADDED",
|
|
1197
|
+
"ALREADY_USED",
|
|
1198
|
+
"NOT_APPLICABLE_TO_PRODUCTS",
|
|
1199
|
+
"NOT_APPLICABLE_TO_SUBSCRIPTION",
|
|
1200
|
+
"INVALID_COMBINATION",
|
|
1201
|
+
"QUANTITY_REQUIREMENT_NOT_MET"
|
|
1202
|
+
];
|
|
1203
|
+
function toAttributeArray(next) {
|
|
1204
|
+
return Object.entries(next).map(([key, value]) => ({ key, value }));
|
|
1205
|
+
}
|
|
1206
|
+
function createMockSubscriptionAdapter(options = {}) {
|
|
1207
|
+
const richBase = (id, itemId, renewalId) => ({
|
|
1208
|
+
id,
|
|
1209
|
+
customerId: "customer_1",
|
|
1210
|
+
items: [
|
|
1211
|
+
{
|
|
1212
|
+
id: itemId,
|
|
1213
|
+
// Aligned with the mock product catalog (createMockProductAdapter):
|
|
1214
|
+
// sample-3 "Gentle deodorant", variant sample-3-1 "Green tea and jasmine",
|
|
1215
|
+
// recurring price 5.36 EUR (10% off → 4.82 with SAVE10).
|
|
1216
|
+
productId: "sample-3",
|
|
1217
|
+
variantId: "sample-3-1",
|
|
1218
|
+
quantity: 2,
|
|
1219
|
+
price: { amount: "5.36", currencyCode: "EUR" },
|
|
1220
|
+
nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-15"),
|
|
1221
|
+
isUpsell: false,
|
|
1222
|
+
isVirtual: false,
|
|
1223
|
+
nextBillingDate: "2025-11-15T00:00:00Z",
|
|
1224
|
+
currentPrice: { amount: "5.36", currencyCode: "EUR" },
|
|
1225
|
+
lineDiscountedPrice: { amount: "4.82", currencyCode: "EUR" },
|
|
1226
|
+
variantAvailableForSale: true,
|
|
1227
|
+
title: "Gentle deodorant for underarms and bust",
|
|
1228
|
+
variantTitle: "Green tea and jasmine",
|
|
1229
|
+
variantImage: { url: "https://placehold.co/400x400?text=Product+3a" },
|
|
1230
|
+
billingPolicy: { intervalCount: 1, interval: "MONTH" },
|
|
1231
|
+
deliveryPolicy: { intervalCount: 1, interval: "MONTH" }
|
|
1232
|
+
}
|
|
1233
|
+
],
|
|
1234
|
+
discounts: [
|
|
1235
|
+
{
|
|
1236
|
+
id: "discount_1",
|
|
1237
|
+
type: "percentage",
|
|
1238
|
+
value: 10,
|
|
1239
|
+
code: "SAVE10",
|
|
1240
|
+
description: "10% off subscription"
|
|
1241
|
+
}
|
|
1242
|
+
],
|
|
1243
|
+
status: "active",
|
|
1244
|
+
nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-15"),
|
|
1245
|
+
frequency: "1 MONTH",
|
|
1246
|
+
createdAt: "2024-10-01T00:00:00Z",
|
|
1247
|
+
updatedAt: "2024-10-14T00:00:00Z",
|
|
1248
|
+
billingPolicy: { intervalCount: 1, interval: "MONTH", minCycles: 6 },
|
|
1249
|
+
deliveryPolicy: {
|
|
1250
|
+
intervalCount: 1,
|
|
1251
|
+
interval: "MONTH",
|
|
1252
|
+
anchors: [{ day: 15, type: "MONTHDAY" }]
|
|
1253
|
+
},
|
|
1254
|
+
nextBillingDate: "2025-11-15T00:00:00Z",
|
|
1255
|
+
nextBillingAttempt: { date: "2025-11-15T00:00:00Z", attempt: 1, ready: true },
|
|
1256
|
+
lastBillingAttempt: { date: "2025-10-15T00:00:00Z", ready: true },
|
|
1257
|
+
nextFulfillment: "2025-11-17T00:00:00Z",
|
|
1258
|
+
estimatedOrderDate: "2025-11-13T00:00:00Z",
|
|
1259
|
+
customerPaymentMethod: {
|
|
1260
|
+
id: "pm_1",
|
|
1261
|
+
instrument: { type: "credit_card", brand: "visa", maskedNumber: "************4242" },
|
|
1262
|
+
active: true,
|
|
1263
|
+
isRecurring: true
|
|
1264
|
+
},
|
|
1265
|
+
requiredBillingCyclesLeft: 3,
|
|
1266
|
+
renewal: { id: renewalId, isInActiveProcessState: false },
|
|
1267
|
+
lastPaymentStatus: "succeeded",
|
|
1268
|
+
pausePeriod: null,
|
|
1269
|
+
isEditable: true
|
|
1270
|
+
});
|
|
1271
|
+
const mockSubscriptions = [
|
|
1272
|
+
{
|
|
839
1273
|
id: "sub_1",
|
|
840
1274
|
customerId: "customer_1",
|
|
841
1275
|
items: [
|
|
842
1276
|
{
|
|
843
1277
|
id: "item_1",
|
|
844
|
-
|
|
845
|
-
|
|
1278
|
+
// Aligned with the mock product catalog (createMockProductAdapter):
|
|
1279
|
+
// sample-1 "Hydrating Gel Cleanser" (default variant), recurring price
|
|
1280
|
+
// 9.99 EUR (10% off → 8.99 with SAVE10).
|
|
1281
|
+
productId: "sample-1",
|
|
1282
|
+
variantId: "sample-1-1",
|
|
846
1283
|
quantity: 2,
|
|
847
|
-
price: { amount: "
|
|
1284
|
+
price: { amount: "9.99", currencyCode: "EUR" },
|
|
848
1285
|
nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-15"),
|
|
1286
|
+
isUpsell: false,
|
|
1287
|
+
isVirtual: false,
|
|
1288
|
+
nextBillingDate: "2025-11-15T00:00:00Z",
|
|
1289
|
+
currentPrice: { amount: "9.99", currencyCode: "EUR" },
|
|
1290
|
+
lineDiscountedPrice: { amount: "8.99", currencyCode: "EUR" },
|
|
1291
|
+
variantAvailableForSale: true,
|
|
1292
|
+
title: "Hydrating Gel Cleanser for Face and Eye Area",
|
|
1293
|
+
variantImage: { url: "https://placehold.co/400x400?text=Product+1" },
|
|
1294
|
+
billingPolicy: { intervalCount: 1, interval: "MONTH" },
|
|
1295
|
+
deliveryPolicy: { intervalCount: 1, interval: "MONTH" },
|
|
849
1296
|
customAttributes: [
|
|
850
|
-
{ key: "
|
|
851
|
-
{ key: "
|
|
1297
|
+
{ key: "skin_type", value: "combination" },
|
|
1298
|
+
{ key: "fragrance", value: "fragrance_free" }
|
|
852
1299
|
]
|
|
853
1300
|
}
|
|
854
1301
|
],
|
|
@@ -866,9 +1313,31 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
866
1313
|
frequency: "1 MONTH",
|
|
867
1314
|
createdAt: "2024-10-01T00:00:00Z",
|
|
868
1315
|
updatedAt: "2024-10-14T00:00:00Z",
|
|
1316
|
+
billingPolicy: { intervalCount: 1, interval: "MONTH", minCycles: 6 },
|
|
1317
|
+
deliveryPolicy: {
|
|
1318
|
+
intervalCount: 1,
|
|
1319
|
+
interval: "MONTH",
|
|
1320
|
+
anchors: [{ day: 15, type: "MONTHDAY" }]
|
|
1321
|
+
},
|
|
1322
|
+
nextBillingDate: "2025-11-15T00:00:00Z",
|
|
1323
|
+
nextBillingAttempt: { date: "2025-11-15T00:00:00Z", attempt: 1, ready: true },
|
|
1324
|
+
lastBillingAttempt: { date: "2025-10-15T00:00:00Z", ready: true },
|
|
1325
|
+
nextFulfillment: "2025-11-17T00:00:00Z",
|
|
1326
|
+
estimatedOrderDate: "2025-11-13T00:00:00Z",
|
|
1327
|
+
customerPaymentMethod: {
|
|
1328
|
+
id: "pm_1",
|
|
1329
|
+
instrument: { type: "credit_card", brand: "visa", maskedNumber: "************4242" },
|
|
1330
|
+
active: true,
|
|
1331
|
+
isRecurring: true
|
|
1332
|
+
},
|
|
1333
|
+
requiredBillingCyclesLeft: 3,
|
|
1334
|
+
renewal: { id: "ren_1", isInActiveProcessState: false },
|
|
1335
|
+
lastPaymentStatus: "succeeded",
|
|
1336
|
+
pausePeriod: null,
|
|
1337
|
+
isEditable: true,
|
|
869
1338
|
customAttributes: [
|
|
870
1339
|
{ key: "subscription_tier", value: "premium" },
|
|
871
|
-
{ key: "gift_message", value: "Enjoy your
|
|
1340
|
+
{ key: "gift_message", value: "Enjoy your skincare routine!" }
|
|
872
1341
|
]
|
|
873
1342
|
},
|
|
874
1343
|
{
|
|
@@ -877,11 +1346,17 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
877
1346
|
items: [
|
|
878
1347
|
{
|
|
879
1348
|
id: "item_2",
|
|
880
|
-
|
|
881
|
-
|
|
1349
|
+
// Aligned with the mock product catalog (createMockProductAdapter):
|
|
1350
|
+
// sample-2 "More COLLAGEN" (default variant), recurring price 10.99 EUR.
|
|
1351
|
+
productId: "sample-2",
|
|
1352
|
+
variantId: "sample-2-1",
|
|
882
1353
|
quantity: 1,
|
|
883
|
-
price: { amount: "
|
|
1354
|
+
price: { amount: "10.99", currencyCode: "EUR" },
|
|
884
1355
|
nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-20"),
|
|
1356
|
+
title: "More COLLAGEN",
|
|
1357
|
+
variantImage: { url: "https://placehold.co/400x400?text=Product+2" },
|
|
1358
|
+
billingPolicy: { intervalCount: 2, interval: "WEEK" },
|
|
1359
|
+
deliveryPolicy: { intervalCount: 2, interval: "WEEK" },
|
|
885
1360
|
customAttributes: []
|
|
886
1361
|
}
|
|
887
1362
|
],
|
|
@@ -892,17 +1367,66 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
892
1367
|
createdAt: "2024-09-15T00:00:00Z",
|
|
893
1368
|
updatedAt: "2024-10-10T00:00:00Z",
|
|
894
1369
|
customAttributes: []
|
|
1370
|
+
},
|
|
1371
|
+
// Per-status fixtures: groundwork for the deferred editor preview picker
|
|
1372
|
+
// (docs/superpowers/specs/2026-06-17-renewalsummary-parity-and-mock-groundwork-design.md §9).
|
|
1373
|
+
// Not consumed yet — the editor still seeds the active sub_1 via getDefaultSubscriptionId().
|
|
1374
|
+
{
|
|
1375
|
+
...richBase("sub_paused", "item_paused", "ren_paused"),
|
|
1376
|
+
status: "paused",
|
|
1377
|
+
pausePeriod: { start: "2025-11-01T00:00:00Z", end: "2026-01-01T00:00:00Z" }
|
|
1378
|
+
},
|
|
1379
|
+
{
|
|
1380
|
+
...richBase("sub_canceled", "item_canceled", "ren_canceled"),
|
|
1381
|
+
status: "canceled",
|
|
1382
|
+
pausePeriod: null
|
|
1383
|
+
},
|
|
1384
|
+
{
|
|
1385
|
+
...richBase("sub_failed", "item_failed", "ren_failed"),
|
|
1386
|
+
status: "failed",
|
|
1387
|
+
lastPaymentStatus: "failed",
|
|
1388
|
+
pausePeriod: null
|
|
1389
|
+
},
|
|
1390
|
+
{
|
|
1391
|
+
...richBase("sub_expired", "item_expired", "ren_expired"),
|
|
1392
|
+
status: "expired",
|
|
1393
|
+
pausePeriod: null
|
|
1394
|
+
}
|
|
1395
|
+
];
|
|
1396
|
+
const mockUpsellProducts = [
|
|
1397
|
+
{
|
|
1398
|
+
id: "upsell-1",
|
|
1399
|
+
variantId: "upsell-v-1",
|
|
1400
|
+
title: "Retinol Night Cream",
|
|
1401
|
+
imageUrl: "https://images.unsplash.com/photo-1617897903246-719242758050?w=200",
|
|
1402
|
+
price: { amount: "38.00", currencyCode: "USD" },
|
|
1403
|
+
description: "Anti-aging night treatment"
|
|
1404
|
+
},
|
|
1405
|
+
{
|
|
1406
|
+
id: "upsell-2",
|
|
1407
|
+
variantId: "upsell-v-2",
|
|
1408
|
+
title: "SPF 50 Day Shield",
|
|
1409
|
+
imageUrl: "https://images.unsplash.com/photo-1556228578-8c89e6adf883?w=200",
|
|
1410
|
+
price: { amount: "24.00", currencyCode: "USD" },
|
|
1411
|
+
description: "Lightweight daily sun protection"
|
|
895
1412
|
}
|
|
896
1413
|
];
|
|
897
1414
|
return {
|
|
898
1415
|
async getById(id) {
|
|
899
1416
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
900
|
-
const subscription = mockSubscriptions.find((sub) => sub.id === id)
|
|
901
|
-
return {
|
|
1417
|
+
const subscription = mockSubscriptions.find((sub) => sub.id === id);
|
|
1418
|
+
return {
|
|
1419
|
+
_tag: "Success",
|
|
1420
|
+
data: subscription ? {
|
|
1421
|
+
...subscription,
|
|
1422
|
+
items: subscription.items.map((item) => ({ ...item })),
|
|
1423
|
+
discounts: subscription.discounts.map((d3) => ({ ...d3 }))
|
|
1424
|
+
} : null
|
|
1425
|
+
};
|
|
902
1426
|
},
|
|
903
1427
|
async search(options2 = {}) {
|
|
904
1428
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
905
|
-
const pageSize =
|
|
1429
|
+
const pageSize = 2;
|
|
906
1430
|
const allItems = mockSubscriptions;
|
|
907
1431
|
let startIndex = 0;
|
|
908
1432
|
let endIndex = pageSize;
|
|
@@ -918,197 +1442,1520 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
918
1442
|
}
|
|
919
1443
|
}
|
|
920
1444
|
const pageItems = allItems.slice(startIndex, endIndex);
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
items:
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
1445
|
+
return {
|
|
1446
|
+
_tag: "Success",
|
|
1447
|
+
data: {
|
|
1448
|
+
items: pageItems.map((sub) => ({
|
|
1449
|
+
...sub,
|
|
1450
|
+
items: sub.items.map((item) => ({ ...item })),
|
|
1451
|
+
discounts: sub.discounts.map((discount) => ({ ...discount }))
|
|
1452
|
+
})),
|
|
1453
|
+
pageInfo: {
|
|
1454
|
+
hasNextPage: endIndex < allItems.length,
|
|
1455
|
+
hasPrevPage: startIndex > 0,
|
|
1456
|
+
startCursor: pageItems.length > 0 ? startIndex.toString() : null,
|
|
1457
|
+
endCursor: pageItems.length > 0 ? (endIndex - 1).toString() : null
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
};
|
|
1461
|
+
},
|
|
1462
|
+
async addItem({ subscriptionId, item }) {
|
|
1463
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
1464
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1465
|
+
const subscription = mockSubscriptions.find((sub) => sub.id === subscriptionId);
|
|
1466
|
+
if (!subscription) {
|
|
1467
|
+
return { _tag: "Failure", error: "Subscription not found" };
|
|
1468
|
+
}
|
|
1469
|
+
const currencyCode = ((_a = subscription.items[0]) == null ? void 0 : _a.price.currencyCode) ?? "USD";
|
|
1470
|
+
const product = await ((_b = options.productAdapter) == null ? void 0 : _b.getByVariantId(item.variantId)) ?? await ((_c = options.productAdapter) == null ? void 0 : _c.getById(item.variantId)) ?? null;
|
|
1471
|
+
const variant = (product == null ? void 0 : product.variants.items.find((v2) => v2.id === item.variantId)) ?? (product == null ? void 0 : product.variants.items[0]);
|
|
1472
|
+
const linePrice = ((_e = (_d = variant == null ? void 0 : variant.purchaseOptions) == null ? void 0 : _d[0]) == null ? void 0 : _e.price) ?? (variant == null ? void 0 : variant.price);
|
|
1473
|
+
subscription.items.push({
|
|
1474
|
+
id: `item_${Date.now()}_${subscription.items.length}`,
|
|
1475
|
+
productId: (product == null ? void 0 : product.id) ?? "",
|
|
1476
|
+
variantId: item.variantId,
|
|
1477
|
+
quantity: item.quantity,
|
|
1478
|
+
price: linePrice ? { ...linePrice } : { amount: "0.00", currencyCode },
|
|
1479
|
+
...linePrice ? { currentPrice: { ...linePrice } } : {},
|
|
1480
|
+
title: (product == null ? void 0 : product.title) ?? `Product ${item.variantId}`,
|
|
1481
|
+
...variant && product && !product.hasOnlyDefaultVariant ? { variantTitle: variant.title } : {},
|
|
1482
|
+
...(variant == null ? void 0 : variant.image) ? { variantImage: variant.image } : {},
|
|
1483
|
+
variantAvailableForSale: true,
|
|
1484
|
+
billingPolicy: ((_f = subscription.items[0]) == null ? void 0 : _f.billingPolicy) ?? subscription.billingPolicy ?? null,
|
|
1485
|
+
deliveryPolicy: ((_g = subscription.items[0]) == null ? void 0 : _g.deliveryPolicy) ?? subscription.deliveryPolicy ?? null
|
|
1486
|
+
});
|
|
1487
|
+
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1488
|
+
return { _tag: "Success", data: void 0 };
|
|
1489
|
+
},
|
|
1490
|
+
async removeItem({ subscriptionId, itemId }) {
|
|
1491
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1492
|
+
const subscription = mockSubscriptions.find((sub) => sub.id === subscriptionId);
|
|
1493
|
+
if (!subscription) {
|
|
1494
|
+
return { _tag: "Failure", error: "Subscription not found" };
|
|
1495
|
+
}
|
|
1496
|
+
subscription.items = subscription.items.filter((i2) => i2.id !== itemId);
|
|
1497
|
+
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1498
|
+
return { _tag: "Success", data: void 0 };
|
|
1499
|
+
},
|
|
1500
|
+
async updateItem({ subscriptionId, lineId, update }) {
|
|
1501
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1502
|
+
const subscription = mockSubscriptions.find((sub) => sub.id === subscriptionId);
|
|
1503
|
+
if (!subscription) {
|
|
1504
|
+
return { _tag: "Failure", error: "Subscription not found" };
|
|
1505
|
+
}
|
|
1506
|
+
const item = subscription.items.find((i2) => i2.id === lineId);
|
|
1507
|
+
if (!item) {
|
|
1508
|
+
return { _tag: "Failure", error: "Line item not found" };
|
|
1509
|
+
}
|
|
1510
|
+
if (update.variantId !== void 0) item.variantId = update.variantId;
|
|
1511
|
+
if (update.quantity !== void 0) item.quantity = update.quantity;
|
|
1512
|
+
if (update.billingPolicy !== void 0) item.billingPolicy = update.billingPolicy;
|
|
1513
|
+
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1514
|
+
return { _tag: "Success", data: void 0 };
|
|
1515
|
+
},
|
|
1516
|
+
async updateItemAttributes({ subscriptionId, lineId, customAttributes }) {
|
|
1517
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1518
|
+
const subscription = mockSubscriptions.find((sub) => sub.id === subscriptionId);
|
|
1519
|
+
if (!subscription) {
|
|
1520
|
+
return { _tag: "Failure", error: "Subscription not found" };
|
|
1521
|
+
}
|
|
1522
|
+
const item = subscription.items.find((i2) => i2.id === lineId);
|
|
1523
|
+
if (!item) {
|
|
1524
|
+
return { _tag: "Failure", error: "Line item not found" };
|
|
1525
|
+
}
|
|
1526
|
+
item.customAttributes = toAttributeArray(customAttributes);
|
|
1527
|
+
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1528
|
+
return { _tag: "Success", data: void 0 };
|
|
1529
|
+
},
|
|
1530
|
+
async updateAttributes({ subscriptionId, customAttributes }) {
|
|
1531
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1532
|
+
const subscription = mockSubscriptions.find((sub) => sub.id === subscriptionId);
|
|
1533
|
+
if (!subscription) {
|
|
1534
|
+
return { _tag: "Failure", error: "Subscription not found" };
|
|
1535
|
+
}
|
|
1536
|
+
subscription.customAttributes = toAttributeArray(customAttributes);
|
|
1537
|
+
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1538
|
+
return { _tag: "Success", data: void 0 };
|
|
1539
|
+
},
|
|
1540
|
+
async skipLineItem() {
|
|
1541
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1542
|
+
return { _tag: "Success", data: void 0 };
|
|
1543
|
+
},
|
|
1544
|
+
async updateBoxItems() {
|
|
1545
|
+
return { _tag: "Success", data: void 0 };
|
|
1546
|
+
},
|
|
1547
|
+
async addDiscount() {
|
|
1548
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1549
|
+
const behavior = options.addDiscountBehavior ?? { kind: "success" };
|
|
1550
|
+
if (behavior.kind === "success") {
|
|
1551
|
+
return { _tag: "Success", data: void 0 };
|
|
1552
|
+
}
|
|
1553
|
+
if (behavior.kind === "rejected") {
|
|
1554
|
+
return {
|
|
1555
|
+
_tag: "Failure",
|
|
1556
|
+
error: { kind: "REJECTED", reason: behavior.reason }
|
|
1557
|
+
};
|
|
1558
|
+
}
|
|
1559
|
+
return { _tag: "Failure", error: { kind: behavior.error } };
|
|
1560
|
+
},
|
|
1561
|
+
async removeDiscount() {
|
|
1562
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1563
|
+
const behavior = options.removeDiscountBehavior ?? { kind: "success" };
|
|
1564
|
+
if (behavior.kind === "success") {
|
|
1565
|
+
return { _tag: "Success", data: void 0 };
|
|
1566
|
+
}
|
|
1567
|
+
return { _tag: "Failure", error: { kind: behavior.error } };
|
|
1568
|
+
},
|
|
1569
|
+
async reschedule(params) {
|
|
1570
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1571
|
+
const subscription = mockSubscriptions.find(
|
|
1572
|
+
(sub) => sub.id === params.subscriptionId
|
|
1573
|
+
);
|
|
1574
|
+
if (!subscription) {
|
|
1575
|
+
return { _tag: "Failure", error: "Subscription not found" };
|
|
1576
|
+
}
|
|
1577
|
+
subscription.nextDeliveryDate = new Date(params.newDate);
|
|
1578
|
+
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1579
|
+
return { _tag: "Success", data: { ...subscription } };
|
|
1580
|
+
},
|
|
1581
|
+
async updateCustomAttributes(params) {
|
|
1582
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1583
|
+
const subscription = mockSubscriptions.find(
|
|
1584
|
+
(sub) => sub.id === params.subscriptionId
|
|
1585
|
+
);
|
|
1586
|
+
if (!subscription) {
|
|
1587
|
+
return { _tag: "Failure", error: "Subscription not found" };
|
|
1588
|
+
}
|
|
1589
|
+
const preserved = (subscription.customAttributes ?? []).filter(
|
|
1590
|
+
(attr) => attr.key.startsWith("_")
|
|
1591
|
+
);
|
|
1592
|
+
const next = Object.entries(params.customAttributes).map(
|
|
1593
|
+
([key, value]) => ({ key, value })
|
|
1594
|
+
);
|
|
1595
|
+
subscription.customAttributes = [...preserved, ...next];
|
|
1596
|
+
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1597
|
+
return { _tag: "Success", data: { ...subscription } };
|
|
1598
|
+
},
|
|
1599
|
+
async resume(params) {
|
|
1600
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1601
|
+
const subscription = mockSubscriptions.find(
|
|
1602
|
+
(sub) => sub.id === params.subscriptionId
|
|
1603
|
+
);
|
|
1604
|
+
if (!subscription) {
|
|
1605
|
+
return { _tag: "Failure", error: "Subscription not found" };
|
|
1606
|
+
}
|
|
1607
|
+
subscription.status = "active";
|
|
1608
|
+
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1609
|
+
return { _tag: "Success", data: { ...subscription } };
|
|
1610
|
+
},
|
|
1611
|
+
async pause(params) {
|
|
1612
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1613
|
+
const subscription = mockSubscriptions.find(
|
|
1614
|
+
(sub) => sub.id === params.subscriptionId
|
|
1615
|
+
);
|
|
1616
|
+
if (!subscription) {
|
|
1617
|
+
return { _tag: "Failure", error: "Subscription not found" };
|
|
1618
|
+
}
|
|
1619
|
+
subscription.status = "paused";
|
|
1620
|
+
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1621
|
+
return { _tag: "Success", data: { ...subscription } };
|
|
1622
|
+
},
|
|
1623
|
+
async reactivate(params) {
|
|
1624
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1625
|
+
const subscription = mockSubscriptions.find(
|
|
1626
|
+
(sub) => sub.id === params.subscriptionId
|
|
1627
|
+
);
|
|
1628
|
+
if (!subscription) {
|
|
1629
|
+
return { _tag: "Failure", error: "Subscription not found" };
|
|
1630
|
+
}
|
|
1631
|
+
subscription.status = "active";
|
|
1632
|
+
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1633
|
+
return { _tag: "Success", data: { ...subscription } };
|
|
1634
|
+
},
|
|
1635
|
+
async cancel(params) {
|
|
1636
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1637
|
+
const subscription = mockSubscriptions.find(
|
|
1638
|
+
(sub) => sub.id === params.subscriptionId
|
|
1639
|
+
);
|
|
1640
|
+
if (!subscription) {
|
|
1641
|
+
return { _tag: "Failure", error: "Subscription not found" };
|
|
1642
|
+
}
|
|
1643
|
+
subscription.status = "canceled";
|
|
1644
|
+
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1645
|
+
return { _tag: "Success", data: { ...subscription } };
|
|
1646
|
+
},
|
|
1647
|
+
async getAvailablePlans() {
|
|
1648
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1649
|
+
return {
|
|
1650
|
+
_tag: "Success",
|
|
1651
|
+
data: [
|
|
1652
|
+
{
|
|
1653
|
+
id: "plan_1",
|
|
1654
|
+
name: "Monthly",
|
|
1655
|
+
billingPolicy: { intervalCount: 1, interval: "MONTH" },
|
|
1656
|
+
deliveryPolicy: { intervalCount: 1, interval: "MONTH" }
|
|
1657
|
+
},
|
|
1658
|
+
{
|
|
1659
|
+
id: "plan_2",
|
|
1660
|
+
name: "Every 2 months",
|
|
1661
|
+
billingPolicy: { intervalCount: 2, interval: "MONTH" },
|
|
1662
|
+
deliveryPolicy: { intervalCount: 2, interval: "MONTH" }
|
|
1663
|
+
},
|
|
1664
|
+
{
|
|
1665
|
+
id: "plan_3",
|
|
1666
|
+
name: "Every 3 months",
|
|
1667
|
+
billingPolicy: { intervalCount: 3, interval: "MONTH" },
|
|
1668
|
+
deliveryPolicy: { intervalCount: 3, interval: "MONTH" }
|
|
1669
|
+
}
|
|
1670
|
+
]
|
|
1671
|
+
};
|
|
1672
|
+
},
|
|
1673
|
+
async getLastOrder(params) {
|
|
1674
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
1675
|
+
const subscription = mockSubscriptions.find((sub) => sub.id === params.subscriptionId);
|
|
1676
|
+
if (!subscription) return { _tag: "Success", data: null };
|
|
1677
|
+
return {
|
|
1678
|
+
_tag: "Success",
|
|
1679
|
+
data: {
|
|
1680
|
+
id: `mock_last_order_${params.subscriptionId}`,
|
|
1681
|
+
createdAt: "2026-04-22T12:00:00Z"
|
|
1682
|
+
}
|
|
1683
|
+
};
|
|
1684
|
+
},
|
|
1685
|
+
async renewNow(_params) {
|
|
1686
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1687
|
+
return { _tag: "Success", data: { result: "success" } };
|
|
1688
|
+
},
|
|
1689
|
+
async changeFrequency(params) {
|
|
1690
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1691
|
+
const subscription = mockSubscriptions.find((sub) => sub.id === params.subscriptionId);
|
|
1692
|
+
if (!subscription) {
|
|
1693
|
+
return { _tag: "Failure", error: "Subscription not found" };
|
|
1694
|
+
}
|
|
1695
|
+
if (typeof params.plan === "string") {
|
|
1696
|
+
subscription.frequency = `1 ${params.plan}`;
|
|
1697
|
+
} else {
|
|
1698
|
+
subscription.frequency = `${params.plan.intervalCount} ${params.plan.interval}`;
|
|
1699
|
+
if (subscription.billingPolicy) {
|
|
1700
|
+
subscription.billingPolicy = { ...subscription.billingPolicy, ...params.plan };
|
|
1701
|
+
}
|
|
1702
|
+
if (subscription.deliveryPolicy) {
|
|
1703
|
+
subscription.deliveryPolicy = { ...subscription.deliveryPolicy, ...params.plan };
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1707
|
+
return {
|
|
1708
|
+
_tag: "Success",
|
|
1709
|
+
data: {
|
|
1710
|
+
...subscription,
|
|
1711
|
+
items: subscription.items.map((item) => ({ ...item })),
|
|
1712
|
+
discounts: subscription.discounts.map((d3) => ({ ...d3 }))
|
|
932
1713
|
}
|
|
933
1714
|
};
|
|
934
|
-
return { _tag: "Success", data: paginatedList };
|
|
935
1715
|
},
|
|
936
|
-
async
|
|
937
|
-
|
|
938
|
-
|
|
1716
|
+
async getDefaultSubscriptionId() {
|
|
1717
|
+
var _a;
|
|
1718
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1719
|
+
return { _tag: "Success", data: ((_a = mockSubscriptions[0]) == null ? void 0 : _a.id) ?? null };
|
|
1720
|
+
},
|
|
1721
|
+
async getUpsellProducts() {
|
|
1722
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1723
|
+
return { _tag: "Success", data: [...mockUpsellProducts] };
|
|
1724
|
+
},
|
|
1725
|
+
async addUpsellProduct() {
|
|
1726
|
+
await new Promise((resolve) => setTimeout(resolve, 400));
|
|
1727
|
+
return { _tag: "Success", data: void 0 };
|
|
1728
|
+
}
|
|
1729
|
+
};
|
|
1730
|
+
}
|
|
1731
|
+
function normalizeInstrumentType(type) {
|
|
1732
|
+
return type.replace(/-/g, "_");
|
|
1733
|
+
}
|
|
1734
|
+
function mapPaymentMethod(rawPm) {
|
|
1735
|
+
if (!rawPm) return null;
|
|
1736
|
+
if (typeof rawPm === "string") {
|
|
1737
|
+
return { id: rawPm };
|
|
1738
|
+
}
|
|
1739
|
+
return {
|
|
1740
|
+
id: rawPm.id ?? "",
|
|
1741
|
+
...rawPm.instrument != null ? {
|
|
1742
|
+
instrument: {
|
|
1743
|
+
...rawPm.instrument,
|
|
1744
|
+
type: normalizeInstrumentType(rawPm.instrument.type)
|
|
1745
|
+
}
|
|
1746
|
+
} : {},
|
|
1747
|
+
...rawPm.active != null ? { active: rawPm.active } : {},
|
|
1748
|
+
...rawPm.isRecurring != null ? { isRecurring: rawPm.isRecurring } : {}
|
|
1749
|
+
};
|
|
1750
|
+
}
|
|
1751
|
+
function mapSubscription(raw) {
|
|
1752
|
+
var _a, _b;
|
|
1753
|
+
const status = raw.status === "merged" ? "canceled" : raw.status;
|
|
1754
|
+
const deliveryPolicy = raw.deliveryPolicy ?? ((_b = (_a = raw.items) == null ? void 0 : _a[0]) == null ? void 0 : _b.deliveryPolicy);
|
|
1755
|
+
const frequency = deliveryPolicy ? `${deliveryPolicy.intervalCount} ${deliveryPolicy.interval}` : "";
|
|
1756
|
+
return {
|
|
1757
|
+
id: raw.id,
|
|
1758
|
+
customerId: "",
|
|
1759
|
+
serial: raw.serial,
|
|
1760
|
+
items: (raw.items ?? []).map(
|
|
1761
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1762
|
+
(item) => ({
|
|
1763
|
+
id: item.id,
|
|
1764
|
+
productId: item.productId ?? item.product ?? "",
|
|
1765
|
+
variantId: item.variantId ?? item.variant ?? "",
|
|
1766
|
+
quantity: item.quantity,
|
|
1767
|
+
price: item.price ?? {
|
|
1768
|
+
amount: String(item.totalPrice ?? 0),
|
|
1769
|
+
currencyCode: raw.currencyCode ?? ""
|
|
1770
|
+
},
|
|
1771
|
+
nextDeliveryDate: item.nextDeliveryDate ? new Date(item.nextDeliveryDate) : null,
|
|
1772
|
+
billingPolicy: item.billingPolicy ?? raw.billingPolicy ?? null,
|
|
1773
|
+
deliveryPolicy: item.deliveryPolicy ?? raw.deliveryPolicy ?? null,
|
|
1774
|
+
// Map human-readable titles from the v1 SubscriptionItemResource
|
|
1775
|
+
title: item.title,
|
|
1776
|
+
variantTitle: item.subtitle ?? void 0,
|
|
1777
|
+
// recurringCycleLimit === 1 means the item charges exactly once (one-time upsell)
|
|
1778
|
+
recurringCycleLimit: item.recurringCycleLimit ?? null,
|
|
1779
|
+
customAttributes: Array.isArray(item.customAttributes) ? item.customAttributes : []
|
|
1780
|
+
})
|
|
1781
|
+
),
|
|
1782
|
+
discounts: (raw.discounts ?? []).map(
|
|
1783
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1784
|
+
(d3) => {
|
|
1785
|
+
var _a2, _b2, _c;
|
|
1786
|
+
return {
|
|
1787
|
+
id: d3.id,
|
|
1788
|
+
type: ((_a2 = d3.value) == null ? void 0 : _a2.type) === "fixed-amount" || d3.type === "fixed_amount" ? "fixed_amount" : "percentage",
|
|
1789
|
+
value: ((_b2 = d3.value) == null ? void 0 : _b2.amount) ?? ((_c = d3.value) == null ? void 0 : _c.percentage) ?? d3.value ?? 0,
|
|
1790
|
+
code: d3.title ?? d3.code ?? "",
|
|
1791
|
+
description: d3.description ?? ""
|
|
1792
|
+
};
|
|
1793
|
+
}
|
|
1794
|
+
),
|
|
1795
|
+
status,
|
|
1796
|
+
nextDeliveryDate: raw.nextBillingDate ? new Date(raw.nextBillingDate) : raw.nextDeliveryDate ? new Date(raw.nextDeliveryDate) : null,
|
|
1797
|
+
// Map nextBillingDate as the raw ISO string for ProductSelect skip() and other consumers
|
|
1798
|
+
nextBillingDate: raw.nextBillingDate ?? null,
|
|
1799
|
+
// Map customerPaymentMethod — expanded to an object (id + instrument) via the
|
|
1800
|
+
// adapter's ?expand=paymentMethod request; mapPaymentMethod tolerates a bare id too.
|
|
1801
|
+
customerPaymentMethod: mapPaymentMethod(raw.paymentMethod),
|
|
1802
|
+
frequency,
|
|
1803
|
+
createdAt: raw.createdAt,
|
|
1804
|
+
updatedAt: raw.updatedAt,
|
|
1805
|
+
billingPolicy: raw.billingPolicy ?? null,
|
|
1806
|
+
deliveryPolicy: raw.deliveryPolicy ?? null,
|
|
1807
|
+
customAttributes: Array.isArray(raw.customAttributes) ? raw.customAttributes : []
|
|
1808
|
+
};
|
|
1809
|
+
}
|
|
1810
|
+
async function readErrorBody(res) {
|
|
1811
|
+
try {
|
|
1812
|
+
const body = await res.json();
|
|
1813
|
+
return body != null && typeof body === "object" ? body : {};
|
|
1814
|
+
} catch {
|
|
1815
|
+
return {};
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
function createApiSubscriptionAdapter(fetcher) {
|
|
1819
|
+
const adapter = {
|
|
1820
|
+
async search(options) {
|
|
1821
|
+
var _a;
|
|
1822
|
+
try {
|
|
1823
|
+
const params = new URLSearchParams();
|
|
1824
|
+
if ((_a = options == null ? void 0 : options.filters) == null ? void 0 : _a.length) {
|
|
1825
|
+
const groups = options.filters.filter((f) => Object.keys(f).length > 0).map((f) => Object.entries(f).filter(([, v2]) => Boolean(v2))).filter((entries) => entries.length > 0).map(
|
|
1826
|
+
(entries) => "(" + entries.map(([k, v2]) => `${k}:'${v2}'`).join(" AND ") + ")"
|
|
1827
|
+
);
|
|
1828
|
+
if (groups.length) params.set("query", groups.join(" OR "));
|
|
1829
|
+
}
|
|
1830
|
+
if (options == null ? void 0 : options.includeCanceledItems) {
|
|
1831
|
+
params.set("includeCanceledItems", "true");
|
|
1832
|
+
}
|
|
1833
|
+
params.set("expand", "paymentMethod");
|
|
1834
|
+
if (options == null ? void 0 : options.pagination) {
|
|
1835
|
+
if ("after" in options.pagination)
|
|
1836
|
+
params.set("after", options.pagination.after);
|
|
1837
|
+
else if ("before" in options.pagination)
|
|
1838
|
+
params.set("before", options.pagination.before);
|
|
1839
|
+
}
|
|
1840
|
+
const query = params.toString() ? `?${params.toString()}` : "";
|
|
1841
|
+
const res = await fetcher(`/customer/v1/subscriptions${query}`);
|
|
1842
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1843
|
+
const body = await res.json();
|
|
1844
|
+
return {
|
|
1845
|
+
_tag: "Success",
|
|
1846
|
+
data: {
|
|
1847
|
+
items: body.data.map(mapSubscription),
|
|
1848
|
+
pageInfo: {
|
|
1849
|
+
hasNextPage: body.hasNextPage,
|
|
1850
|
+
hasPrevPage: body.hasPrevPage,
|
|
1851
|
+
startCursor: body.startCursor,
|
|
1852
|
+
endCursor: body.endCursor
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
};
|
|
1856
|
+
} catch (e2) {
|
|
1857
|
+
return { _tag: "Failure", error: e2 };
|
|
1858
|
+
}
|
|
1859
|
+
},
|
|
1860
|
+
async getById(id) {
|
|
1861
|
+
try {
|
|
1862
|
+
const res = await fetcher(
|
|
1863
|
+
`/customer/v1/subscriptions?query=id:"${id}"&limit=1&expand=paymentMethod`
|
|
1864
|
+
);
|
|
1865
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1866
|
+
const body = await res.json();
|
|
1867
|
+
const raw = body.data[0];
|
|
1868
|
+
return { _tag: "Success", data: raw ? mapSubscription(raw) : null };
|
|
1869
|
+
} catch (e2) {
|
|
1870
|
+
return { _tag: "Failure", error: e2 };
|
|
1871
|
+
}
|
|
1872
|
+
},
|
|
1873
|
+
async addItem({ subscriptionId, item }) {
|
|
1874
|
+
try {
|
|
1875
|
+
const res = await fetcher(
|
|
1876
|
+
`/customer/v1/subscriptions/${subscriptionId}/items`,
|
|
1877
|
+
{
|
|
1878
|
+
method: "POST",
|
|
1879
|
+
headers: { "Content-Type": "application/json" },
|
|
1880
|
+
body: JSON.stringify({ variant: item.variantId, quantity: item.quantity })
|
|
1881
|
+
}
|
|
1882
|
+
);
|
|
1883
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1884
|
+
return { _tag: "Success", data: void 0 };
|
|
1885
|
+
} catch (e2) {
|
|
1886
|
+
return { _tag: "Failure", error: e2 };
|
|
1887
|
+
}
|
|
1888
|
+
},
|
|
1889
|
+
async removeItem({ subscriptionId, itemId }) {
|
|
1890
|
+
try {
|
|
1891
|
+
const res = await fetcher(
|
|
1892
|
+
`/customer/v1/subscriptions/${subscriptionId}/items/${itemId}`,
|
|
1893
|
+
{ method: "DELETE" }
|
|
1894
|
+
);
|
|
1895
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1896
|
+
return { _tag: "Success", data: void 0 };
|
|
1897
|
+
} catch (e2) {
|
|
1898
|
+
return { _tag: "Failure", error: e2 };
|
|
1899
|
+
}
|
|
1900
|
+
},
|
|
1901
|
+
async updateItem({ subscriptionId, lineId, update }) {
|
|
1902
|
+
try {
|
|
1903
|
+
const body = {
|
|
1904
|
+
...update.variantId !== void 0 ? { productVariantId: update.variantId } : {},
|
|
1905
|
+
...update.quantity !== void 0 ? { quantity: update.quantity } : {},
|
|
1906
|
+
...update.billingPolicy !== void 0 ? { billingPolicy: update.billingPolicy } : {},
|
|
1907
|
+
...update.shippingMethodHandle !== void 0 ? { shippingMethodHandle: update.shippingMethodHandle } : {}
|
|
1908
|
+
};
|
|
1909
|
+
const res = await fetcher(
|
|
1910
|
+
`/customer/subscriptions/${subscriptionId}/lines/${lineId}`,
|
|
1911
|
+
{
|
|
1912
|
+
method: "PUT",
|
|
1913
|
+
headers: { "Content-Type": "application/json" },
|
|
1914
|
+
body: JSON.stringify(body)
|
|
1915
|
+
}
|
|
1916
|
+
);
|
|
1917
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1918
|
+
return { _tag: "Success", data: void 0 };
|
|
1919
|
+
} catch (e2) {
|
|
1920
|
+
return { _tag: "Failure", error: e2 };
|
|
1921
|
+
}
|
|
1922
|
+
},
|
|
1923
|
+
async updateItemAttributes({ subscriptionId, lineId, customAttributes }) {
|
|
1924
|
+
try {
|
|
1925
|
+
const res = await fetcher(
|
|
1926
|
+
`/customer/v1/subscriptions/${subscriptionId}/items/${lineId}`,
|
|
1927
|
+
{
|
|
1928
|
+
method: "PATCH",
|
|
1929
|
+
headers: { "Content-Type": "application/json" },
|
|
1930
|
+
body: JSON.stringify({ customAttributes })
|
|
1931
|
+
}
|
|
1932
|
+
);
|
|
1933
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1934
|
+
return { _tag: "Success", data: void 0 };
|
|
1935
|
+
} catch (e2) {
|
|
1936
|
+
return { _tag: "Failure", error: e2 };
|
|
1937
|
+
}
|
|
1938
|
+
},
|
|
1939
|
+
async updateAttributes({ subscriptionId, customAttributes }) {
|
|
1940
|
+
try {
|
|
1941
|
+
const res = await fetcher(
|
|
1942
|
+
`/customer/v1/subscriptions/${subscriptionId}`,
|
|
1943
|
+
{
|
|
1944
|
+
method: "PATCH",
|
|
1945
|
+
headers: { "Content-Type": "application/json" },
|
|
1946
|
+
body: JSON.stringify({ customAttributes })
|
|
1947
|
+
}
|
|
1948
|
+
);
|
|
1949
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1950
|
+
return { _tag: "Success", data: void 0 };
|
|
1951
|
+
} catch (e2) {
|
|
1952
|
+
return { _tag: "Failure", error: e2 };
|
|
1953
|
+
}
|
|
1954
|
+
},
|
|
1955
|
+
async skipLineItem({ subscriptionId, lineId, nextBillingDate }) {
|
|
1956
|
+
try {
|
|
1957
|
+
const res = await fetcher(
|
|
1958
|
+
`/customer/subscriptions/${subscriptionId}/lines/${lineId}`,
|
|
1959
|
+
{
|
|
1960
|
+
method: "PUT",
|
|
1961
|
+
headers: { "Content-Type": "application/json" },
|
|
1962
|
+
body: JSON.stringify({ nextBillingDate })
|
|
1963
|
+
}
|
|
1964
|
+
);
|
|
1965
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1966
|
+
return { _tag: "Success", data: void 0 };
|
|
1967
|
+
} catch (e2) {
|
|
1968
|
+
return { _tag: "Failure", error: e2 };
|
|
1969
|
+
}
|
|
1970
|
+
},
|
|
1971
|
+
async updateBoxItems({ subscriptionId, parentId, items, shippingMethodHandle }) {
|
|
1972
|
+
try {
|
|
1973
|
+
const res = await fetcher(
|
|
1974
|
+
`/customer/subscriptions/${subscriptionId}/update-box-products`,
|
|
1975
|
+
{
|
|
1976
|
+
method: "POST",
|
|
1977
|
+
headers: { "Content-Type": "application/json" },
|
|
1978
|
+
body: JSON.stringify({ parentId, items, shippingMethodHandle })
|
|
1979
|
+
}
|
|
1980
|
+
);
|
|
1981
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1982
|
+
return { _tag: "Success", data: void 0 };
|
|
1983
|
+
} catch (e2) {
|
|
1984
|
+
return { _tag: "Failure", error: e2 };
|
|
1985
|
+
}
|
|
1986
|
+
},
|
|
1987
|
+
async addDiscount({ subscriptionId, code }) {
|
|
1988
|
+
try {
|
|
1989
|
+
const res = await fetcher(
|
|
1990
|
+
`/customer/v1/subscriptions/${subscriptionId}/discounts`,
|
|
1991
|
+
{
|
|
1992
|
+
method: "POST",
|
|
1993
|
+
headers: { "Content-Type": "application/json" },
|
|
1994
|
+
body: JSON.stringify({ code })
|
|
1995
|
+
}
|
|
1996
|
+
);
|
|
1997
|
+
if (!res.ok) {
|
|
1998
|
+
const body = await readErrorBody(res);
|
|
1999
|
+
if (res.status === 400 && body.code === "DISCOUNT_CODE_REJECTED") {
|
|
2000
|
+
const raw = body.rejectionReason;
|
|
2001
|
+
const reason = typeof raw === "string" && DISCOUNT_REJECTION_REASONS.includes(raw) ? raw : "INVALID_CODE";
|
|
2002
|
+
return { _tag: "Failure", error: { kind: "REJECTED", reason } };
|
|
2003
|
+
}
|
|
2004
|
+
if (res.status === 404) {
|
|
2005
|
+
return { _tag: "Failure", error: { kind: "SUBSCRIPTION_NOT_FOUND" } };
|
|
2006
|
+
}
|
|
2007
|
+
return { _tag: "Failure", error: { kind: "UNKNOWN", message: `HTTP ${res.status}` } };
|
|
2008
|
+
}
|
|
2009
|
+
return { _tag: "Success", data: void 0 };
|
|
2010
|
+
} catch (e2) {
|
|
2011
|
+
return {
|
|
2012
|
+
_tag: "Failure",
|
|
2013
|
+
error: { kind: "NETWORK", message: e2 instanceof Error ? e2.message : void 0 }
|
|
2014
|
+
};
|
|
2015
|
+
}
|
|
2016
|
+
},
|
|
2017
|
+
async removeDiscount({ subscriptionId, discountId }) {
|
|
2018
|
+
try {
|
|
2019
|
+
const res = await fetcher(
|
|
2020
|
+
`/customer/v1/subscriptions/${subscriptionId}/discounts/${discountId}`,
|
|
2021
|
+
{ method: "DELETE" }
|
|
2022
|
+
);
|
|
2023
|
+
if (!res.ok) {
|
|
2024
|
+
if (res.status === 404) {
|
|
2025
|
+
const body = await readErrorBody(res);
|
|
2026
|
+
const message = typeof body.message === "string" ? body.message : "";
|
|
2027
|
+
if (message.toLowerCase().includes("discount")) {
|
|
2028
|
+
return { _tag: "Failure", error: { kind: "DISCOUNT_NOT_FOUND" } };
|
|
2029
|
+
}
|
|
2030
|
+
return { _tag: "Failure", error: { kind: "SUBSCRIPTION_NOT_FOUND" } };
|
|
2031
|
+
}
|
|
2032
|
+
return { _tag: "Failure", error: { kind: "UNKNOWN", message: `HTTP ${res.status}` } };
|
|
2033
|
+
}
|
|
2034
|
+
return { _tag: "Success", data: void 0 };
|
|
2035
|
+
} catch (e2) {
|
|
2036
|
+
return {
|
|
2037
|
+
_tag: "Failure",
|
|
2038
|
+
error: { kind: "NETWORK", message: e2 instanceof Error ? e2.message : void 0 }
|
|
2039
|
+
};
|
|
2040
|
+
}
|
|
2041
|
+
},
|
|
2042
|
+
// Uses PATCH /customer/v1/subscriptions/{id} — the dedicated /reschedule route does not exist.
|
|
2043
|
+
async reschedule({ subscriptionId, newDate }) {
|
|
2044
|
+
try {
|
|
2045
|
+
const res = await fetcher(
|
|
2046
|
+
`/customer/v1/subscriptions/${subscriptionId}`,
|
|
2047
|
+
{
|
|
2048
|
+
method: "PATCH",
|
|
2049
|
+
headers: { "Content-Type": "application/json" },
|
|
2050
|
+
body: JSON.stringify({ nextBillingDate: newDate })
|
|
2051
|
+
}
|
|
2052
|
+
);
|
|
2053
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2054
|
+
const data = await res.json();
|
|
2055
|
+
return { _tag: "Success", data: mapSubscription(data) };
|
|
2056
|
+
} catch (e2) {
|
|
2057
|
+
return { _tag: "Failure", error: e2 };
|
|
2058
|
+
}
|
|
2059
|
+
},
|
|
2060
|
+
// PATCH /customer/v1/subscriptions/{id} with the full customer-visible set.
|
|
2061
|
+
async updateCustomAttributes({ subscriptionId, customAttributes }) {
|
|
2062
|
+
try {
|
|
2063
|
+
const res = await fetcher(
|
|
2064
|
+
`/customer/v1/subscriptions/${subscriptionId}`,
|
|
2065
|
+
{
|
|
2066
|
+
method: "PATCH",
|
|
2067
|
+
headers: { "Content-Type": "application/json" },
|
|
2068
|
+
body: JSON.stringify({ customAttributes })
|
|
2069
|
+
}
|
|
2070
|
+
);
|
|
2071
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2072
|
+
const data = await res.json();
|
|
2073
|
+
return { _tag: "Success", data: mapSubscription(data) };
|
|
2074
|
+
} catch (e2) {
|
|
2075
|
+
return { _tag: "Failure", error: e2 };
|
|
2076
|
+
}
|
|
2077
|
+
},
|
|
2078
|
+
async resume({ subscriptionId }) {
|
|
2079
|
+
try {
|
|
2080
|
+
const res = await fetcher(
|
|
2081
|
+
`/customer/v1/subscriptions/${subscriptionId}/resume`,
|
|
2082
|
+
{ method: "POST" }
|
|
2083
|
+
);
|
|
2084
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2085
|
+
const data = await res.json();
|
|
2086
|
+
return { _tag: "Success", data: mapSubscription(data) };
|
|
2087
|
+
} catch (e2) {
|
|
2088
|
+
return { _tag: "Failure", error: e2 };
|
|
2089
|
+
}
|
|
2090
|
+
},
|
|
2091
|
+
async pause({ subscriptionId }) {
|
|
2092
|
+
try {
|
|
2093
|
+
const res = await fetcher(
|
|
2094
|
+
`/customer/v1/subscriptions/${subscriptionId}/pause`,
|
|
2095
|
+
{ method: "POST" }
|
|
2096
|
+
);
|
|
2097
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2098
|
+
const data = await res.json();
|
|
2099
|
+
return { _tag: "Success", data: mapSubscription(data) };
|
|
2100
|
+
} catch (e2) {
|
|
2101
|
+
return { _tag: "Failure", error: e2 };
|
|
2102
|
+
}
|
|
2103
|
+
},
|
|
2104
|
+
async reactivate({ subscriptionId }) {
|
|
2105
|
+
try {
|
|
2106
|
+
const res = await fetcher(
|
|
2107
|
+
`/customer/v1/subscriptions/${subscriptionId}/reactivate`,
|
|
2108
|
+
{ method: "POST" }
|
|
2109
|
+
);
|
|
2110
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2111
|
+
const data = await res.json();
|
|
2112
|
+
return { _tag: "Success", data: mapSubscription(data) };
|
|
2113
|
+
} catch (e2) {
|
|
2114
|
+
return { _tag: "Failure", error: e2 };
|
|
2115
|
+
}
|
|
2116
|
+
},
|
|
2117
|
+
async cancel({ subscriptionId, reason }) {
|
|
2118
|
+
try {
|
|
2119
|
+
const res = await fetcher(
|
|
2120
|
+
`/customer/v1/subscriptions/${subscriptionId}/cancel`,
|
|
2121
|
+
{
|
|
2122
|
+
method: "POST",
|
|
2123
|
+
headers: { "Content-Type": "application/json" },
|
|
2124
|
+
body: JSON.stringify({ cancellationReason: reason })
|
|
2125
|
+
}
|
|
2126
|
+
);
|
|
2127
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2128
|
+
const data = await res.json();
|
|
2129
|
+
return { _tag: "Success", data: mapSubscription(data) };
|
|
2130
|
+
} catch (e2) {
|
|
2131
|
+
return { _tag: "Failure", error: e2 };
|
|
2132
|
+
}
|
|
2133
|
+
},
|
|
2134
|
+
// Legacy endpoint — no /customer/v1/subscriptions/{id}/plans exists yet.
|
|
2135
|
+
async getAvailablePlans({ subscriptionId }) {
|
|
2136
|
+
try {
|
|
2137
|
+
const res = await fetcher(
|
|
2138
|
+
`/customer/subscriptions/${subscriptionId}/plans`
|
|
2139
|
+
);
|
|
2140
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2141
|
+
const data = await res.json();
|
|
2142
|
+
return { _tag: "Success", data };
|
|
2143
|
+
} catch (e2) {
|
|
2144
|
+
return { _tag: "Failure", error: e2 };
|
|
2145
|
+
}
|
|
2146
|
+
},
|
|
2147
|
+
// Legacy endpoint — no /customer/v1 equivalent exists yet.
|
|
2148
|
+
async getDefaultSubscriptionId() {
|
|
2149
|
+
try {
|
|
2150
|
+
const res = await fetcher(
|
|
2151
|
+
`/customer/subscriptions/one-by-next-billing-date`
|
|
2152
|
+
);
|
|
2153
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2154
|
+
const data = await res.json();
|
|
2155
|
+
return { _tag: "Success", data: (data == null ? void 0 : data.id) ?? null };
|
|
2156
|
+
} catch (e2) {
|
|
2157
|
+
return { _tag: "Failure", error: e2 };
|
|
2158
|
+
}
|
|
2159
|
+
},
|
|
2160
|
+
// Legacy endpoint — no /customer/v1/subscriptions/{id}/upsells exists yet.
|
|
2161
|
+
async getUpsellProducts({ subscriptionId }) {
|
|
2162
|
+
try {
|
|
2163
|
+
const res = await fetcher(
|
|
2164
|
+
`/customer/subscriptions/${subscriptionId}/upsell-products`
|
|
2165
|
+
);
|
|
2166
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2167
|
+
const data = await res.json();
|
|
2168
|
+
const products = data.map((p2) => {
|
|
2169
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
2170
|
+
return {
|
|
2171
|
+
id: p2.id,
|
|
2172
|
+
variantId: ((_b = (_a = p2.variants) == null ? void 0 : _a[0]) == null ? void 0 : _b.id) ?? "",
|
|
2173
|
+
title: p2.title ?? "",
|
|
2174
|
+
imageUrl: ((_c = p2.featuredImage) == null ? void 0 : _c.url) ?? void 0,
|
|
2175
|
+
price: {
|
|
2176
|
+
amount: String(((_f = (_e = (_d = p2.variants) == null ? void 0 : _d[0]) == null ? void 0 : _e.currentPrice) == null ? void 0 : _f.amount) ?? 0),
|
|
2177
|
+
currencyCode: ((_i = (_h = (_g = p2.variants) == null ? void 0 : _g[0]) == null ? void 0 : _h.currentPrice) == null ? void 0 : _i.currencyCode) ?? ""
|
|
2178
|
+
},
|
|
2179
|
+
// recurring comes from the upsell rule on variants[0]; present in PreparedProductSchema
|
|
2180
|
+
recurring: (_k = (_j = p2.variants) == null ? void 0 : _j[0]) == null ? void 0 : _k.recurring
|
|
2181
|
+
};
|
|
2182
|
+
});
|
|
2183
|
+
return { _tag: "Success", data: products };
|
|
2184
|
+
} catch (e2) {
|
|
2185
|
+
return { _tag: "Failure", error: e2 };
|
|
2186
|
+
}
|
|
2187
|
+
},
|
|
2188
|
+
// Legacy endpoint — no /customer/v1/subscriptions/{id}/upsells POST exists yet.
|
|
2189
|
+
async addUpsellProduct({ subscriptionId, variantId, quantity, oneTime }) {
|
|
2190
|
+
try {
|
|
2191
|
+
const res = await fetcher(
|
|
2192
|
+
`/customer/subscriptions/${subscriptionId}/upsell`,
|
|
2193
|
+
{
|
|
2194
|
+
method: "POST",
|
|
2195
|
+
headers: { "Content-Type": "application/json" },
|
|
2196
|
+
body: JSON.stringify({
|
|
2197
|
+
productVariantId: variantId,
|
|
2198
|
+
quantity,
|
|
2199
|
+
...oneTime ? { renewalLimit: 1 } : {}
|
|
2200
|
+
})
|
|
2201
|
+
}
|
|
2202
|
+
);
|
|
2203
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2204
|
+
return { _tag: "Success", data: void 0 };
|
|
2205
|
+
} catch (e2) {
|
|
2206
|
+
return { _tag: "Failure", error: e2 };
|
|
2207
|
+
}
|
|
2208
|
+
},
|
|
2209
|
+
// Legacy endpoint — no /customer/v1 equivalent exists yet.
|
|
2210
|
+
async getLastOrder({ subscriptionId }) {
|
|
2211
|
+
try {
|
|
2212
|
+
const res = await fetcher(
|
|
2213
|
+
`/customer/subscriptions/${subscriptionId}/last-order`
|
|
2214
|
+
);
|
|
2215
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2216
|
+
const data = await res.json();
|
|
2217
|
+
if (!data) return { _tag: "Success", data: null };
|
|
2218
|
+
return {
|
|
2219
|
+
_tag: "Success",
|
|
2220
|
+
data: { id: data.id, createdAt: data.createdAt }
|
|
2221
|
+
};
|
|
2222
|
+
} catch (e2) {
|
|
2223
|
+
return { _tag: "Failure", error: e2 };
|
|
2224
|
+
}
|
|
2225
|
+
},
|
|
2226
|
+
// Legacy endpoints, split by stage (see SubscriptionService.renewNow docs):
|
|
2227
|
+
// retry → POST /customer/subscriptions/{id}/retry
|
|
2228
|
+
// renewalId → POST /customer/renewals/{renewalId}/renew
|
|
2229
|
+
// default → POST /customer/subscriptions/{id}/order-now
|
|
2230
|
+
async renewNow({ subscriptionId, renewalId, retry }) {
|
|
2231
|
+
try {
|
|
2232
|
+
const url = retry ? `/customer/subscriptions/${subscriptionId}/retry` : renewalId ? `/customer/renewals/${renewalId}/renew` : `/customer/subscriptions/${subscriptionId}/order-now`;
|
|
2233
|
+
const res = await fetcher(url, { method: "POST" });
|
|
2234
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2235
|
+
const body = res.status === 204 ? null : await res.json();
|
|
2236
|
+
return {
|
|
2237
|
+
_tag: "Success",
|
|
2238
|
+
data: {
|
|
2239
|
+
nextActionUrl: (body == null ? void 0 : body.nextActionUrl) ?? void 0,
|
|
2240
|
+
result: (body == null ? void 0 : body.result) ?? "success"
|
|
2241
|
+
}
|
|
2242
|
+
};
|
|
2243
|
+
} catch (e2) {
|
|
2244
|
+
return { _tag: "Failure", error: e2 };
|
|
2245
|
+
}
|
|
2246
|
+
},
|
|
2247
|
+
// Legacy endpoint — POST /plan returns 204; re-fetch to return the updated subscription.
|
|
2248
|
+
async changeFrequency({ subscriptionId, plan }) {
|
|
2249
|
+
try {
|
|
2250
|
+
const res = await fetcher(
|
|
2251
|
+
`/customer/subscriptions/${subscriptionId}/plan`,
|
|
2252
|
+
{
|
|
2253
|
+
method: "POST",
|
|
2254
|
+
headers: { "Content-Type": "application/json" },
|
|
2255
|
+
body: JSON.stringify({ plan })
|
|
2256
|
+
}
|
|
2257
|
+
);
|
|
2258
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2259
|
+
const refreshed = await adapter.getById(subscriptionId);
|
|
2260
|
+
if (refreshed._tag === "Failure") return refreshed;
|
|
2261
|
+
if (refreshed.data) return { _tag: "Success", data: refreshed.data };
|
|
2262
|
+
return {
|
|
2263
|
+
_tag: "Failure",
|
|
2264
|
+
error: "Subscription not found after frequency change"
|
|
2265
|
+
};
|
|
2266
|
+
} catch (e2) {
|
|
2267
|
+
return { _tag: "Failure", error: e2 };
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2270
|
+
};
|
|
2271
|
+
return adapter;
|
|
2272
|
+
}
|
|
2273
|
+
function expandableId(field) {
|
|
2274
|
+
if (typeof field === "string") return field;
|
|
2275
|
+
if (field != null && typeof field === "object" && "id" in field) {
|
|
2276
|
+
const id = field.id;
|
|
2277
|
+
return typeof id === "string" ? id : void 0;
|
|
2278
|
+
}
|
|
2279
|
+
return void 0;
|
|
2280
|
+
}
|
|
2281
|
+
function mapScheduleOrder(order) {
|
|
2282
|
+
return {
|
|
2283
|
+
id: order.id,
|
|
2284
|
+
customerId: order.customerId,
|
|
2285
|
+
subscriptionId: order.subscriptionId,
|
|
2286
|
+
date: order.date,
|
|
2287
|
+
skipped: order.skipped ?? false,
|
|
2288
|
+
currencyCode: order.currencyCode,
|
|
2289
|
+
subtotal: order.subtotal,
|
|
2290
|
+
deliveryPrice: order.deliveryPrice,
|
|
2291
|
+
cycleIndex: order.cycleIndex,
|
|
2292
|
+
deliveryDate: order.deliveryDate ?? order.date,
|
|
2293
|
+
status: order.status ?? (order.skipped ? "skipped" : "scheduled"),
|
|
2294
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2295
|
+
items: (order.items ?? []).map((item) => {
|
|
2296
|
+
var _a;
|
|
2297
|
+
return {
|
|
2298
|
+
id: item.id,
|
|
2299
|
+
title: item.title,
|
|
2300
|
+
subtitle: item.subtitle ?? null,
|
|
2301
|
+
imageUrl: item.imageUrl ?? ((_a = item.variantImage) == null ? void 0 : _a.url) ?? void 0,
|
|
2302
|
+
variantId: expandableId(item.variant) ?? "",
|
|
2303
|
+
quantity: item.quantity,
|
|
2304
|
+
price: item.price,
|
|
2305
|
+
totalPrice: item.totalPrice,
|
|
2306
|
+
skipped: item.skipped ?? false,
|
|
2307
|
+
variantImage: item.variantImage != null ? { url: item.variantImage.url ?? "", altText: item.variantImage.altText ?? void 0 } : null,
|
|
2308
|
+
variantAvailableForSale: item.variantAvailableForSale ?? void 0,
|
|
2309
|
+
subscriptionId: expandableId(item.subscription) ?? "",
|
|
2310
|
+
subscriptionLineId: expandableId(item.subscriptionItem) ?? "",
|
|
2311
|
+
productId: expandableId(item.product),
|
|
2312
|
+
parentProductId: item.parentProductId ?? null,
|
|
2313
|
+
parentLineId: item.parentLineId ?? null,
|
|
2314
|
+
isVirtual: item.isVirtual ?? void 0,
|
|
2315
|
+
isUpsell: item.isUpsell ?? void 0,
|
|
2316
|
+
recurringCycleLimit: item.recurringCycleLimit ?? null,
|
|
2317
|
+
customAttributes: item.customAttributes ?? [],
|
|
2318
|
+
sellingPlanId: item.sellingPlanId ?? null,
|
|
2319
|
+
sellingPlanName: item.sellingPlanName ?? null,
|
|
2320
|
+
nextBillingDate: item.nextBillingDate ?? null,
|
|
2321
|
+
billingPolicy: item.billingPolicy ?? null,
|
|
2322
|
+
subscriptionStatus: item.subscriptionStatus ?? null,
|
|
2323
|
+
subscriptionIsEditable: item.subscriptionIsEditable ?? null,
|
|
2324
|
+
requiredBillingCyclesLeft: item.requiredBillingCyclesLeft ?? null
|
|
2325
|
+
};
|
|
2326
|
+
}),
|
|
2327
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2328
|
+
discounts: (order.discounts ?? []).map((d3) => {
|
|
2329
|
+
var _a, _b, _c;
|
|
2330
|
+
return {
|
|
2331
|
+
id: d3.id,
|
|
2332
|
+
title: d3.title ?? void 0,
|
|
2333
|
+
type: ((_a = d3.value) == null ? void 0 : _a.type) === "fixed-amount" ? "fixed_amount" : "percentage",
|
|
2334
|
+
value: ((_b = d3.value) == null ? void 0 : _b.amount) ?? ((_c = d3.value) == null ? void 0 : _c.percentage) ?? 0
|
|
2335
|
+
};
|
|
2336
|
+
})
|
|
2337
|
+
};
|
|
2338
|
+
}
|
|
2339
|
+
function toIsoOrNull(value) {
|
|
2340
|
+
const date = new Date(value);
|
|
2341
|
+
return Number.isNaN(date.getTime()) ? null : date.toISOString();
|
|
2342
|
+
}
|
|
2343
|
+
function buildMatcher(params) {
|
|
2344
|
+
const { cycle, date } = params;
|
|
2345
|
+
let iso;
|
|
2346
|
+
if (date != null) {
|
|
2347
|
+
const parsed = toIsoOrNull(date);
|
|
2348
|
+
if (parsed == null) return { ok: false, error: "date must be a valid date" };
|
|
2349
|
+
iso = parsed;
|
|
2350
|
+
}
|
|
2351
|
+
if (cycle != null && iso != null) {
|
|
2352
|
+
return { ok: true, matcher: { type: "CYCLE_AND_DATE", input: { cycle, date: iso } } };
|
|
2353
|
+
}
|
|
2354
|
+
if (cycle != null) return { ok: true, matcher: { type: "CYCLE", input: { cycle } } };
|
|
2355
|
+
if (iso != null) return { ok: true, matcher: { type: "DATE", input: { date: iso } } };
|
|
2356
|
+
return { ok: false, error: "Either cycle or date is required" };
|
|
2357
|
+
}
|
|
2358
|
+
function buildSkipItemMatcher(params) {
|
|
2359
|
+
const { cycle, date } = params;
|
|
2360
|
+
if (cycle != null) {
|
|
2361
|
+
return { ok: true, matcher: { type: "CYCLE", input: { cycle } } };
|
|
2362
|
+
}
|
|
2363
|
+
if (date != null) {
|
|
2364
|
+
const iso = toIsoOrNull(date);
|
|
2365
|
+
if (iso == null) return { ok: false, error: "date must be a valid date" };
|
|
2366
|
+
return { ok: true, matcher: { type: "DATE", input: { date: iso } } };
|
|
2367
|
+
}
|
|
2368
|
+
return { ok: false, error: "Either cycle or date is required" };
|
|
2369
|
+
}
|
|
2370
|
+
const DELIVERY_PRICE = 5;
|
|
2371
|
+
const DELIVERY_OFFSET_DAYS = 2;
|
|
2372
|
+
function toPrice(amount) {
|
|
2373
|
+
const n = Number.parseFloat(amount ?? "0");
|
|
2374
|
+
return Number.isFinite(n) ? n : 0;
|
|
2375
|
+
}
|
|
2376
|
+
function addInterval(date, interval, units) {
|
|
2377
|
+
const d3 = new Date(date);
|
|
2378
|
+
switch (interval) {
|
|
2379
|
+
case "DAY":
|
|
2380
|
+
d3.setDate(d3.getDate() + units);
|
|
2381
|
+
break;
|
|
2382
|
+
case "WEEK":
|
|
2383
|
+
d3.setDate(d3.getDate() + units * 7);
|
|
2384
|
+
break;
|
|
2385
|
+
case "YEAR":
|
|
2386
|
+
d3.setFullYear(d3.getFullYear() + units);
|
|
2387
|
+
break;
|
|
2388
|
+
default:
|
|
2389
|
+
d3.setMonth(d3.getMonth() + units);
|
|
2390
|
+
break;
|
|
2391
|
+
}
|
|
2392
|
+
return d3;
|
|
2393
|
+
}
|
|
2394
|
+
function mapItem(sub, item, cycle) {
|
|
2395
|
+
var _a, _b;
|
|
2396
|
+
const price = toPrice(((_a = item.currentPrice) == null ? void 0 : _a.amount) ?? item.price.amount);
|
|
2397
|
+
return {
|
|
2398
|
+
id: `${sub.id}_cycle_${cycle}_${item.id}`,
|
|
2399
|
+
title: item.title ?? `Product ${item.variantId}`,
|
|
2400
|
+
subtitle: item.variantTitle ?? null,
|
|
2401
|
+
imageUrl: (_b = item.variantImage) == null ? void 0 : _b.url,
|
|
2402
|
+
variantId: item.variantId,
|
|
2403
|
+
quantity: item.quantity,
|
|
2404
|
+
price,
|
|
2405
|
+
totalPrice: price * item.quantity,
|
|
2406
|
+
skipped: false,
|
|
2407
|
+
variantImage: item.variantImage ?? null,
|
|
2408
|
+
variantAvailableForSale: item.variantAvailableForSale ?? true,
|
|
2409
|
+
subscriptionId: sub.id,
|
|
2410
|
+
subscriptionLineId: item.id,
|
|
2411
|
+
productId: item.productId,
|
|
2412
|
+
parentProductId: item.parentProductId ?? null,
|
|
2413
|
+
parentLineId: item.parentLineId ?? null,
|
|
2414
|
+
isVirtual: item.isVirtual ?? false,
|
|
2415
|
+
isUpsell: item.isUpsell ?? false,
|
|
2416
|
+
recurringCycleLimit: item.recurringCycleLimit ?? null,
|
|
2417
|
+
customAttributes: item.customAttributes ?? []
|
|
2418
|
+
};
|
|
2419
|
+
}
|
|
2420
|
+
function projectUpcomingOrders(sub, count) {
|
|
2421
|
+
var _a, _b;
|
|
2422
|
+
if (sub.status !== "active") return [];
|
|
2423
|
+
const billing = sub.billingPolicy ?? ((_a = sub.items[0]) == null ? void 0 : _a.billingPolicy) ?? { interval: "MONTH", intervalCount: 1 };
|
|
2424
|
+
const baseStr = sub.nextBillingDate ?? (sub.nextDeliveryDate ? sub.nextDeliveryDate.toISOString() : null);
|
|
2425
|
+
const base = baseStr ? new Date(baseStr) : /* @__PURE__ */ new Date();
|
|
2426
|
+
const currencyCode = ((_b = sub.items[0]) == null ? void 0 : _b.price.currencyCode) ?? "USD";
|
|
2427
|
+
const orders = [];
|
|
2428
|
+
for (let cycle = 0; cycle < count; cycle++) {
|
|
2429
|
+
const billingDate = addInterval(
|
|
2430
|
+
base,
|
|
2431
|
+
billing.interval,
|
|
2432
|
+
cycle * billing.intervalCount
|
|
2433
|
+
);
|
|
2434
|
+
const deliveryDate = addInterval(billingDate, "DAY", DELIVERY_OFFSET_DAYS);
|
|
2435
|
+
const items = sub.items.map((item) => mapItem(sub, item, cycle));
|
|
2436
|
+
const subtotal = items.reduce((sum, it) => sum + it.totalPrice, 0);
|
|
2437
|
+
orders.push({
|
|
2438
|
+
id: `${sub.id}_cycle_${cycle}`,
|
|
2439
|
+
customerId: sub.customerId,
|
|
2440
|
+
subscriptionId: sub.id,
|
|
2441
|
+
date: billingDate.toISOString(),
|
|
2442
|
+
skipped: false,
|
|
2443
|
+
currencyCode,
|
|
2444
|
+
subtotal,
|
|
2445
|
+
deliveryPrice: DELIVERY_PRICE,
|
|
2446
|
+
cycleIndex: cycle,
|
|
2447
|
+
deliveryDate: deliveryDate.toISOString(),
|
|
2448
|
+
status: "scheduled",
|
|
2449
|
+
items,
|
|
2450
|
+
discounts: sub.discounts.map((d3) => ({
|
|
2451
|
+
id: d3.id,
|
|
2452
|
+
title: d3.code,
|
|
2453
|
+
type: d3.type,
|
|
2454
|
+
value: d3.value
|
|
2455
|
+
}))
|
|
2456
|
+
});
|
|
2457
|
+
}
|
|
2458
|
+
return orders;
|
|
2459
|
+
}
|
|
2460
|
+
function applyAdjustments(orders, stored) {
|
|
2461
|
+
return orders.map((order) => {
|
|
2462
|
+
let next = order;
|
|
2463
|
+
for (const { adjustment, subscriptionId, cycle } of stored) {
|
|
2464
|
+
if (subscriptionId !== order.subscriptionId) continue;
|
|
2465
|
+
if (cycle !== order.cycleIndex) continue;
|
|
2466
|
+
const { action } = adjustment;
|
|
2467
|
+
if (action.type === "SKIP_ORDER") {
|
|
2468
|
+
next = { ...next, skipped: true, status: "skipped" };
|
|
2469
|
+
} else if (action.type === "CHANGE_DATE") {
|
|
2470
|
+
next = {
|
|
2471
|
+
...next,
|
|
2472
|
+
date: action.input.newDate,
|
|
2473
|
+
deliveryDate: action.input.newDate
|
|
2474
|
+
};
|
|
2475
|
+
} else if (action.type === "SKIP_ITEM") {
|
|
2476
|
+
const lineIds = new Set(action.input.lineIds);
|
|
2477
|
+
const items = next.items.map(
|
|
2478
|
+
(item) => lineIds.has(item.subscriptionLineId) ? { ...item, skipped: true, totalPrice: 0 } : item
|
|
2479
|
+
);
|
|
2480
|
+
const subtotal = items.reduce((sum, it) => sum + it.totalPrice, 0);
|
|
2481
|
+
next = { ...next, items, subtotal };
|
|
2482
|
+
if (items.length > 0 && items.every((i2) => i2.skipped)) {
|
|
2483
|
+
next = { ...next, skipped: true, status: "skipped", deliveryPrice: 0 };
|
|
2484
|
+
}
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2487
|
+
return next;
|
|
2488
|
+
});
|
|
2489
|
+
}
|
|
2490
|
+
function createMockSchedulesAdapter(subscriptions = createMockSubscriptionAdapter()) {
|
|
2491
|
+
const RESOLVE_DEPTH = 12;
|
|
2492
|
+
let stored = [];
|
|
2493
|
+
let idSeq = 0;
|
|
2494
|
+
async function subscriptionsToProject(subscriptionId) {
|
|
2495
|
+
if (subscriptionId) {
|
|
2496
|
+
const res = await subscriptions.getById(subscriptionId);
|
|
2497
|
+
return res._tag === "Success" && res.data ? [res.data] : [];
|
|
2498
|
+
}
|
|
2499
|
+
const all = [];
|
|
2500
|
+
let pagination;
|
|
2501
|
+
for (let guard = 0; guard < 50; guard++) {
|
|
2502
|
+
const res = await subscriptions.search(pagination ? { pagination } : {});
|
|
2503
|
+
if (res._tag !== "Success") break;
|
|
2504
|
+
all.push(...res.data.items);
|
|
2505
|
+
const { hasNextPage, endCursor } = res.data.pageInfo;
|
|
2506
|
+
if (!hasNextPage || !endCursor) break;
|
|
2507
|
+
pagination = { after: endCursor };
|
|
2508
|
+
}
|
|
2509
|
+
return all;
|
|
2510
|
+
}
|
|
2511
|
+
function resolveCycle(orders, opts) {
|
|
2512
|
+
if (opts.cycle != null) return opts.cycle;
|
|
2513
|
+
if (opts.date) {
|
|
2514
|
+
const iso = toIsoOrNull(opts.date);
|
|
2515
|
+
const target = iso == null ? void 0 : iso.split("T")[0];
|
|
2516
|
+
const match = target ? orders.find(
|
|
2517
|
+
(o2) => o2.deliveryDate.split("T")[0] === target || o2.date.split("T")[0] === target
|
|
2518
|
+
) : void 0;
|
|
2519
|
+
return match ? match.cycleIndex : null;
|
|
2520
|
+
}
|
|
2521
|
+
const firstScheduled = orders.find((o2) => o2.status === "scheduled");
|
|
2522
|
+
return (firstScheduled == null ? void 0 : firstScheduled.cycleIndex) ?? null;
|
|
2523
|
+
}
|
|
2524
|
+
async function recordAdjustment(subscriptionId, opts, buildAction, matcherOverride) {
|
|
2525
|
+
const res = await subscriptions.getById(subscriptionId);
|
|
2526
|
+
const sub = res._tag === "Success" ? res.data : null;
|
|
2527
|
+
if (!sub) return { _tag: "Failure", error: "No scheduled order found" };
|
|
2528
|
+
const orders = applyAdjustments(
|
|
2529
|
+
projectUpcomingOrders(sub, RESOLVE_DEPTH),
|
|
2530
|
+
stored
|
|
2531
|
+
);
|
|
2532
|
+
const cycle = resolveCycle(orders, opts);
|
|
2533
|
+
if (cycle == null) {
|
|
2534
|
+
return { _tag: "Failure", error: "No scheduled order found" };
|
|
2535
|
+
}
|
|
2536
|
+
const built = buildMatcher({ cycle, date: opts.date });
|
|
2537
|
+
const matcher = matcherOverride ?? (built.ok ? built.matcher : { type: "CYCLE", input: { cycle } });
|
|
2538
|
+
stored = [
|
|
2539
|
+
...stored,
|
|
2540
|
+
{
|
|
2541
|
+
adjustment: { id: `adj-${idSeq++}`, matcher, action: buildAction() },
|
|
2542
|
+
subscriptionId,
|
|
2543
|
+
cycle
|
|
2544
|
+
}
|
|
2545
|
+
];
|
|
2546
|
+
return { _tag: "Success", data: void 0 };
|
|
2547
|
+
}
|
|
2548
|
+
return {
|
|
2549
|
+
async getUpcomingOrders({ count, subscriptionId, excludeSkipped = true }) {
|
|
2550
|
+
await new Promise((r2) => setTimeout(r2, 200));
|
|
2551
|
+
const subs = await subscriptionsToProject(subscriptionId);
|
|
2552
|
+
const skipCount = new Set(
|
|
2553
|
+
stored.filter(
|
|
2554
|
+
(s) => s.adjustment.action.type === "SKIP_ORDER" || s.adjustment.action.type === "SKIP_ITEM"
|
|
2555
|
+
).map((s) => `${s.subscriptionId}:${s.cycle}`)
|
|
2556
|
+
).size;
|
|
2557
|
+
const depth = excludeSkipped ? count + skipCount : count;
|
|
2558
|
+
const orders = subs.flatMap(
|
|
2559
|
+
(sub) => applyAdjustments(projectUpcomingOrders(sub, depth), stored)
|
|
2560
|
+
).filter((o2) => excludeSkipped ? !o2.skipped : true).map(
|
|
2561
|
+
(o2) => (
|
|
2562
|
+
// Mirror backend removeSkippedItems: the billable view also strips
|
|
2563
|
+
// skipped lines from surviving (partially-skipped) orders. No
|
|
2564
|
+
// recompute needed — skipped lines already carry totalPrice 0 (R4).
|
|
2565
|
+
excludeSkipped && o2.items.some((i2) => i2.skipped) ? { ...o2, items: o2.items.filter((i2) => !i2.skipped) } : o2
|
|
2566
|
+
)
|
|
2567
|
+
).sort((a8, b2) => a8.date.localeCompare(b2.date));
|
|
2568
|
+
return { _tag: "Success", data: orders.slice(0, count) };
|
|
2569
|
+
},
|
|
2570
|
+
async getAdjustments() {
|
|
2571
|
+
await new Promise((r2) => setTimeout(r2, 200));
|
|
2572
|
+
return { _tag: "Success", data: stored.map((s) => s.adjustment) };
|
|
2573
|
+
},
|
|
2574
|
+
async skipOrder({ subscriptionId, cycle, date }) {
|
|
2575
|
+
await new Promise((r2) => setTimeout(r2, 300));
|
|
2576
|
+
return recordAdjustment(subscriptionId, { cycle, date }, () => ({
|
|
2577
|
+
type: "SKIP_ORDER",
|
|
2578
|
+
input: { reason: "" }
|
|
2579
|
+
}));
|
|
2580
|
+
},
|
|
2581
|
+
async skipItem({ subscriptionId, lineId, cycle, date, reason }) {
|
|
2582
|
+
await new Promise((r2) => setTimeout(r2, 300));
|
|
2583
|
+
const built = buildSkipItemMatcher({ cycle, date });
|
|
2584
|
+
if (!built.ok) return { _tag: "Failure", error: built.error };
|
|
2585
|
+
return recordAdjustment(
|
|
2586
|
+
subscriptionId,
|
|
2587
|
+
{ cycle, date },
|
|
2588
|
+
() => ({
|
|
2589
|
+
type: "SKIP_ITEM",
|
|
2590
|
+
input: { lineIds: [lineId], reason: reason ?? "" }
|
|
2591
|
+
}),
|
|
2592
|
+
built.matcher
|
|
2593
|
+
);
|
|
2594
|
+
},
|
|
2595
|
+
async changeDateAdjustment({ subscriptionId, newDate, cycle, currentDate }) {
|
|
2596
|
+
await new Promise((r2) => setTimeout(r2, 300));
|
|
2597
|
+
const newIso = toIsoOrNull(newDate);
|
|
2598
|
+
if (newIso == null) {
|
|
2599
|
+
return { _tag: "Failure", error: "newDate must be a valid date" };
|
|
2600
|
+
}
|
|
2601
|
+
return recordAdjustment(
|
|
2602
|
+
subscriptionId,
|
|
2603
|
+
{ cycle, date: currentDate },
|
|
2604
|
+
() => ({
|
|
2605
|
+
type: "CHANGE_DATE",
|
|
2606
|
+
input: { newDate: newIso }
|
|
2607
|
+
})
|
|
2608
|
+
);
|
|
2609
|
+
},
|
|
2610
|
+
async updateShippingAdjustment({ subscriptionId, address, cycle, date }) {
|
|
2611
|
+
await new Promise((r2) => setTimeout(r2, 300));
|
|
2612
|
+
return recordAdjustment(subscriptionId, { cycle, date }, () => ({
|
|
2613
|
+
type: "UPDATE_SHIPPING",
|
|
2614
|
+
input: { address }
|
|
2615
|
+
}));
|
|
2616
|
+
},
|
|
2617
|
+
async updatePaymentMethodAdjustment({
|
|
2618
|
+
subscriptionId,
|
|
2619
|
+
paymentMethodId,
|
|
2620
|
+
cycle,
|
|
2621
|
+
date
|
|
2622
|
+
}) {
|
|
2623
|
+
await new Promise((r2) => setTimeout(r2, 300));
|
|
2624
|
+
return recordAdjustment(subscriptionId, { cycle, date }, () => ({
|
|
2625
|
+
type: "UPDATE_PAYMENT_METHOD",
|
|
2626
|
+
input: { paymentMethodId }
|
|
2627
|
+
}));
|
|
2628
|
+
},
|
|
2629
|
+
async updateProductsAdjustment({ subscriptionId, lines, cycle, date }) {
|
|
2630
|
+
await new Promise((r2) => setTimeout(r2, 300));
|
|
2631
|
+
return recordAdjustment(subscriptionId, { cycle, date }, () => ({
|
|
2632
|
+
type: "UPDATE_PRODUCTS",
|
|
2633
|
+
input: { lines }
|
|
2634
|
+
}));
|
|
939
2635
|
},
|
|
940
|
-
async
|
|
941
|
-
await new Promise((
|
|
942
|
-
return {
|
|
2636
|
+
async applyDiscountAdjustment({ subscriptionId, discountCode, cycle }) {
|
|
2637
|
+
await new Promise((r2) => setTimeout(r2, 300));
|
|
2638
|
+
return recordAdjustment(subscriptionId, { cycle }, () => ({
|
|
2639
|
+
type: "APPLY_DISCOUNT",
|
|
2640
|
+
input: { discountSource: "code", discountCode }
|
|
2641
|
+
}));
|
|
943
2642
|
},
|
|
944
|
-
async
|
|
945
|
-
await new Promise((
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
2643
|
+
async removeAdjustment({ adjustmentId }) {
|
|
2644
|
+
await new Promise((r2) => setTimeout(r2, 200));
|
|
2645
|
+
stored = stored.filter((s) => s.adjustment.id !== adjustmentId);
|
|
2646
|
+
return { _tag: "Success", data: void 0 };
|
|
2647
|
+
}
|
|
2648
|
+
};
|
|
2649
|
+
}
|
|
2650
|
+
function createApiSchedulesAdapter(fetcher) {
|
|
2651
|
+
async function postAdjustment(matcher, action) {
|
|
2652
|
+
try {
|
|
2653
|
+
const res = await fetcher(`/customer/v1/schedules/adjustments`, {
|
|
2654
|
+
method: "POST",
|
|
2655
|
+
headers: { "Content-Type": "application/json" },
|
|
2656
|
+
body: JSON.stringify({ matcher, action })
|
|
2657
|
+
});
|
|
2658
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2659
|
+
return { _tag: "Success", data: void 0 };
|
|
2660
|
+
} catch (e2) {
|
|
2661
|
+
return { _tag: "Failure", error: e2 };
|
|
2662
|
+
}
|
|
2663
|
+
}
|
|
2664
|
+
return {
|
|
2665
|
+
async getUpcomingOrders({ count, subscriptionId, excludeSkipped = true }) {
|
|
2666
|
+
try {
|
|
2667
|
+
const params = new URLSearchParams({ count: String(count) });
|
|
2668
|
+
params.set("excludeSkipped", String(excludeSkipped));
|
|
2669
|
+
if (subscriptionId) params.set("query", `id:"${subscriptionId}"`);
|
|
2670
|
+
const res = await fetcher(`/customer/v1/schedules/?${params.toString()}`);
|
|
2671
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2672
|
+
const data = await res.json();
|
|
2673
|
+
return { _tag: "Success", data: (data.orders ?? []).map(mapScheduleOrder) };
|
|
2674
|
+
} catch (e2) {
|
|
2675
|
+
return { _tag: "Failure", error: e2 };
|
|
949
2676
|
}
|
|
950
|
-
|
|
2677
|
+
},
|
|
2678
|
+
async getAdjustments() {
|
|
2679
|
+
try {
|
|
2680
|
+
const all = [];
|
|
2681
|
+
let after;
|
|
2682
|
+
for (let guard = 0; guard < 50; guard++) {
|
|
2683
|
+
const params = new URLSearchParams({ limit: "100" });
|
|
2684
|
+
if (after) params.set("after", after);
|
|
2685
|
+
const res = await fetcher(
|
|
2686
|
+
`/customer/v1/schedules/adjustments?${params.toString()}`
|
|
2687
|
+
);
|
|
2688
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2689
|
+
const body = await res.json();
|
|
2690
|
+
all.push(
|
|
2691
|
+
...body.data.map((a8) => ({
|
|
2692
|
+
id: a8.id,
|
|
2693
|
+
matcher: a8.matcher,
|
|
2694
|
+
action: a8.action
|
|
2695
|
+
}))
|
|
2696
|
+
);
|
|
2697
|
+
if (!body.hasNextPage) return { _tag: "Success", data: all };
|
|
2698
|
+
if (!body.endCursor || body.endCursor === after) {
|
|
2699
|
+
return {
|
|
2700
|
+
_tag: "Failure",
|
|
2701
|
+
error: "Adjustments pagination cursor did not advance"
|
|
2702
|
+
};
|
|
2703
|
+
}
|
|
2704
|
+
after = body.endCursor;
|
|
2705
|
+
}
|
|
951
2706
|
return {
|
|
952
2707
|
_tag: "Failure",
|
|
953
|
-
error:
|
|
2708
|
+
error: "Adjustments pagination exceeded 50 pages"
|
|
954
2709
|
};
|
|
2710
|
+
} catch (e2) {
|
|
2711
|
+
return { _tag: "Failure", error: e2 };
|
|
955
2712
|
}
|
|
956
|
-
return { _tag: "Failure", error: { kind: behavior.error } };
|
|
957
2713
|
},
|
|
958
|
-
async
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
2714
|
+
async skipOrder({ cycle, date }) {
|
|
2715
|
+
const built = buildMatcher({ cycle, date });
|
|
2716
|
+
if (!built.ok) return { _tag: "Failure", error: built.error };
|
|
2717
|
+
return postAdjustment(built.matcher, {
|
|
2718
|
+
type: "SKIP_ORDER",
|
|
2719
|
+
input: { reason: "" }
|
|
2720
|
+
});
|
|
965
2721
|
},
|
|
966
|
-
async
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
return {
|
|
973
|
-
_tag: "Failure",
|
|
974
|
-
error: "Subscription not found"
|
|
975
|
-
};
|
|
976
|
-
}
|
|
977
|
-
subscription.nextDeliveryDate = new Date(params.newDate);
|
|
978
|
-
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
979
|
-
subscription.items.forEach((item) => {
|
|
980
|
-
item.nextDeliveryDate = new Date(params.newDate);
|
|
2722
|
+
async skipItem({ lineId, cycle, date, reason }) {
|
|
2723
|
+
const built = buildSkipItemMatcher({ cycle, date });
|
|
2724
|
+
if (!built.ok) return { _tag: "Failure", error: built.error };
|
|
2725
|
+
return postAdjustment(built.matcher, {
|
|
2726
|
+
type: "SKIP_ITEM",
|
|
2727
|
+
input: { lineIds: [lineId], reason: reason ?? "" }
|
|
981
2728
|
});
|
|
982
|
-
return {
|
|
983
|
-
_tag: "Success",
|
|
984
|
-
data: {
|
|
985
|
-
...subscription,
|
|
986
|
-
items: subscription.items.map((item) => ({ ...item })),
|
|
987
|
-
discounts: subscription.discounts.map((discount) => ({ ...discount }))
|
|
988
|
-
}
|
|
989
|
-
};
|
|
990
2729
|
},
|
|
991
|
-
async
|
|
992
|
-
|
|
993
|
-
const subscription = mockSubscriptions.find(
|
|
994
|
-
(sub) => sub.id === params.subscriptionId
|
|
995
|
-
);
|
|
996
|
-
if (!subscription) {
|
|
2730
|
+
async changeDateAdjustment({ newDate, cycle }) {
|
|
2731
|
+
if (cycle == null) {
|
|
997
2732
|
return {
|
|
998
2733
|
_tag: "Failure",
|
|
999
|
-
error: "
|
|
2734
|
+
error: "cycle is required for changeDateAdjustment"
|
|
1000
2735
|
};
|
|
1001
2736
|
}
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
}
|
|
1011
|
-
};
|
|
2737
|
+
const newIso = toIsoOrNull(newDate);
|
|
2738
|
+
if (newIso == null) {
|
|
2739
|
+
return { _tag: "Failure", error: "newDate must be a valid date" };
|
|
2740
|
+
}
|
|
2741
|
+
return postAdjustment(
|
|
2742
|
+
{ type: "CYCLE", input: { cycle } },
|
|
2743
|
+
{ type: "CHANGE_DATE", input: { newDate: newIso } }
|
|
2744
|
+
);
|
|
1012
2745
|
},
|
|
1013
|
-
async
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
2746
|
+
async updateShippingAdjustment({ address, cycle, date }) {
|
|
2747
|
+
const built = buildMatcher({ cycle, date });
|
|
2748
|
+
if (!built.ok) return { _tag: "Failure", error: built.error };
|
|
2749
|
+
return postAdjustment(built.matcher, {
|
|
2750
|
+
type: "UPDATE_SHIPPING",
|
|
2751
|
+
input: { address }
|
|
2752
|
+
});
|
|
2753
|
+
},
|
|
2754
|
+
async updatePaymentMethodAdjustment({ paymentMethodId, cycle, date }) {
|
|
2755
|
+
const built = buildMatcher({ cycle, date });
|
|
2756
|
+
if (!built.ok) return { _tag: "Failure", error: built.error };
|
|
2757
|
+
return postAdjustment(built.matcher, {
|
|
2758
|
+
type: "UPDATE_PAYMENT_METHOD",
|
|
2759
|
+
input: { paymentMethodId }
|
|
2760
|
+
});
|
|
2761
|
+
},
|
|
2762
|
+
async updateProductsAdjustment({ lines, cycle, date }) {
|
|
2763
|
+
const built = buildMatcher({ cycle, date });
|
|
2764
|
+
if (!built.ok) return { _tag: "Failure", error: built.error };
|
|
2765
|
+
return postAdjustment(built.matcher, {
|
|
2766
|
+
type: "UPDATE_PRODUCTS",
|
|
2767
|
+
input: { lines }
|
|
2768
|
+
});
|
|
2769
|
+
},
|
|
2770
|
+
async applyDiscountAdjustment({ discountCode, cycle }) {
|
|
2771
|
+
return postAdjustment(
|
|
2772
|
+
{ type: "CYCLE", input: { cycle } },
|
|
2773
|
+
{
|
|
2774
|
+
type: "APPLY_DISCOUNT",
|
|
2775
|
+
input: { discountSource: "code", discountCode }
|
|
2776
|
+
}
|
|
1017
2777
|
);
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
2778
|
+
},
|
|
2779
|
+
async removeAdjustment({ adjustmentId }) {
|
|
2780
|
+
try {
|
|
2781
|
+
const res = await fetcher(
|
|
2782
|
+
`/customer/v1/schedules/adjustments/${adjustmentId}`,
|
|
2783
|
+
{ method: "DELETE" }
|
|
2784
|
+
);
|
|
2785
|
+
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2786
|
+
return { _tag: "Success", data: void 0 };
|
|
2787
|
+
} catch (e2) {
|
|
2788
|
+
return { _tag: "Failure", error: e2 };
|
|
1023
2789
|
}
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
2790
|
+
}
|
|
2791
|
+
};
|
|
2792
|
+
}
|
|
2793
|
+
const SchedulesServiceContext = createContext("services/schedules");
|
|
2794
|
+
function isSameCalendarDay(a8, b2) {
|
|
2795
|
+
const da = new Date(a8);
|
|
2796
|
+
const db = new Date(b2);
|
|
2797
|
+
if (Number.isNaN(da.getTime()) || Number.isNaN(db.getTime())) return false;
|
|
2798
|
+
return da.toISOString().slice(0, 10) === db.toISOString().slice(0, 10);
|
|
2799
|
+
}
|
|
2800
|
+
function matcherArms(matcher, target) {
|
|
2801
|
+
const cycleArm = (matcher.type === "CYCLE" || matcher.type === "CYCLE_AND_DATE") && matcher.input.cycle === target.cycle;
|
|
2802
|
+
const dateArm = (matcher.type === "DATE" || matcher.type === "CYCLE_AND_DATE") && isSameCalendarDay(matcher.input.date, target.date);
|
|
2803
|
+
return { cycleArm, dateArm };
|
|
2804
|
+
}
|
|
2805
|
+
function matcherAffects(matcher, target) {
|
|
2806
|
+
const { cycleArm, dateArm } = matcherArms(matcher, target);
|
|
2807
|
+
return cycleArm || dateArm;
|
|
2808
|
+
}
|
|
2809
|
+
function matcherExactlyTargets(matcher, target) {
|
|
2810
|
+
const { cycleArm, dateArm } = matcherArms(matcher, target);
|
|
2811
|
+
if (matcher.type === "CYCLE") return cycleArm;
|
|
2812
|
+
if (matcher.type === "DATE") return dateArm;
|
|
2813
|
+
return cycleArm && dateArm;
|
|
2814
|
+
}
|
|
2815
|
+
function createSchedulesService(adapter, opts) {
|
|
2816
|
+
const current = d(null);
|
|
2817
|
+
const currentLoading = d(false);
|
|
2818
|
+
const hasPendingBilling = (opts == null ? void 0 : opts.hasPendingBilling) ?? d(false);
|
|
2819
|
+
const schedulesChangedAt = d(0);
|
|
2820
|
+
let reloadToken = 0;
|
|
2821
|
+
let currentSubscriptionId;
|
|
2822
|
+
async function getUpcomingOrder() {
|
|
2823
|
+
const result = await adapter.getUpcomingOrders({ count: 1 });
|
|
2824
|
+
if (result._tag === "Failure") return result;
|
|
2825
|
+
return { _tag: "Success", data: result.data[0] ?? null };
|
|
2826
|
+
}
|
|
2827
|
+
async function getUpcomingOrders(count, opts2) {
|
|
2828
|
+
return adapter.getUpcomingOrders({ count, ...opts2 });
|
|
2829
|
+
}
|
|
2830
|
+
async function reloadCurrent(subscriptionId) {
|
|
2831
|
+
var _a;
|
|
2832
|
+
const scopeChanged = currentSubscriptionId !== subscriptionId;
|
|
2833
|
+
currentSubscriptionId = subscriptionId;
|
|
2834
|
+
const token = ++reloadToken;
|
|
2835
|
+
currentLoading.value = true;
|
|
2836
|
+
const count = Math.max(((_a = current.value) == null ? void 0 : _a.length) ?? 0, 3);
|
|
2837
|
+
if (scopeChanged) current.value = null;
|
|
2838
|
+
const result = await adapter.getUpcomingOrders({
|
|
2839
|
+
count,
|
|
2840
|
+
subscriptionId
|
|
2841
|
+
});
|
|
2842
|
+
if (token !== reloadToken) return;
|
|
2843
|
+
currentLoading.value = false;
|
|
2844
|
+
if (result._tag === "Success") {
|
|
2845
|
+
current.value = result.data;
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
2848
|
+
async function notifyChanged(subscriptionId) {
|
|
2849
|
+
if (subscriptionId) await reloadCurrent(subscriptionId);
|
|
2850
|
+
schedulesChangedAt.value++;
|
|
2851
|
+
}
|
|
2852
|
+
return {
|
|
2853
|
+
current,
|
|
2854
|
+
currentLoading,
|
|
2855
|
+
hasPendingBilling,
|
|
2856
|
+
schedulesChangedAt,
|
|
2857
|
+
getUpcomingOrder,
|
|
2858
|
+
getUpcomingOrders,
|
|
2859
|
+
async getAdjustments() {
|
|
2860
|
+
return adapter.getAdjustments();
|
|
1034
2861
|
},
|
|
1035
|
-
async
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
2862
|
+
async skipOrder(params) {
|
|
2863
|
+
const result = await adapter.skipOrder(params);
|
|
2864
|
+
if (result._tag === "Success") await notifyChanged(params.subscriptionId);
|
|
2865
|
+
return result;
|
|
2866
|
+
},
|
|
2867
|
+
async skipItem(params) {
|
|
2868
|
+
const result = await adapter.skipItem(params);
|
|
2869
|
+
if (result._tag === "Success") await notifyChanged(params.subscriptionId);
|
|
2870
|
+
return result;
|
|
2871
|
+
},
|
|
2872
|
+
async unskipItem({ subscriptionId, lineId, cycle, date }) {
|
|
2873
|
+
const adjustments = await adapter.getAdjustments();
|
|
2874
|
+
if (adjustments._tag === "Failure") return adjustments;
|
|
2875
|
+
const covering = adjustments.data.filter(
|
|
2876
|
+
(a8) => a8.action.type === "SKIP_ITEM" && a8.action.input.lineIds.includes(lineId) && matcherAffects(a8.matcher, { cycle, date })
|
|
1039
2877
|
);
|
|
1040
|
-
if (
|
|
2878
|
+
if (covering.length === 0) {
|
|
2879
|
+
return { _tag: "Failure", error: "No skip found for this item" };
|
|
2880
|
+
}
|
|
2881
|
+
if (covering.some((a8) => a8.action.input.lineIds.length > 1)) {
|
|
1041
2882
|
return {
|
|
1042
2883
|
_tag: "Failure",
|
|
1043
|
-
error: "
|
|
2884
|
+
error: "This item was skipped together with other items and can't be unskipped individually"
|
|
1044
2885
|
};
|
|
1045
2886
|
}
|
|
1046
|
-
|
|
1047
|
-
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1048
|
-
return {
|
|
1049
|
-
_tag: "Success",
|
|
1050
|
-
data: {
|
|
1051
|
-
...subscription,
|
|
1052
|
-
items: subscription.items.map((item) => ({ ...item })),
|
|
1053
|
-
discounts: subscription.discounts.map((discount) => ({ ...discount }))
|
|
1054
|
-
}
|
|
1055
|
-
};
|
|
1056
|
-
},
|
|
1057
|
-
async cancel(params) {
|
|
1058
|
-
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1059
|
-
const subscription = mockSubscriptions.find(
|
|
1060
|
-
(sub) => sub.id === params.subscriptionId
|
|
1061
|
-
);
|
|
1062
|
-
if (!subscription) {
|
|
2887
|
+
if (covering.some((a8) => !matcherExactlyTargets(a8.matcher, { cycle, date }))) {
|
|
1063
2888
|
return {
|
|
1064
2889
|
_tag: "Failure",
|
|
1065
|
-
error: "
|
|
2890
|
+
error: "This skip also affects another order and can't be removed from here"
|
|
1066
2891
|
};
|
|
1067
2892
|
}
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
_tag
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
items: subscription.items.map((item) => ({ ...item })),
|
|
1075
|
-
discounts: subscription.discounts.map((discount) => ({ ...discount }))
|
|
2893
|
+
let deleted = 0;
|
|
2894
|
+
for (const a8 of covering) {
|
|
2895
|
+
const result = await adapter.removeAdjustment({ adjustmentId: a8.id });
|
|
2896
|
+
if (result._tag === "Failure") {
|
|
2897
|
+
if (deleted > 0) await notifyChanged(subscriptionId);
|
|
2898
|
+
return result;
|
|
1076
2899
|
}
|
|
1077
|
-
|
|
2900
|
+
deleted++;
|
|
2901
|
+
}
|
|
2902
|
+
await notifyChanged(subscriptionId);
|
|
2903
|
+
return { _tag: "Success", data: void 0 };
|
|
1078
2904
|
},
|
|
1079
|
-
async
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
data: [
|
|
1084
|
-
{
|
|
1085
|
-
id: "plan_1",
|
|
1086
|
-
name: "Monthly",
|
|
1087
|
-
billingPolicy: { intervalCount: 1, interval: "MONTH" },
|
|
1088
|
-
deliveryPolicy: { intervalCount: 1, interval: "MONTH" }
|
|
1089
|
-
},
|
|
1090
|
-
{
|
|
1091
|
-
id: "plan_2",
|
|
1092
|
-
name: "Every 2 months",
|
|
1093
|
-
billingPolicy: { intervalCount: 2, interval: "MONTH" },
|
|
1094
|
-
deliveryPolicy: { intervalCount: 2, interval: "MONTH" }
|
|
1095
|
-
},
|
|
1096
|
-
{
|
|
1097
|
-
id: "plan_3",
|
|
1098
|
-
name: "Every 3 months",
|
|
1099
|
-
billingPolicy: { intervalCount: 3, interval: "MONTH" },
|
|
1100
|
-
deliveryPolicy: { intervalCount: 3, interval: "MONTH" }
|
|
1101
|
-
}
|
|
1102
|
-
]
|
|
1103
|
-
};
|
|
2905
|
+
async changeDateAdjustment(params) {
|
|
2906
|
+
const result = await adapter.changeDateAdjustment(params);
|
|
2907
|
+
if (result._tag === "Success") await notifyChanged(params.subscriptionId);
|
|
2908
|
+
return result;
|
|
1104
2909
|
},
|
|
1105
|
-
async
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
return
|
|
2910
|
+
async updateShippingAdjustment(params) {
|
|
2911
|
+
const result = await adapter.updateShippingAdjustment(params);
|
|
2912
|
+
if (result._tag === "Success") await notifyChanged(params.subscriptionId);
|
|
2913
|
+
return result;
|
|
2914
|
+
},
|
|
2915
|
+
async updatePaymentMethodAdjustment(params) {
|
|
2916
|
+
const result = await adapter.updatePaymentMethodAdjustment(params);
|
|
2917
|
+
if (result._tag === "Success") await notifyChanged(params.subscriptionId);
|
|
2918
|
+
return result;
|
|
2919
|
+
},
|
|
2920
|
+
async updateProductsAdjustment(params) {
|
|
2921
|
+
const result = await adapter.updateProductsAdjustment(params);
|
|
2922
|
+
if (result._tag === "Success") await notifyChanged(params.subscriptionId);
|
|
2923
|
+
return result;
|
|
2924
|
+
},
|
|
2925
|
+
async applyDiscountAdjustment(params) {
|
|
2926
|
+
const result = await adapter.applyDiscountAdjustment(params);
|
|
2927
|
+
if (result._tag === "Success") await notifyChanged(params.subscriptionId);
|
|
2928
|
+
return result;
|
|
2929
|
+
},
|
|
2930
|
+
async removeAdjustment(params) {
|
|
2931
|
+
var _a, _b, _c;
|
|
2932
|
+
const result = await adapter.removeAdjustment(params);
|
|
2933
|
+
if (result._tag === "Success") {
|
|
2934
|
+
const order = (_a = current.value) == null ? void 0 : _a[0];
|
|
2935
|
+
const subscriptionId = currentSubscriptionId ?? (order == null ? void 0 : order.subscriptionId) ?? ((_c = (_b = order == null ? void 0 : order.items) == null ? void 0 : _b[0]) == null ? void 0 : _c.subscriptionId);
|
|
2936
|
+
await notifyChanged(subscriptionId);
|
|
2937
|
+
}
|
|
2938
|
+
return result;
|
|
2939
|
+
},
|
|
2940
|
+
reloadCurrent
|
|
2941
|
+
};
|
|
2942
|
+
}
|
|
2943
|
+
const SettingsServiceContext = createContext("services/settings");
|
|
2944
|
+
function createSettingsService(initial = {}) {
|
|
2945
|
+
const current = d(initial);
|
|
2946
|
+
return {
|
|
2947
|
+
current,
|
|
2948
|
+
get(key) {
|
|
2949
|
+
return this.current.value[key];
|
|
1109
2950
|
}
|
|
1110
2951
|
};
|
|
1111
2952
|
}
|
|
2953
|
+
function createShopService(options) {
|
|
2954
|
+
const contactEmail = d(options.getContactEmail());
|
|
2955
|
+
const service = { contactEmail };
|
|
2956
|
+
return service;
|
|
2957
|
+
}
|
|
2958
|
+
const ShopServiceContext = createContext("services/shop");
|
|
1112
2959
|
const SubscriptionServiceContext = createContext("services/subscription");
|
|
1113
2960
|
function getCurrentSubscriptionId() {
|
|
1114
2961
|
if (typeof window === "undefined") {
|
|
@@ -1131,41 +2978,102 @@ function getCurrentSubscriptionId() {
|
|
|
1131
2978
|
}
|
|
1132
2979
|
function createSubscriptionService(adapter) {
|
|
1133
2980
|
const current = d(null);
|
|
1134
|
-
const
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
if (result._tag === "Success" && result.data != null) {
|
|
1139
|
-
current.value = result.data;
|
|
2981
|
+
const hasPendingBilling = w(
|
|
2982
|
+
() => {
|
|
2983
|
+
var _a, _b, _c, _d;
|
|
2984
|
+
return ((_b = (_a = current.value) == null ? void 0 : _a.lastBillingAttempt) == null ? void 0 : _b.ready) === false || ((_d = (_c = current.value) == null ? void 0 : _c.renewal) == null ? void 0 : _d.isInActiveProcessState) === true;
|
|
1140
2985
|
}
|
|
2986
|
+
);
|
|
2987
|
+
const discountsChangedAt = d(0);
|
|
2988
|
+
const itemsChangedAt = d(0);
|
|
2989
|
+
const lastOrder = d(null);
|
|
2990
|
+
async function refreshAfterDiscountChange() {
|
|
2991
|
+
await reload();
|
|
2992
|
+
discountsChangedAt.value = discountsChangedAt.value + 1;
|
|
1141
2993
|
}
|
|
1142
|
-
async function
|
|
1143
|
-
await
|
|
1144
|
-
|
|
1145
|
-
}
|
|
1146
|
-
async function refreshAfterItemChange(subscriptionId) {
|
|
1147
|
-
await refreshCurrent(subscriptionId);
|
|
1148
|
-
itemsChangedAtSignal.value = itemsChangedAtSignal.value + 1;
|
|
2994
|
+
async function refreshAfterItemChange() {
|
|
2995
|
+
await reload();
|
|
2996
|
+
itemsChangedAt.value = itemsChangedAt.value + 1;
|
|
1149
2997
|
}
|
|
1150
2998
|
function subscriptionIdFromCurrent() {
|
|
1151
2999
|
var _a;
|
|
1152
3000
|
return ((_a = current.value) == null ? void 0 : _a.id) ?? null;
|
|
1153
3001
|
}
|
|
3002
|
+
async function reload() {
|
|
3003
|
+
var _a, _b;
|
|
3004
|
+
const id = (_a = current.value) == null ? void 0 : _a.id;
|
|
3005
|
+
if (!id) return;
|
|
3006
|
+
const [subResult, lastOrderResult] = await Promise.all([
|
|
3007
|
+
adapter.getById(id),
|
|
3008
|
+
adapter.getLastOrder({ subscriptionId: id })
|
|
3009
|
+
]);
|
|
3010
|
+
if (((_b = current.value) == null ? void 0 : _b.id) !== id) return;
|
|
3011
|
+
if (subResult._tag === "Success") current.value = subResult.data;
|
|
3012
|
+
if (lastOrderResult._tag === "Success") lastOrder.value = lastOrderResult.data;
|
|
3013
|
+
}
|
|
1154
3014
|
const service = {
|
|
3015
|
+
current,
|
|
3016
|
+
hasPendingBilling,
|
|
3017
|
+
discountsChangedAt,
|
|
3018
|
+
itemsChangedAt,
|
|
3019
|
+
lastOrder,
|
|
1155
3020
|
search: adapter.search,
|
|
3021
|
+
// Prefer the adapter's rich cross-subscription list (legacy endpoint, active-first).
|
|
3022
|
+
// Fall back to a single `search` page for adapters that don't implement it
|
|
3023
|
+
// (e.g. the editor mock) so the cross-subscription views still render.
|
|
3024
|
+
listAll: adapter.listAll ? adapter.listAll.bind(adapter) : async () => {
|
|
3025
|
+
const result = await adapter.search();
|
|
3026
|
+
if (result._tag !== "Success") return result;
|
|
3027
|
+
return { _tag: "Success", data: result.data.items };
|
|
3028
|
+
},
|
|
1156
3029
|
getById: adapter.getById,
|
|
1157
3030
|
getCurrentSubscriptionId,
|
|
1158
3031
|
addItem: async (subscriptionId, item) => {
|
|
1159
3032
|
const result = await adapter.addItem({ subscriptionId, item });
|
|
1160
3033
|
if (result._tag === "Success") {
|
|
1161
|
-
await refreshAfterItemChange(
|
|
3034
|
+
await refreshAfterItemChange();
|
|
1162
3035
|
}
|
|
1163
3036
|
return result;
|
|
1164
3037
|
},
|
|
1165
3038
|
removeItem: async (subscriptionId, itemId) => {
|
|
1166
3039
|
const result = await adapter.removeItem({ subscriptionId, itemId });
|
|
1167
3040
|
if (result._tag === "Success") {
|
|
1168
|
-
await refreshAfterItemChange(
|
|
3041
|
+
await refreshAfterItemChange();
|
|
3042
|
+
}
|
|
3043
|
+
return result;
|
|
3044
|
+
},
|
|
3045
|
+
updateItem: async (subscriptionId, lineId, update) => {
|
|
3046
|
+
const result = await adapter.updateItem({ subscriptionId, lineId, update });
|
|
3047
|
+
if (result._tag === "Success") {
|
|
3048
|
+
await refreshAfterItemChange();
|
|
3049
|
+
}
|
|
3050
|
+
return result;
|
|
3051
|
+
},
|
|
3052
|
+
updateItemAttributes: async (subscriptionId, lineId, customAttributes) => {
|
|
3053
|
+
const result = await adapter.updateItemAttributes({ subscriptionId, lineId, customAttributes });
|
|
3054
|
+
if (result._tag === "Success") {
|
|
3055
|
+
await refreshAfterItemChange();
|
|
3056
|
+
}
|
|
3057
|
+
return result;
|
|
3058
|
+
},
|
|
3059
|
+
updateAttributes: async (subscriptionId, customAttributes) => {
|
|
3060
|
+
const result = await adapter.updateAttributes({ subscriptionId, customAttributes });
|
|
3061
|
+
if (result._tag === "Success") {
|
|
3062
|
+
await reload();
|
|
3063
|
+
}
|
|
3064
|
+
return result;
|
|
3065
|
+
},
|
|
3066
|
+
skipLineItem: async (subscriptionId, lineId, nextBillingDate) => {
|
|
3067
|
+
const result = await adapter.skipLineItem({ subscriptionId, lineId, nextBillingDate });
|
|
3068
|
+
if (result._tag === "Success") {
|
|
3069
|
+
await refreshAfterItemChange();
|
|
3070
|
+
}
|
|
3071
|
+
return result;
|
|
3072
|
+
},
|
|
3073
|
+
updateBoxItems: async (subscriptionId, params) => {
|
|
3074
|
+
const result = await adapter.updateBoxItems({ subscriptionId, ...params });
|
|
3075
|
+
if (result._tag === "Success") {
|
|
3076
|
+
await refreshAfterItemChange();
|
|
1169
3077
|
}
|
|
1170
3078
|
return result;
|
|
1171
3079
|
},
|
|
@@ -1179,7 +3087,7 @@ function createSubscriptionService(adapter) {
|
|
|
1179
3087
|
}
|
|
1180
3088
|
const result = await adapter.addDiscount({ subscriptionId: id, code });
|
|
1181
3089
|
if (result._tag === "Success") {
|
|
1182
|
-
await refreshAfterDiscountChange(
|
|
3090
|
+
await refreshAfterDiscountChange();
|
|
1183
3091
|
}
|
|
1184
3092
|
return result;
|
|
1185
3093
|
},
|
|
@@ -1196,38 +3104,30 @@ function createSubscriptionService(adapter) {
|
|
|
1196
3104
|
discountId
|
|
1197
3105
|
});
|
|
1198
3106
|
if (result._tag === "Success") {
|
|
1199
|
-
await refreshAfterDiscountChange(
|
|
3107
|
+
await refreshAfterDiscountChange();
|
|
1200
3108
|
}
|
|
1201
3109
|
return result;
|
|
1202
3110
|
},
|
|
1203
3111
|
reschedule: (subscriptionId, newDate) => adapter.reschedule({ subscriptionId, newDate }),
|
|
3112
|
+
updateCustomAttributes: (subscriptionId, customAttributes) => adapter.updateCustomAttributes({ subscriptionId, customAttributes }),
|
|
1204
3113
|
resume: (subscriptionId) => adapter.resume({ subscriptionId }),
|
|
1205
3114
|
pause: (subscriptionId) => adapter.pause({ subscriptionId }),
|
|
1206
3115
|
reactivate: (subscriptionId) => adapter.reactivate({ subscriptionId }),
|
|
1207
3116
|
cancel: (subscriptionId, reason) => adapter.cancel({ subscriptionId, reason }),
|
|
3117
|
+
renewNow: (params) => adapter.renewNow(params),
|
|
3118
|
+
changeFrequency: (subscriptionId, plan) => adapter.changeFrequency({ subscriptionId, plan }),
|
|
3119
|
+
reload,
|
|
1208
3120
|
getAvailablePlans: (subscriptionId) => adapter.getAvailablePlans({ subscriptionId }),
|
|
1209
3121
|
getDefaultSubscriptionId: () => adapter.getDefaultSubscriptionId(),
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
itemsChangedAt: itemsChangedAtSignal
|
|
3122
|
+
getUpsellProducts: (params) => adapter.getUpsellProducts ? adapter.getUpsellProducts(params) : Promise.resolve({ _tag: "Success", data: [] }),
|
|
3123
|
+
addUpsellProduct: (params) => adapter.addUpsellProduct ? adapter.addUpsellProduct(params) : Promise.resolve({ _tag: "Success", data: void 0 })
|
|
1213
3124
|
};
|
|
1214
3125
|
return service;
|
|
1215
3126
|
}
|
|
1216
|
-
const DISCOUNT_REJECTION_REASONS = [
|
|
1217
|
-
"INVALID_CODE",
|
|
1218
|
-
"INACTIVE",
|
|
1219
|
-
"NOT_APPLICABLE_TO_CUSTOMER",
|
|
1220
|
-
"ALREADY_ADDED",
|
|
1221
|
-
"ALREADY_USED",
|
|
1222
|
-
"NOT_APPLICABLE_TO_PRODUCTS",
|
|
1223
|
-
"NOT_APPLICABLE_TO_SUBSCRIPTION",
|
|
1224
|
-
"INVALID_COMBINATION",
|
|
1225
|
-
"QUANTITY_REQUIREMENT_NOT_MET"
|
|
1226
|
-
];
|
|
1227
3127
|
function getDefaultBlockForAction(actionType, actionConfig, blockId) {
|
|
1228
3128
|
const blockName = `action:${actionType}`;
|
|
1229
3129
|
const definedBlocks = getDefinedBlocks();
|
|
1230
|
-
const exists = definedBlocks.some((
|
|
3130
|
+
const exists = definedBlocks.some((b2) => b2.name === blockName);
|
|
1231
3131
|
if (!exists) {
|
|
1232
3132
|
throw new Error(
|
|
1233
3133
|
`No default block registered for action type "${actionType}". Expected a block named "${blockName}" to be registered in blocks-extensions.`
|
|
@@ -1237,7 +3137,7 @@ function getDefaultBlockForAction(actionType, actionConfig, blockId) {
|
|
|
1237
3137
|
}
|
|
1238
3138
|
function hasDefaultBlockForAction(actionType) {
|
|
1239
3139
|
const blockName = `action:${actionType}`;
|
|
1240
|
-
return getDefinedBlocks().some((
|
|
3140
|
+
return getDefinedBlocks().some((b2) => b2.name === blockName);
|
|
1241
3141
|
}
|
|
1242
3142
|
const WorkflowServiceContext = createContext("services/workflow");
|
|
1243
3143
|
const RUN_SCOPED_RESET = {
|
|
@@ -1470,7 +3370,7 @@ function createWorkflowService(deps) {
|
|
|
1470
3370
|
const presets = {};
|
|
1471
3371
|
const baseLabelFor = (step) => {
|
|
1472
3372
|
var _a;
|
|
1473
|
-
return ((_a = getDefinedBlocks().find((
|
|
3373
|
+
return ((_a = getDefinedBlocks().find((b2) => b2.name === `action:${step.actionType}`)) == null ? void 0 : _a.displayName) ?? step.stepId;
|
|
1474
3374
|
};
|
|
1475
3375
|
const totalCounts = /* @__PURE__ */ new Map();
|
|
1476
3376
|
for (const step of allSteps) {
|
|
@@ -2065,9 +3965,9 @@ const gridV1Engine = {
|
|
|
2065
3965
|
case "resize": {
|
|
2066
3966
|
const current = layout.items[cmd.blockId];
|
|
2067
3967
|
if (current) {
|
|
2068
|
-
const
|
|
3968
|
+
const w3 = Math.max(1, Math.min(cmd.size.w, ctx2.cols - current.c));
|
|
2069
3969
|
const h = Math.max(1, cmd.size.h);
|
|
2070
|
-
layout.items[cmd.blockId] = { ...current, w:
|
|
3970
|
+
layout.items[cmd.blockId] = { ...current, w: w3, h };
|
|
2071
3971
|
}
|
|
2072
3972
|
break;
|
|
2073
3973
|
}
|
|
@@ -2115,32 +4015,40 @@ const gridV1Engine = {
|
|
|
2115
4015
|
return next;
|
|
2116
4016
|
}
|
|
2117
4017
|
};
|
|
4018
|
+
function resetBreakpoint(doc, breakpointId) {
|
|
4019
|
+
var _a;
|
|
4020
|
+
const baseId = (_a = doc.breakpoints[0]) == null ? void 0 : _a.id;
|
|
4021
|
+
if (breakpointId === baseId) return doc;
|
|
4022
|
+
if (!doc.layouts[breakpointId]) return doc;
|
|
4023
|
+
const next = structuredClone(doc);
|
|
4024
|
+
delete next.layouts[breakpointId];
|
|
4025
|
+
return next;
|
|
4026
|
+
}
|
|
2118
4027
|
const DEFAULT_NEW_BLOCK_HEIGHT = 4;
|
|
2119
4028
|
function insertRectRelative(doc, args) {
|
|
2120
4029
|
var _a, _b;
|
|
2121
|
-
const baseId = (_a = doc.breakpoints[0]) == null ? void 0 : _a.id;
|
|
2122
|
-
if (!baseId) return doc;
|
|
2123
|
-
const baseLayout = doc.layouts[baseId];
|
|
2124
|
-
if (!baseLayout) return doc;
|
|
2125
|
-
const target = baseLayout.items[args.targetId];
|
|
2126
|
-
if (!target) return doc;
|
|
2127
4030
|
const next = structuredClone(doc);
|
|
2128
|
-
const layout = next.layouts[baseId];
|
|
2129
|
-
if (!layout) return doc;
|
|
2130
|
-
const cols = ((_b = layout.grid) == null ? void 0 : _b.cols) ?? DEFAULT_GRID.cols;
|
|
2131
4031
|
const newHeight = DEFAULT_NEW_BLOCK_HEIGHT;
|
|
2132
|
-
|
|
2133
|
-
for (const [
|
|
2134
|
-
if (
|
|
2135
|
-
|
|
2136
|
-
|
|
4032
|
+
let changed = false;
|
|
4033
|
+
for (const [breakpointId, layout] of Object.entries(next.layouts)) {
|
|
4034
|
+
if (!layout) continue;
|
|
4035
|
+
const target = layout.items[args.targetId];
|
|
4036
|
+
if (!target) continue;
|
|
4037
|
+
const cols = ((_a = doc.breakpoints.find((b2) => b2.id === breakpointId)) == null ? void 0 : _a.cols) ?? ((_b = layout.grid) == null ? void 0 : _b.cols) ?? DEFAULT_GRID.cols;
|
|
4038
|
+
const insertRow = args.position === "before" ? target.r : target.r + target.h;
|
|
4039
|
+
for (const [id, rect] of Object.entries(layout.items)) {
|
|
4040
|
+
if (id === args.blockId) continue;
|
|
4041
|
+
if (rect.r >= insertRow) {
|
|
4042
|
+
layout.items[id] = { ...rect, r: rect.r + newHeight };
|
|
4043
|
+
}
|
|
2137
4044
|
}
|
|
4045
|
+
layout.items[args.blockId] = clampRect(
|
|
4046
|
+
{ c: target.c, r: insertRow, w: target.w, h: newHeight },
|
|
4047
|
+
cols
|
|
4048
|
+
);
|
|
4049
|
+
changed = true;
|
|
2138
4050
|
}
|
|
2139
|
-
|
|
2140
|
-
{ c: target.c, r: insertRow, w: target.w, h: newHeight },
|
|
2141
|
-
cols
|
|
2142
|
-
);
|
|
2143
|
-
return next;
|
|
4051
|
+
return changed ? next : doc;
|
|
2144
4052
|
}
|
|
2145
4053
|
const canvasMode = d(false);
|
|
2146
4054
|
const ctx = {};
|
|
@@ -2177,11 +4085,11 @@ export {
|
|
|
2177
4085
|
AuthenticationError,
|
|
2178
4086
|
AuthenticationErrorReason,
|
|
2179
4087
|
B as BlockWrapperEvent,
|
|
2180
|
-
|
|
2181
|
-
|
|
4088
|
+
v as ContextProvideEvent,
|
|
4089
|
+
x as ContextRequestEvent,
|
|
2182
4090
|
CustomerServiceContext,
|
|
2183
|
-
|
|
2184
|
-
|
|
4091
|
+
y as DEFAULT_BREAKPOINTS,
|
|
4092
|
+
z as DEFAULT_GLOBAL_STYLES,
|
|
2185
4093
|
DEFAULT_GRID,
|
|
2186
4094
|
DEFAULT_NEW_BLOCK_HEIGHT,
|
|
2187
4095
|
DISCOUNT_REJECTION_REASONS,
|
|
@@ -2192,54 +4100,70 @@ export {
|
|
|
2192
4100
|
P as PAGE_ROOT_SLOT,
|
|
2193
4101
|
ProductServiceContext,
|
|
2194
4102
|
RouterServiceContext,
|
|
4103
|
+
SchedulesServiceContext,
|
|
2195
4104
|
S as SelectedBlockContext,
|
|
4105
|
+
SettingsServiceContext,
|
|
4106
|
+
ShopServiceContext,
|
|
2196
4107
|
u as Signal,
|
|
2197
4108
|
StateContext,
|
|
2198
4109
|
SubscriptionServiceContext,
|
|
2199
|
-
|
|
2200
|
-
|
|
4110
|
+
c as ThemeStateContext,
|
|
4111
|
+
T as TranslationContext,
|
|
2201
4112
|
WorkflowServiceContext,
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
4113
|
+
A as attachEmptyTracking,
|
|
4114
|
+
d2 as backfillInstanceDefaults,
|
|
4115
|
+
b as buildGridLayout,
|
|
4116
|
+
F as buildLayoutCss,
|
|
2206
4117
|
canvasMode,
|
|
2207
4118
|
clampRect,
|
|
2208
|
-
|
|
4119
|
+
H as classifyRowDividers,
|
|
2209
4120
|
clearAllSurfaceContexts,
|
|
2210
4121
|
clearLayoutModeAdapter,
|
|
2211
4122
|
compactItems,
|
|
2212
|
-
|
|
4123
|
+
I as compactLayoutDocument,
|
|
2213
4124
|
w as computed,
|
|
4125
|
+
createApiCustomerAdapter,
|
|
4126
|
+
createApiLoginAdapter,
|
|
4127
|
+
createApiOrdersAdapter,
|
|
4128
|
+
createApiProductAdapter,
|
|
4129
|
+
createApiSchedulesAdapter,
|
|
4130
|
+
createApiSubscriptionAdapter,
|
|
2214
4131
|
createBlockInstance,
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
4132
|
+
J as createBlockInstanceFromJSON,
|
|
4133
|
+
K as createBlockInstanceFromObject,
|
|
4134
|
+
N as createCeRenderer,
|
|
2218
4135
|
createContext,
|
|
2219
4136
|
createCustomerService,
|
|
2220
|
-
|
|
4137
|
+
O as createDefaultLayoutDocument,
|
|
4138
|
+
createLoginService,
|
|
4139
|
+
createMockCustomerAdapter,
|
|
4140
|
+
createMockLoginAdapter,
|
|
2221
4141
|
createMockOrdersAdapter,
|
|
2222
4142
|
createMockProductAdapter,
|
|
2223
4143
|
createMockRouterService,
|
|
4144
|
+
createMockSchedulesAdapter,
|
|
2224
4145
|
createMockSubscriptionAdapter,
|
|
2225
4146
|
createMockWorkflowApiAdapter,
|
|
2226
4147
|
createOrdersService,
|
|
2227
4148
|
createProductService,
|
|
2228
4149
|
createRouterService,
|
|
2229
|
-
|
|
4150
|
+
createSchedulesService,
|
|
4151
|
+
Q as createSelectedBlock,
|
|
4152
|
+
createSettingsService,
|
|
4153
|
+
createShopService,
|
|
2230
4154
|
createState,
|
|
2231
4155
|
createSubscriptionService,
|
|
2232
|
-
|
|
2233
|
-
|
|
4156
|
+
R as createThemeState,
|
|
4157
|
+
U as createTranslationService,
|
|
2234
4158
|
createWorkflowApiAdapter,
|
|
2235
4159
|
createWorkflowService,
|
|
2236
4160
|
defineBlock,
|
|
2237
|
-
|
|
2238
|
-
|
|
4161
|
+
V as defineBlockRuntime,
|
|
4162
|
+
W as deriveLayout,
|
|
2239
4163
|
E as effect,
|
|
2240
4164
|
emitLayoutCommand,
|
|
2241
4165
|
emitLayoutSetRects,
|
|
2242
|
-
|
|
4166
|
+
X as expandFillLayoutDocument,
|
|
2243
4167
|
findFreeRect,
|
|
2244
4168
|
getActiveSurfaceContext,
|
|
2245
4169
|
getDefaultBlockForAction,
|
|
@@ -2247,34 +4171,35 @@ export {
|
|
|
2247
4171
|
getLayoutModeAdapter,
|
|
2248
4172
|
gridV1Engine,
|
|
2249
4173
|
hasDefaultBlockForAction,
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
4174
|
+
Y as hasRenderedContent,
|
|
4175
|
+
Z as indexOverrides,
|
|
4176
|
+
i as injectContext,
|
|
2253
4177
|
insertRectRelative,
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
4178
|
+
_ as isEditorPreview,
|
|
4179
|
+
a as isStubDefinition,
|
|
4180
|
+
$ as layoutScopeSelector,
|
|
4181
|
+
a0 as layoutSystemEnabled,
|
|
2258
4182
|
loadBlockRegistry,
|
|
2259
4183
|
loginService,
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
4184
|
+
o as onRegisterBlock,
|
|
4185
|
+
a1 as projectToCss,
|
|
4186
|
+
p as projectToGridStack,
|
|
4187
|
+
a2 as provideContext,
|
|
4188
|
+
a3 as readingOrder,
|
|
4189
|
+
e as reconcileLayout,
|
|
4190
|
+
a4 as rectsOverlap,
|
|
2267
4191
|
registerBlock,
|
|
2268
4192
|
registerLayoutModeAdapter,
|
|
2269
|
-
|
|
2270
|
-
|
|
4193
|
+
resetBreakpoint,
|
|
4194
|
+
r as resolveLayout,
|
|
4195
|
+
a5 as serializeBlocks,
|
|
2271
4196
|
setActiveSurfaceContext,
|
|
2272
4197
|
setLayoutCommandDispatcher,
|
|
2273
4198
|
setLayoutCommandEmitter,
|
|
2274
|
-
|
|
4199
|
+
a6 as setLayoutSystemEnabled,
|
|
2275
4200
|
d as signal,
|
|
2276
4201
|
subscribeContext,
|
|
2277
|
-
|
|
4202
|
+
a7 as untracked,
|
|
2278
4203
|
waitForDevBlocksUrl,
|
|
2279
|
-
|
|
4204
|
+
w2 as withWrapper
|
|
2280
4205
|
};
|