@useairfoil/producer-shopify 0.2.0 → 0.2.2

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.
Files changed (61) hide show
  1. package/LICENSE.txt +201 -0
  2. package/README.md +85 -248
  3. package/dist/index.d.mts +6993 -0
  4. package/dist/index.mjs +3 -0
  5. package/dist/main.d.mts +1 -0
  6. package/dist/main.mjs +700 -0
  7. package/dist/main.mjs.map +1 -0
  8. package/dist/manifest.d.mts +32 -0
  9. package/dist/manifest.mjs +51 -0
  10. package/dist/manifest.mjs.map +1 -0
  11. package/dist/{chunk-Bp6m_JJh.js → rolldown-runtime-D7D4PA-g.mjs} +4 -4
  12. package/dist/src-YBU_KSf5.mjs +2472 -0
  13. package/dist/src-YBU_KSf5.mjs.map +1 -0
  14. package/package.json +27 -27
  15. package/src/{auth.ts → api/auth.ts} +15 -35
  16. package/src/api/client.ts +429 -0
  17. package/src/api/graphql.ts +221 -0
  18. package/src/api/http.ts +25 -0
  19. package/src/connector.ts +266 -163
  20. package/src/create-table.ts +10 -0
  21. package/src/demo/customers.ts +412 -0
  22. package/src/demo/index.ts +31 -0
  23. package/src/demo/setup.ts +63 -0
  24. package/src/demo/shared.ts +114 -0
  25. package/src/index.ts +35 -21
  26. package/src/main.ts +14 -8
  27. package/src/manifest.ts +4 -55
  28. package/src/resources/carts/row.ts +82 -0
  29. package/src/resources/carts/webhook.ts +98 -0
  30. package/src/resources/customers/graphql.ts +175 -0
  31. package/src/resources/customers/row.ts +140 -0
  32. package/src/resources/customers/webhook.ts +30 -0
  33. package/src/resources/orders/graphql.ts +419 -0
  34. package/src/resources/orders/row.ts +306 -0
  35. package/src/resources/orders/webhook.ts +248 -0
  36. package/src/resources/products/graphql.ts +142 -0
  37. package/src/resources/products/row.ts +122 -0
  38. package/src/resources/products/webhook.ts +130 -0
  39. package/src/resources/refunds/graphql.ts +196 -0
  40. package/src/resources/refunds/row.ts +137 -0
  41. package/src/resources/refunds/webhook.ts +123 -0
  42. package/src/resources/shared.ts +300 -0
  43. package/src/sandbox.ts +3 -3
  44. package/src/start.ts +4 -6
  45. package/src/tables.ts +15 -0
  46. package/dist/index.d.ts +0 -965
  47. package/dist/index.js +0 -4
  48. package/dist/main.d.ts +0 -1
  49. package/dist/main.js +0 -49
  50. package/dist/main.js.map +0 -1
  51. package/dist/manifest-BFfx2LkS.js +0 -91
  52. package/dist/manifest-BFfx2LkS.js.map +0 -1
  53. package/dist/manifest-CPOEExQU.d.ts +0 -34
  54. package/dist/manifest.d.ts +0 -2
  55. package/dist/manifest.js +0 -3
  56. package/dist/src-DqmiLrYS.js +0 -984
  57. package/dist/src-DqmiLrYS.js.map +0 -1
  58. package/src/api.ts +0 -636
  59. package/src/schemas.ts +0 -319
  60. package/src/throttle.ts +0 -89
  61. package/src/transient-retry.ts +0 -74
