@juo/blocks 1.12.0-alpha.2 → 1.12.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-peABLAZZ.js → ExtensionRoot-9XfhG8YM.js} +3 -15
- package/dist/block.d.ts +0 -50
- package/dist/block.d.ts.map +1 -1
- package/dist/{bridge-r7Yk8fjY.js → bridge-w5KQ-czl.js} +25 -34
- package/dist/context.d.ts.map +1 -1
- package/dist/core.js +323 -1686
- package/dist/editor/bridge.d.ts +0 -7
- package/dist/editor/bridge.d.ts.map +1 -1
- package/dist/editor/components.d.ts.map +1 -1
- package/dist/editor/main.d.ts +1 -2
- package/dist/editor/main.d.ts.map +1 -1
- package/dist/editor/messages.d.ts +2 -13
- package/dist/editor/messages.d.ts.map +1 -1
- package/dist/editor.js +45 -64
- package/dist/{juo-workflow-DY4rd256.js → juo-workflow-CkyZRq60.js} +873 -74
- package/dist/main.d.ts +1 -3
- package/dist/main.d.ts.map +1 -1
- package/dist/preact.js +1 -1
- package/dist/react.js +1 -1
- package/dist/services/customer/customerService.d.ts +2 -3
- package/dist/services/customer/customerService.d.ts.map +1 -1
- package/dist/services/customer/index.d.ts +0 -1
- package/dist/services/customer/index.d.ts.map +1 -1
- package/dist/services/customer/types.d.ts +1 -4
- package/dist/services/customer/types.d.ts.map +1 -1
- package/dist/services/index.d.ts +0 -3
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/login/index.d.ts +1 -3
- package/dist/services/login/index.d.ts.map +1 -1
- package/dist/services/login/loginService.d.ts +2 -6
- package/dist/services/login/loginService.d.ts.map +1 -1
- package/dist/services/product/adapter.d.ts +2 -27
- package/dist/services/product/adapter.d.ts.map +1 -1
- 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 +0 -68
- package/dist/services/product/types.d.ts.map +1 -1
- package/dist/services/subscription/adapter.d.ts +2 -49
- package/dist/services/subscription/adapter.d.ts.map +1 -1
- 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 +6 -1
- package/dist/services/subscription/subscriptionService.d.ts.map +1 -1
- package/dist/services/subscription/types.d.ts +9 -216
- package/dist/services/subscription/types.d.ts.map +1 -1
- package/dist/theme-state.d.ts +1 -2
- package/dist/theme-state.d.ts.map +1 -1
- package/dist/utils/index.d.ts +0 -6
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/vue/block.d.ts.map +1 -1
- package/dist/vue.js +4 -25
- package/dist/web-components/ExtensionRoot.d.ts +0 -1
- package/dist/web-components/ExtensionRoot.d.ts.map +1 -1
- package/dist/web-components-editor.js +3 -4
- package/dist/web-components-runtime.js +2 -3
- package/package.json +1 -5
- package/dist/block-B6s9jL7l.js +0 -966
- package/dist/block.devtools.test.d.ts +0 -2
- package/dist/block.devtools.test.d.ts.map +0 -1
- package/dist/context.devtools.test.d.ts +0 -2
- package/dist/context.devtools.test.d.ts.map +0 -1
- package/dist/devtools/env.d.ts +0 -3
- package/dist/devtools/env.d.ts.map +0 -1
- package/dist/devtools/hook.d.ts +0 -35
- package/dist/devtools/hook.d.ts.map +0 -1
- package/dist/devtools/index.d.ts +0 -4
- package/dist/devtools/index.d.ts.map +0 -1
- package/dist/devtools/protocol.d.ts +0 -91
- package/dist/devtools/protocol.d.ts.map +0 -1
- package/dist/devtools.js +0 -44
- package/dist/editor/messages.test.d.ts +0 -2
- package/dist/editor/messages.test.d.ts.map +0 -1
- package/dist/editor/overlays.d.ts +0 -14
- package/dist/editor/overlays.d.ts.map +0 -1
- package/dist/editor/overlays.test.d.ts +0 -2
- package/dist/editor/overlays.test.d.ts.map +0 -1
- package/dist/overlay-wrapper.d.ts +0 -69
- package/dist/overlay-wrapper.d.ts.map +0 -1
- package/dist/overlay-wrapper.test.d.ts +0 -2
- package/dist/overlay-wrapper.test.d.ts.map +0 -1
- package/dist/overlays/OverlayService.d.ts +0 -34
- package/dist/overlays/OverlayService.d.ts.map +0 -1
- package/dist/overlays/OverlayService.test.d.ts +0 -2
- package/dist/overlays/OverlayService.test.d.ts.map +0 -1
- package/dist/registry.devtools.test.d.ts +0 -2
- package/dist/registry.devtools.test.d.ts.map +0 -1
- package/dist/services/customer/adapter.d.ts +0 -8
- package/dist/services/customer/adapter.d.ts.map +0 -1
- package/dist/services/login/adapter.d.ts +0 -32
- package/dist/services/login/adapter.d.ts.map +0 -1
- package/dist/services/order/adapter.d.ts +0 -9
- package/dist/services/order/adapter.d.ts.map +0 -1
- package/dist/services/order/index.d.ts +0 -5
- package/dist/services/order/index.d.ts.map +0 -1
- package/dist/services/order/orderService.d.ts +0 -7
- package/dist/services/order/orderService.d.ts.map +0 -1
- package/dist/services/order/types.d.ts +0 -21
- package/dist/services/order/types.d.ts.map +0 -1
- package/dist/services/product/adapter.test.d.ts +0 -2
- package/dist/services/product/adapter.test.d.ts.map +0 -1
- package/dist/services/schedules/adapter.d.ts +0 -25
- package/dist/services/schedules/adapter.d.ts.map +0 -1
- package/dist/services/schedules/adapter.test.d.ts +0 -2
- package/dist/services/schedules/adapter.test.d.ts.map +0 -1
- package/dist/services/schedules/index.d.ts +0 -5
- package/dist/services/schedules/index.d.ts.map +0 -1
- package/dist/services/schedules/schedulesService.d.ts +0 -11
- package/dist/services/schedules/schedulesService.d.ts.map +0 -1
- package/dist/services/schedules/schedulesService.test.d.ts +0 -2
- package/dist/services/schedules/schedulesService.test.d.ts.map +0 -1
- package/dist/services/schedules/types.d.ts +0 -101
- package/dist/services/schedules/types.d.ts.map +0 -1
- package/dist/services/settings/index.d.ts +0 -3
- package/dist/services/settings/index.d.ts.map +0 -1
- package/dist/services/settings/settingsService.d.ts +0 -7
- package/dist/services/settings/settingsService.d.ts.map +0 -1
- package/dist/services/settings/settingsService.test.d.ts +0 -2
- package/dist/services/settings/settingsService.test.d.ts.map +0 -1
- package/dist/services/settings/types.d.ts +0 -35
- package/dist/services/settings/types.d.ts.map +0 -1
- package/dist/services/shop/index.d.ts +0 -3
- package/dist/services/shop/index.d.ts.map +0 -1
- package/dist/services/shop/shopService.d.ts +0 -12
- package/dist/services/shop/shopService.d.ts.map +0 -1
- package/dist/services/shop/shopService.test.d.ts +0 -2
- package/dist/services/shop/shopService.test.d.ts.map +0 -1
- package/dist/services/shop/types.d.ts +0 -7
- package/dist/services/shop/types.d.ts.map +0 -1
- package/dist/services/subscription/adapter.test.d.ts +0 -2
- package/dist/services/subscription/adapter.test.d.ts.map +0 -1
- package/dist/services/subscription/types.test.d.ts +0 -2
- package/dist/services/subscription/types.test.d.ts.map +0 -1
- package/dist/theme-state.params.test.d.ts +0 -2
- package/dist/theme-state.params.test.d.ts.map +0 -1
- package/dist/theme-state.translations.test.d.ts +0 -2
- package/dist/theme-state.translations.test.d.ts.map +0 -1
package/dist/core.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
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,
|
|
6
|
-
import { D as D2, a as a2, T, W, c, b as b2, d as d2, e, g, h, i as i2 } from "./juo-workflow-DY4rd256.js";
|
|
4
|
+
import { c as createContext, d, w, r as registerBlock, E, u } from "./juo-workflow-CkyZRq60.js";
|
|
5
|
+
import { B, C, a, D, S, T, b, W, e, f, g, h, i, j, k, l, m, n, o, p, q, s, t, v, x, y, z, A, F, G, H, I, J, K, L } from "./juo-workflow-CkyZRq60.js";
|
|
7
6
|
import { reportBlockWarning, reportBlockError } from "./observability.js";
|
|
8
7
|
function createState() {
|
|
9
8
|
const discountCode = d(null);
|
|
@@ -73,111 +72,6 @@ function createState() {
|
|
|
73
72
|
};
|
|
74
73
|
}
|
|
75
74
|
const StateContext = createContext("state");
|
|
76
|
-
const OVERLAY_WRAPPER_SCHEMA = {
|
|
77
|
-
type: "object",
|
|
78
|
-
properties: {
|
|
79
|
-
props: {
|
|
80
|
-
type: "object",
|
|
81
|
-
properties: {
|
|
82
|
-
layout: {
|
|
83
|
-
type: "string",
|
|
84
|
-
enum: ["modal", "sidebar"]
|
|
85
|
-
},
|
|
86
|
-
canAbort: {
|
|
87
|
-
type: "boolean"
|
|
88
|
-
}
|
|
89
|
-
},
|
|
90
|
-
required: ["layout"],
|
|
91
|
-
additionalProperties: false
|
|
92
|
-
},
|
|
93
|
-
slots: {
|
|
94
|
-
type: "object",
|
|
95
|
-
properties: {
|
|
96
|
-
content: {}
|
|
97
|
-
},
|
|
98
|
-
additionalProperties: false
|
|
99
|
-
}
|
|
100
|
-
},
|
|
101
|
-
required: ["props", "slots"],
|
|
102
|
-
additionalProperties: false
|
|
103
|
-
};
|
|
104
|
-
function defineOverlayWrapper(opts) {
|
|
105
|
-
const layout = opts.layout ?? "sidebar";
|
|
106
|
-
const canAbort = opts.canAbort ?? true;
|
|
107
|
-
return defineBlock(opts.name, {
|
|
108
|
-
displayName: opts.title,
|
|
109
|
-
group: "internal",
|
|
110
|
-
schema: OVERLAY_WRAPPER_SCHEMA,
|
|
111
|
-
initialValue: () => ({
|
|
112
|
-
props: { layout, canAbort },
|
|
113
|
-
slots: {
|
|
114
|
-
content: [createBlockInstance(opts.content)]
|
|
115
|
-
}
|
|
116
|
-
}),
|
|
117
|
-
renderer: () => null
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
const OVERLAY_PREFIX$1 = "overlay:";
|
|
121
|
-
function callInitialValue(block) {
|
|
122
|
-
if (typeof block.initialValue !== "function") return block.initialValue;
|
|
123
|
-
try {
|
|
124
|
-
return block.initialValue();
|
|
125
|
-
} catch (error) {
|
|
126
|
-
console.warn(
|
|
127
|
-
`[overlays] initialValue() threw for "${block.name}"; its content block will not be hidden from the palette.`,
|
|
128
|
-
error
|
|
129
|
-
);
|
|
130
|
-
return void 0;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
function buildOverlayRegistry(blocks) {
|
|
134
|
-
var _a, _b;
|
|
135
|
-
const registry = {};
|
|
136
|
-
for (const block of blocks) {
|
|
137
|
-
if (!block.name.startsWith(OVERLAY_PREFIX$1)) continue;
|
|
138
|
-
const id = block.name.slice(OVERLAY_PREFIX$1.length);
|
|
139
|
-
const initial = callInitialValue(block);
|
|
140
|
-
const first = (_b = (_a = initial == null ? void 0 : initial.slots) == null ? void 0 : _a.content) == null ? void 0 : _b[0];
|
|
141
|
-
const contentBlock = typeof (first == null ? void 0 : first.type) === "string" ? first.type : "";
|
|
142
|
-
registry[id] = {
|
|
143
|
-
block: block.name,
|
|
144
|
-
title: block.displayName ?? id,
|
|
145
|
-
contentBlock
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
return registry;
|
|
149
|
-
}
|
|
150
|
-
const OVERLAY_PREFIX = "overlay:";
|
|
151
|
-
const OverlayServiceContext = createContext("services/overlay");
|
|
152
|
-
function createOverlayService(themeState, options = {}) {
|
|
153
|
-
const getBlocks = options.getBlocks ?? getDefinedBlocks;
|
|
154
|
-
const current = w(() => {
|
|
155
|
-
const root = themeState.overlay.blocks.value[0];
|
|
156
|
-
const type = root == null ? void 0 : root.type;
|
|
157
|
-
if (typeof type !== "string" || !type.startsWith(OVERLAY_PREFIX)) {
|
|
158
|
-
return null;
|
|
159
|
-
}
|
|
160
|
-
const props = root.props ?? {};
|
|
161
|
-
return {
|
|
162
|
-
name: type,
|
|
163
|
-
// Defaults mirror defineOverlayWrapper's seeding in overlay-wrapper.ts — keep in sync.
|
|
164
|
-
layout: props.layout ?? "sidebar",
|
|
165
|
-
canAbort: Boolean(props.canAbort ?? true)
|
|
166
|
-
};
|
|
167
|
-
});
|
|
168
|
-
return {
|
|
169
|
-
getEditableOverlays() {
|
|
170
|
-
return buildOverlayRegistry(getBlocks());
|
|
171
|
-
},
|
|
172
|
-
open(name, params) {
|
|
173
|
-
void themeState.resolve("overlay", name, params);
|
|
174
|
-
},
|
|
175
|
-
close() {
|
|
176
|
-
themeState.overlay.blocks.value = [];
|
|
177
|
-
},
|
|
178
|
-
current
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
75
|
function isLocalhostOrigin(origin) {
|
|
182
76
|
try {
|
|
183
77
|
const url = new URL(origin);
|
|
@@ -238,7 +132,7 @@ async function loadBlockRegistry(options = {}) {
|
|
|
238
132
|
if ("registerBlocks" in module && typeof module.registerBlocks === "function") {
|
|
239
133
|
module.registerBlocks();
|
|
240
134
|
} else {
|
|
241
|
-
Object.entries(module).forEach(([
|
|
135
|
+
Object.entries(module).forEach(([_, item]) => {
|
|
242
136
|
if (typeof item === "object" && item != null) {
|
|
243
137
|
registerBlock(item);
|
|
244
138
|
}
|
|
@@ -262,43 +156,13 @@ async function loadBlockRegistry(options = {}) {
|
|
|
262
156
|
function createCustomerService(options) {
|
|
263
157
|
const current = d(null);
|
|
264
158
|
const accountOrdersUrl = d(options.getAccountOrdersUrl());
|
|
265
|
-
|
|
159
|
+
const service = {
|
|
266
160
|
current,
|
|
267
|
-
accountOrdersUrl
|
|
268
|
-
async getCurrent() {
|
|
269
|
-
const result = await options.adapter.getCurrent();
|
|
270
|
-
if (result._tag === "Success") current.value = result.data;
|
|
271
|
-
return result;
|
|
272
|
-
}
|
|
161
|
+
accountOrdersUrl
|
|
273
162
|
};
|
|
163
|
+
return service;
|
|
274
164
|
}
|
|
275
165
|
const CustomerServiceContext = createContext("services/customer");
|
|
276
|
-
function createMockCustomerAdapter() {
|
|
277
|
-
const customer = { id: "customer-1", name: "Customer 1" };
|
|
278
|
-
return {
|
|
279
|
-
async getCurrent() {
|
|
280
|
-
return { _tag: "Success", data: customer };
|
|
281
|
-
}
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
function createApiCustomerAdapter(fetcher) {
|
|
285
|
-
return {
|
|
286
|
-
async getCurrent() {
|
|
287
|
-
try {
|
|
288
|
-
const res = await fetcher("/customer/info");
|
|
289
|
-
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
290
|
-
const data = await res.json();
|
|
291
|
-
const name = [data.firstName, data.lastName].filter(Boolean).join(" ") || data.email || "";
|
|
292
|
-
return {
|
|
293
|
-
_tag: "Success",
|
|
294
|
-
data: { id: data.id, name, email: data.email }
|
|
295
|
-
};
|
|
296
|
-
} catch (e2) {
|
|
297
|
-
return { _tag: "Failure", error: e2 };
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
};
|
|
301
|
-
}
|
|
302
166
|
const PARAMS = {
|
|
303
167
|
DELETGATED_TOKEN: "delegated-token",
|
|
304
168
|
REDIRECT_TO: "redirect-to",
|
|
@@ -312,125 +176,9 @@ const PARAMS = {
|
|
|
312
176
|
function uuid() {
|
|
313
177
|
return ("10000000-1000-4000-8000" + -1e11).replace(
|
|
314
178
|
/[018]/g,
|
|
315
|
-
(
|
|
179
|
+
(c) => (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
|
|
316
180
|
);
|
|
317
181
|
}
|
|
318
|
-
function createMockLoginAdapter() {
|
|
319
|
-
return {
|
|
320
|
-
async passwordlessStart() {
|
|
321
|
-
return { _tag: "Success", data: void 0 };
|
|
322
|
-
},
|
|
323
|
-
async authenticate() {
|
|
324
|
-
return {
|
|
325
|
-
_tag: "Success",
|
|
326
|
-
data: {
|
|
327
|
-
access_token: "mock-access-token",
|
|
328
|
-
refresh_token: "mock-refresh-token",
|
|
329
|
-
expires_in: 3600
|
|
330
|
-
}
|
|
331
|
-
};
|
|
332
|
-
},
|
|
333
|
-
getAuthorizeUrl({ connection, state, shop, appUrl }) {
|
|
334
|
-
const url = new URL(`${appUrl}/auth/customer/authorize`);
|
|
335
|
-
url.searchParams.set("connection", connection);
|
|
336
|
-
url.searchParams.set("state", state);
|
|
337
|
-
url.searchParams.set("shop", shop);
|
|
338
|
-
return url.toString();
|
|
339
|
-
}
|
|
340
|
-
};
|
|
341
|
-
}
|
|
342
|
-
function createApiLoginAdapter(fetcher, opts) {
|
|
343
|
-
const { shopDomain, appUrl } = opts;
|
|
344
|
-
async function callAuthEndpoint(path, body, lang) {
|
|
345
|
-
const url = lang ? `${path}?lang=${lang}` : path;
|
|
346
|
-
const res = await fetcher(url, {
|
|
347
|
-
method: "POST",
|
|
348
|
-
headers: {
|
|
349
|
-
Accept: "application/json",
|
|
350
|
-
"Content-Type": "application/json",
|
|
351
|
-
"X-Shopify-Shop-Domain": shopDomain
|
|
352
|
-
},
|
|
353
|
-
body: JSON.stringify(body)
|
|
354
|
-
});
|
|
355
|
-
if (res.status === 403) {
|
|
356
|
-
const data = await res.json();
|
|
357
|
-
if ("error_description" in data) {
|
|
358
|
-
if (data.error_description === "Wrong email or verification code.") {
|
|
359
|
-
throw new AuthenticationError(AuthenticationErrorReason.IncorrectCode);
|
|
360
|
-
} else if (data.error_description === "You've reached the maximum number of attempts. Please try to login again.") {
|
|
361
|
-
throw new AuthenticationError(AuthenticationErrorReason.TooManyTries);
|
|
362
|
-
} else if (data.error_description === "The verification code has expired. Please try to login again.") {
|
|
363
|
-
throw new AuthenticationError(AuthenticationErrorReason.Expired);
|
|
364
|
-
}
|
|
365
|
-
} else if ("code" in data && data.code === "AUTH_ERROR") {
|
|
366
|
-
throw new AuthenticationError(AuthenticationErrorReason.NoEmail, {
|
|
367
|
-
connection: data.connection
|
|
368
|
-
});
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
if (!res.ok) {
|
|
372
|
-
throw new Error(`Unexpected response: ${res.statusText}`);
|
|
373
|
-
}
|
|
374
|
-
return { _tag: "Success", data: void 0 };
|
|
375
|
-
}
|
|
376
|
-
return {
|
|
377
|
-
async passwordlessStart({ email, lang }) {
|
|
378
|
-
try {
|
|
379
|
-
await callAuthEndpoint(
|
|
380
|
-
"/auth/customer/passwordless/start",
|
|
381
|
-
{ email },
|
|
382
|
-
lang
|
|
383
|
-
);
|
|
384
|
-
return { _tag: "Success", data: void 0 };
|
|
385
|
-
} catch (e2) {
|
|
386
|
-
return { _tag: "Failure", error: e2 };
|
|
387
|
-
}
|
|
388
|
-
},
|
|
389
|
-
async authenticate(params) {
|
|
390
|
-
try {
|
|
391
|
-
const res = await fetcher("/auth/customer/token", {
|
|
392
|
-
method: "POST",
|
|
393
|
-
headers: {
|
|
394
|
-
Accept: "application/json",
|
|
395
|
-
"Content-Type": "application/json",
|
|
396
|
-
"X-Shopify-Shop-Domain": shopDomain
|
|
397
|
-
},
|
|
398
|
-
body: JSON.stringify(params)
|
|
399
|
-
});
|
|
400
|
-
if (res.status === 403) {
|
|
401
|
-
const data = await res.json();
|
|
402
|
-
if ("error_description" in data) {
|
|
403
|
-
if (data.error_description === "Wrong email or verification code.") {
|
|
404
|
-
throw new AuthenticationError(AuthenticationErrorReason.IncorrectCode);
|
|
405
|
-
} else if (data.error_description === "You've reached the maximum number of attempts. Please try to login again.") {
|
|
406
|
-
throw new AuthenticationError(AuthenticationErrorReason.TooManyTries);
|
|
407
|
-
} else if (data.error_description === "The verification code has expired. Please try to login again.") {
|
|
408
|
-
throw new AuthenticationError(AuthenticationErrorReason.Expired);
|
|
409
|
-
}
|
|
410
|
-
} else if ("code" in data && data.code === "AUTH_ERROR") {
|
|
411
|
-
throw new AuthenticationError(AuthenticationErrorReason.NoEmail, {
|
|
412
|
-
connection: data.connection
|
|
413
|
-
});
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
if (!res.ok) {
|
|
417
|
-
throw new Error(`Unexpected response: ${res.statusText}`);
|
|
418
|
-
}
|
|
419
|
-
return { _tag: "Success", data: await res.json() };
|
|
420
|
-
} catch (e2) {
|
|
421
|
-
return { _tag: "Failure", error: e2 };
|
|
422
|
-
}
|
|
423
|
-
},
|
|
424
|
-
getAuthorizeUrl({ connection, state, shop }) {
|
|
425
|
-
const url = new URL(`${appUrl}/auth/customer/authorize`);
|
|
426
|
-
url.searchParams.set("connection", connection);
|
|
427
|
-
url.searchParams.set("state", state);
|
|
428
|
-
url.searchParams.set("shop", shop);
|
|
429
|
-
return url.toString();
|
|
430
|
-
}
|
|
431
|
-
};
|
|
432
|
-
}
|
|
433
|
-
const __vite_import_meta_env__ = {};
|
|
434
182
|
var AuthenticationErrorReason = /* @__PURE__ */ ((AuthenticationErrorReason2) => {
|
|
435
183
|
AuthenticationErrorReason2["Expired"] = "expired";
|
|
436
184
|
AuthenticationErrorReason2["IncorrectCode"] = "wrong-code";
|
|
@@ -449,9 +197,6 @@ class AuthenticationError extends Error {
|
|
|
449
197
|
this.data = data ?? {};
|
|
450
198
|
}
|
|
451
199
|
}
|
|
452
|
-
const STORAGE_DELEGATED_TOKEN = "delegated-token";
|
|
453
|
-
const STORAGE_REFRESH_TOKEN_KEY = "refresh-token";
|
|
454
|
-
const STORAGE_STATE_KEY = "state";
|
|
455
200
|
function storage(key, value) {
|
|
456
201
|
const storageKey = `auth:${key}`;
|
|
457
202
|
try {
|
|
@@ -467,33 +212,83 @@ function storage(key, value) {
|
|
|
467
212
|
}
|
|
468
213
|
return null;
|
|
469
214
|
}
|
|
215
|
+
const STORAGE_DELEGATED_TOKEN = "delegated-token";
|
|
216
|
+
const STORAGE_REFRESH_TOKEN_KEY = "refresh-token";
|
|
217
|
+
const STORAGE_STATE_KEY = "state";
|
|
218
|
+
function createAuthFunctions(basePath, shop) {
|
|
219
|
+
async function start(email, lang) {
|
|
220
|
+
const response = await fetch(
|
|
221
|
+
`${basePath}/auth/customer/passwordless/start?lang=${lang}`,
|
|
222
|
+
{
|
|
223
|
+
method: "POST",
|
|
224
|
+
headers: {
|
|
225
|
+
Accept: "application/json",
|
|
226
|
+
"Content-Type": "application/json",
|
|
227
|
+
"X-Shopify-Shop-Domain": shop
|
|
228
|
+
},
|
|
229
|
+
body: JSON.stringify({ email })
|
|
230
|
+
}
|
|
231
|
+
);
|
|
232
|
+
if (response.status !== 200) {
|
|
233
|
+
throw new Error(`Unexpected response: ${response.statusText}`);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
async function authenticate(data) {
|
|
237
|
+
const response = await fetch(`${basePath}/auth/customer/token`, {
|
|
238
|
+
method: "POST",
|
|
239
|
+
headers: {
|
|
240
|
+
Accept: "application/json",
|
|
241
|
+
"Content-Type": "application/json",
|
|
242
|
+
"X-Shopify-Shop-Domain": shop
|
|
243
|
+
},
|
|
244
|
+
body: JSON.stringify(data)
|
|
245
|
+
});
|
|
246
|
+
if (response.status === 403) {
|
|
247
|
+
const data2 = await response.json();
|
|
248
|
+
if ("error_description" in data2) {
|
|
249
|
+
if (data2.error_description === "Wrong email or verification code.") {
|
|
250
|
+
throw new AuthenticationError(
|
|
251
|
+
"wrong-code"
|
|
252
|
+
/* IncorrectCode */
|
|
253
|
+
);
|
|
254
|
+
} else if (data2.error_description === "You've reached the maximum number of attempts. Please try to login again.") {
|
|
255
|
+
throw new AuthenticationError(
|
|
256
|
+
"too-many-tries"
|
|
257
|
+
/* TooManyTries */
|
|
258
|
+
);
|
|
259
|
+
} else if (data2.error_description === "The verification code has expired. Please try to login again.") {
|
|
260
|
+
throw new AuthenticationError(
|
|
261
|
+
"expired"
|
|
262
|
+
/* Expired */
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
} else if ("code" in data2) {
|
|
266
|
+
if (data2.code === "AUTH_ERROR") {
|
|
267
|
+
throw new AuthenticationError("no-email", {
|
|
268
|
+
connection: data2.connection
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
if (response.status !== 200) {
|
|
274
|
+
throw new Error(`Unexpected response: ${response.statusText}`);
|
|
275
|
+
}
|
|
276
|
+
return await response.json();
|
|
277
|
+
}
|
|
278
|
+
return { start, authenticate };
|
|
279
|
+
}
|
|
470
280
|
function nonce(len = 10) {
|
|
471
281
|
return uuid().replace(/-/g, "").slice(0, len);
|
|
472
282
|
}
|
|
473
|
-
function
|
|
474
|
-
const
|
|
475
|
-
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";
|
|
476
|
-
const fetcher = (path, init) => fetch(`${apiBase}${path}`, init);
|
|
477
|
-
const adapter = createApiLoginAdapter(fetcher, {
|
|
478
|
-
shopDomain: deps.shop.domain,
|
|
479
|
-
appUrl
|
|
480
|
-
});
|
|
481
|
-
return createLoginService({
|
|
482
|
-
adapter,
|
|
483
|
-
router: deps.router,
|
|
484
|
-
formatError: deps.formatError,
|
|
485
|
-
shop: {
|
|
486
|
-
domain: deps.shop.domain,
|
|
487
|
-
locale: w(() => deps.getLocale())
|
|
488
|
-
},
|
|
489
|
-
onLoginSuccess: deps.onLoginSuccess
|
|
490
|
-
});
|
|
491
|
-
}
|
|
492
|
-
function createLoginService(init) {
|
|
493
|
-
const { adapter, formatError, router, shop, onLoginSuccess } = init;
|
|
283
|
+
function createAuthService(apiUrl, shopDomain) {
|
|
284
|
+
const { start: authStart, authenticate: authAuthenticate } = createAuthFunctions(apiUrl, shopDomain);
|
|
494
285
|
const accessToken = d(null);
|
|
495
|
-
const delegatedToken = d(
|
|
496
|
-
|
|
286
|
+
const delegatedToken = d(
|
|
287
|
+
storage(STORAGE_DELEGATED_TOKEN)
|
|
288
|
+
);
|
|
289
|
+
const refreshToken = d(
|
|
290
|
+
storage(STORAGE_REFRESH_TOKEN_KEY)
|
|
291
|
+
);
|
|
497
292
|
const expires = d(null);
|
|
498
293
|
const state = d(storage(STORAGE_STATE_KEY));
|
|
499
294
|
E(() => {
|
|
@@ -505,28 +300,67 @@ function createLoginService(init) {
|
|
|
505
300
|
E(() => {
|
|
506
301
|
storage(STORAGE_STATE_KEY, state.value);
|
|
507
302
|
});
|
|
508
|
-
function
|
|
303
|
+
function store(data) {
|
|
509
304
|
const { access_token, refresh_token, expires_in } = data;
|
|
510
305
|
accessToken.value = access_token;
|
|
511
306
|
refreshToken.value = refresh_token;
|
|
512
307
|
expires.value = new Date(Date.now() + expires_in);
|
|
513
308
|
}
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
309
|
+
return {
|
|
310
|
+
state,
|
|
311
|
+
async start(email, lang) {
|
|
312
|
+
await authStart(email, lang);
|
|
313
|
+
},
|
|
314
|
+
async authenticate(data) {
|
|
315
|
+
store(await authAuthenticate(data));
|
|
316
|
+
},
|
|
317
|
+
getDelegatedToken() {
|
|
318
|
+
return delegatedToken.value;
|
|
319
|
+
},
|
|
320
|
+
setDelegatedToken(token) {
|
|
321
|
+
delegatedToken.value = token;
|
|
322
|
+
},
|
|
323
|
+
async getToken() {
|
|
324
|
+
if (refreshToken.value == null) {
|
|
325
|
+
return null;
|
|
326
|
+
}
|
|
327
|
+
if (accessToken.value == null || typeof accessToken.value === "string" && (expires.value == null || expires.value < /* @__PURE__ */ new Date())) {
|
|
328
|
+
accessToken.value = new Promise(
|
|
329
|
+
(resolve, reject) => authAuthenticate({ refreshToken: refreshToken.value }).then((data) => data).then((data) => {
|
|
330
|
+
store(data);
|
|
331
|
+
resolve(data.access_token);
|
|
521
332
|
return;
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
333
|
+
}).catch(reject)
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
return Promise.resolve(accessToken.value);
|
|
337
|
+
},
|
|
338
|
+
logout() {
|
|
339
|
+
accessToken.value = null;
|
|
340
|
+
delegatedToken.value = null;
|
|
341
|
+
refreshToken.value = null;
|
|
342
|
+
expires.value = null;
|
|
343
|
+
},
|
|
344
|
+
isAuthenticated: w(
|
|
345
|
+
() => delegatedToken.value != null || refreshToken.value != null
|
|
346
|
+
)
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
function loginService(deps) {
|
|
350
|
+
return createLoginService({
|
|
351
|
+
apiUrl: `${deps.shop.rootUrl.replace(/\/$/, "")}/api`,
|
|
352
|
+
router: deps.router,
|
|
353
|
+
formatError: deps.formatError,
|
|
354
|
+
shop: {
|
|
355
|
+
domain: deps.shop.domain,
|
|
356
|
+
locale: w(() => deps.getLocale())
|
|
357
|
+
},
|
|
358
|
+
onLoginSuccess: deps.onLoginSuccess
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
function createLoginService(init) {
|
|
362
|
+
const { formatError, router, shop, onLoginSuccess } = init;
|
|
363
|
+
const auth = createAuthService(init.apiUrl.replace(/\/$/, ""), shop.domain);
|
|
530
364
|
const userEmail = d(null);
|
|
531
365
|
async function onSuccess(query) {
|
|
532
366
|
await (onLoginSuccess == null ? void 0 : onLoginSuccess());
|
|
@@ -539,9 +373,13 @@ function createLoginService(init) {
|
|
|
539
373
|
queryParams[key] = value;
|
|
540
374
|
}
|
|
541
375
|
});
|
|
542
|
-
router.push({
|
|
376
|
+
router.push({
|
|
377
|
+
path: decodeURIComponent(redirect),
|
|
378
|
+
query: queryParams
|
|
379
|
+
});
|
|
543
380
|
} else {
|
|
544
|
-
|
|
381
|
+
const redirect = query.get(PARAMS.REDIRECT_TO);
|
|
382
|
+
router.replace(decodeURIComponent(redirect));
|
|
545
383
|
}
|
|
546
384
|
} else {
|
|
547
385
|
router.replace("/");
|
|
@@ -553,17 +391,16 @@ function createLoginService(init) {
|
|
|
553
391
|
async start(email) {
|
|
554
392
|
try {
|
|
555
393
|
userEmail.value = email;
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
});
|
|
561
|
-
if (result._tag === "Failure") {
|
|
562
|
-
return { _tag: "Failure", error: formatError(result.error) };
|
|
563
|
-
}
|
|
394
|
+
await auth.start(
|
|
395
|
+
userEmail.value,
|
|
396
|
+
shop.locale instanceof u ? shop.locale.value : shop.locale
|
|
397
|
+
);
|
|
564
398
|
return { _tag: "Success", data: void 0 };
|
|
565
399
|
} catch (error) {
|
|
566
|
-
return {
|
|
400
|
+
return {
|
|
401
|
+
_tag: "Failure",
|
|
402
|
+
error: formatError(error)
|
|
403
|
+
};
|
|
567
404
|
}
|
|
568
405
|
},
|
|
569
406
|
async verify(email, code) {
|
|
@@ -574,61 +411,63 @@ function createLoginService(init) {
|
|
|
574
411
|
};
|
|
575
412
|
}
|
|
576
413
|
try {
|
|
577
|
-
const result = await
|
|
414
|
+
const result = await auth.authenticate({
|
|
578
415
|
email: userEmail.value,
|
|
579
416
|
code
|
|
580
417
|
});
|
|
581
|
-
if (result._tag === "Failure") {
|
|
582
|
-
return { _tag: "Failure", error: formatError(result.error) };
|
|
583
|
-
}
|
|
584
|
-
storeTokens(result.data);
|
|
585
418
|
await onSuccess();
|
|
586
|
-
return { _tag: "Success", data:
|
|
419
|
+
return { _tag: "Success", data: result };
|
|
587
420
|
} catch (error) {
|
|
588
|
-
return {
|
|
421
|
+
return {
|
|
422
|
+
_tag: "Failure",
|
|
423
|
+
error: formatError(error)
|
|
424
|
+
};
|
|
589
425
|
}
|
|
590
426
|
}
|
|
591
427
|
}
|
|
592
428
|
},
|
|
593
429
|
social: {
|
|
594
430
|
async start(provider) {
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
)
|
|
605
|
-
}
|
|
606
|
-
|
|
431
|
+
function socialUrl(key, shop2) {
|
|
432
|
+
auth.state.value = nonce();
|
|
433
|
+
const url = new URL(
|
|
434
|
+
// TODO: double check admin url
|
|
435
|
+
`${"https://admin.juo.io"}/auth/customer/authorize`
|
|
436
|
+
);
|
|
437
|
+
url.searchParams.append("connection", key);
|
|
438
|
+
url.searchParams.append("state", auth.state.value ?? "");
|
|
439
|
+
url.searchParams.append("shop", shop2);
|
|
440
|
+
return url.toString();
|
|
441
|
+
}
|
|
442
|
+
window.location.href = socialUrl(
|
|
443
|
+
provider === "Facebook" ? "facebook" : "google-oauth2",
|
|
444
|
+
shop.domain
|
|
445
|
+
);
|
|
446
|
+
return {
|
|
447
|
+
_tag: "Success",
|
|
448
|
+
data: void 0
|
|
449
|
+
};
|
|
607
450
|
},
|
|
608
451
|
async verify(code) {
|
|
609
452
|
try {
|
|
610
|
-
const result = await
|
|
611
|
-
if (result._tag === "Failure") {
|
|
612
|
-
return { _tag: "Failure", error: formatError(result.error) };
|
|
613
|
-
}
|
|
614
|
-
storeTokens(result.data);
|
|
453
|
+
const result = await auth.authenticate({ code });
|
|
615
454
|
await onSuccess();
|
|
616
|
-
return { _tag: "Success", data:
|
|
455
|
+
return { _tag: "Success", data: result };
|
|
617
456
|
} catch (error) {
|
|
618
|
-
return {
|
|
457
|
+
return {
|
|
458
|
+
_tag: "Failure",
|
|
459
|
+
error: formatError(error)
|
|
460
|
+
};
|
|
619
461
|
}
|
|
620
462
|
}
|
|
621
463
|
},
|
|
622
464
|
async logout() {
|
|
623
|
-
|
|
624
|
-
delegatedToken.value = null;
|
|
625
|
-
refreshToken.value = null;
|
|
626
|
-
expires.value = null;
|
|
465
|
+
await auth.logout();
|
|
627
466
|
return { _tag: "Success", data: void 0 };
|
|
628
467
|
},
|
|
629
468
|
async handleQueryParams(query) {
|
|
630
469
|
if (query.has(PARAMS.DELETGATED_TOKEN)) {
|
|
631
|
-
|
|
470
|
+
auth.setDelegatedToken(query.get(PARAMS.DELETGATED_TOKEN));
|
|
632
471
|
await onSuccess(query);
|
|
633
472
|
return { _tag: "Success", data: void 0 };
|
|
634
473
|
}
|
|
@@ -640,13 +479,16 @@ function createLoginService(init) {
|
|
|
640
479
|
}
|
|
641
480
|
const code = query.get(PARAMS.CODE);
|
|
642
481
|
if (code != null) {
|
|
643
|
-
if (query.get(PARAMS.STATE) !== state.value) {
|
|
644
|
-
return {
|
|
482
|
+
if (query.get(PARAMS.STATE) !== auth.state.value) {
|
|
483
|
+
return {
|
|
484
|
+
_tag: "Failure",
|
|
485
|
+
error: formatError("Invalid state")
|
|
486
|
+
};
|
|
645
487
|
} else {
|
|
646
488
|
return service.social.verify(code);
|
|
647
489
|
}
|
|
648
490
|
}
|
|
649
|
-
if (query.get(PARAMS.REFRESH) === "1" && await getToken() != null) {
|
|
491
|
+
if (query.get(PARAMS.REFRESH) === "1" && await auth.getToken() != null) {
|
|
650
492
|
await onSuccess();
|
|
651
493
|
return { _tag: "Success", data: void 0 };
|
|
652
494
|
}
|
|
@@ -663,20 +505,8 @@ function createLoginService(init) {
|
|
|
663
505
|
}
|
|
664
506
|
return { _tag: "Success", data: void 0 };
|
|
665
507
|
},
|
|
666
|
-
isAuthenticated:
|
|
667
|
-
|
|
668
|
-
),
|
|
669
|
-
getToken,
|
|
670
|
-
async getAuthHeaders() {
|
|
671
|
-
if (delegatedToken.value) {
|
|
672
|
-
return { "X-Delegated-Token": delegatedToken.value };
|
|
673
|
-
}
|
|
674
|
-
const token = await getToken();
|
|
675
|
-
if (token) {
|
|
676
|
-
return { Authorization: `Bearer ${token}` };
|
|
677
|
-
}
|
|
678
|
-
return {};
|
|
679
|
-
}
|
|
508
|
+
isAuthenticated: auth.isAuthenticated,
|
|
509
|
+
getToken: () => auth.getToken()
|
|
680
510
|
};
|
|
681
511
|
return service;
|
|
682
512
|
}
|
|
@@ -895,18 +725,6 @@ function createMockProductAdapter() {
|
|
|
895
725
|
);
|
|
896
726
|
return product == null ? null : processProduct(product);
|
|
897
727
|
},
|
|
898
|
-
async getProductsByIds() {
|
|
899
|
-
return [];
|
|
900
|
-
},
|
|
901
|
-
async getBoxVariants() {
|
|
902
|
-
return [];
|
|
903
|
-
},
|
|
904
|
-
async getAvailableProducts() {
|
|
905
|
-
return [];
|
|
906
|
-
},
|
|
907
|
-
async getBox() {
|
|
908
|
-
return null;
|
|
909
|
-
},
|
|
910
728
|
async search(opts) {
|
|
911
729
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
912
730
|
let filteredProducts = [...products];
|
|
@@ -947,185 +765,6 @@ function createMockProductAdapter() {
|
|
|
947
765
|
}
|
|
948
766
|
};
|
|
949
767
|
}
|
|
950
|
-
function mapVariant(variant, currencyCode) {
|
|
951
|
-
var _a;
|
|
952
|
-
return {
|
|
953
|
-
id: variant.id,
|
|
954
|
-
title: variant.title,
|
|
955
|
-
image: variant.image == null ? null : { url: variant.image.url, altText: variant.image.altText ?? void 0 },
|
|
956
|
-
price: { amount: variant.price.toFixed(2), currencyCode },
|
|
957
|
-
...((_a = variant.purchaseOptions) == null ? void 0 : _a.length) ? {
|
|
958
|
-
purchaseOptions: variant.purchaseOptions.map((opt) => ({
|
|
959
|
-
name: opt.name,
|
|
960
|
-
price: { amount: opt.price.toFixed(2), currencyCode },
|
|
961
|
-
interval: opt.interval
|
|
962
|
-
}))
|
|
963
|
-
} : {}
|
|
964
|
-
};
|
|
965
|
-
}
|
|
966
|
-
function mapProduct(product, currencyCode) {
|
|
967
|
-
return {
|
|
968
|
-
id: product.id,
|
|
969
|
-
title: product.title,
|
|
970
|
-
featuredImage: product.image == null ? null : { url: product.image.url, altText: product.image.altText ?? void 0 },
|
|
971
|
-
hasOnlyDefaultVariant: product.variants.length <= 1,
|
|
972
|
-
variants: {
|
|
973
|
-
items: product.variants.map((v2) => mapVariant(v2, currencyCode)),
|
|
974
|
-
pageInfo: {
|
|
975
|
-
hasNextPage: false,
|
|
976
|
-
hasPrevPage: false,
|
|
977
|
-
startCursor: null,
|
|
978
|
-
endCursor: null
|
|
979
|
-
}
|
|
980
|
-
},
|
|
981
|
-
collections: product.collections
|
|
982
|
-
};
|
|
983
|
-
}
|
|
984
|
-
function createApiProductAdapter(fetcher, opts = { currencyCode: "USD" }) {
|
|
985
|
-
const { currencyCode } = opts;
|
|
986
|
-
async function fetchProducts(params) {
|
|
987
|
-
const query = params.toString() ? `?${params.toString()}` : "";
|
|
988
|
-
const res = await fetcher(`/customer/v1/products${query}`);
|
|
989
|
-
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
990
|
-
return res.json();
|
|
991
|
-
}
|
|
992
|
-
return {
|
|
993
|
-
async getById(id) {
|
|
994
|
-
try {
|
|
995
|
-
const params = new URLSearchParams({ query: `id:"${id}"`, limit: "1" });
|
|
996
|
-
const body = await fetchProducts(params);
|
|
997
|
-
return body.data.length > 0 ? mapProduct(body.data[0], currencyCode) : null;
|
|
998
|
-
} catch {
|
|
999
|
-
return null;
|
|
1000
|
-
}
|
|
1001
|
-
},
|
|
1002
|
-
async getByVariantId(variantId) {
|
|
1003
|
-
try {
|
|
1004
|
-
const params = new URLSearchParams({
|
|
1005
|
-
query: `variants.id:"${variantId}"`,
|
|
1006
|
-
limit: "1"
|
|
1007
|
-
});
|
|
1008
|
-
const body = await fetchProducts(params);
|
|
1009
|
-
return body.data.length > 0 ? mapProduct(body.data[0], currencyCode) : null;
|
|
1010
|
-
} catch {
|
|
1011
|
-
return null;
|
|
1012
|
-
}
|
|
1013
|
-
},
|
|
1014
|
-
async search(opts2) {
|
|
1015
|
-
try {
|
|
1016
|
-
const params = new URLSearchParams();
|
|
1017
|
-
if (opts2 == null ? void 0 : opts2.query) {
|
|
1018
|
-
params.set("query", `"${opts2.query.trim().replace(/"/g, '\\"')}"`);
|
|
1019
|
-
}
|
|
1020
|
-
if (opts2 == null ? void 0 : opts2.pagination) {
|
|
1021
|
-
if ("after" in opts2.pagination)
|
|
1022
|
-
params.set("after", opts2.pagination.after);
|
|
1023
|
-
else if ("before" in opts2.pagination)
|
|
1024
|
-
params.set("before", opts2.pagination.before);
|
|
1025
|
-
}
|
|
1026
|
-
const body = await fetchProducts(params);
|
|
1027
|
-
return {
|
|
1028
|
-
items: body.data.map((p2) => mapProduct(p2, currencyCode)),
|
|
1029
|
-
pageInfo: {
|
|
1030
|
-
hasNextPage: body.hasNextPage,
|
|
1031
|
-
hasPrevPage: body.hasPrevPage,
|
|
1032
|
-
startCursor: body.startCursor,
|
|
1033
|
-
endCursor: body.endCursor
|
|
1034
|
-
}
|
|
1035
|
-
};
|
|
1036
|
-
} catch {
|
|
1037
|
-
return {
|
|
1038
|
-
items: [],
|
|
1039
|
-
pageInfo: {
|
|
1040
|
-
hasNextPage: false,
|
|
1041
|
-
hasPrevPage: false,
|
|
1042
|
-
startCursor: null,
|
|
1043
|
-
endCursor: null
|
|
1044
|
-
}
|
|
1045
|
-
};
|
|
1046
|
-
}
|
|
1047
|
-
},
|
|
1048
|
-
async getProductsByIds({ ids }) {
|
|
1049
|
-
const params = new URLSearchParams();
|
|
1050
|
-
for (const id of ids) params.append("ids", id);
|
|
1051
|
-
const res = await fetcher(`/customer/products/by-ids?${params.toString()}`);
|
|
1052
|
-
if (!res.ok) return [];
|
|
1053
|
-
const data = await res.json();
|
|
1054
|
-
return (data ?? []).map((p2) => ({
|
|
1055
|
-
id: p2.id,
|
|
1056
|
-
title: p2.title ?? "",
|
|
1057
|
-
featuredImage: p2.featuredImage ? { url: p2.featuredImage.url, altText: p2.featuredImage.altText } : null,
|
|
1058
|
-
hasOnlyDefaultVariant: true,
|
|
1059
|
-
collections: []
|
|
1060
|
-
}));
|
|
1061
|
-
},
|
|
1062
|
-
async getBoxVariants({ ids, subscriptionId }) {
|
|
1063
|
-
const params = new URLSearchParams();
|
|
1064
|
-
for (const id of ids) params.append("ids", id);
|
|
1065
|
-
params.set("subscriptionId", subscriptionId);
|
|
1066
|
-
const res = await fetcher(`/customer/products/variants?${params.toString()}`);
|
|
1067
|
-
if (!res.ok) return [];
|
|
1068
|
-
const data = await res.json();
|
|
1069
|
-
return (data ?? []).map((p2) => {
|
|
1070
|
-
var _a;
|
|
1071
|
-
return {
|
|
1072
|
-
id: p2.id,
|
|
1073
|
-
title: p2.title,
|
|
1074
|
-
description: p2.description ?? "",
|
|
1075
|
-
featuredImage: p2.featuredImage,
|
|
1076
|
-
hasOnlyDefaultVariant: p2.hasOnlyDefaultVariant ?? (((_a = p2.variants) == null ? void 0 : _a.length) ?? 0) <= 1,
|
|
1077
|
-
variants: (p2.variants ?? []).map((v2) => ({
|
|
1078
|
-
id: v2.id,
|
|
1079
|
-
availableForSale: v2.availableForSale,
|
|
1080
|
-
title: v2.title,
|
|
1081
|
-
currentPrice: v2.currentPrice,
|
|
1082
|
-
basePrice: v2.basePrice,
|
|
1083
|
-
image: v2.image,
|
|
1084
|
-
recurring: v2.recurring
|
|
1085
|
-
}))
|
|
1086
|
-
};
|
|
1087
|
-
});
|
|
1088
|
-
},
|
|
1089
|
-
async getAvailableProducts({ subscriptionId, lineId, q: q2, mode, action, pagination }) {
|
|
1090
|
-
const params = new URLSearchParams();
|
|
1091
|
-
params.set("action", action);
|
|
1092
|
-
if (q2) params.set("q", q2);
|
|
1093
|
-
if (mode) params.set("mode", mode);
|
|
1094
|
-
if (pagination && "after" in pagination) params.set("after", pagination.after);
|
|
1095
|
-
if (pagination && "before" in pagination) params.set("before", pagination.before);
|
|
1096
|
-
const res = await fetcher(
|
|
1097
|
-
`/customer/subscriptions/${subscriptionId}/lines/${lineId}/available-products?${params.toString()}`
|
|
1098
|
-
);
|
|
1099
|
-
if (!res.ok) return [];
|
|
1100
|
-
const data = await res.json();
|
|
1101
|
-
return (data ?? []).map((p2) => {
|
|
1102
|
-
var _a;
|
|
1103
|
-
return {
|
|
1104
|
-
id: p2.id,
|
|
1105
|
-
title: p2.title,
|
|
1106
|
-
description: p2.description ?? "",
|
|
1107
|
-
featuredImage: p2.featuredImage,
|
|
1108
|
-
hasOnlyDefaultVariant: p2.hasOnlyDefaultVariant ?? (((_a = p2.variants) == null ? void 0 : _a.length) ?? 0) <= 1,
|
|
1109
|
-
variants: (p2.variants ?? []).map((v2) => ({
|
|
1110
|
-
id: v2.id,
|
|
1111
|
-
availableForSale: v2.availableForSale,
|
|
1112
|
-
title: v2.title,
|
|
1113
|
-
currentPrice: v2.currentPrice,
|
|
1114
|
-
basePrice: v2.basePrice,
|
|
1115
|
-
image: v2.image,
|
|
1116
|
-
recurring: v2.recurring
|
|
1117
|
-
}))
|
|
1118
|
-
};
|
|
1119
|
-
});
|
|
1120
|
-
},
|
|
1121
|
-
async getBox({ id }) {
|
|
1122
|
-
const res = await fetcher(`/customer/boxes/${id}`);
|
|
1123
|
-
if (!res.ok) return null;
|
|
1124
|
-
const data = await res.json();
|
|
1125
|
-
return { id, ...data };
|
|
1126
|
-
}
|
|
1127
|
-
};
|
|
1128
|
-
}
|
|
1129
768
|
const ProductServiceContext = createContext("services/product");
|
|
1130
769
|
function createProductService(adapter) {
|
|
1131
770
|
return {
|
|
@@ -1137,11 +776,7 @@ function createProductService(adapter) {
|
|
|
1137
776
|
},
|
|
1138
777
|
async search(opts) {
|
|
1139
778
|
return adapter.search(opts);
|
|
1140
|
-
}
|
|
1141
|
-
getProductsByIds: (ids) => adapter.getProductsByIds({ ids }),
|
|
1142
|
-
getBoxVariants: (ids, subscriptionId) => adapter.getBoxVariants({ ids, subscriptionId }),
|
|
1143
|
-
getAvailableProducts: (params) => adapter.getAvailableProducts(params),
|
|
1144
|
-
getBox: (id) => adapter.getBox({ id })
|
|
779
|
+
}
|
|
1145
780
|
};
|
|
1146
781
|
}
|
|
1147
782
|
function routeNameTableFromPages(pages) {
|
|
@@ -1195,407 +830,10 @@ function createMockRouterService() {
|
|
|
1195
830
|
}
|
|
1196
831
|
}).routerService;
|
|
1197
832
|
}
|
|
1198
|
-
function
|
|
1199
|
-
const
|
|
1200
|
-
d3.setDate(d3.getDate() + daysFromNow);
|
|
1201
|
-
return d3.toISOString().split("T")[0] ?? "";
|
|
1202
|
-
}
|
|
1203
|
-
function createMockSchedulesAdapter() {
|
|
1204
|
-
let orders = [
|
|
833
|
+
function createMockSubscriptionAdapter(options = {}) {
|
|
834
|
+
const mockSubscriptions = [
|
|
1205
835
|
{
|
|
1206
|
-
id: "
|
|
1207
|
-
customerId: "customer_1",
|
|
1208
|
-
subscriptionId: "sub_1",
|
|
1209
|
-
date: "2026-05-22T12:00:00Z",
|
|
1210
|
-
skipped: false,
|
|
1211
|
-
currencyCode: "USD",
|
|
1212
|
-
subtotal: 2999,
|
|
1213
|
-
deliveryPrice: 500,
|
|
1214
|
-
cycleIndex: 4,
|
|
1215
|
-
deliveryDate: futureDateStr(7),
|
|
1216
|
-
status: "scheduled",
|
|
1217
|
-
items: [
|
|
1218
|
-
{
|
|
1219
|
-
id: "sched_item_1",
|
|
1220
|
-
title: "Rose Face Serum",
|
|
1221
|
-
subtitle: null,
|
|
1222
|
-
imageUrl: "https://images.unsplash.com/photo-1611930022073-b7a4ba5fcccd?w=200",
|
|
1223
|
-
variantId: "variant_1",
|
|
1224
|
-
quantity: 1,
|
|
1225
|
-
price: 2999,
|
|
1226
|
-
totalPrice: 2999,
|
|
1227
|
-
variantImage: null,
|
|
1228
|
-
variantAvailableForSale: true,
|
|
1229
|
-
subscriptionId: "sub_1",
|
|
1230
|
-
subscriptionLineId: "item_1",
|
|
1231
|
-
productId: "product_1",
|
|
1232
|
-
parentProductId: null,
|
|
1233
|
-
parentLineId: null,
|
|
1234
|
-
isVirtual: false,
|
|
1235
|
-
isUpsell: false,
|
|
1236
|
-
isOneTime: false,
|
|
1237
|
-
customAttributes: []
|
|
1238
|
-
},
|
|
1239
|
-
{
|
|
1240
|
-
id: "sched_item_2",
|
|
1241
|
-
title: "Vitamin C Moisturizer",
|
|
1242
|
-
subtitle: null,
|
|
1243
|
-
imageUrl: "https://images.unsplash.com/photo-1556228578-8c89e6adf883?w=200",
|
|
1244
|
-
variantId: "variant_2",
|
|
1245
|
-
quantity: 1,
|
|
1246
|
-
price: 2999,
|
|
1247
|
-
totalPrice: 2999,
|
|
1248
|
-
variantImage: null,
|
|
1249
|
-
variantAvailableForSale: true,
|
|
1250
|
-
subscriptionId: "sub_1",
|
|
1251
|
-
subscriptionLineId: "item_2",
|
|
1252
|
-
productId: "product_2",
|
|
1253
|
-
parentProductId: null,
|
|
1254
|
-
parentLineId: null,
|
|
1255
|
-
isVirtual: false,
|
|
1256
|
-
isUpsell: false,
|
|
1257
|
-
isOneTime: false,
|
|
1258
|
-
customAttributes: []
|
|
1259
|
-
}
|
|
1260
|
-
],
|
|
1261
|
-
discounts: []
|
|
1262
|
-
},
|
|
1263
|
-
{
|
|
1264
|
-
id: "sched_2",
|
|
1265
|
-
customerId: "customer_1",
|
|
1266
|
-
subscriptionId: "sub_1",
|
|
1267
|
-
date: "2026-05-22T12:00:00Z",
|
|
1268
|
-
skipped: false,
|
|
1269
|
-
currencyCode: "USD",
|
|
1270
|
-
subtotal: 2999,
|
|
1271
|
-
deliveryPrice: 500,
|
|
1272
|
-
cycleIndex: 4,
|
|
1273
|
-
deliveryDate: futureDateStr(7),
|
|
1274
|
-
status: "scheduled",
|
|
1275
|
-
items: [
|
|
1276
|
-
{
|
|
1277
|
-
id: "sched_item_3",
|
|
1278
|
-
title: "Rose Face Serum",
|
|
1279
|
-
subtitle: null,
|
|
1280
|
-
imageUrl: "https://images.unsplash.com/photo-1611930022073-b7a4ba5fcccd?w=200",
|
|
1281
|
-
variantId: "variant_1",
|
|
1282
|
-
quantity: 1,
|
|
1283
|
-
price: 2999,
|
|
1284
|
-
totalPrice: 2999,
|
|
1285
|
-
variantImage: null,
|
|
1286
|
-
variantAvailableForSale: true,
|
|
1287
|
-
subscriptionId: "sub_1",
|
|
1288
|
-
subscriptionLineId: "item_1",
|
|
1289
|
-
productId: "product_1",
|
|
1290
|
-
parentProductId: null,
|
|
1291
|
-
parentLineId: null,
|
|
1292
|
-
isVirtual: false,
|
|
1293
|
-
isUpsell: false,
|
|
1294
|
-
isOneTime: false,
|
|
1295
|
-
customAttributes: []
|
|
1296
|
-
},
|
|
1297
|
-
{
|
|
1298
|
-
id: "sched_item_4",
|
|
1299
|
-
title: "Hyaluronic Acid Serum",
|
|
1300
|
-
subtitle: null,
|
|
1301
|
-
imageUrl: "https://images.unsplash.com/photo-1621232201696-ba6c9db10a97?w=200",
|
|
1302
|
-
variantId: "variant_3",
|
|
1303
|
-
quantity: 1,
|
|
1304
|
-
price: 2999,
|
|
1305
|
-
totalPrice: 2999,
|
|
1306
|
-
variantImage: null,
|
|
1307
|
-
variantAvailableForSale: true,
|
|
1308
|
-
subscriptionId: "sub_1",
|
|
1309
|
-
subscriptionLineId: "item_3",
|
|
1310
|
-
productId: "product_3",
|
|
1311
|
-
parentProductId: null,
|
|
1312
|
-
parentLineId: null,
|
|
1313
|
-
isVirtual: false,
|
|
1314
|
-
isUpsell: false,
|
|
1315
|
-
isOneTime: false,
|
|
1316
|
-
customAttributes: []
|
|
1317
|
-
}
|
|
1318
|
-
],
|
|
1319
|
-
discounts: []
|
|
1320
|
-
}
|
|
1321
|
-
];
|
|
1322
|
-
let adjustments = [];
|
|
1323
|
-
return {
|
|
1324
|
-
async getUpcomingOrders(params) {
|
|
1325
|
-
await new Promise((r) => setTimeout(r, 200));
|
|
1326
|
-
const filtered = params.subscriptionId ? orders.filter((o2) => o2.subscriptionId === params.subscriptionId) : orders;
|
|
1327
|
-
return { _tag: "Success", data: filtered.slice(0, params.count) };
|
|
1328
|
-
},
|
|
1329
|
-
async getAdjustments() {
|
|
1330
|
-
await new Promise((r) => setTimeout(r, 200));
|
|
1331
|
-
return { _tag: "Success", data: [...adjustments] };
|
|
1332
|
-
},
|
|
1333
|
-
async skipOrder({ subscriptionId }) {
|
|
1334
|
-
await new Promise((r) => setTimeout(r, 300));
|
|
1335
|
-
const order = orders.find(
|
|
1336
|
-
(o2) => o2.subscriptionId === subscriptionId && o2.status === "scheduled"
|
|
1337
|
-
);
|
|
1338
|
-
if (!order) return { _tag: "Failure", error: "No scheduled order found" };
|
|
1339
|
-
orders = orders.map(
|
|
1340
|
-
(o2) => o2.id === order.id ? { ...o2, status: "skipped" } : o2
|
|
1341
|
-
);
|
|
1342
|
-
const adj = {
|
|
1343
|
-
id: `adj-${Date.now()}`,
|
|
1344
|
-
subscriptionId,
|
|
1345
|
-
type: "skip"
|
|
1346
|
-
};
|
|
1347
|
-
adjustments = [...adjustments, adj];
|
|
1348
|
-
return { _tag: "Success", data: void 0 };
|
|
1349
|
-
},
|
|
1350
|
-
async changeDateAdjustment({ subscriptionId, newDate }) {
|
|
1351
|
-
await new Promise((r) => setTimeout(r, 300));
|
|
1352
|
-
const order = orders.find(
|
|
1353
|
-
(o2) => o2.subscriptionId === subscriptionId && o2.status === "scheduled"
|
|
1354
|
-
);
|
|
1355
|
-
if (!order) return { _tag: "Failure", error: "No scheduled order found" };
|
|
1356
|
-
orders = orders.map(
|
|
1357
|
-
(o2) => o2.id === order.id ? { ...o2, deliveryDate: newDate } : o2
|
|
1358
|
-
);
|
|
1359
|
-
const adj = {
|
|
1360
|
-
id: `adj-${Date.now()}`,
|
|
1361
|
-
subscriptionId,
|
|
1362
|
-
type: "date_change",
|
|
1363
|
-
newDate
|
|
1364
|
-
};
|
|
1365
|
-
adjustments = [...adjustments, adj];
|
|
1366
|
-
return { _tag: "Success", data: void 0 };
|
|
1367
|
-
},
|
|
1368
|
-
async removeAdjustment({ adjustmentId }) {
|
|
1369
|
-
await new Promise((r) => setTimeout(r, 200));
|
|
1370
|
-
adjustments = adjustments.filter((a3) => a3.id !== adjustmentId);
|
|
1371
|
-
return { _tag: "Success", data: void 0 };
|
|
1372
|
-
}
|
|
1373
|
-
};
|
|
1374
|
-
}
|
|
1375
|
-
function createApiSchedulesAdapter(fetcher) {
|
|
1376
|
-
return {
|
|
1377
|
-
async getUpcomingOrders({ count, subscriptionId }) {
|
|
1378
|
-
try {
|
|
1379
|
-
const params = new URLSearchParams({ count: String(count) });
|
|
1380
|
-
if (subscriptionId) params.set("query", `id:"${subscriptionId}"`);
|
|
1381
|
-
const res = await fetcher(`/customer/v1/schedules/?${params.toString()}`);
|
|
1382
|
-
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1383
|
-
const data = await res.json();
|
|
1384
|
-
return { _tag: "Success", data: data.orders };
|
|
1385
|
-
} catch (e2) {
|
|
1386
|
-
return { _tag: "Failure", error: e2 };
|
|
1387
|
-
}
|
|
1388
|
-
},
|
|
1389
|
-
async getAdjustments() {
|
|
1390
|
-
try {
|
|
1391
|
-
const res = await fetcher(`/customer/v1/schedules/adjustments`);
|
|
1392
|
-
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1393
|
-
const data = await res.json();
|
|
1394
|
-
const adjustments = data.data.map((a3) => ({
|
|
1395
|
-
id: a3.id,
|
|
1396
|
-
subscriptionId: a3.subscriptionId ?? "",
|
|
1397
|
-
type: a3.type === "CHANGE_DATE" ? "date_change" : "skip",
|
|
1398
|
-
newDate: a3.newDate
|
|
1399
|
-
}));
|
|
1400
|
-
return { _tag: "Success", data: adjustments };
|
|
1401
|
-
} catch (e2) {
|
|
1402
|
-
return { _tag: "Failure", error: e2 };
|
|
1403
|
-
}
|
|
1404
|
-
},
|
|
1405
|
-
async skipOrder({ cycle, date }) {
|
|
1406
|
-
if (cycle == null && date == null) {
|
|
1407
|
-
return { _tag: "Failure", error: "Either cycle or date is required for skipOrder" };
|
|
1408
|
-
}
|
|
1409
|
-
try {
|
|
1410
|
-
const matcher = cycle != null ? { type: "CYCLE", input: { cycle } } : { type: "DATE", input: { date: new Date(date).toISOString() } };
|
|
1411
|
-
const res = await fetcher(`/customer/v1/schedules/adjustments`, {
|
|
1412
|
-
method: "POST",
|
|
1413
|
-
headers: { "Content-Type": "application/json" },
|
|
1414
|
-
body: JSON.stringify({
|
|
1415
|
-
matcher,
|
|
1416
|
-
action: { type: "SKIP_ORDER", input: { reason: "" } }
|
|
1417
|
-
})
|
|
1418
|
-
});
|
|
1419
|
-
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1420
|
-
return { _tag: "Success", data: void 0 };
|
|
1421
|
-
} catch (e2) {
|
|
1422
|
-
return { _tag: "Failure", error: e2 };
|
|
1423
|
-
}
|
|
1424
|
-
},
|
|
1425
|
-
async changeDateAdjustment({ newDate, currentDate }) {
|
|
1426
|
-
if (currentDate == null) {
|
|
1427
|
-
return {
|
|
1428
|
-
_tag: "Failure",
|
|
1429
|
-
error: "currentDate is required for changeDateAdjustment"
|
|
1430
|
-
};
|
|
1431
|
-
}
|
|
1432
|
-
try {
|
|
1433
|
-
const matcher = { type: "DATE", input: { date: new Date(currentDate).toISOString() } };
|
|
1434
|
-
const res = await fetcher(`/customer/v1/schedules/adjustments`, {
|
|
1435
|
-
method: "POST",
|
|
1436
|
-
headers: { "Content-Type": "application/json" },
|
|
1437
|
-
body: JSON.stringify({
|
|
1438
|
-
matcher,
|
|
1439
|
-
action: {
|
|
1440
|
-
type: "CHANGE_DATE",
|
|
1441
|
-
input: { newDate: new Date(newDate).toISOString() }
|
|
1442
|
-
}
|
|
1443
|
-
})
|
|
1444
|
-
});
|
|
1445
|
-
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1446
|
-
return { _tag: "Success", data: void 0 };
|
|
1447
|
-
} catch (e2) {
|
|
1448
|
-
return { _tag: "Failure", error: e2 };
|
|
1449
|
-
}
|
|
1450
|
-
},
|
|
1451
|
-
async removeAdjustment({ adjustmentId }) {
|
|
1452
|
-
try {
|
|
1453
|
-
const res = await fetcher(
|
|
1454
|
-
`/customer/v1/schedules/adjustments/${adjustmentId}`,
|
|
1455
|
-
{ method: "DELETE" }
|
|
1456
|
-
);
|
|
1457
|
-
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
1458
|
-
return { _tag: "Success", data: void 0 };
|
|
1459
|
-
} catch (e2) {
|
|
1460
|
-
return { _tag: "Failure", error: e2 };
|
|
1461
|
-
}
|
|
1462
|
-
}
|
|
1463
|
-
};
|
|
1464
|
-
}
|
|
1465
|
-
const SchedulesServiceContext = createContext("services/schedules");
|
|
1466
|
-
function createSchedulesService(adapter, opts) {
|
|
1467
|
-
const current = d(null);
|
|
1468
|
-
const currentLoading = d(false);
|
|
1469
|
-
const hasPendingBilling = (opts == null ? void 0 : opts.hasPendingBilling) ?? d(false);
|
|
1470
|
-
let reloadToken = 0;
|
|
1471
|
-
async function getUpcomingOrder() {
|
|
1472
|
-
const result = await adapter.getUpcomingOrders({ count: 1 });
|
|
1473
|
-
if (result._tag === "Failure") return result;
|
|
1474
|
-
return { _tag: "Success", data: result.data[0] ?? null };
|
|
1475
|
-
}
|
|
1476
|
-
async function getUpcomingOrders(count) {
|
|
1477
|
-
return adapter.getUpcomingOrders({ count });
|
|
1478
|
-
}
|
|
1479
|
-
async function reloadCurrent(subscriptionId) {
|
|
1480
|
-
var _a;
|
|
1481
|
-
const token = ++reloadToken;
|
|
1482
|
-
currentLoading.value = true;
|
|
1483
|
-
const count = Math.max(((_a = current.value) == null ? void 0 : _a.length) ?? 0, 3);
|
|
1484
|
-
const result = await adapter.getUpcomingOrders({
|
|
1485
|
-
count,
|
|
1486
|
-
subscriptionId
|
|
1487
|
-
});
|
|
1488
|
-
if (token !== reloadToken) return;
|
|
1489
|
-
currentLoading.value = false;
|
|
1490
|
-
if (result._tag === "Success") {
|
|
1491
|
-
current.value = result.data;
|
|
1492
|
-
}
|
|
1493
|
-
}
|
|
1494
|
-
return {
|
|
1495
|
-
current,
|
|
1496
|
-
currentLoading,
|
|
1497
|
-
hasPendingBilling,
|
|
1498
|
-
getUpcomingOrder,
|
|
1499
|
-
getUpcomingOrders,
|
|
1500
|
-
async getAdjustments() {
|
|
1501
|
-
return adapter.getAdjustments();
|
|
1502
|
-
},
|
|
1503
|
-
async skipOrder(params) {
|
|
1504
|
-
const result = await adapter.skipOrder(params);
|
|
1505
|
-
if (result._tag === "Success") {
|
|
1506
|
-
await reloadCurrent(params.subscriptionId);
|
|
1507
|
-
}
|
|
1508
|
-
return result;
|
|
1509
|
-
},
|
|
1510
|
-
async changeDateAdjustment(params) {
|
|
1511
|
-
const result = await adapter.changeDateAdjustment(params);
|
|
1512
|
-
if (result._tag === "Success") {
|
|
1513
|
-
await reloadCurrent(params.subscriptionId);
|
|
1514
|
-
}
|
|
1515
|
-
return result;
|
|
1516
|
-
},
|
|
1517
|
-
async removeAdjustment(params) {
|
|
1518
|
-
return adapter.removeAdjustment(params);
|
|
1519
|
-
},
|
|
1520
|
-
reloadCurrent
|
|
1521
|
-
};
|
|
1522
|
-
}
|
|
1523
|
-
const SettingsServiceContext = createContext("services/settings");
|
|
1524
|
-
function createSettingsService(initial = {}) {
|
|
1525
|
-
const current = d(initial);
|
|
1526
|
-
return {
|
|
1527
|
-
current,
|
|
1528
|
-
get(key) {
|
|
1529
|
-
return current.value[key];
|
|
1530
|
-
}
|
|
1531
|
-
};
|
|
1532
|
-
}
|
|
1533
|
-
function createShopService(options) {
|
|
1534
|
-
const contactEmail = d(options.getContactEmail());
|
|
1535
|
-
const service = { contactEmail };
|
|
1536
|
-
return service;
|
|
1537
|
-
}
|
|
1538
|
-
const ShopServiceContext = createContext("services/shop");
|
|
1539
|
-
function createMockSubscriptionAdapter(options = {}) {
|
|
1540
|
-
const richBase = (id, itemId, renewalId) => ({
|
|
1541
|
-
id,
|
|
1542
|
-
customerId: "customer_1",
|
|
1543
|
-
items: [
|
|
1544
|
-
{
|
|
1545
|
-
id: itemId,
|
|
1546
|
-
productId: "product_1",
|
|
1547
|
-
variantId: "variant_1",
|
|
1548
|
-
quantity: 2,
|
|
1549
|
-
price: { amount: "29.99", currencyCode: "USD" },
|
|
1550
|
-
nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-15"),
|
|
1551
|
-
isUpsell: false,
|
|
1552
|
-
isVirtual: false,
|
|
1553
|
-
nextBillingDate: "2025-11-15T00:00:00Z",
|
|
1554
|
-
currentPrice: { amount: "29.99", currencyCode: "USD" },
|
|
1555
|
-
lineDiscountedPrice: { amount: "26.99", currencyCode: "USD" },
|
|
1556
|
-
variantAvailableForSale: true
|
|
1557
|
-
}
|
|
1558
|
-
],
|
|
1559
|
-
discounts: [
|
|
1560
|
-
{
|
|
1561
|
-
id: "discount_1",
|
|
1562
|
-
type: "percentage",
|
|
1563
|
-
value: 10,
|
|
1564
|
-
code: "SAVE10",
|
|
1565
|
-
description: "10% off subscription"
|
|
1566
|
-
}
|
|
1567
|
-
],
|
|
1568
|
-
status: "active",
|
|
1569
|
-
nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-15"),
|
|
1570
|
-
frequency: "1 MONTH",
|
|
1571
|
-
createdAt: "2024-10-01T00:00:00Z",
|
|
1572
|
-
updatedAt: "2024-10-14T00:00:00Z",
|
|
1573
|
-
billingPolicy: { intervalCount: 1, interval: "MONTH", minCycles: 6 },
|
|
1574
|
-
deliveryPolicy: {
|
|
1575
|
-
intervalCount: 1,
|
|
1576
|
-
interval: "MONTH",
|
|
1577
|
-
anchors: [{ day: 15, type: "MONTHDAY" }]
|
|
1578
|
-
},
|
|
1579
|
-
nextBillingDate: "2025-11-15T00:00:00Z",
|
|
1580
|
-
nextBillingAttempt: { date: "2025-11-15T00:00:00Z", attempt: 1, ready: true },
|
|
1581
|
-
lastBillingAttempt: { date: "2025-10-15T00:00:00Z", ready: true },
|
|
1582
|
-
nextFulfillment: "2025-11-17T00:00:00Z",
|
|
1583
|
-
estimatedOrderDate: "2025-11-13T00:00:00Z",
|
|
1584
|
-
customerPaymentMethod: {
|
|
1585
|
-
id: "pm_1",
|
|
1586
|
-
instrument: { type: "credit_card", brand: "visa", maskedNumber: "************4242" },
|
|
1587
|
-
active: true,
|
|
1588
|
-
isRecurring: true
|
|
1589
|
-
},
|
|
1590
|
-
requiredBillingCyclesLeft: 3,
|
|
1591
|
-
renewal: { id: renewalId, isInActiveProcessState: false },
|
|
1592
|
-
lastPaymentStatus: "succeeded",
|
|
1593
|
-
pausePeriod: null,
|
|
1594
|
-
isEditable: true
|
|
1595
|
-
});
|
|
1596
|
-
const mockSubscriptions = [
|
|
1597
|
-
{
|
|
1598
|
-
id: "sub_1",
|
|
836
|
+
id: "sub_1",
|
|
1599
837
|
customerId: "customer_1",
|
|
1600
838
|
items: [
|
|
1601
839
|
{
|
|
@@ -1605,14 +843,6 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
1605
843
|
quantity: 2,
|
|
1606
844
|
price: { amount: "29.99", currencyCode: "USD" },
|
|
1607
845
|
nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-15"),
|
|
1608
|
-
isUpsell: false,
|
|
1609
|
-
isVirtual: false,
|
|
1610
|
-
nextBillingDate: "2025-11-15T00:00:00Z",
|
|
1611
|
-
currentPrice: { amount: "29.99", currencyCode: "USD" },
|
|
1612
|
-
lineDiscountedPrice: { amount: "26.99", currencyCode: "USD" },
|
|
1613
|
-
variantAvailableForSale: true,
|
|
1614
|
-
billingPolicy: { intervalCount: 1, interval: "MONTH" },
|
|
1615
|
-
deliveryPolicy: { intervalCount: 1, interval: "MONTH" },
|
|
1616
846
|
customAttributes: [
|
|
1617
847
|
{ key: "roast_level", value: "medium" },
|
|
1618
848
|
{ key: "grind_type", value: "whole_bean" }
|
|
@@ -1633,28 +863,6 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
1633
863
|
frequency: "1 MONTH",
|
|
1634
864
|
createdAt: "2024-10-01T00:00:00Z",
|
|
1635
865
|
updatedAt: "2024-10-14T00:00:00Z",
|
|
1636
|
-
billingPolicy: { intervalCount: 1, interval: "MONTH", minCycles: 6 },
|
|
1637
|
-
deliveryPolicy: {
|
|
1638
|
-
intervalCount: 1,
|
|
1639
|
-
interval: "MONTH",
|
|
1640
|
-
anchors: [{ day: 15, type: "MONTHDAY" }]
|
|
1641
|
-
},
|
|
1642
|
-
nextBillingDate: "2025-11-15T00:00:00Z",
|
|
1643
|
-
nextBillingAttempt: { date: "2025-11-15T00:00:00Z", attempt: 1, ready: true },
|
|
1644
|
-
lastBillingAttempt: { date: "2025-10-15T00:00:00Z", ready: true },
|
|
1645
|
-
nextFulfillment: "2025-11-17T00:00:00Z",
|
|
1646
|
-
estimatedOrderDate: "2025-11-13T00:00:00Z",
|
|
1647
|
-
customerPaymentMethod: {
|
|
1648
|
-
id: "pm_1",
|
|
1649
|
-
instrument: { type: "credit_card", brand: "visa", maskedNumber: "************4242" },
|
|
1650
|
-
active: true,
|
|
1651
|
-
isRecurring: true
|
|
1652
|
-
},
|
|
1653
|
-
requiredBillingCyclesLeft: 3,
|
|
1654
|
-
renewal: { id: "ren_1", isInActiveProcessState: false },
|
|
1655
|
-
lastPaymentStatus: "succeeded",
|
|
1656
|
-
pausePeriod: null,
|
|
1657
|
-
isEditable: true,
|
|
1658
866
|
customAttributes: [
|
|
1659
867
|
{ key: "subscription_tier", value: "premium" },
|
|
1660
868
|
{ key: "gift_message", value: "Enjoy your coffee!" }
|
|
@@ -1671,8 +879,6 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
1671
879
|
quantity: 1,
|
|
1672
880
|
price: { amount: "49.99", currencyCode: "USD" },
|
|
1673
881
|
nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-20"),
|
|
1674
|
-
billingPolicy: { intervalCount: 2, interval: "WEEK" },
|
|
1675
|
-
deliveryPolicy: { intervalCount: 2, interval: "WEEK" },
|
|
1676
882
|
customAttributes: []
|
|
1677
883
|
}
|
|
1678
884
|
],
|
|
@@ -1683,48 +889,6 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
1683
889
|
createdAt: "2024-09-15T00:00:00Z",
|
|
1684
890
|
updatedAt: "2024-10-10T00:00:00Z",
|
|
1685
891
|
customAttributes: []
|
|
1686
|
-
},
|
|
1687
|
-
// Per-status fixtures: groundwork for the deferred editor preview picker
|
|
1688
|
-
// (docs/superpowers/specs/2026-06-17-renewalsummary-parity-and-mock-groundwork-design.md §9).
|
|
1689
|
-
// Not consumed yet — the editor still seeds the active sub_1 via getDefaultSubscriptionId().
|
|
1690
|
-
{
|
|
1691
|
-
...richBase("sub_paused", "item_paused", "ren_paused"),
|
|
1692
|
-
status: "paused",
|
|
1693
|
-
pausePeriod: { start: "2025-11-01T00:00:00Z", end: "2026-01-01T00:00:00Z" }
|
|
1694
|
-
},
|
|
1695
|
-
{
|
|
1696
|
-
...richBase("sub_canceled", "item_canceled", "ren_canceled"),
|
|
1697
|
-
status: "canceled",
|
|
1698
|
-
pausePeriod: null
|
|
1699
|
-
},
|
|
1700
|
-
{
|
|
1701
|
-
...richBase("sub_failed", "item_failed", "ren_failed"),
|
|
1702
|
-
status: "failed",
|
|
1703
|
-
lastPaymentStatus: "failed",
|
|
1704
|
-
pausePeriod: null
|
|
1705
|
-
},
|
|
1706
|
-
{
|
|
1707
|
-
...richBase("sub_expired", "item_expired", "ren_expired"),
|
|
1708
|
-
status: "expired",
|
|
1709
|
-
pausePeriod: null
|
|
1710
|
-
}
|
|
1711
|
-
];
|
|
1712
|
-
const mockUpsellProducts = [
|
|
1713
|
-
{
|
|
1714
|
-
id: "upsell-1",
|
|
1715
|
-
variantId: "upsell-v-1",
|
|
1716
|
-
title: "Retinol Night Cream",
|
|
1717
|
-
imageUrl: "https://images.unsplash.com/photo-1617897903246-719242758050?w=200",
|
|
1718
|
-
price: { amount: "38.00", currencyCode: "USD" },
|
|
1719
|
-
description: "Anti-aging night treatment"
|
|
1720
|
-
},
|
|
1721
|
-
{
|
|
1722
|
-
id: "upsell-2",
|
|
1723
|
-
variantId: "upsell-v-2",
|
|
1724
|
-
title: "SPF 50 Day Shield",
|
|
1725
|
-
imageUrl: "https://images.unsplash.com/photo-1556228578-8c89e6adf883?w=200",
|
|
1726
|
-
price: { amount: "24.00", currencyCode: "USD" },
|
|
1727
|
-
description: "Lightweight daily sun protection"
|
|
1728
892
|
}
|
|
1729
893
|
];
|
|
1730
894
|
return {
|
|
@@ -1735,7 +899,7 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
1735
899
|
},
|
|
1736
900
|
async search(options2 = {}) {
|
|
1737
901
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1738
|
-
const pageSize =
|
|
902
|
+
const pageSize = 1;
|
|
1739
903
|
const allItems = mockSubscriptions;
|
|
1740
904
|
let startIndex = 0;
|
|
1741
905
|
let endIndex = pageSize;
|
|
@@ -1751,22 +915,20 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
1751
915
|
}
|
|
1752
916
|
}
|
|
1753
917
|
const pageItems = allItems.slice(startIndex, endIndex);
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
items:
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
startCursor: pageItems.length > 0 ? startIndex.toString() : null,
|
|
1766
|
-
endCursor: pageItems.length > 0 ? (endIndex - 1).toString() : null
|
|
1767
|
-
}
|
|
918
|
+
const paginatedList = {
|
|
919
|
+
items: pageItems.map((sub) => ({
|
|
920
|
+
...sub,
|
|
921
|
+
items: sub.items.map((item) => ({ ...item })),
|
|
922
|
+
discounts: sub.discounts.map((discount) => ({ ...discount }))
|
|
923
|
+
})),
|
|
924
|
+
pageInfo: {
|
|
925
|
+
hasNextPage: endIndex < allItems.length,
|
|
926
|
+
hasPrevPage: startIndex > 0,
|
|
927
|
+
startCursor: pageItems.length > 0 ? startIndex.toString() : null,
|
|
928
|
+
endCursor: pageItems.length > 0 ? (endIndex - 1).toString() : null
|
|
1768
929
|
}
|
|
1769
930
|
};
|
|
931
|
+
return { _tag: "Success", data: paginatedList };
|
|
1770
932
|
},
|
|
1771
933
|
async addItem() {
|
|
1772
934
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
@@ -1776,17 +938,6 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
1776
938
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1777
939
|
return { _tag: "Success", data: void 0 };
|
|
1778
940
|
},
|
|
1779
|
-
async updateItem() {
|
|
1780
|
-
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1781
|
-
return { _tag: "Success", data: void 0 };
|
|
1782
|
-
},
|
|
1783
|
-
async skipLineItem() {
|
|
1784
|
-
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1785
|
-
return { _tag: "Success", data: void 0 };
|
|
1786
|
-
},
|
|
1787
|
-
async updateBoxItems() {
|
|
1788
|
-
return { _tag: "Success", data: void 0 };
|
|
1789
|
-
},
|
|
1790
941
|
async addDiscount() {
|
|
1791
942
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1792
943
|
const behavior = options.addDiscountBehavior ?? { kind: "success" };
|
|
@@ -1815,11 +966,24 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
1815
966
|
(sub) => sub.id === params.subscriptionId
|
|
1816
967
|
);
|
|
1817
968
|
if (!subscription) {
|
|
1818
|
-
return {
|
|
969
|
+
return {
|
|
970
|
+
_tag: "Failure",
|
|
971
|
+
error: "Subscription not found"
|
|
972
|
+
};
|
|
1819
973
|
}
|
|
1820
974
|
subscription.nextDeliveryDate = new Date(params.newDate);
|
|
1821
975
|
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1822
|
-
|
|
976
|
+
subscription.items.forEach((item) => {
|
|
977
|
+
item.nextDeliveryDate = new Date(params.newDate);
|
|
978
|
+
});
|
|
979
|
+
return {
|
|
980
|
+
_tag: "Success",
|
|
981
|
+
data: {
|
|
982
|
+
...subscription,
|
|
983
|
+
items: subscription.items.map((item) => ({ ...item })),
|
|
984
|
+
discounts: subscription.discounts.map((discount) => ({ ...discount }))
|
|
985
|
+
}
|
|
986
|
+
};
|
|
1823
987
|
},
|
|
1824
988
|
async resume(params) {
|
|
1825
989
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
@@ -1827,11 +991,21 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
1827
991
|
(sub) => sub.id === params.subscriptionId
|
|
1828
992
|
);
|
|
1829
993
|
if (!subscription) {
|
|
1830
|
-
return {
|
|
994
|
+
return {
|
|
995
|
+
_tag: "Failure",
|
|
996
|
+
error: "Subscription not found"
|
|
997
|
+
};
|
|
1831
998
|
}
|
|
1832
999
|
subscription.status = "active";
|
|
1833
1000
|
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1834
|
-
return {
|
|
1001
|
+
return {
|
|
1002
|
+
_tag: "Success",
|
|
1003
|
+
data: {
|
|
1004
|
+
...subscription,
|
|
1005
|
+
items: subscription.items.map((item) => ({ ...item })),
|
|
1006
|
+
discounts: subscription.discounts.map((discount) => ({ ...discount }))
|
|
1007
|
+
}
|
|
1008
|
+
};
|
|
1835
1009
|
},
|
|
1836
1010
|
async pause(params) {
|
|
1837
1011
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
@@ -1839,11 +1013,21 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
1839
1013
|
(sub) => sub.id === params.subscriptionId
|
|
1840
1014
|
);
|
|
1841
1015
|
if (!subscription) {
|
|
1842
|
-
return {
|
|
1016
|
+
return {
|
|
1017
|
+
_tag: "Failure",
|
|
1018
|
+
error: "Subscription not found"
|
|
1019
|
+
};
|
|
1843
1020
|
}
|
|
1844
1021
|
subscription.status = "paused";
|
|
1845
1022
|
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1846
|
-
return {
|
|
1023
|
+
return {
|
|
1024
|
+
_tag: "Success",
|
|
1025
|
+
data: {
|
|
1026
|
+
...subscription,
|
|
1027
|
+
items: subscription.items.map((item) => ({ ...item })),
|
|
1028
|
+
discounts: subscription.discounts.map((discount) => ({ ...discount }))
|
|
1029
|
+
}
|
|
1030
|
+
};
|
|
1847
1031
|
},
|
|
1848
1032
|
async reactivate(params) {
|
|
1849
1033
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
@@ -1851,11 +1035,21 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
1851
1035
|
(sub) => sub.id === params.subscriptionId
|
|
1852
1036
|
);
|
|
1853
1037
|
if (!subscription) {
|
|
1854
|
-
return {
|
|
1038
|
+
return {
|
|
1039
|
+
_tag: "Failure",
|
|
1040
|
+
error: "Subscription not found"
|
|
1041
|
+
};
|
|
1855
1042
|
}
|
|
1856
1043
|
subscription.status = "active";
|
|
1857
1044
|
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1858
|
-
return {
|
|
1045
|
+
return {
|
|
1046
|
+
_tag: "Success",
|
|
1047
|
+
data: {
|
|
1048
|
+
...subscription,
|
|
1049
|
+
items: subscription.items.map((item) => ({ ...item })),
|
|
1050
|
+
discounts: subscription.discounts.map((discount) => ({ ...discount }))
|
|
1051
|
+
}
|
|
1052
|
+
};
|
|
1859
1053
|
},
|
|
1860
1054
|
async cancel(params) {
|
|
1861
1055
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
@@ -1863,11 +1057,21 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
1863
1057
|
(sub) => sub.id === params.subscriptionId
|
|
1864
1058
|
);
|
|
1865
1059
|
if (!subscription) {
|
|
1866
|
-
return {
|
|
1060
|
+
return {
|
|
1061
|
+
_tag: "Failure",
|
|
1062
|
+
error: "Subscription not found"
|
|
1063
|
+
};
|
|
1867
1064
|
}
|
|
1868
1065
|
subscription.status = "canceled";
|
|
1869
1066
|
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1870
|
-
return {
|
|
1067
|
+
return {
|
|
1068
|
+
_tag: "Success",
|
|
1069
|
+
data: {
|
|
1070
|
+
...subscription,
|
|
1071
|
+
items: subscription.items.map((item) => ({ ...item })),
|
|
1072
|
+
discounts: subscription.discounts.map((discount) => ({ ...discount }))
|
|
1073
|
+
}
|
|
1074
|
+
};
|
|
1871
1075
|
},
|
|
1872
1076
|
async getAvailablePlans() {
|
|
1873
1077
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
@@ -1895,514 +1099,13 @@ function createMockSubscriptionAdapter(options = {}) {
|
|
|
1895
1099
|
]
|
|
1896
1100
|
};
|
|
1897
1101
|
},
|
|
1898
|
-
async getLastOrder(params) {
|
|
1899
|
-
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
1900
|
-
const subscription = mockSubscriptions.find((sub) => sub.id === params.subscriptionId);
|
|
1901
|
-
if (!subscription) return { _tag: "Success", data: null };
|
|
1902
|
-
return {
|
|
1903
|
-
_tag: "Success",
|
|
1904
|
-
data: {
|
|
1905
|
-
id: `mock_last_order_${params.subscriptionId}`,
|
|
1906
|
-
createdAt: "2026-04-22T12:00:00Z"
|
|
1907
|
-
}
|
|
1908
|
-
};
|
|
1909
|
-
},
|
|
1910
|
-
async renewNow(_params) {
|
|
1911
|
-
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1912
|
-
return { _tag: "Success", data: { result: "success" } };
|
|
1913
|
-
},
|
|
1914
|
-
async changeFrequency(params) {
|
|
1915
|
-
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1916
|
-
const subscription = mockSubscriptions.find((sub) => sub.id === params.subscriptionId);
|
|
1917
|
-
if (!subscription) {
|
|
1918
|
-
return { _tag: "Failure", error: "Subscription not found" };
|
|
1919
|
-
}
|
|
1920
|
-
if (typeof params.plan === "string") {
|
|
1921
|
-
subscription.frequency = `1 ${params.plan}`;
|
|
1922
|
-
} else {
|
|
1923
|
-
subscription.frequency = `${params.plan.intervalCount} ${params.plan.interval}`;
|
|
1924
|
-
if (subscription.billingPolicy) {
|
|
1925
|
-
subscription.billingPolicy = { ...subscription.billingPolicy, ...params.plan };
|
|
1926
|
-
}
|
|
1927
|
-
if (subscription.deliveryPolicy) {
|
|
1928
|
-
subscription.deliveryPolicy = { ...subscription.deliveryPolicy, ...params.plan };
|
|
1929
|
-
}
|
|
1930
|
-
}
|
|
1931
|
-
subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1932
|
-
return {
|
|
1933
|
-
_tag: "Success",
|
|
1934
|
-
data: {
|
|
1935
|
-
...subscription,
|
|
1936
|
-
items: subscription.items.map((item) => ({ ...item })),
|
|
1937
|
-
discounts: subscription.discounts.map((d3) => ({ ...d3 }))
|
|
1938
|
-
}
|
|
1939
|
-
};
|
|
1940
|
-
},
|
|
1941
1102
|
async getDefaultSubscriptionId() {
|
|
1942
1103
|
var _a;
|
|
1943
1104
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1944
1105
|
return { _tag: "Success", data: ((_a = mockSubscriptions[0]) == null ? void 0 : _a.id) ?? null };
|
|
1945
|
-
},
|
|
1946
|
-
async getUpsellProducts() {
|
|
1947
|
-
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1948
|
-
return { _tag: "Success", data: [...mockUpsellProducts] };
|
|
1949
|
-
},
|
|
1950
|
-
async addUpsellProduct() {
|
|
1951
|
-
await new Promise((resolve) => setTimeout(resolve, 400));
|
|
1952
|
-
return { _tag: "Success", data: void 0 };
|
|
1953
1106
|
}
|
|
1954
1107
|
};
|
|
1955
1108
|
}
|
|
1956
|
-
function normalizeInstrumentType(type) {
|
|
1957
|
-
return type.replace(/-/g, "_");
|
|
1958
|
-
}
|
|
1959
|
-
function mapPaymentMethod(rawPm) {
|
|
1960
|
-
if (!rawPm) return null;
|
|
1961
|
-
if (typeof rawPm === "string") {
|
|
1962
|
-
return { id: rawPm };
|
|
1963
|
-
}
|
|
1964
|
-
return {
|
|
1965
|
-
id: rawPm.id ?? "",
|
|
1966
|
-
...rawPm.instrument != null ? {
|
|
1967
|
-
instrument: {
|
|
1968
|
-
...rawPm.instrument,
|
|
1969
|
-
type: normalizeInstrumentType(rawPm.instrument.type)
|
|
1970
|
-
}
|
|
1971
|
-
} : {},
|
|
1972
|
-
...rawPm.active != null ? { active: rawPm.active } : {},
|
|
1973
|
-
...rawPm.isRecurring != null ? { isRecurring: rawPm.isRecurring } : {}
|
|
1974
|
-
};
|
|
1975
|
-
}
|
|
1976
|
-
function mapSubscription(raw) {
|
|
1977
|
-
var _a, _b;
|
|
1978
|
-
const status = raw.status === "merged" ? "canceled" : raw.status;
|
|
1979
|
-
const deliveryPolicy = raw.deliveryPolicy ?? ((_b = (_a = raw.items) == null ? void 0 : _a[0]) == null ? void 0 : _b.deliveryPolicy);
|
|
1980
|
-
const frequency = deliveryPolicy ? `${deliveryPolicy.intervalCount} ${deliveryPolicy.interval}` : "";
|
|
1981
|
-
return {
|
|
1982
|
-
id: raw.id,
|
|
1983
|
-
customerId: "",
|
|
1984
|
-
serial: raw.serial,
|
|
1985
|
-
items: (raw.items ?? []).map(
|
|
1986
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1987
|
-
(item) => ({
|
|
1988
|
-
id: item.id,
|
|
1989
|
-
productId: item.productId ?? item.product ?? "",
|
|
1990
|
-
variantId: item.variantId ?? item.variant ?? "",
|
|
1991
|
-
quantity: item.quantity,
|
|
1992
|
-
price: item.price ?? {
|
|
1993
|
-
amount: String(item.totalPrice ?? 0),
|
|
1994
|
-
currencyCode: raw.currencyCode ?? ""
|
|
1995
|
-
},
|
|
1996
|
-
nextDeliveryDate: item.nextDeliveryDate ? new Date(item.nextDeliveryDate) : null,
|
|
1997
|
-
billingPolicy: item.billingPolicy ?? raw.billingPolicy ?? null,
|
|
1998
|
-
deliveryPolicy: item.deliveryPolicy ?? raw.deliveryPolicy ?? null,
|
|
1999
|
-
// Map human-readable titles from the v1 SubscriptionItemResource
|
|
2000
|
-
title: item.title,
|
|
2001
|
-
variantTitle: item.subtitle ?? void 0,
|
|
2002
|
-
// recurringCycleLimit === 1 means the item charges exactly once (one-time upsell)
|
|
2003
|
-
isOneTime: item.recurringCycleLimit === 1
|
|
2004
|
-
})
|
|
2005
|
-
),
|
|
2006
|
-
discounts: (raw.discounts ?? []).map(
|
|
2007
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2008
|
-
(d3) => {
|
|
2009
|
-
var _a2, _b2, _c;
|
|
2010
|
-
return {
|
|
2011
|
-
id: d3.id,
|
|
2012
|
-
type: ((_a2 = d3.value) == null ? void 0 : _a2.type) === "fixed-amount" || d3.type === "fixed_amount" ? "fixed_amount" : "percentage",
|
|
2013
|
-
value: ((_b2 = d3.value) == null ? void 0 : _b2.amount) ?? ((_c = d3.value) == null ? void 0 : _c.percentage) ?? d3.value ?? 0,
|
|
2014
|
-
code: d3.title ?? d3.code ?? "",
|
|
2015
|
-
description: d3.description ?? ""
|
|
2016
|
-
};
|
|
2017
|
-
}
|
|
2018
|
-
),
|
|
2019
|
-
status,
|
|
2020
|
-
nextDeliveryDate: raw.nextBillingDate ? new Date(raw.nextBillingDate) : raw.nextDeliveryDate ? new Date(raw.nextDeliveryDate) : null,
|
|
2021
|
-
// Map nextBillingDate as the raw ISO string for ProductSelect skip() and other consumers
|
|
2022
|
-
nextBillingDate: raw.nextBillingDate ?? null,
|
|
2023
|
-
// Map customerPaymentMethod — note: arrives as bare id (not expanded) since the
|
|
2024
|
-
// subscriptions endpoint does not support ?expand=paymentMethod; instrument digits
|
|
2025
|
-
// remain a data gap pending backend API extension.
|
|
2026
|
-
customerPaymentMethod: mapPaymentMethod(raw.paymentMethod),
|
|
2027
|
-
frequency,
|
|
2028
|
-
createdAt: raw.createdAt,
|
|
2029
|
-
updatedAt: raw.updatedAt,
|
|
2030
|
-
billingPolicy: raw.billingPolicy ?? null,
|
|
2031
|
-
deliveryPolicy: raw.deliveryPolicy ?? null
|
|
2032
|
-
};
|
|
2033
|
-
}
|
|
2034
|
-
function createApiSubscriptionAdapter(fetcher) {
|
|
2035
|
-
const adapter = {
|
|
2036
|
-
async search(options) {
|
|
2037
|
-
var _a;
|
|
2038
|
-
try {
|
|
2039
|
-
const params = new URLSearchParams();
|
|
2040
|
-
if ((_a = options == null ? void 0 : options.filters) == null ? void 0 : _a.length) {
|
|
2041
|
-
const q2 = options.filters.filter((f2) => Object.keys(f2).length > 0).flatMap((f2) => Object.entries(f2).filter(([, v2]) => Boolean(v2))).map(([k, v2]) => `${k}:'${v2}'`).join(" AND ");
|
|
2042
|
-
if (q2) params.set("query", q2);
|
|
2043
|
-
}
|
|
2044
|
-
if (options == null ? void 0 : options.pagination) {
|
|
2045
|
-
if ("after" in options.pagination)
|
|
2046
|
-
params.set("after", options.pagination.after);
|
|
2047
|
-
else if ("before" in options.pagination)
|
|
2048
|
-
params.set("before", options.pagination.before);
|
|
2049
|
-
}
|
|
2050
|
-
const query = params.toString() ? `?${params.toString()}` : "";
|
|
2051
|
-
const res = await fetcher(`/customer/v1/subscriptions${query}`);
|
|
2052
|
-
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2053
|
-
const body = await res.json();
|
|
2054
|
-
return {
|
|
2055
|
-
_tag: "Success",
|
|
2056
|
-
data: {
|
|
2057
|
-
items: body.data.map(mapSubscription),
|
|
2058
|
-
pageInfo: {
|
|
2059
|
-
hasNextPage: body.hasNextPage,
|
|
2060
|
-
hasPrevPage: body.hasPrevPage,
|
|
2061
|
-
startCursor: body.startCursor,
|
|
2062
|
-
endCursor: body.endCursor
|
|
2063
|
-
}
|
|
2064
|
-
}
|
|
2065
|
-
};
|
|
2066
|
-
} catch (e2) {
|
|
2067
|
-
return { _tag: "Failure", error: e2 };
|
|
2068
|
-
}
|
|
2069
|
-
},
|
|
2070
|
-
async getById(id) {
|
|
2071
|
-
try {
|
|
2072
|
-
const res = await fetcher(
|
|
2073
|
-
`/customer/v1/subscriptions?query=id:"${id}"&limit=1`
|
|
2074
|
-
);
|
|
2075
|
-
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2076
|
-
const body = await res.json();
|
|
2077
|
-
const raw = body.data[0];
|
|
2078
|
-
return { _tag: "Success", data: raw ? mapSubscription(raw) : null };
|
|
2079
|
-
} catch (e2) {
|
|
2080
|
-
return { _tag: "Failure", error: e2 };
|
|
2081
|
-
}
|
|
2082
|
-
},
|
|
2083
|
-
async addItem({ subscriptionId, item }) {
|
|
2084
|
-
try {
|
|
2085
|
-
const res = await fetcher(
|
|
2086
|
-
`/customer/v1/subscriptions/${subscriptionId}/items`,
|
|
2087
|
-
{
|
|
2088
|
-
method: "POST",
|
|
2089
|
-
headers: { "Content-Type": "application/json" },
|
|
2090
|
-
body: JSON.stringify(item)
|
|
2091
|
-
}
|
|
2092
|
-
);
|
|
2093
|
-
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2094
|
-
return { _tag: "Success", data: void 0 };
|
|
2095
|
-
} catch (e2) {
|
|
2096
|
-
return { _tag: "Failure", error: e2 };
|
|
2097
|
-
}
|
|
2098
|
-
},
|
|
2099
|
-
async removeItem({ subscriptionId, itemId }) {
|
|
2100
|
-
try {
|
|
2101
|
-
const res = await fetcher(
|
|
2102
|
-
`/customer/v1/subscriptions/${subscriptionId}/items/${itemId}`,
|
|
2103
|
-
{ method: "DELETE" }
|
|
2104
|
-
);
|
|
2105
|
-
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2106
|
-
return { _tag: "Success", data: void 0 };
|
|
2107
|
-
} catch (e2) {
|
|
2108
|
-
return { _tag: "Failure", error: e2 };
|
|
2109
|
-
}
|
|
2110
|
-
},
|
|
2111
|
-
async updateItem({ subscriptionId, lineId, update }) {
|
|
2112
|
-
try {
|
|
2113
|
-
const body = {
|
|
2114
|
-
...update.variantId !== void 0 ? { productVariantId: update.variantId } : {},
|
|
2115
|
-
...update.quantity !== void 0 ? { quantity: update.quantity } : {},
|
|
2116
|
-
...update.billingPolicy !== void 0 ? { billingPolicy: update.billingPolicy } : {},
|
|
2117
|
-
...update.shippingMethodHandle !== void 0 ? { shippingMethodHandle: update.shippingMethodHandle } : {}
|
|
2118
|
-
};
|
|
2119
|
-
const res = await fetcher(
|
|
2120
|
-
`/customer/subscriptions/${subscriptionId}/lines/${lineId}`,
|
|
2121
|
-
{
|
|
2122
|
-
method: "PUT",
|
|
2123
|
-
headers: { "Content-Type": "application/json" },
|
|
2124
|
-
body: JSON.stringify(body)
|
|
2125
|
-
}
|
|
2126
|
-
);
|
|
2127
|
-
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2128
|
-
return { _tag: "Success", data: void 0 };
|
|
2129
|
-
} catch (e2) {
|
|
2130
|
-
return { _tag: "Failure", error: e2 };
|
|
2131
|
-
}
|
|
2132
|
-
},
|
|
2133
|
-
async skipLineItem({ subscriptionId, lineId, nextBillingDate }) {
|
|
2134
|
-
try {
|
|
2135
|
-
const res = await fetcher(
|
|
2136
|
-
`/customer/subscriptions/${subscriptionId}/lines/${lineId}`,
|
|
2137
|
-
{
|
|
2138
|
-
method: "PUT",
|
|
2139
|
-
headers: { "Content-Type": "application/json" },
|
|
2140
|
-
body: JSON.stringify({ nextBillingDate })
|
|
2141
|
-
}
|
|
2142
|
-
);
|
|
2143
|
-
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2144
|
-
return { _tag: "Success", data: void 0 };
|
|
2145
|
-
} catch (e2) {
|
|
2146
|
-
return { _tag: "Failure", error: e2 };
|
|
2147
|
-
}
|
|
2148
|
-
},
|
|
2149
|
-
async updateBoxItems({ subscriptionId, parentId, items, shippingMethodHandle }) {
|
|
2150
|
-
try {
|
|
2151
|
-
const res = await fetcher(
|
|
2152
|
-
`/customer/subscriptions/${subscriptionId}/update-box-products`,
|
|
2153
|
-
{
|
|
2154
|
-
method: "POST",
|
|
2155
|
-
headers: { "Content-Type": "application/json" },
|
|
2156
|
-
body: JSON.stringify({ parentId, items, shippingMethodHandle })
|
|
2157
|
-
}
|
|
2158
|
-
);
|
|
2159
|
-
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2160
|
-
return { _tag: "Success", data: void 0 };
|
|
2161
|
-
} catch (e2) {
|
|
2162
|
-
return { _tag: "Failure", error: e2 };
|
|
2163
|
-
}
|
|
2164
|
-
},
|
|
2165
|
-
async addDiscount({ subscriptionId, code }) {
|
|
2166
|
-
try {
|
|
2167
|
-
const res = await fetcher(
|
|
2168
|
-
`/customer/v1/subscriptions/${subscriptionId}/discounts`,
|
|
2169
|
-
{
|
|
2170
|
-
method: "POST",
|
|
2171
|
-
headers: { "Content-Type": "application/json" },
|
|
2172
|
-
body: JSON.stringify({ code })
|
|
2173
|
-
}
|
|
2174
|
-
);
|
|
2175
|
-
if (!res.ok) return { _tag: "Failure", error: { kind: "NETWORK", message: `HTTP ${res.status}` } };
|
|
2176
|
-
return { _tag: "Success", data: void 0 };
|
|
2177
|
-
} catch (e2) {
|
|
2178
|
-
return { _tag: "Failure", error: { kind: "UNKNOWN", message: e2 instanceof Error ? e2.message : void 0 } };
|
|
2179
|
-
}
|
|
2180
|
-
},
|
|
2181
|
-
async removeDiscount({ subscriptionId, discountId }) {
|
|
2182
|
-
try {
|
|
2183
|
-
const res = await fetcher(
|
|
2184
|
-
`/customer/v1/subscriptions/${subscriptionId}/discounts/${discountId}`,
|
|
2185
|
-
{ method: "DELETE" }
|
|
2186
|
-
);
|
|
2187
|
-
if (!res.ok) return { _tag: "Failure", error: { kind: "NETWORK", message: `HTTP ${res.status}` } };
|
|
2188
|
-
return { _tag: "Success", data: void 0 };
|
|
2189
|
-
} catch (e2) {
|
|
2190
|
-
return { _tag: "Failure", error: { kind: "UNKNOWN", message: e2 instanceof Error ? e2.message : void 0 } };
|
|
2191
|
-
}
|
|
2192
|
-
},
|
|
2193
|
-
// Uses PATCH /customer/v1/subscriptions/{id} — the dedicated /reschedule route does not exist.
|
|
2194
|
-
async reschedule({ subscriptionId, newDate }) {
|
|
2195
|
-
try {
|
|
2196
|
-
const res = await fetcher(
|
|
2197
|
-
`/customer/v1/subscriptions/${subscriptionId}`,
|
|
2198
|
-
{
|
|
2199
|
-
method: "PATCH",
|
|
2200
|
-
headers: { "Content-Type": "application/json" },
|
|
2201
|
-
body: JSON.stringify({ nextBillingDate: newDate })
|
|
2202
|
-
}
|
|
2203
|
-
);
|
|
2204
|
-
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2205
|
-
const data = await res.json();
|
|
2206
|
-
return { _tag: "Success", data: mapSubscription(data) };
|
|
2207
|
-
} catch (e2) {
|
|
2208
|
-
return { _tag: "Failure", error: e2 };
|
|
2209
|
-
}
|
|
2210
|
-
},
|
|
2211
|
-
async resume({ subscriptionId }) {
|
|
2212
|
-
try {
|
|
2213
|
-
const res = await fetcher(
|
|
2214
|
-
`/customer/v1/subscriptions/${subscriptionId}/resume`,
|
|
2215
|
-
{ method: "POST" }
|
|
2216
|
-
);
|
|
2217
|
-
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2218
|
-
const data = await res.json();
|
|
2219
|
-
return { _tag: "Success", data: mapSubscription(data) };
|
|
2220
|
-
} catch (e2) {
|
|
2221
|
-
return { _tag: "Failure", error: e2 };
|
|
2222
|
-
}
|
|
2223
|
-
},
|
|
2224
|
-
async pause({ subscriptionId }) {
|
|
2225
|
-
try {
|
|
2226
|
-
const res = await fetcher(
|
|
2227
|
-
`/customer/v1/subscriptions/${subscriptionId}/pause`,
|
|
2228
|
-
{ method: "POST" }
|
|
2229
|
-
);
|
|
2230
|
-
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2231
|
-
const data = await res.json();
|
|
2232
|
-
return { _tag: "Success", data: mapSubscription(data) };
|
|
2233
|
-
} catch (e2) {
|
|
2234
|
-
return { _tag: "Failure", error: e2 };
|
|
2235
|
-
}
|
|
2236
|
-
},
|
|
2237
|
-
async reactivate({ subscriptionId }) {
|
|
2238
|
-
try {
|
|
2239
|
-
const res = await fetcher(
|
|
2240
|
-
`/customer/v1/subscriptions/${subscriptionId}/reactivate`,
|
|
2241
|
-
{ method: "POST" }
|
|
2242
|
-
);
|
|
2243
|
-
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2244
|
-
const data = await res.json();
|
|
2245
|
-
return { _tag: "Success", data: mapSubscription(data) };
|
|
2246
|
-
} catch (e2) {
|
|
2247
|
-
return { _tag: "Failure", error: e2 };
|
|
2248
|
-
}
|
|
2249
|
-
},
|
|
2250
|
-
async cancel({ subscriptionId, reason }) {
|
|
2251
|
-
try {
|
|
2252
|
-
const res = await fetcher(
|
|
2253
|
-
`/customer/v1/subscriptions/${subscriptionId}/cancel`,
|
|
2254
|
-
{
|
|
2255
|
-
method: "POST",
|
|
2256
|
-
headers: { "Content-Type": "application/json" },
|
|
2257
|
-
body: JSON.stringify({ cancellationReason: reason })
|
|
2258
|
-
}
|
|
2259
|
-
);
|
|
2260
|
-
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2261
|
-
const data = await res.json();
|
|
2262
|
-
return { _tag: "Success", data: mapSubscription(data) };
|
|
2263
|
-
} catch (e2) {
|
|
2264
|
-
return { _tag: "Failure", error: e2 };
|
|
2265
|
-
}
|
|
2266
|
-
},
|
|
2267
|
-
// Legacy endpoint — no /customer/v1/subscriptions/{id}/plans exists yet.
|
|
2268
|
-
async getAvailablePlans({ subscriptionId }) {
|
|
2269
|
-
try {
|
|
2270
|
-
const res = await fetcher(
|
|
2271
|
-
`/customer/subscriptions/${subscriptionId}/plans`
|
|
2272
|
-
);
|
|
2273
|
-
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2274
|
-
const data = await res.json();
|
|
2275
|
-
return { _tag: "Success", data };
|
|
2276
|
-
} catch (e2) {
|
|
2277
|
-
return { _tag: "Failure", error: e2 };
|
|
2278
|
-
}
|
|
2279
|
-
},
|
|
2280
|
-
// Legacy endpoint — no /customer/v1 equivalent exists yet.
|
|
2281
|
-
async getDefaultSubscriptionId() {
|
|
2282
|
-
try {
|
|
2283
|
-
const res = await fetcher(
|
|
2284
|
-
`/customer/subscriptions/one-by-next-billing-date`
|
|
2285
|
-
);
|
|
2286
|
-
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2287
|
-
const data = await res.json();
|
|
2288
|
-
return { _tag: "Success", data: (data == null ? void 0 : data.id) ?? null };
|
|
2289
|
-
} catch (e2) {
|
|
2290
|
-
return { _tag: "Failure", error: e2 };
|
|
2291
|
-
}
|
|
2292
|
-
},
|
|
2293
|
-
// Legacy endpoint — no /customer/v1/subscriptions/{id}/upsells exists yet.
|
|
2294
|
-
async getUpsellProducts({ subscriptionId }) {
|
|
2295
|
-
try {
|
|
2296
|
-
const res = await fetcher(
|
|
2297
|
-
`/customer/subscriptions/${subscriptionId}/upsell-products`
|
|
2298
|
-
);
|
|
2299
|
-
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2300
|
-
const data = await res.json();
|
|
2301
|
-
const products = data.map((p2) => {
|
|
2302
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
2303
|
-
return {
|
|
2304
|
-
id: p2.id,
|
|
2305
|
-
variantId: ((_b = (_a = p2.variants) == null ? void 0 : _a[0]) == null ? void 0 : _b.id) ?? "",
|
|
2306
|
-
title: p2.title ?? "",
|
|
2307
|
-
imageUrl: ((_c = p2.featuredImage) == null ? void 0 : _c.url) ?? void 0,
|
|
2308
|
-
price: {
|
|
2309
|
-
amount: String(((_f = (_e = (_d = p2.variants) == null ? void 0 : _d[0]) == null ? void 0 : _e.currentPrice) == null ? void 0 : _f.amount) ?? 0),
|
|
2310
|
-
currencyCode: ((_i = (_h = (_g = p2.variants) == null ? void 0 : _g[0]) == null ? void 0 : _h.currentPrice) == null ? void 0 : _i.currencyCode) ?? ""
|
|
2311
|
-
},
|
|
2312
|
-
// recurring comes from the upsell rule on variants[0]; present in PreparedProductSchema
|
|
2313
|
-
recurring: (_k = (_j = p2.variants) == null ? void 0 : _j[0]) == null ? void 0 : _k.recurring
|
|
2314
|
-
};
|
|
2315
|
-
});
|
|
2316
|
-
return { _tag: "Success", data: products };
|
|
2317
|
-
} catch (e2) {
|
|
2318
|
-
return { _tag: "Failure", error: e2 };
|
|
2319
|
-
}
|
|
2320
|
-
},
|
|
2321
|
-
// Legacy endpoint — no /customer/v1/subscriptions/{id}/upsells POST exists yet.
|
|
2322
|
-
async addUpsellProduct({ subscriptionId, variantId, quantity, oneTime }) {
|
|
2323
|
-
try {
|
|
2324
|
-
const res = await fetcher(
|
|
2325
|
-
`/customer/subscriptions/${subscriptionId}/upsell`,
|
|
2326
|
-
{
|
|
2327
|
-
method: "POST",
|
|
2328
|
-
headers: { "Content-Type": "application/json" },
|
|
2329
|
-
body: JSON.stringify({
|
|
2330
|
-
productVariantId: variantId,
|
|
2331
|
-
quantity,
|
|
2332
|
-
...oneTime ? { renewalLimit: 1 } : {}
|
|
2333
|
-
})
|
|
2334
|
-
}
|
|
2335
|
-
);
|
|
2336
|
-
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2337
|
-
return { _tag: "Success", data: void 0 };
|
|
2338
|
-
} catch (e2) {
|
|
2339
|
-
return { _tag: "Failure", error: e2 };
|
|
2340
|
-
}
|
|
2341
|
-
},
|
|
2342
|
-
// Legacy endpoint — no /customer/v1 equivalent exists yet.
|
|
2343
|
-
async getLastOrder({ subscriptionId }) {
|
|
2344
|
-
try {
|
|
2345
|
-
const res = await fetcher(
|
|
2346
|
-
`/customer/subscriptions/${subscriptionId}/last-order`
|
|
2347
|
-
);
|
|
2348
|
-
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2349
|
-
const data = await res.json();
|
|
2350
|
-
if (!data) return { _tag: "Success", data: null };
|
|
2351
|
-
return {
|
|
2352
|
-
_tag: "Success",
|
|
2353
|
-
data: { id: data.id, createdAt: data.createdAt }
|
|
2354
|
-
};
|
|
2355
|
-
} catch (e2) {
|
|
2356
|
-
return { _tag: "Failure", error: e2 };
|
|
2357
|
-
}
|
|
2358
|
-
},
|
|
2359
|
-
// Legacy endpoints, split by stage (see SubscriptionService.renewNow docs):
|
|
2360
|
-
// retry → POST /customer/subscriptions/{id}/retry
|
|
2361
|
-
// renewalId → POST /customer/renewals/{renewalId}/renew
|
|
2362
|
-
// default → POST /customer/subscriptions/{id}/order-now
|
|
2363
|
-
async renewNow({ subscriptionId, renewalId, retry }) {
|
|
2364
|
-
try {
|
|
2365
|
-
const url = retry ? `/customer/subscriptions/${subscriptionId}/retry` : renewalId ? `/customer/renewals/${renewalId}/renew` : `/customer/subscriptions/${subscriptionId}/order-now`;
|
|
2366
|
-
const res = await fetcher(url, { method: "POST" });
|
|
2367
|
-
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2368
|
-
const body = res.status === 204 ? null : await res.json();
|
|
2369
|
-
return {
|
|
2370
|
-
_tag: "Success",
|
|
2371
|
-
data: {
|
|
2372
|
-
nextActionUrl: (body == null ? void 0 : body.nextActionUrl) ?? void 0,
|
|
2373
|
-
result: (body == null ? void 0 : body.result) ?? "success"
|
|
2374
|
-
}
|
|
2375
|
-
};
|
|
2376
|
-
} catch (e2) {
|
|
2377
|
-
return { _tag: "Failure", error: e2 };
|
|
2378
|
-
}
|
|
2379
|
-
},
|
|
2380
|
-
// Legacy endpoint — POST /plan returns 204; re-fetch to return the updated subscription.
|
|
2381
|
-
async changeFrequency({ subscriptionId, plan }) {
|
|
2382
|
-
try {
|
|
2383
|
-
const res = await fetcher(
|
|
2384
|
-
`/customer/subscriptions/${subscriptionId}/plan`,
|
|
2385
|
-
{
|
|
2386
|
-
method: "POST",
|
|
2387
|
-
headers: { "Content-Type": "application/json" },
|
|
2388
|
-
body: JSON.stringify({ plan })
|
|
2389
|
-
}
|
|
2390
|
-
);
|
|
2391
|
-
if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
|
|
2392
|
-
const refreshed = await adapter.getById(subscriptionId);
|
|
2393
|
-
if (refreshed._tag === "Failure") return refreshed;
|
|
2394
|
-
if (refreshed.data) return { _tag: "Success", data: refreshed.data };
|
|
2395
|
-
return {
|
|
2396
|
-
_tag: "Failure",
|
|
2397
|
-
error: "Subscription not found after frequency change"
|
|
2398
|
-
};
|
|
2399
|
-
} catch (e2) {
|
|
2400
|
-
return { _tag: "Failure", error: e2 };
|
|
2401
|
-
}
|
|
2402
|
-
}
|
|
2403
|
-
};
|
|
2404
|
-
return adapter;
|
|
2405
|
-
}
|
|
2406
1109
|
const SubscriptionServiceContext = createContext("services/subscription");
|
|
2407
1110
|
function getCurrentSubscriptionId() {
|
|
2408
1111
|
if (typeof window === "undefined") {
|
|
@@ -2425,83 +1128,41 @@ function getCurrentSubscriptionId() {
|
|
|
2425
1128
|
}
|
|
2426
1129
|
function createSubscriptionService(adapter) {
|
|
2427
1130
|
const current = d(null);
|
|
2428
|
-
const
|
|
2429
|
-
const
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
1131
|
+
const discountsChangedAtSignal = d(0);
|
|
1132
|
+
const itemsChangedAtSignal = d(0);
|
|
1133
|
+
async function refreshCurrent(subscriptionId) {
|
|
1134
|
+
const result = await adapter.getById(subscriptionId);
|
|
1135
|
+
if (result._tag === "Success" && result.data != null) {
|
|
1136
|
+
current.value = result.data;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
async function refreshAfterDiscountChange(subscriptionId) {
|
|
1140
|
+
await refreshCurrent(subscriptionId);
|
|
1141
|
+
discountsChangedAtSignal.value = discountsChangedAtSignal.value + 1;
|
|
2435
1142
|
}
|
|
2436
|
-
async function refreshAfterItemChange() {
|
|
2437
|
-
await
|
|
2438
|
-
|
|
1143
|
+
async function refreshAfterItemChange(subscriptionId) {
|
|
1144
|
+
await refreshCurrent(subscriptionId);
|
|
1145
|
+
itemsChangedAtSignal.value = itemsChangedAtSignal.value + 1;
|
|
2439
1146
|
}
|
|
2440
1147
|
function subscriptionIdFromCurrent() {
|
|
2441
1148
|
var _a;
|
|
2442
1149
|
return ((_a = current.value) == null ? void 0 : _a.id) ?? null;
|
|
2443
1150
|
}
|
|
2444
|
-
async function reload() {
|
|
2445
|
-
var _a, _b;
|
|
2446
|
-
const id = (_a = current.value) == null ? void 0 : _a.id;
|
|
2447
|
-
if (!id) return;
|
|
2448
|
-
const [subResult, lastOrderResult] = await Promise.all([
|
|
2449
|
-
adapter.getById(id),
|
|
2450
|
-
adapter.getLastOrder({ subscriptionId: id })
|
|
2451
|
-
]);
|
|
2452
|
-
if (((_b = current.value) == null ? void 0 : _b.id) !== id) return;
|
|
2453
|
-
if (subResult._tag === "Success") current.value = subResult.data;
|
|
2454
|
-
if (lastOrderResult._tag === "Success") lastOrder.value = lastOrderResult.data;
|
|
2455
|
-
}
|
|
2456
1151
|
const service = {
|
|
2457
|
-
current,
|
|
2458
|
-
hasPendingBilling,
|
|
2459
|
-
discountsChangedAt,
|
|
2460
|
-
itemsChangedAt,
|
|
2461
|
-
lastOrder,
|
|
2462
1152
|
search: adapter.search,
|
|
2463
|
-
// Prefer the adapter's rich cross-subscription list (legacy endpoint, active-first).
|
|
2464
|
-
// Fall back to a single `search` page for adapters that don't implement it
|
|
2465
|
-
// (e.g. the editor mock) so the cross-subscription views still render.
|
|
2466
|
-
listAll: adapter.listAll ? adapter.listAll.bind(adapter) : async () => {
|
|
2467
|
-
const result = await adapter.search();
|
|
2468
|
-
if (result._tag !== "Success") return result;
|
|
2469
|
-
return { _tag: "Success", data: result.data.items };
|
|
2470
|
-
},
|
|
2471
1153
|
getById: adapter.getById,
|
|
2472
1154
|
getCurrentSubscriptionId,
|
|
2473
1155
|
addItem: async (subscriptionId, item) => {
|
|
2474
1156
|
const result = await adapter.addItem({ subscriptionId, item });
|
|
2475
1157
|
if (result._tag === "Success") {
|
|
2476
|
-
await refreshAfterItemChange();
|
|
1158
|
+
await refreshAfterItemChange(subscriptionId);
|
|
2477
1159
|
}
|
|
2478
1160
|
return result;
|
|
2479
1161
|
},
|
|
2480
1162
|
removeItem: async (subscriptionId, itemId) => {
|
|
2481
1163
|
const result = await adapter.removeItem({ subscriptionId, itemId });
|
|
2482
1164
|
if (result._tag === "Success") {
|
|
2483
|
-
await refreshAfterItemChange();
|
|
2484
|
-
}
|
|
2485
|
-
return result;
|
|
2486
|
-
},
|
|
2487
|
-
updateItem: async (subscriptionId, lineId, update) => {
|
|
2488
|
-
const result = await adapter.updateItem({ subscriptionId, lineId, update });
|
|
2489
|
-
if (result._tag === "Success") {
|
|
2490
|
-
await refreshAfterItemChange();
|
|
2491
|
-
}
|
|
2492
|
-
return result;
|
|
2493
|
-
},
|
|
2494
|
-
skipLineItem: async (subscriptionId, lineId, nextBillingDate) => {
|
|
2495
|
-
const result = await adapter.skipLineItem({ subscriptionId, lineId, nextBillingDate });
|
|
2496
|
-
if (result._tag === "Success") {
|
|
2497
|
-
await refreshAfterItemChange();
|
|
2498
|
-
}
|
|
2499
|
-
return result;
|
|
2500
|
-
},
|
|
2501
|
-
updateBoxItems: async (subscriptionId, params) => {
|
|
2502
|
-
const result = await adapter.updateBoxItems({ subscriptionId, ...params });
|
|
2503
|
-
if (result._tag === "Success") {
|
|
2504
|
-
await refreshAfterItemChange();
|
|
1165
|
+
await refreshAfterItemChange(subscriptionId);
|
|
2505
1166
|
}
|
|
2506
1167
|
return result;
|
|
2507
1168
|
},
|
|
@@ -2515,7 +1176,7 @@ function createSubscriptionService(adapter) {
|
|
|
2515
1176
|
}
|
|
2516
1177
|
const result = await adapter.addDiscount({ subscriptionId: id, code });
|
|
2517
1178
|
if (result._tag === "Success") {
|
|
2518
|
-
await refreshAfterDiscountChange();
|
|
1179
|
+
await refreshAfterDiscountChange(id);
|
|
2519
1180
|
}
|
|
2520
1181
|
return result;
|
|
2521
1182
|
},
|
|
@@ -2532,7 +1193,7 @@ function createSubscriptionService(adapter) {
|
|
|
2532
1193
|
discountId
|
|
2533
1194
|
});
|
|
2534
1195
|
if (result._tag === "Success") {
|
|
2535
|
-
await refreshAfterDiscountChange();
|
|
1196
|
+
await refreshAfterDiscountChange(id);
|
|
2536
1197
|
}
|
|
2537
1198
|
return result;
|
|
2538
1199
|
},
|
|
@@ -2541,13 +1202,11 @@ function createSubscriptionService(adapter) {
|
|
|
2541
1202
|
pause: (subscriptionId) => adapter.pause({ subscriptionId }),
|
|
2542
1203
|
reactivate: (subscriptionId) => adapter.reactivate({ subscriptionId }),
|
|
2543
1204
|
cancel: (subscriptionId, reason) => adapter.cancel({ subscriptionId, reason }),
|
|
2544
|
-
renewNow: (params) => adapter.renewNow(params),
|
|
2545
|
-
changeFrequency: (subscriptionId, plan) => adapter.changeFrequency({ subscriptionId, plan }),
|
|
2546
|
-
reload,
|
|
2547
1205
|
getAvailablePlans: (subscriptionId) => adapter.getAvailablePlans({ subscriptionId }),
|
|
2548
1206
|
getDefaultSubscriptionId: () => adapter.getDefaultSubscriptionId(),
|
|
2549
|
-
|
|
2550
|
-
|
|
1207
|
+
current,
|
|
1208
|
+
discountsChangedAt: discountsChangedAtSignal,
|
|
1209
|
+
itemsChangedAt: itemsChangedAtSignal
|
|
2551
1210
|
};
|
|
2552
1211
|
return service;
|
|
2553
1212
|
}
|
|
@@ -2816,88 +1475,66 @@ export {
|
|
|
2816
1475
|
AuthenticationError,
|
|
2817
1476
|
AuthenticationErrorReason,
|
|
2818
1477
|
B as BlockWrapperEvent,
|
|
2819
|
-
|
|
2820
|
-
|
|
1478
|
+
C as ContextProvideEvent,
|
|
1479
|
+
a as ContextRequestEvent,
|
|
2821
1480
|
CustomerServiceContext,
|
|
2822
|
-
|
|
1481
|
+
D as DEFAULT_GLOBAL_STYLES,
|
|
2823
1482
|
DISCOUNT_REJECTION_REASONS,
|
|
2824
1483
|
LoginServiceContext,
|
|
2825
1484
|
OrdersServiceContext,
|
|
2826
|
-
OverlayServiceContext,
|
|
2827
1485
|
ProductServiceContext,
|
|
2828
1486
|
RouterServiceContext,
|
|
2829
|
-
SchedulesServiceContext,
|
|
2830
1487
|
S as SelectedBlockContext,
|
|
2831
|
-
SettingsServiceContext,
|
|
2832
|
-
ShopServiceContext,
|
|
2833
1488
|
u as Signal,
|
|
2834
1489
|
StateContext,
|
|
2835
1490
|
SubscriptionServiceContext,
|
|
2836
|
-
|
|
2837
|
-
|
|
1491
|
+
T as ThemeStateContext,
|
|
1492
|
+
b as TranslationContext,
|
|
2838
1493
|
W as WorkflowServiceContext,
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
t as attachEmptyTracking,
|
|
2842
|
-
b as backfillInstanceDefaults,
|
|
1494
|
+
e as attachEmptyTracking,
|
|
1495
|
+
f as backfillInstanceDefaults,
|
|
2843
1496
|
w as computed,
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
createApiSubscriptionAdapter,
|
|
2849
|
-
createBlockInstance,
|
|
2850
|
-
v as createBlockInstanceFromJSON,
|
|
2851
|
-
f as createBlockInstanceFromObject,
|
|
2852
|
-
x as createCeRenderer,
|
|
1497
|
+
g as createBlockInstance,
|
|
1498
|
+
h as createBlockInstanceFromJSON,
|
|
1499
|
+
i as createBlockInstanceFromObject,
|
|
1500
|
+
j as createCeRenderer,
|
|
2853
1501
|
createContext,
|
|
2854
1502
|
createCustomerService,
|
|
2855
|
-
createLoginService,
|
|
2856
|
-
createMockCustomerAdapter,
|
|
2857
|
-
createMockLoginAdapter,
|
|
2858
1503
|
createMockOrdersAdapter,
|
|
2859
1504
|
createMockProductAdapter,
|
|
2860
1505
|
createMockRouterService,
|
|
2861
|
-
createMockSchedulesAdapter,
|
|
2862
1506
|
createMockSubscriptionAdapter,
|
|
2863
1507
|
createMockWorkflowApiAdapter,
|
|
2864
1508
|
createOrdersService,
|
|
2865
|
-
createOverlayService,
|
|
2866
1509
|
createProductService,
|
|
2867
1510
|
createRouterService,
|
|
2868
|
-
|
|
2869
|
-
y as createSelectedBlock,
|
|
2870
|
-
createSettingsService,
|
|
2871
|
-
createShopService,
|
|
1511
|
+
k as createSelectedBlock,
|
|
2872
1512
|
createState,
|
|
2873
1513
|
createSubscriptionService,
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
defineBlock,
|
|
2879
|
-
|
|
2880
|
-
defineOverlayWrapper,
|
|
1514
|
+
l as createThemeState,
|
|
1515
|
+
m as createTranslationService,
|
|
1516
|
+
n as createWorkflowApiAdapter,
|
|
1517
|
+
o as createWorkflowService,
|
|
1518
|
+
p as defineBlock,
|
|
1519
|
+
q as defineBlockRuntime,
|
|
2881
1520
|
E as effect,
|
|
2882
|
-
|
|
2883
|
-
getDefinedBlocks,
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
F as
|
|
2890
|
-
a as isStubDefinition,
|
|
1521
|
+
s as getDefaultBlockForAction,
|
|
1522
|
+
t as getDefinedBlocks,
|
|
1523
|
+
v as hasDefaultBlockForAction,
|
|
1524
|
+
x as hasRenderedContent,
|
|
1525
|
+
y as indexOverrides,
|
|
1526
|
+
z as injectContext,
|
|
1527
|
+
A as isEditorPreview,
|
|
1528
|
+
F as isStubDefinition,
|
|
2891
1529
|
loadBlockRegistry,
|
|
2892
1530
|
loginService,
|
|
2893
|
-
|
|
2894
|
-
|
|
1531
|
+
G as onRegisterBlock,
|
|
1532
|
+
H as provideContext,
|
|
2895
1533
|
registerBlock,
|
|
2896
|
-
|
|
2897
|
-
H as setDevtoolsBlockProp,
|
|
1534
|
+
I as serializeBlocks,
|
|
2898
1535
|
d as signal,
|
|
2899
|
-
|
|
2900
|
-
|
|
1536
|
+
J as subscribeContext,
|
|
1537
|
+
K as untracked,
|
|
2901
1538
|
waitForDevBlocksUrl,
|
|
2902
|
-
|
|
1539
|
+
L as withWrapper
|
|
2903
1540
|
};
|