@drawbridge/drawbridge-utils 0.0.175 → 0.0.177
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/admin-B9ZaLvan.d.cts +697 -0
- package/dist/admin-C3HtEM6h.d.ts +697 -0
- package/dist/billing-Bc4yo9XG.d.cts +175 -0
- package/dist/billing-mNsKflmQ.d.ts +175 -0
- package/dist/billing.d.cts +1 -1
- package/dist/billing.d.ts +1 -1
- package/dist/connections/index.cjs +5928 -1920
- package/dist/connections/index.d.cts +20 -9163
- package/dist/connections/index.d.ts +20 -9163
- package/dist/connections/index.js +5917 -1921
- package/dist/connections/oauth.cjs +4 -4
- package/dist/connections/oauth.d.cts +10 -8
- package/dist/connections/oauth.d.ts +10 -8
- package/dist/connections/oauth.js +4 -4
- package/dist/features.cjs +10260 -42
- package/dist/features.d.cts +55 -42
- package/dist/features.d.ts +55 -42
- package/dist/features.js +10254 -42
- package/dist/http.cjs +10 -1
- package/dist/http.d.cts +10 -1
- package/dist/http.d.ts +10 -1
- package/dist/http.js +10 -1
- package/dist/index-B546oDNo.d.ts +13809 -0
- package/dist/index-C59xHago.d.cts +13809 -0
- package/dist/oauth/index.d.cts +1 -1
- package/dist/oauth/index.d.ts +1 -1
- package/dist/oauth-BJDh0sdM.d.cts +527 -0
- package/dist/oauth-DveZMLHx.d.ts +527 -0
- package/dist/partner-BOZltuh2.d.ts +94 -0
- package/dist/partner-ed2OfW1J.d.cts +94 -0
- package/dist/plans.cjs +10256 -51
- package/dist/plans.d.cts +37 -10
- package/dist/plans.d.ts +37 -10
- package/dist/plans.js +10262 -51
- package/dist/pricing.cjs +10401 -209
- package/dist/pricing.d.cts +34 -17
- package/dist/pricing.d.ts +34 -17
- package/dist/pricing.js +10407 -209
- package/dist/providers.cjs +5433 -1755
- package/dist/providers.d.cts +13 -15
- package/dist/providers.d.ts +13 -15
- package/dist/providers.js +5436 -1752
- package/dist/sendgrid.cjs +10 -1
- package/dist/sendgrid.js +10 -1
- package/dist/shopify/admin.cjs +562 -0
- package/dist/shopify/admin.d.cts +3 -0
- package/dist/shopify/admin.d.ts +3 -0
- package/dist/shopify/admin.js +528 -0
- package/dist/shopify/billing.cjs +166 -0
- package/dist/shopify/billing.d.cts +3 -0
- package/dist/shopify/billing.d.ts +3 -0
- package/dist/shopify/billing.js +140 -0
- package/dist/shopify/constants.cjs +63 -0
- package/dist/shopify/constants.d.cts +58 -0
- package/dist/shopify/constants.d.ts +58 -0
- package/dist/shopify/constants.js +32 -0
- package/dist/shopify/oauth.cjs +509 -0
- package/dist/shopify/oauth.d.cts +7 -0
- package/dist/shopify/oauth.d.ts +7 -0
- package/dist/shopify/oauth.js +466 -0
- package/dist/shopify/partner.cjs +156 -0
- package/dist/shopify/partner.d.cts +3 -0
- package/dist/shopify/partner.d.ts +3 -0
- package/dist/shopify/partner.js +130 -0
- package/dist/shopify/storefront.cjs +611 -0
- package/dist/shopify/storefront.d.cts +3 -0
- package/dist/shopify/storefront.d.ts +3 -0
- package/dist/shopify/storefront.js +576 -0
- package/dist/storefront-C8FKOGeD.d.cts +659 -0
- package/dist/storefront-DJFGLqPl.d.ts +659 -0
- package/dist/twilio.cjs +10 -1
- package/dist/twilio.js +10 -1
- package/package.json +98 -68
|
@@ -0,0 +1,659 @@
|
|
|
1
|
+
import { request } from './http.js';
|
|
2
|
+
import { SHOPIFY_STOREFRONT_API_VERSION } from './shopify/constants.js';
|
|
3
|
+
|
|
4
|
+
const SORT_KEY_MAP = {
|
|
5
|
+
bestSelling : 'BEST_SELLING',
|
|
6
|
+
createdAt : 'CREATED_AT',
|
|
7
|
+
id : 'ID',
|
|
8
|
+
price : 'PRICE',
|
|
9
|
+
productType : 'PRODUCT_TYPE',
|
|
10
|
+
title : 'TITLE',
|
|
11
|
+
updatedAt : 'UPDATED_AT',
|
|
12
|
+
vendor : 'VENDOR'
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const resolveProductSort = ( sort ) => {
|
|
16
|
+
|
|
17
|
+
const entry = sort && Object.entries( sort )[ 0 ];
|
|
18
|
+
|
|
19
|
+
if( ! entry ) return null;
|
|
20
|
+
|
|
21
|
+
const [ field, direction ] = entry;
|
|
22
|
+
const sortKey = SORT_KEY_MAP[ field ];
|
|
23
|
+
|
|
24
|
+
if( ! sortKey ) return null;
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
sortKey,
|
|
28
|
+
reverse : Number( direction ) < 0
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const storefrontUrl = ( domain ) =>
|
|
34
|
+
`https://${ domain }/api/${ SHOPIFY_STOREFRONT_API_VERSION }/graphql.json`;
|
|
35
|
+
|
|
36
|
+
const storefrontFetch = async ({ fetcher, domain, storefrontAccessToken, query, variables }) => {
|
|
37
|
+
|
|
38
|
+
return request({
|
|
39
|
+
method : 'POST',
|
|
40
|
+
url : storefrontUrl( domain ),
|
|
41
|
+
headers : {
|
|
42
|
+
'X-Shopify-Storefront-Access-Token' : storefrontAccessToken
|
|
43
|
+
},
|
|
44
|
+
body : {
|
|
45
|
+
query,
|
|
46
|
+
...( variables && { variables } )
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// Shop's primary country, memoized per domain. Needed as buyer context for the
|
|
53
|
+
// out-of-stock probe below: cart warnings only surface when the cart carries a
|
|
54
|
+
// buyer country.
|
|
55
|
+
const shopCountryCache = {};
|
|
56
|
+
|
|
57
|
+
const getShopCountry = async ({ domain, fetcher, storefrontAccessToken }) => {
|
|
58
|
+
|
|
59
|
+
if( shopCountryCache[ domain ] ) return shopCountryCache[ domain ];
|
|
60
|
+
|
|
61
|
+
const { data } = await storefrontFetch({
|
|
62
|
+
fetcher,
|
|
63
|
+
domain,
|
|
64
|
+
storefrontAccessToken,
|
|
65
|
+
query : `{
|
|
66
|
+
shop {
|
|
67
|
+
paymentSettings {
|
|
68
|
+
countryCode
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}`
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const countryCode = data?.shop?.paymentSettings?.countryCode || null;
|
|
75
|
+
|
|
76
|
+
if( countryCode ) shopCountryCache[ domain ] = countryCode;
|
|
77
|
+
|
|
78
|
+
return countryCode;
|
|
79
|
+
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// Cart warning codes that mean a line is not actually purchasable, even when
|
|
83
|
+
// merchandise.availableForSale / quantityAvailable report it as available (e.g.
|
|
84
|
+
// stock at a fulfillment-service location that cannot fulfill the online order).
|
|
85
|
+
const OUT_OF_STOCK_WARNING_CODES = [
|
|
86
|
+
'MERCHANDISE_OUT_OF_STOCK',
|
|
87
|
+
'MERCHANDISE_NOT_ENOUGH_STOCK'
|
|
88
|
+
];
|
|
89
|
+
|
|
90
|
+
// Probe real purchasability for a set of variants by creating a throwaway cart
|
|
91
|
+
// (with the shop's country as buyer context) and reading the out-of-stock
|
|
92
|
+
// warnings the availability fields miss. Returns the variant ids Shopify reports
|
|
93
|
+
// as out of stock. One cart call covers all variants.
|
|
94
|
+
const probeVariantsOutOfStock = async ({ fetcher, domain, storefrontAccessToken, variantIds, countryCode }) => {
|
|
95
|
+
|
|
96
|
+
if( ! variantIds?.length ) return [];
|
|
97
|
+
|
|
98
|
+
const resolvedCountry = countryCode || await getShopCountry({ domain, fetcher, storefrontAccessToken });
|
|
99
|
+
|
|
100
|
+
const { data, errors } = await storefrontFetch({
|
|
101
|
+
fetcher,
|
|
102
|
+
domain,
|
|
103
|
+
storefrontAccessToken,
|
|
104
|
+
query : `
|
|
105
|
+
mutation probe( $input : CartInput! ){
|
|
106
|
+
cartCreate( input : $input ){
|
|
107
|
+
cart {
|
|
108
|
+
lines( first : 250 ){
|
|
109
|
+
edges {
|
|
110
|
+
node {
|
|
111
|
+
id
|
|
112
|
+
merchandise {
|
|
113
|
+
... on ProductVariant {
|
|
114
|
+
id
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
warnings {
|
|
122
|
+
code
|
|
123
|
+
message
|
|
124
|
+
target
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
`,
|
|
129
|
+
variables : {
|
|
130
|
+
input : {
|
|
131
|
+
lines : variantIds.map( ( merchandiseId ) => ({
|
|
132
|
+
merchandiseId,
|
|
133
|
+
quantity : 1
|
|
134
|
+
}) ),
|
|
135
|
+
...( resolvedCountry && {
|
|
136
|
+
buyerIdentity : {
|
|
137
|
+
countryCode : resolvedCountry
|
|
138
|
+
}
|
|
139
|
+
})
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
if( errors ) return [];
|
|
145
|
+
|
|
146
|
+
const payload = data?.cartCreate;
|
|
147
|
+
|
|
148
|
+
const soldOut = new Set(
|
|
149
|
+
( payload?.warnings || [] )
|
|
150
|
+
.filter( ( warning ) => OUT_OF_STOCK_WARNING_CODES.includes( warning?.code ) && warning?.target )
|
|
151
|
+
.map( ( warning ) => warning.target )
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
return ( payload?.cart?.lines?.edges || [] )
|
|
155
|
+
.filter( ( { node } ) => soldOut.has( node.id ) )
|
|
156
|
+
.map( ( { node } ) => node?.merchandise?.id )
|
|
157
|
+
.filter( Boolean );
|
|
158
|
+
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const productsQuery = ( search, cursor, limit, sort ) => {
|
|
162
|
+
|
|
163
|
+
const resolved = resolveProductSort( sort );
|
|
164
|
+
const sortArgs = resolved ? `, sortKey: ${ resolved.sortKey }, reverse: ${ resolved.reverse }` : '';
|
|
165
|
+
|
|
166
|
+
return `{
|
|
167
|
+
products(first: ${ limit }${ search ? `, query: "title:*${ search }*"` : '' }${ cursor ? `, after: "${ cursor }"` : '' }${ sortArgs }) {
|
|
168
|
+
edges {
|
|
169
|
+
node {
|
|
170
|
+
id
|
|
171
|
+
title
|
|
172
|
+
description
|
|
173
|
+
handle
|
|
174
|
+
productType
|
|
175
|
+
featuredImage {
|
|
176
|
+
url
|
|
177
|
+
}
|
|
178
|
+
variants(first: 10) {
|
|
179
|
+
edges {
|
|
180
|
+
node {
|
|
181
|
+
id
|
|
182
|
+
title
|
|
183
|
+
image {
|
|
184
|
+
url
|
|
185
|
+
}
|
|
186
|
+
price {
|
|
187
|
+
amount
|
|
188
|
+
currencyCode
|
|
189
|
+
}
|
|
190
|
+
compareAtPrice {
|
|
191
|
+
amount
|
|
192
|
+
currencyCode
|
|
193
|
+
}
|
|
194
|
+
availableForSale
|
|
195
|
+
requiresShipping
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
pageInfo {
|
|
202
|
+
endCursor
|
|
203
|
+
hasNextPage
|
|
204
|
+
hasPreviousPage
|
|
205
|
+
startCursor
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}`;
|
|
209
|
+
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const productQuery = ( productId ) => `{
|
|
213
|
+
product(id: "${ productId }") {
|
|
214
|
+
id
|
|
215
|
+
title
|
|
216
|
+
description
|
|
217
|
+
handle
|
|
218
|
+
productType
|
|
219
|
+
featuredImage {
|
|
220
|
+
url
|
|
221
|
+
}
|
|
222
|
+
variants(first: 10) {
|
|
223
|
+
edges {
|
|
224
|
+
node {
|
|
225
|
+
id
|
|
226
|
+
title
|
|
227
|
+
image {
|
|
228
|
+
url
|
|
229
|
+
}
|
|
230
|
+
price {
|
|
231
|
+
amount
|
|
232
|
+
currencyCode
|
|
233
|
+
}
|
|
234
|
+
compareAtPrice {
|
|
235
|
+
amount
|
|
236
|
+
currencyCode
|
|
237
|
+
}
|
|
238
|
+
availableForSale
|
|
239
|
+
requiresShipping
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}`;
|
|
245
|
+
|
|
246
|
+
// Storefront API caps variants per page at 250. Products with more than
|
|
247
|
+
// that need cursor-based pagination — variants past `first:` are silently
|
|
248
|
+
// dropped without a `pageInfo` hint, so we always request it and loop
|
|
249
|
+
// until `hasNextPage` is false. Returns the flat list of edges from every
|
|
250
|
+
// page concatenated.
|
|
251
|
+
const variantsQuery = ( productId, cursor ) => `{
|
|
252
|
+
product(id: "${ productId }") {
|
|
253
|
+
variants(first: 250${ cursor ? `, after: "${ cursor }"` : '' }) {
|
|
254
|
+
edges {
|
|
255
|
+
node {
|
|
256
|
+
id
|
|
257
|
+
title
|
|
258
|
+
image {
|
|
259
|
+
url
|
|
260
|
+
}
|
|
261
|
+
price {
|
|
262
|
+
amount
|
|
263
|
+
currencyCode
|
|
264
|
+
}
|
|
265
|
+
compareAtPrice {
|
|
266
|
+
amount
|
|
267
|
+
currencyCode
|
|
268
|
+
}
|
|
269
|
+
availableForSale
|
|
270
|
+
requiresShipping
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
pageInfo {
|
|
274
|
+
hasNextPage
|
|
275
|
+
endCursor
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}`;
|
|
280
|
+
|
|
281
|
+
const getProducts = async ({ fetcher, domain, storefrontAccessToken, search, cursor, limit, sort }) => {
|
|
282
|
+
|
|
283
|
+
const { data, errors } = await storefrontFetch({
|
|
284
|
+
fetcher,
|
|
285
|
+
domain,
|
|
286
|
+
storefrontAccessToken,
|
|
287
|
+
query : productsQuery( search, cursor, limit, sort )
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
if( errors ){
|
|
291
|
+
|
|
292
|
+
throw new Error( 'Failed to fetch Shopify products' );
|
|
293
|
+
|
|
294
|
+
}
|
|
295
|
+
return data?.products;
|
|
296
|
+
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
const getProduct = async ({ fetcher, domain, storefrontAccessToken, productId }) => {
|
|
300
|
+
|
|
301
|
+
const { data, errors } = await storefrontFetch({
|
|
302
|
+
fetcher,
|
|
303
|
+
domain,
|
|
304
|
+
storefrontAccessToken,
|
|
305
|
+
query : productQuery( productId )
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
if( errors ){
|
|
309
|
+
|
|
310
|
+
throw new Error( 'Shopify storefront error: ' + errors[ 0 ]?.message );
|
|
311
|
+
|
|
312
|
+
}
|
|
313
|
+
// The exact 'Shopify product not found' message is a contract: it's how a
|
|
314
|
+
// null product (not published to this channel, or deleted) stays
|
|
315
|
+
// distinguishable from transport/GraphQL failures. drawbridge-sync's
|
|
316
|
+
// product worker matches on it to send publish-this-product feedback
|
|
317
|
+
// instead of retrying the job.
|
|
318
|
+
if( ! data?.product ){
|
|
319
|
+
|
|
320
|
+
throw new Error( 'Shopify product not found' );
|
|
321
|
+
|
|
322
|
+
}
|
|
323
|
+
return data.product;
|
|
324
|
+
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
// Returns ONE page of variants — `{ edges, pageInfo }`. Callers drive the
|
|
328
|
+
// cursor loop themselves. This shape exists (instead of a built-in loop)
|
|
329
|
+
// so a BullMQ worker can persist each page and self-enqueue the next,
|
|
330
|
+
// keeping per-job wall-clock under the worker's lock duration regardless
|
|
331
|
+
// of how many variants the product has.
|
|
332
|
+
const getProductVariantsPage = async ({ fetcher, domain, storefrontAccessToken, productId, cursor }) => {
|
|
333
|
+
|
|
334
|
+
const { data, errors } = await storefrontFetch({
|
|
335
|
+
fetcher,
|
|
336
|
+
domain,
|
|
337
|
+
storefrontAccessToken,
|
|
338
|
+
query : variantsQuery( productId, cursor )
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
if( errors ){
|
|
342
|
+
|
|
343
|
+
throw new Error( 'Shopify storefront error: ' + errors[ 0 ]?.message );
|
|
344
|
+
|
|
345
|
+
}
|
|
346
|
+
// Same not-found contract as getProduct above.
|
|
347
|
+
if( ! data?.product ){
|
|
348
|
+
|
|
349
|
+
throw new Error( 'Shopify product not found' );
|
|
350
|
+
|
|
351
|
+
}
|
|
352
|
+
return data.product.variants;
|
|
353
|
+
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
const CART_FIELDS = `
|
|
357
|
+
id
|
|
358
|
+
checkoutUrl
|
|
359
|
+
lines(first: 100) {
|
|
360
|
+
edges {
|
|
361
|
+
node {
|
|
362
|
+
id
|
|
363
|
+
quantity
|
|
364
|
+
merchandise {
|
|
365
|
+
... on ProductVariant {
|
|
366
|
+
id
|
|
367
|
+
title
|
|
368
|
+
availableForSale
|
|
369
|
+
image {
|
|
370
|
+
url
|
|
371
|
+
}
|
|
372
|
+
price {
|
|
373
|
+
amount
|
|
374
|
+
currencyCode
|
|
375
|
+
}
|
|
376
|
+
product {
|
|
377
|
+
title
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
`;
|
|
385
|
+
|
|
386
|
+
const mapCart = ( cart ) => {
|
|
387
|
+
|
|
388
|
+
if( ! cart ) return null;
|
|
389
|
+
|
|
390
|
+
return {
|
|
391
|
+
checkoutUrl : cart.checkoutUrl,
|
|
392
|
+
id : cart.id,
|
|
393
|
+
lines : ( cart.lines?.edges || [] ).map( ( { node } ) => ({
|
|
394
|
+
availableForSale : node?.merchandise?.availableForSale ?? true,
|
|
395
|
+
currency : node?.merchandise?.price?.currencyCode || null,
|
|
396
|
+
id : node.id,
|
|
397
|
+
image : node?.merchandise?.image?.url || null,
|
|
398
|
+
price : node?.merchandise?.price?.amount || null,
|
|
399
|
+
productTitle : node?.merchandise?.product?.title || null,
|
|
400
|
+
quantity : node.quantity,
|
|
401
|
+
variantId : node?.merchandise?.id,
|
|
402
|
+
variantTitle : node?.merchandise?.title || null
|
|
403
|
+
}))
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
const firstUserError = ( userErrors ) => {
|
|
409
|
+
|
|
410
|
+
const error = userErrors?.[ 0 ];
|
|
411
|
+
|
|
412
|
+
if( ! error ) return null;
|
|
413
|
+
|
|
414
|
+
const message = new Error( error.message || 'Shopify cart error' );
|
|
415
|
+
|
|
416
|
+
message.userError = true;
|
|
417
|
+
|
|
418
|
+
return message;
|
|
419
|
+
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
// Mints the JWT required by Checkout Kit's inline (embedded) checkout — passed
|
|
423
|
+
// as the `auth` attribute on <shopify-checkout target="inline">. This is a
|
|
424
|
+
// client-credentials grant against Shopify's auth service, NOT a Storefront/
|
|
425
|
+
// Admin GraphQL call, so it takes app credentials rather than the per-shop
|
|
426
|
+
// storefront token.
|
|
427
|
+
//
|
|
428
|
+
// NOTE: Shopify's docs are thin here — they say the client_id/client_secret are
|
|
429
|
+
// "provided by Shopify" for authenticated checkouts. Callers pass the app's own
|
|
430
|
+
// credentials; verify against a real store before this ships and swap to
|
|
431
|
+
// dedicated checkout credentials if Shopify requires them.
|
|
432
|
+
const createCheckoutToken = async ({ fetcher, clientId, clientSecret } = {}) => {
|
|
433
|
+
|
|
434
|
+
// Throw rather than grant with a missing half: Shopify answers a bad
|
|
435
|
+
// client_credentials pair with a generic rejection, so the resulting
|
|
436
|
+
// 'Failed to create Shopify checkout token' would say nothing about the
|
|
437
|
+
// credential actually being absent.
|
|
438
|
+
if( ! clientId || ! clientSecret ){
|
|
439
|
+
|
|
440
|
+
throw new Error( 'createCheckoutToken requires clientId and clientSecret' );
|
|
441
|
+
|
|
442
|
+
}
|
|
443
|
+
const response = await request({
|
|
444
|
+
fetcher,
|
|
445
|
+
method : 'POST',
|
|
446
|
+
url : 'https://api.shopify.com/auth/access_token',
|
|
447
|
+
body : {
|
|
448
|
+
client_id : clientId,
|
|
449
|
+
client_secret : clientSecret,
|
|
450
|
+
grant_type : 'client_credentials'
|
|
451
|
+
}
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
const token = response?.access_token;
|
|
455
|
+
|
|
456
|
+
if( ! token ){
|
|
457
|
+
|
|
458
|
+
throw new Error( 'Failed to create Shopify checkout token' );
|
|
459
|
+
|
|
460
|
+
}
|
|
461
|
+
return token;
|
|
462
|
+
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
const cartCreate = async ({ fetcher, domain, storefrontAccessToken, lines }) => {
|
|
466
|
+
|
|
467
|
+
const { data, errors } = await storefrontFetch({
|
|
468
|
+
fetcher,
|
|
469
|
+
domain,
|
|
470
|
+
storefrontAccessToken,
|
|
471
|
+
query : `
|
|
472
|
+
mutation cartCreate($input: CartInput!) {
|
|
473
|
+
cartCreate(input: $input) {
|
|
474
|
+
cart {
|
|
475
|
+
${ CART_FIELDS }
|
|
476
|
+
}
|
|
477
|
+
userErrors {
|
|
478
|
+
field
|
|
479
|
+
message
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
`,
|
|
484
|
+
variables : {
|
|
485
|
+
input : {
|
|
486
|
+
lines
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
if( errors ){
|
|
492
|
+
|
|
493
|
+
throw new Error( 'Failed to create Shopify cart' );
|
|
494
|
+
|
|
495
|
+
}
|
|
496
|
+
const userError = firstUserError( data?.cartCreate?.userErrors );
|
|
497
|
+
|
|
498
|
+
if( userError ) throw userError;
|
|
499
|
+
|
|
500
|
+
return mapCart( data?.cartCreate?.cart );
|
|
501
|
+
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
const cartGet = async ({ fetcher, domain, storefrontAccessToken, cartId }) => {
|
|
505
|
+
|
|
506
|
+
const { data, errors } = await storefrontFetch({
|
|
507
|
+
fetcher,
|
|
508
|
+
domain,
|
|
509
|
+
storefrontAccessToken,
|
|
510
|
+
query : `
|
|
511
|
+
query cart($id: ID!) {
|
|
512
|
+
cart(id: $id) {
|
|
513
|
+
${ CART_FIELDS }
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
`,
|
|
517
|
+
variables : {
|
|
518
|
+
id : cartId
|
|
519
|
+
}
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
if( errors ){
|
|
523
|
+
|
|
524
|
+
throw new Error( 'Failed to fetch Shopify cart' );
|
|
525
|
+
|
|
526
|
+
}
|
|
527
|
+
return mapCart( data?.cart );
|
|
528
|
+
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
const cartLinesAdd = async ({ fetcher, domain, storefrontAccessToken, cartId, lines }) => {
|
|
532
|
+
|
|
533
|
+
const { data, errors } = await storefrontFetch({
|
|
534
|
+
fetcher,
|
|
535
|
+
domain,
|
|
536
|
+
storefrontAccessToken,
|
|
537
|
+
query : `
|
|
538
|
+
mutation cartLinesAdd($cartId: ID!, $lines: [CartLineInput!]!) {
|
|
539
|
+
cartLinesAdd(cartId: $cartId, lines: $lines) {
|
|
540
|
+
cart {
|
|
541
|
+
${ CART_FIELDS }
|
|
542
|
+
}
|
|
543
|
+
userErrors {
|
|
544
|
+
field
|
|
545
|
+
message
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
`,
|
|
550
|
+
variables : {
|
|
551
|
+
cartId,
|
|
552
|
+
lines
|
|
553
|
+
}
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
if( errors ){
|
|
557
|
+
|
|
558
|
+
throw new Error( 'Failed to add Shopify cart lines' );
|
|
559
|
+
|
|
560
|
+
}
|
|
561
|
+
const userError = firstUserError( data?.cartLinesAdd?.userErrors );
|
|
562
|
+
|
|
563
|
+
if( userError ) throw userError;
|
|
564
|
+
|
|
565
|
+
return mapCart( data?.cartLinesAdd?.cart );
|
|
566
|
+
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
const cartLinesRemove = async ({ fetcher, domain, storefrontAccessToken, cartId, lineIds }) => {
|
|
570
|
+
|
|
571
|
+
const { data, errors } = await storefrontFetch({
|
|
572
|
+
fetcher,
|
|
573
|
+
domain,
|
|
574
|
+
storefrontAccessToken,
|
|
575
|
+
query : `
|
|
576
|
+
mutation cartLinesRemove($cartId: ID!, $lineIds: [ID!]!) {
|
|
577
|
+
cartLinesRemove(cartId: $cartId, lineIds: $lineIds) {
|
|
578
|
+
cart {
|
|
579
|
+
${ CART_FIELDS }
|
|
580
|
+
}
|
|
581
|
+
userErrors {
|
|
582
|
+
field
|
|
583
|
+
message
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
`,
|
|
588
|
+
variables : {
|
|
589
|
+
cartId,
|
|
590
|
+
lineIds
|
|
591
|
+
}
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
if( errors ){
|
|
595
|
+
|
|
596
|
+
throw new Error( 'Failed to remove Shopify cart lines' );
|
|
597
|
+
|
|
598
|
+
}
|
|
599
|
+
const userError = firstUserError( data?.cartLinesRemove?.userErrors );
|
|
600
|
+
|
|
601
|
+
if( userError ) throw userError;
|
|
602
|
+
|
|
603
|
+
return mapCart( data?.cartLinesRemove?.cart );
|
|
604
|
+
|
|
605
|
+
};
|
|
606
|
+
|
|
607
|
+
const cartLinesUpdate = async ({ fetcher, domain, storefrontAccessToken, cartId, lines }) => {
|
|
608
|
+
|
|
609
|
+
const { data, errors } = await storefrontFetch({
|
|
610
|
+
fetcher,
|
|
611
|
+
domain,
|
|
612
|
+
storefrontAccessToken,
|
|
613
|
+
query : `
|
|
614
|
+
mutation cartLinesUpdate($cartId: ID!, $lines: [CartLineUpdateInput!]!) {
|
|
615
|
+
cartLinesUpdate(cartId: $cartId, lines: $lines) {
|
|
616
|
+
cart {
|
|
617
|
+
${ CART_FIELDS }
|
|
618
|
+
}
|
|
619
|
+
userErrors {
|
|
620
|
+
field
|
|
621
|
+
message
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
`,
|
|
626
|
+
variables : {
|
|
627
|
+
cartId,
|
|
628
|
+
lines
|
|
629
|
+
}
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
if( errors ){
|
|
633
|
+
|
|
634
|
+
throw new Error( 'Failed to update Shopify cart lines' );
|
|
635
|
+
|
|
636
|
+
}
|
|
637
|
+
const userError = firstUserError( data?.cartLinesUpdate?.userErrors );
|
|
638
|
+
|
|
639
|
+
if( userError ) throw userError;
|
|
640
|
+
|
|
641
|
+
return mapCart( data?.cartLinesUpdate?.cart );
|
|
642
|
+
|
|
643
|
+
};
|
|
644
|
+
|
|
645
|
+
declare const shopifyStorefront_cartCreate: typeof cartCreate;
|
|
646
|
+
declare const shopifyStorefront_cartGet: typeof cartGet;
|
|
647
|
+
declare const shopifyStorefront_cartLinesAdd: typeof cartLinesAdd;
|
|
648
|
+
declare const shopifyStorefront_cartLinesRemove: typeof cartLinesRemove;
|
|
649
|
+
declare const shopifyStorefront_cartLinesUpdate: typeof cartLinesUpdate;
|
|
650
|
+
declare const shopifyStorefront_createCheckoutToken: typeof createCheckoutToken;
|
|
651
|
+
declare const shopifyStorefront_getProduct: typeof getProduct;
|
|
652
|
+
declare const shopifyStorefront_getProductVariantsPage: typeof getProductVariantsPage;
|
|
653
|
+
declare const shopifyStorefront_getProducts: typeof getProducts;
|
|
654
|
+
declare const shopifyStorefront_probeVariantsOutOfStock: typeof probeVariantsOutOfStock;
|
|
655
|
+
declare namespace shopifyStorefront {
|
|
656
|
+
export { shopifyStorefront_cartCreate as cartCreate, shopifyStorefront_cartGet as cartGet, shopifyStorefront_cartLinesAdd as cartLinesAdd, shopifyStorefront_cartLinesRemove as cartLinesRemove, shopifyStorefront_cartLinesUpdate as cartLinesUpdate, shopifyStorefront_createCheckoutToken as createCheckoutToken, shopifyStorefront_getProduct as getProduct, shopifyStorefront_getProductVariantsPage as getProductVariantsPage, shopifyStorefront_getProducts as getProducts, shopifyStorefront_probeVariantsOutOfStock as probeVariantsOutOfStock };
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
export { cartGet as a, cartLinesAdd as b, cartCreate as c, cartLinesRemove as d, cartLinesUpdate as e, createCheckoutToken as f, getProduct as g, getProductVariantsPage as h, getProducts as i, probeVariantsOutOfStock as p, shopifyStorefront as s };
|
package/dist/twilio.cjs
CHANGED
|
@@ -27,6 +27,15 @@ module.exports = __toCommonJS(twilio_exports);
|
|
|
27
27
|
var DEFAULT_TIMEOUT_MS = 15e3;
|
|
28
28
|
var request = async ({
|
|
29
29
|
body,
|
|
30
|
+
// THE TRANSPORT, injectable and defaulted to the real one.
|
|
31
|
+
//
|
|
32
|
+
// Every other vendor client in this package takes a `fetcher` — it is how
|
|
33
|
+
// klaviyo, mailchimp and attentive are tested without a network, and it is a
|
|
34
|
+
// declared HOOK_OPTION. The Shopify client had no seam at all, so the only way
|
|
35
|
+
// to test a hook that used it was to inject the whole SDK namespace; that
|
|
36
|
+
// injection existed to work around an import cycle, and when the cycle went
|
|
37
|
+
// the tests lost their only hold. This is the seam they should have had.
|
|
38
|
+
fetcher = fetch,
|
|
30
39
|
headers = {},
|
|
31
40
|
method = "GET",
|
|
32
41
|
query,
|
|
@@ -40,7 +49,7 @@ var request = async ({
|
|
|
40
49
|
}
|
|
41
50
|
;
|
|
42
51
|
const isForm = type === "form";
|
|
43
|
-
const response = await
|
|
52
|
+
const response = await fetcher(fullUrl.toString(), {
|
|
44
53
|
method,
|
|
45
54
|
headers: {
|
|
46
55
|
"Content-Type": isForm ? "application/x-www-form-urlencoded" : "application/json",
|
package/dist/twilio.js
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
var DEFAULT_TIMEOUT_MS = 15e3;
|
|
3
3
|
var request = async ({
|
|
4
4
|
body,
|
|
5
|
+
// THE TRANSPORT, injectable and defaulted to the real one.
|
|
6
|
+
//
|
|
7
|
+
// Every other vendor client in this package takes a `fetcher` — it is how
|
|
8
|
+
// klaviyo, mailchimp and attentive are tested without a network, and it is a
|
|
9
|
+
// declared HOOK_OPTION. The Shopify client had no seam at all, so the only way
|
|
10
|
+
// to test a hook that used it was to inject the whole SDK namespace; that
|
|
11
|
+
// injection existed to work around an import cycle, and when the cycle went
|
|
12
|
+
// the tests lost their only hold. This is the seam they should have had.
|
|
13
|
+
fetcher = fetch,
|
|
5
14
|
headers = {},
|
|
6
15
|
method = "GET",
|
|
7
16
|
query,
|
|
@@ -15,7 +24,7 @@ var request = async ({
|
|
|
15
24
|
}
|
|
16
25
|
;
|
|
17
26
|
const isForm = type === "form";
|
|
18
|
-
const response = await
|
|
27
|
+
const response = await fetcher(fullUrl.toString(), {
|
|
19
28
|
method,
|
|
20
29
|
headers: {
|
|
21
30
|
"Content-Type": isForm ? "application/x-www-form-urlencoded" : "application/json",
|