package/src/api.ts DELETED
@@ -1,636 +0,0 @@
1
- import { ConnectorError, Metrics, Telemetry } from "@useairfoil/connector-kit";
2
- import {
3
- Config,
4
- Context,
5
- Duration,
6
- Effect,
7
- Layer,
8
- Option,
9
- Redacted,
10
- Schedule,
11
- Schema,
12
- Stream,
13
- } from "effect";
14
- import { HttpClient, HttpClientRequest } from "effect/unstable/http";
15
-
16
- import type { PageInfo, Product } from "./schemas";
17
-
18
- import * as ShopifyAuth from "./auth";
19
- import { manifest, type ShopifyConfig } from "./manifest";
20
- import {
21
- PageInfoSchema,
22
- ProductOptionSchema,
23
- ProductStatusSchema,
24
- ProductVariantInventoryPolicySchema,
25
- } from "./schemas";
26
- import * as Throttle from "./throttle";
27
- import * as TransientRetry from "./transient-retry";
28
-
29
- export type ShopifyProductPage = {
30
- readonly items: ReadonlyArray<Product>;
31
- readonly endCursor: string | null;
32
- readonly hasMore: boolean;
33
- };
34
-
35
- export type ShopifyApiClientService = {
36
- readonly checkConnection: Effect.Effect<void, ConnectorError>;
37
- readonly checkProductsAccess: Effect.Effect<void, ConnectorError>;
38
- readonly fetchGraphQL: <A>(options: {
39
- readonly operationName: string;
40
- readonly query: string;
41
- readonly variables?: Record<string, unknown>;
42
- readonly schema: Schema.Decoder<A>;
43
- }) => Effect.Effect<A, ConnectorError>;
44
- readonly fetchProducts: (options: {
45
- readonly first: number;
46
- readonly after?: string;
47
- }) => Effect.Effect<ShopifyProductPage, ConnectorError>;
48
- /**
49
- * Fetches the complete product with every variant page.
50
- * Webhook payloads include only the first 100 variants.
51
- */
52
- readonly fetchProductById: (id: string) => Effect.Effect<Product, ConnectorError>;
53
- };
54
-
55
- export class ShopifyApiClient extends Context.Service<ShopifyApiClient, ShopifyApiClientService>()(
56
- "@useairfoil/producer-shopify/ShopifyApiClient",
57
- ) {}
58
-
59
- const ProductsQuery = `#graphql
60
- query AirfoilProducts($first: Int!, $after: String) {
61
- products(first: $first, after: $after, sortKey: UPDATED_AT, reverse: true) {
62
- nodes {
63
- id
64
- legacyResourceId
65
- title
66
- handle
67
- descriptionHtml
68
- productType
69
- vendor
70
- status
71
- tags
72
- createdAt
73
- updatedAt
74
- publishedAt
75
- templateSuffix
76
- featuredMedia {
77
- ... on MediaImage {
78
- image {
79
- url
80
- altText
81
- }
82
- }
83
- }
84
- options(first: 100) {
85
- id
86
- name
87
- position
88
- values
89
- }
90
- variants(first: 25) {
91
- nodes {
92
- id
93
- legacyResourceId
94
- title
95
- sku
96
- barcode
97
- price
98
- compareAtPrice
99
- inventoryPolicy
100
- taxable
101
- createdAt
102
- updatedAt
103
- }
104
- pageInfo {
105
- hasNextPage
106
- endCursor
107
- }
108
- }
109
- }
110
- pageInfo {
111
- hasNextPage
112
- endCursor
113
- }
114
- }
115
- }
116
- `;
117
-
118
- const ProductByIdQuery = `#graphql
119
- query AirfoilProductById($id: ID!) {
120
- product(id: $id) {
121
- id
122
- legacyResourceId
123
- title
124
- handle
125
- descriptionHtml
126
- productType
127
- vendor
128
- status
129
- tags
130
- createdAt
131
- updatedAt
132
- publishedAt
133
- templateSuffix
134
- featuredMedia {
135
- ... on MediaImage {
136
- image {
137
- url
138
- altText
139
- }
140
- }
141
- }
142
- options(first: 100) {
143
- id
144
- name
145
- position
146
- values
147
- }
148
- variants(first: 25) {
149
- nodes {
150
- id
151
- legacyResourceId
152
- title
153
- sku
154
- barcode
155
- price
156
- compareAtPrice
157
- inventoryPolicy
158
- taxable
159
- createdAt
160
- updatedAt
161
- }
162
- pageInfo {
163
- hasNextPage
164
- endCursor
165
- }
166
- }
167
- }
168
- }
169
- `;
170
-
171
- const ProductVariantsQuery = `#graphql
172
- query AirfoilProductVariants($id: ID!, $first: Int!, $after: String) {
173
- product(id: $id) {
174
- variants(first: $first, after: $after) {
175
- nodes {
176
- id
177
- legacyResourceId
178
- title
179
- sku
180
- barcode
181
- price
182
- compareAtPrice
183
- inventoryPolicy
184
- taxable
185
- createdAt
186
- updatedAt
187
- }
188
- pageInfo {
189
- hasNextPage
190
- endCursor
191
- }
192
- }
193
- }
194
- }
195
- `;
196
-
197
- const ProductsAccessQuery = `#graphql
198
- query AirfoilProductsAccess {
199
- products(first: 1) {
200
- nodes {
201
- id
202
- }
203
- }
204
- }
205
- `;
206
-
207
- const ShopIdentityQuery = `#graphql
208
- query AirfoilShopIdentity {
209
- shop {
210
- id
211
- }
212
- }
213
- `;
214
-
215
- const ShopIdentitySchema = Schema.Struct({
216
- shop: Schema.Struct({ id: Schema.String }),
217
- });
218
-
219
- const ProductsAccessSchema = Schema.Struct({
220
- products: Schema.Struct({
221
- nodes: Schema.Array(Schema.Struct({ id: Schema.String })),
222
- }),
223
- });
224
-
225
- const LegacyResourceIdSchema = Schema.Union([Schema.String, Schema.Number]);
226
-
227
- const GraphQLProductVariantNodeSchema = Schema.Struct({
228
- id: Schema.String,
229
- legacyResourceId: LegacyResourceIdSchema,
230
- title: Schema.String,
231
- sku: Schema.NullOr(Schema.String),
232
- barcode: Schema.NullOr(Schema.String),
233
- price: Schema.String,
234
- compareAtPrice: Schema.NullOr(Schema.String),
235
- inventoryPolicy: ProductVariantInventoryPolicySchema,
236
- taxable: Schema.Boolean,
237
- createdAt: Schema.String,
238
- updatedAt: Schema.String,
239
- });
240
-
241
- const GraphQLProductNodeSchema = Schema.Struct({
242
- id: Schema.String,
243
- legacyResourceId: LegacyResourceIdSchema,
244
- title: Schema.String,
245
- handle: Schema.String,
246
- descriptionHtml: Schema.String,
247
- productType: Schema.String,
248
- vendor: Schema.String,
249
- status: ProductStatusSchema,
250
- tags: Schema.Array(Schema.String),
251
- createdAt: Schema.String,
252
- updatedAt: Schema.String,
253
- publishedAt: Schema.NullOr(Schema.String),
254
- templateSuffix: Schema.NullOr(Schema.String),
255
- featuredMedia: Schema.NullOr(Schema.Any),
256
- options: Schema.Array(ProductOptionSchema),
257
- variants: Schema.Struct({
258
- nodes: Schema.Array(GraphQLProductVariantNodeSchema),
259
- pageInfo: PageInfoSchema,
260
- }),
261
- });
262
-
263
- const GraphQLProductsDataSchema = Schema.Struct({
264
- products: Schema.Struct({
265
- nodes: Schema.Array(GraphQLProductNodeSchema),
266
- pageInfo: PageInfoSchema,
267
- }),
268
- });
269
-
270
- const GraphQLProductByIdDataSchema = Schema.Struct({
271
- product: Schema.NullOr(GraphQLProductNodeSchema),
272
- });
273
-
274
- const GraphQLProductVariantsDataSchema = Schema.Struct({
275
- product: Schema.NullOr(
276
- Schema.Struct({
277
- variants: Schema.Struct({
278
- nodes: Schema.Array(GraphQLProductVariantNodeSchema),
279
- pageInfo: PageInfoSchema,
280
- }),
281
- }),
282
- ),
283
- });
284
-
285
- type GraphQLProductsData = Schema.Schema.Type<typeof GraphQLProductsDataSchema>;
286
- type GraphQLProductNode = Schema.Schema.Type<typeof GraphQLProductNodeSchema>;
287
- type GraphQLProductVariantNode = Schema.Schema.Type<typeof GraphQLProductVariantNodeSchema>;
288
-
289
- const normalizeProductNode = (
290
- node: GraphQLProductNode,
291
- variants: ReadonlyArray<GraphQLProductVariantNode>,
292
- ): Product => ({
293
- id: node.id,
294
- legacyResourceId: String(node.legacyResourceId),
295
- title: node.title,
296
- handle: node.handle,
297
- descriptionHtml: node.descriptionHtml,
298
- productType: node.productType,
299
- vendor: node.vendor,
300
- status: node.status,
301
- tags: node.tags,
302
- createdAt: node.createdAt,
303
- updatedAt: node.updatedAt,
304
- publishedAt: node.publishedAt,
305
- templateSuffix: node.templateSuffix,
306
- featuredMedia: node.featuredMedia,
307
- options: node.options,
308
- variants: variants.map((variant) => ({
309
- ...variant,
310
- legacyResourceId: String(variant.legacyResourceId),
311
- })),
312
- });
313
-
314
- const graphqlEndpoint = (config: ShopifyConfig): string => {
315
- const shopDomain = config.shopDomain.replace(/^https?:\/\//i, "").replace(/\/+$/g, "");
316
- return `https://${shopDomain}/admin/api/${config.apiVersion}/graphql.json`;
317
- };
318
-
319
- const hasGraphqlErrors = (body: unknown): boolean => {
320
- if (typeof body !== "object" || body === null) {
321
- return false;
322
- }
323
- const errors = (body as { readonly errors?: unknown }).errors;
324
- return Array.isArray(errors) && errors.length > 0;
325
- };
326
-
327
- const summarizeBody = (body: unknown): string => {
328
- try {
329
- return JSON.stringify(body);
330
- } catch {
331
- return String(body);
332
- }
333
- };
334
-
335
- export const make = Effect.fnUntraced(function* (config: ShopifyConfig) {
336
- const auth = yield* ShopifyAuth.ShopifyAuth;
337
- const retryBaseDelay = Duration.millis(config.retryBaseDelayMs);
338
- const graphqlRetryBaseDelay = Duration.millis(config.graphqlRetryBaseDelayMs);
339
- const requestTimeout = Duration.seconds(config.requestTimeoutSeconds);
340
- const transportRetrySchedule = Schedule.exponential(retryBaseDelay).pipe(
341
- Schedule.jittered,
342
- Schedule.upTo({ times: config.transportMaxRetries }),
343
- Schedule.tap(() => Metrics.recordApiRetry({ connector: manifest.name, reason: "transport" })),
344
- );
345
- const client = (yield* HttpClient.HttpClient).pipe(
346
- HttpClient.mapRequestEffect((request) =>
347
- auth.get.pipe(
348
- Effect.map((token) =>
349
- HttpClientRequest.setHeader(request, "X-Shopify-Access-Token", Redacted.value(token)),
350
- ),
351
- ),
352
- ),
353
- HttpClient.mapRequest(HttpClientRequest.acceptJson),
354
- (c) =>
355
- TransientRetry.withTransientRetry(c, {
356
- maxRetries: config.responseMaxRetries,
357
- baseDelay: retryBaseDelay,
358
- retryAfterFallback: Duration.seconds(config.retryAfterFallbackSeconds),
359
- }),
360
- // HTTP responses are handled above, so this retries transport errors only.
361
- HttpClient.retryTransient({
362
- retryOn: "errors-only",
363
- schedule: transportRetrySchedule,
364
- }),
365
- );
366
- const endpoint = graphqlEndpoint(config);
367
-
368
- const fetchGraphQL = <A>(options: {
369
- readonly operationName: string;
370
- readonly query: string;
371
- readonly variables?: Record<string, unknown>;
372
- readonly schema: Schema.Decoder<A>;
373
- }): Effect.Effect<A, ConnectorError> => {
374
- const attempt = (remainingRetries: number): Effect.Effect<A, ConnectorError> =>
375
- Effect.gen(function* () {
376
- const request = yield* HttpClientRequest.post(endpoint).pipe(
377
- HttpClientRequest.bodyJson({
378
- query: options.query,
379
- variables: options.variables ?? {},
380
- }),
381
- Effect.mapError(
382
- (cause) =>
383
- new ConnectorError({ message: "Failed to encode Shopify GraphQL request", cause }),
384
- ),
385
- );
386
-
387
- const { body, status } = yield* Effect.scoped(
388
- client.execute(request).pipe(
389
- Effect.tapError((error) => Telemetry.annotateError("api_http", error)),
390
- Effect.mapError((error) => {
391
- if (error._tag === "ShopifyAuthError") {
392
- return new ConnectorError({ message: error.message, cause: error });
393
- }
394
- return new ConnectorError({
395
- message: "Shopify GraphQL request failed",
396
- cause: error,
397
- });
398
- }),
399
- Effect.flatMap((response) =>
400
- response.json.pipe(
401
- Effect.tapError((error) => Telemetry.annotateError("api_json", error)),
402
- Effect.mapError(
403
- (error) =>
404
- new ConnectorError({
405
- message: "Shopify GraphQL returned invalid JSON",
406
- cause: error,
407
- }),
408
- ),
409
- Effect.map((body) => ({
410
- body,
411
- status: response.status,
412
- })),
413
- ),
414
- ),
415
- ),
416
- );
417
-
418
- if (status < 200 || status >= 300) {
419
- const error = { status, body, operationName: options.operationName };
420
- yield* Effect.logWarning("Shopify GraphQL returned non-2xx status").pipe(
421
- Effect.annotateLogs({
422
- operationName: options.operationName,
423
- status,
424
- body: summarizeBody(body),
425
- }),
426
- );
427
- yield* Telemetry.annotateError("api_status", error);
428
- return yield* Effect.fail(
429
- new ConnectorError({
430
- message: "Shopify GraphQL returned non-2xx status",
431
- cause: error,
432
- }),
433
- );
434
- }
435
-
436
- if (hasGraphqlErrors(body)) {
437
- const retryableCode = Throttle.retryableErrorCode(body);
438
- if (retryableCode !== undefined && remainingRetries > 0) {
439
- const attemptIndex = config.graphqlMaxRetries - remainingRetries;
440
- const wait = yield* Throttle.retryDelay(retryableCode, body, attemptIndex, {
441
- baseDelay: graphqlRetryBaseDelay,
442
- });
443
- yield* Effect.logWarning(
444
- "Shopify GraphQL request returned a retryable error, retrying",
445
- ).pipe(
446
- Effect.annotateLogs({
447
- operationName: options.operationName,
448
- code: retryableCode,
449
- remainingRetries,
450
- waitMillis: Duration.toMillis(wait),
451
- }),
452
- );
453
- yield* Metrics.recordApiRetry({
454
- connector: manifest.name,
455
- reason: retryableCode === "THROTTLED" ? "rate_limit" : "server_error",
456
- });
457
- yield* Effect.sleep(wait);
458
- return yield* attempt(remainingRetries - 1);
459
- }
460
-
461
- yield* Effect.logWarning("Shopify GraphQL returned errors").pipe(
462
- Effect.annotateLogs({
463
- operationName: options.operationName,
464
- body: summarizeBody(body),
465
- }),
466
- );
467
- yield* Telemetry.annotateError("api_graphql", body);
468
- return yield* Effect.fail(
469
- new ConnectorError({ message: "Shopify GraphQL returned errors", cause: body }),
470
- );
471
- }
472
-
473
- const data =
474
- typeof body === "object" && body !== null ? (body as { data?: unknown }).data : undefined;
475
- return yield* Schema.decodeUnknownEffect(options.schema)(data).pipe(
476
- Effect.tapError((error) => Telemetry.annotateError("api_decode", error)),
477
- Effect.mapError(
478
- (error) =>
479
- new ConnectorError({
480
- message: "Shopify GraphQL response schema decode failed",
481
- cause: error,
482
- }),
483
- ),
484
- );
485
- }).pipe(
486
- Effect.withSpan(Telemetry.SpanName.apiFetch, {
487
- kind: "client",
488
- attributes: { [Telemetry.Attr.apiPath]: `graphql:${options.operationName}` },
489
- }),
490
- );
491
-
492
- return attempt(config.graphqlMaxRetries).pipe(
493
- Effect.timeout(requestTimeout),
494
- Effect.mapError((error) =>
495
- error instanceof ConnectorError
496
- ? error
497
- : new ConnectorError({ message: "Shopify GraphQL request timed out", cause: error }),
498
- ),
499
- );
500
- };
501
-
502
- const nextPageCursor = (
503
- pageInfo: PageInfo,
504
- ): Effect.Effect<Option.Option<string>, ConnectorError> => {
505
- if (!pageInfo.hasNextPage) {
506
- return Effect.succeed(Option.none());
507
- }
508
- if (pageInfo.endCursor === null) {
509
- return Effect.fail(
510
- new ConnectorError({
511
- message: "Shopify GraphQL pageInfo.endCursor is required when hasNextPage is true",
512
- }),
513
- );
514
- }
515
- return Effect.succeed(Option.some(pageInfo.endCursor));
516
- };
517
-
518
- const fetchRemainingProductVariants = (
519
- productId: string,
520
- initialCursor: string,
521
- ): Effect.Effect<ReadonlyArray<GraphQLProductVariantNode>, ConnectorError> =>
522
- Stream.paginate(initialCursor, (after) =>
523
- fetchGraphQL({
524
- operationName: "AirfoilProductVariants",
525
- query: ProductVariantsQuery,
526
- variables: { id: productId, first: 100, after },
527
- schema: GraphQLProductVariantsDataSchema,
528
- }).pipe(
529
- Effect.flatMap((data) => {
530
- if (data.product === null) {
531
- return Effect.fail(
532
- new ConnectorError({
533
- message: "Shopify product disappeared while fetching variants",
534
- }),
535
- );
536
- }
537
- const variants = data.product.variants;
538
- return nextPageCursor(variants.pageInfo).pipe(
539
- Effect.map((cursor) => [variants.nodes, cursor] as const),
540
- );
541
- }),
542
- ),
543
- ).pipe(Stream.runCollect);
544
-
545
- const loadProductVariants = (
546
- node: GraphQLProductNode,
547
- ): Effect.Effect<ReadonlyArray<GraphQLProductVariantNode>, ConnectorError> =>
548
- nextPageCursor(node.variants.pageInfo).pipe(
549
- Effect.flatMap(
550
- Option.match({
551
- onNone: () => Effect.succeed(node.variants.nodes),
552
- onSome: (cursor) =>
553
- fetchRemainingProductVariants(node.id, cursor).pipe(
554
- Effect.map((remaining) => [...node.variants.nodes, ...remaining]),
555
- ),
556
- }),
557
- ),
558
- );
559
-
560
- const fetchProducts = (options: {
561
- readonly first: number;
562
- readonly after?: string;
563
- }): Effect.Effect<ShopifyProductPage, ConnectorError> =>
564
- fetchGraphQL({
565
- operationName: "AirfoilProducts",
566
- query: ProductsQuery,
567
- variables: { first: options.first, after: options.after ?? null },
568
- schema: GraphQLProductsDataSchema,
569
- }).pipe(
570
- Effect.flatMap((data: GraphQLProductsData) =>
571
- nextPageCursor(data.products.pageInfo).pipe(
572
- Effect.andThen(
573
- Effect.forEach(data.products.nodes, (node) =>
574
- loadProductVariants(node).pipe(
575
- Effect.map((variants) => normalizeProductNode(node, variants)),
576
- ),
577
- ),
578
- ),
579
- Effect.map((items) => ({
580
- items,
581
- endCursor: data.products.pageInfo.endCursor,
582
- hasMore: data.products.pageInfo.hasNextPage,
583
- })),
584
- ),
585
- ),
586
- );
587
-
588
- const fetchProductById = (id: string): Effect.Effect<Product, ConnectorError> =>
589
- fetchGraphQL({
590
- operationName: "AirfoilProductById",
591
- query: ProductByIdQuery,
592
- variables: { id },
593
- schema: GraphQLProductByIdDataSchema,
594
- }).pipe(
595
- Effect.flatMap((data) => {
596
- const node = data.product;
597
- if (node === null) {
598
- return Effect.fail(
599
- new ConnectorError({
600
- message: "Shopify product not found while refreshing from webhook",
601
- }),
602
- );
603
- }
604
- return loadProductVariants(node).pipe(
605
- Effect.map((variants) => normalizeProductNode(node, variants)),
606
- );
607
- }),
608
- );
609
-
610
- const checkConnection = fetchGraphQL({
611
- operationName: "AirfoilShopIdentity",
612
- query: ShopIdentityQuery,
613
- schema: ShopIdentitySchema,
614
- }).pipe(Effect.asVoid);
615
-
616
- const checkProductsAccess = fetchGraphQL({
617
- operationName: "AirfoilProductsAccess",
618
- query: ProductsAccessQuery,
619
- schema: ProductsAccessSchema,
620
- }).pipe(Effect.asVoid);
621
-
622
- return { checkConnection, checkProductsAccess, fetchGraphQL, fetchProducts, fetchProductById };
623
- });
624
-
625
- export const layer = (
626
- config: ShopifyConfig,
627
- ): Layer.Layer<ShopifyApiClient, ConnectorError, HttpClient.HttpClient | ShopifyAuth.ShopifyAuth> =>
628
- Layer.effect(ShopifyApiClient)(make(config));
629
-
630
- export const layerConfig = (
631
- config: Config.Wrap<ShopifyConfig>,
632
- ): Layer.Layer<
633
- ShopifyApiClient,
634
- ConnectorError | Config.ConfigError,
635
- HttpClient.HttpClient | ShopifyAuth.ShopifyAuth
636
- > => Layer.effect(ShopifyApiClient)(Config.unwrap(config).pipe(Effect.flatMap(make)));