@keystrokehq/shopify 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +178 -0
- package/dist/_official/index.d.mts +3 -0
- package/dist/_official/index.mjs +3 -0
- package/dist/_runtime/index.d.mts +56 -0
- package/dist/_runtime/index.mjs +3 -0
- package/dist/articles.d.mts +174 -0
- package/dist/articles.mjs +252 -0
- package/dist/blogs.d.mts +113 -0
- package/dist/blogs.mjs +174 -0
- package/dist/client.d.mts +22 -0
- package/dist/client.mjs +90 -0
- package/dist/collections.d.mts +140 -0
- package/dist/collections.mjs +209 -0
- package/dist/connection.d.mts +2 -0
- package/dist/connection.mjs +3 -0
- package/dist/customers.d.mts +161 -0
- package/dist/customers.mjs +225 -0
- package/dist/events.d.mts +181 -0
- package/dist/events.mjs +67 -0
- package/dist/factory-DC-gY8L0.mjs +8 -0
- package/dist/fulfillments.d.mts +99 -0
- package/dist/fulfillments.mjs +167 -0
- package/dist/index.d.mts +1 -0
- package/dist/index.mjs +1 -0
- package/dist/integration-BN3pjCz4.mjs +204 -0
- package/dist/integration-BwDBsGX-.d.mts +71 -0
- package/dist/inventory.d.mts +103 -0
- package/dist/inventory.mjs +182 -0
- package/dist/locations.d.mts +47 -0
- package/dist/locations.mjs +81 -0
- package/dist/messaging.d.mts +1 -0
- package/dist/messaging.mjs +1 -0
- package/dist/operation-helpers-CKEDIx0o.mjs +21 -0
- package/dist/orders.d.mts +229 -0
- package/dist/orders.mjs +271 -0
- package/dist/products.d.mts +155 -0
- package/dist/products.mjs +171 -0
- package/dist/provider-app-DHxVZI6q.d.mts +37 -0
- package/dist/schemas.d.mts +233 -0
- package/dist/schemas.mjs +164 -0
- package/dist/shop.d.mts +20 -0
- package/dist/shop.mjs +31 -0
- package/dist/triggers.d.mts +23 -0
- package/dist/triggers.mjs +138 -0
- package/dist/webhook-management-BmIxO0vr.mjs +150 -0
- package/package.json +130 -0
package/README.md
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# @keystrokehq/shopify
|
|
2
|
+
|
|
3
|
+
Shopify Admin API integration for Keystroke workflows.
|
|
4
|
+
|
|
5
|
+
The package root is intentionally non-canonical. Import from explicit subpaths.
|
|
6
|
+
|
|
7
|
+
## Public Subpaths
|
|
8
|
+
|
|
9
|
+
- `@keystrokehq/shopify/connection`
|
|
10
|
+
- `@keystrokehq/shopify/client`
|
|
11
|
+
- `@keystrokehq/shopify/schemas`
|
|
12
|
+
- `@keystrokehq/shopify/events`
|
|
13
|
+
- `@keystrokehq/shopify/shop`
|
|
14
|
+
- `@keystrokehq/shopify/products`
|
|
15
|
+
- `@keystrokehq/shopify/collections`
|
|
16
|
+
- `@keystrokehq/shopify/orders`
|
|
17
|
+
- `@keystrokehq/shopify/customers`
|
|
18
|
+
- `@keystrokehq/shopify/blogs`
|
|
19
|
+
- `@keystrokehq/shopify/articles`
|
|
20
|
+
- `@keystrokehq/shopify/locations`
|
|
21
|
+
- `@keystrokehq/shopify/inventory`
|
|
22
|
+
- `@keystrokehq/shopify/fulfillments`
|
|
23
|
+
- `@keystrokehq/shopify/triggers`
|
|
24
|
+
|
|
25
|
+
## Read Store Data
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { Workflow } from '@keystrokehq/core';
|
|
29
|
+
import { z } from 'zod';
|
|
30
|
+
import { shopify } from '@keystrokehq/shopify/connection';
|
|
31
|
+
import { getShop } from '@keystrokehq/shopify/shop';
|
|
32
|
+
import { listProducts } from '@keystrokehq/shopify/products';
|
|
33
|
+
|
|
34
|
+
export const syncCatalog = new Workflow({
|
|
35
|
+
id: 'sync-shopify-catalog',
|
|
36
|
+
name: 'Sync Shopify Catalog',
|
|
37
|
+
input: z.object({}),
|
|
38
|
+
output: z.object({
|
|
39
|
+
credentialSetId: z.string(),
|
|
40
|
+
storeName: z.string(),
|
|
41
|
+
productCount: z.number(),
|
|
42
|
+
}),
|
|
43
|
+
run: async () => {
|
|
44
|
+
const store = await getShop.run({});
|
|
45
|
+
const products = await listProducts.run({
|
|
46
|
+
first: 25,
|
|
47
|
+
query: 'status:active',
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
credentialSetId: shopify.resolvedCredentialSetId,
|
|
52
|
+
storeName: store.name,
|
|
53
|
+
productCount: products.nodes.length,
|
|
54
|
+
};
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Create Orders And Adjust Inventory
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import { Workflow } from '@keystrokehq/core';
|
|
63
|
+
import { z } from 'zod';
|
|
64
|
+
import { createOrder } from '@keystrokehq/shopify/orders';
|
|
65
|
+
import { adjustInventoryQuantities } from '@keystrokehq/shopify/inventory';
|
|
66
|
+
|
|
67
|
+
export const backofficeWriteFlow = new Workflow({
|
|
68
|
+
id: 'shopify-backoffice-write-flow',
|
|
69
|
+
name: 'Shopify Backoffice Write Flow',
|
|
70
|
+
input: z.object({ variantId: z.string(), inventoryItemId: z.string(), locationId: z.string() }),
|
|
71
|
+
output: z.object({ queued: z.boolean() }),
|
|
72
|
+
run: async (input) => {
|
|
73
|
+
await createOrder.run({
|
|
74
|
+
currency: 'USD',
|
|
75
|
+
email: 'buyer@example.com',
|
|
76
|
+
lineItems: [
|
|
77
|
+
{
|
|
78
|
+
variantId: input.variantId,
|
|
79
|
+
quantity: 1,
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
await adjustInventoryQuantities.run({
|
|
85
|
+
reason: 'correction',
|
|
86
|
+
referenceDocumentUri: 'gid://keystroke/inventory-adjustment/docs-example',
|
|
87
|
+
changes: [
|
|
88
|
+
{
|
|
89
|
+
inventoryItemId: input.inventoryItemId,
|
|
90
|
+
locationId: input.locationId,
|
|
91
|
+
delta: -1,
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
return { queued: true };
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Bind Webhook Triggers
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
import { Workflow } from '@keystrokehq/core';
|
|
105
|
+
import { z } from 'zod';
|
|
106
|
+
import { webhooks } from '@keystrokehq/shopify/triggers';
|
|
107
|
+
|
|
108
|
+
const orderCreated = webhooks.orderCreated({
|
|
109
|
+
name: 'Shopify Order Created',
|
|
110
|
+
transform: (event) => ({
|
|
111
|
+
orderId: event.resourceId ?? event.payload.admin_graphql_api_id ?? '',
|
|
112
|
+
topic: event.topic,
|
|
113
|
+
}),
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
export const onNewShopifyOrder = new Workflow({
|
|
117
|
+
id: 'on-new-shopify-order',
|
|
118
|
+
name: 'On New Shopify Order',
|
|
119
|
+
input: z.object({
|
|
120
|
+
orderId: z.string(),
|
|
121
|
+
topic: z.literal('orders/create'),
|
|
122
|
+
}),
|
|
123
|
+
output: z.object({ ok: z.boolean() }),
|
|
124
|
+
triggers: [orderCreated],
|
|
125
|
+
run: async () => ({ ok: true }),
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Notes
|
|
130
|
+
|
|
131
|
+
- Mutating operations are marked with `needsApproval: true`.
|
|
132
|
+
- Shopify webhook helpers are direct-binding surfaces under `@keystrokehq/shopify/triggers`.
|
|
133
|
+
- Hidden `_official` and `_runtime` exports are for repo internals, not workflow authoring.
|
|
134
|
+
|
|
135
|
+
## Live Test Runbook
|
|
136
|
+
|
|
137
|
+
The Shopify package now has dedicated `test:int` coverage for every exported operation plus live webhook subscription management:
|
|
138
|
+
|
|
139
|
+
- `shop.int.test.ts`
|
|
140
|
+
- `products.int.test.ts`
|
|
141
|
+
- `collections.int.test.ts`
|
|
142
|
+
- `customers.int.test.ts`
|
|
143
|
+
- `blogs.int.test.ts`
|
|
144
|
+
- `articles.int.test.ts`
|
|
145
|
+
- `orders.int.test.ts`
|
|
146
|
+
- `inventory.int.test.ts`
|
|
147
|
+
- `locations.int.test.ts`
|
|
148
|
+
- `fulfillments.int.test.ts`
|
|
149
|
+
- `webhook-management.int.test.ts`
|
|
150
|
+
|
|
151
|
+
Set up `.env.test` with at least:
|
|
152
|
+
|
|
153
|
+
- `RUN_LIVE_INT=true`
|
|
154
|
+
- `SHOPIFY_LIVE_STORE_DOMAIN`
|
|
155
|
+
- `SHOPIFY_LIVE_ACCESS_TOKEN`
|
|
156
|
+
- `SHOPIFY_LIVE_REFRESH_TOKEN` for control-plane refresh coverage
|
|
157
|
+
- `PUBLIC_SERVER_URL` for webhook subscription coverage
|
|
158
|
+
- `KEYSTROKE_PLATFORM_SHOPIFY_APP_ID`
|
|
159
|
+
- `KEYSTROKE_PLATFORM_SHOPIFY_CLIENT_ID`
|
|
160
|
+
- `KEYSTROKE_PLATFORM_SHOPIFY_CLIENT_SECRET`
|
|
161
|
+
- `KEYSTROKE_PLATFORM_SHOPIFY_WEBHOOK_SECRET`
|
|
162
|
+
|
|
163
|
+
Recommended execution flow:
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
pnpm test:int:wiring
|
|
167
|
+
pnpm --filter @keystrokehq/shopify test:int
|
|
168
|
+
pnpm --filter @keystroke/server test:int
|
|
169
|
+
pnpm --filter @keystroke/control-plane test:int
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Operational guidance:
|
|
173
|
+
|
|
174
|
+
- Use a dedicated disposable dev store. `orders`, `inventory`, `fulfillments`, and webhook tests are destructive by design.
|
|
175
|
+
- Keep `PUBLIC_SERVER_URL` pointed at a real public tunnel or staging URL when exercising webhook subscription or OAuth callback flows.
|
|
176
|
+
- The package test helpers try to clean up created products, collections, blogs, articles, and order-free customers automatically.
|
|
177
|
+
- Orders with fulfillment history and customers with placed orders should be treated as disposable-store artifacts rather than shared-environment fixtures.
|
|
178
|
+
- Shopify webhook subscription management requires `read_webhooks` and `write_webhooks` scopes in addition to the Admin API scopes used by the operation surface.
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { i as shopifyOfficialIntegration, n as shopify, r as shopifyBundle, t as ShopifyCredentials } from "../integration-BwDBsGX-.mjs";
|
|
2
|
+
import { n as shopifyAppCredentialSet, r as shopifyPlatformProviderSeed, t as ShopifyAppCredentials } from "../provider-app-DHxVZI6q.mjs";
|
|
3
|
+
export { type ShopifyAppCredentials, type ShopifyCredentials, shopify, shopifyAppCredentialSet, shopifyBundle, shopifyOfficialIntegration, shopifyPlatformProviderSeed };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { a as shopifyAppCredentialSet, n as shopifyBundle, o as shopifyPlatformProviderSeed, r as shopifyOfficialIntegration, t as shopify } from "../integration-BN3pjCz4.mjs";
|
|
2
|
+
|
|
3
|
+
export { shopify, shopifyAppCredentialSet, shopifyBundle, shopifyOfficialIntegration, shopifyPlatformProviderSeed };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { t as ShopifyCredentials } from "../integration-BwDBsGX-.mjs";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
//#region src/webhook-management.d.ts
|
|
5
|
+
declare const SHOPIFY_WEBHOOK_RUNTIME_DEFINITIONS: readonly [{
|
|
6
|
+
readonly eventTopic: "products/create";
|
|
7
|
+
readonly subscriptionTopic: "PRODUCTS_CREATE";
|
|
8
|
+
readonly path: "/shopify/products/create";
|
|
9
|
+
}, {
|
|
10
|
+
readonly eventTopic: "products/update";
|
|
11
|
+
readonly subscriptionTopic: "PRODUCTS_UPDATE";
|
|
12
|
+
readonly path: "/shopify/products/update";
|
|
13
|
+
}, {
|
|
14
|
+
readonly eventTopic: "products/delete";
|
|
15
|
+
readonly subscriptionTopic: "PRODUCTS_DELETE";
|
|
16
|
+
readonly path: "/shopify/products/delete";
|
|
17
|
+
}, {
|
|
18
|
+
readonly eventTopic: "orders/create";
|
|
19
|
+
readonly subscriptionTopic: "ORDERS_CREATE";
|
|
20
|
+
readonly path: "/shopify/orders/create";
|
|
21
|
+
}, {
|
|
22
|
+
readonly eventTopic: "orders/updated";
|
|
23
|
+
readonly subscriptionTopic: "ORDERS_UPDATED";
|
|
24
|
+
readonly path: "/shopify/orders/updated";
|
|
25
|
+
}, {
|
|
26
|
+
readonly eventTopic: "orders/cancelled";
|
|
27
|
+
readonly subscriptionTopic: "ORDERS_CANCELLED";
|
|
28
|
+
readonly path: "/shopify/orders/cancelled";
|
|
29
|
+
}, {
|
|
30
|
+
readonly eventTopic: "orders/fulfilled";
|
|
31
|
+
readonly subscriptionTopic: "ORDERS_FULFILLED";
|
|
32
|
+
readonly path: "/shopify/orders/fulfilled";
|
|
33
|
+
}];
|
|
34
|
+
declare const webhookSubscriptionSchema: z.ZodObject<{
|
|
35
|
+
id: z.ZodString;
|
|
36
|
+
topic: z.ZodEnum<{
|
|
37
|
+
PRODUCTS_CREATE: "PRODUCTS_CREATE";
|
|
38
|
+
PRODUCTS_UPDATE: "PRODUCTS_UPDATE";
|
|
39
|
+
PRODUCTS_DELETE: "PRODUCTS_DELETE";
|
|
40
|
+
ORDERS_CREATE: "ORDERS_CREATE";
|
|
41
|
+
ORDERS_UPDATED: "ORDERS_UPDATED";
|
|
42
|
+
ORDERS_CANCELLED: "ORDERS_CANCELLED";
|
|
43
|
+
ORDERS_FULFILLED: "ORDERS_FULFILLED";
|
|
44
|
+
}>;
|
|
45
|
+
uri: z.ZodString;
|
|
46
|
+
}, z.core.$strip>;
|
|
47
|
+
interface EnsureShopifyWebhookSubscriptionsResult {
|
|
48
|
+
readonly kept: readonly string[];
|
|
49
|
+
readonly created: readonly string[];
|
|
50
|
+
readonly deleted: readonly string[];
|
|
51
|
+
}
|
|
52
|
+
declare function buildShopifyWebhookCallbackUri(baseUrl: string, path: string): string;
|
|
53
|
+
declare function listShopifyWebhookSubscriptions(credentials: ShopifyCredentials): Promise<readonly z.infer<typeof webhookSubscriptionSchema>[]>;
|
|
54
|
+
declare function ensureShopifyWebhookSubscriptions(credentials: ShopifyCredentials, callbackBaseUrl: string): Promise<EnsureShopifyWebhookSubscriptionsResult>;
|
|
55
|
+
//#endregion
|
|
56
|
+
export { SHOPIFY_WEBHOOK_RUNTIME_DEFINITIONS, buildShopifyWebhookCallbackUri, ensureShopifyWebhookSubscriptions, listShopifyWebhookSubscriptions };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { i as listShopifyWebhookSubscriptions, n as buildShopifyWebhookCallbackUri, r as ensureShopifyWebhookSubscriptions, t as SHOPIFY_WEBHOOK_RUNTIME_DEFINITIONS } from "../webhook-management-BmIxO0vr.mjs";
|
|
2
|
+
|
|
3
|
+
export { SHOPIFY_WEBHOOK_RUNTIME_DEFINITIONS, buildShopifyWebhookCallbackUri, ensureShopifyWebhookSubscriptions, listShopifyWebhookSubscriptions };
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import * as _keystrokehq_core0 from "@keystrokehq/core";
|
|
3
|
+
import * as _keystrokehq_core_credential_set0 from "@keystrokehq/core/credential-set";
|
|
4
|
+
|
|
5
|
+
//#region src/articles.d.ts
|
|
6
|
+
declare const listArticles: _keystrokehq_core0.Operation<z.ZodObject<{
|
|
7
|
+
first: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
8
|
+
after: z.ZodOptional<z.ZodString>;
|
|
9
|
+
query: z.ZodOptional<z.ZodString>;
|
|
10
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
11
|
+
nodes: z.ZodArray<z.ZodObject<{
|
|
12
|
+
id: z.ZodString;
|
|
13
|
+
title: z.ZodString;
|
|
14
|
+
handle: z.ZodString;
|
|
15
|
+
blog: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
16
|
+
id: z.ZodString;
|
|
17
|
+
title: z.ZodString;
|
|
18
|
+
handle: z.ZodString;
|
|
19
|
+
createdAt: z.ZodOptional<z.ZodISODateTime>;
|
|
20
|
+
updatedAt: z.ZodOptional<z.ZodISODateTime>;
|
|
21
|
+
commentPolicy: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
22
|
+
templateSuffix: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
23
|
+
}, z.core.$strip>>>;
|
|
24
|
+
author: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
25
|
+
body: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
26
|
+
summary: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
27
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
28
|
+
image: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
29
|
+
url: z.ZodURL;
|
|
30
|
+
altText: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
31
|
+
}, z.core.$strip>>>;
|
|
32
|
+
publishedAt: z.ZodOptional<z.ZodNullable<z.ZodISODateTime>>;
|
|
33
|
+
updatedAt: z.ZodOptional<z.ZodISODateTime>;
|
|
34
|
+
}, z.core.$strip>>;
|
|
35
|
+
pageInfo: z.ZodObject<{
|
|
36
|
+
hasNextPage: z.ZodBoolean;
|
|
37
|
+
hasPreviousPage: z.ZodOptional<z.ZodBoolean>;
|
|
38
|
+
startCursor: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
39
|
+
endCursor: z.ZodNullable<z.ZodString>;
|
|
40
|
+
}, z.core.$strip>;
|
|
41
|
+
}, z.core.$strip>, readonly [_keystrokehq_core0.CredentialSet<"shopify", z.ZodObject<{
|
|
42
|
+
SHOPIFY_STORE_DOMAIN: z.ZodString;
|
|
43
|
+
SHOPIFY_ACCESS_TOKEN: z.ZodString;
|
|
44
|
+
}, z.core.$strip>, readonly _keystrokehq_core_credential_set0.CredentialConnection<z.ZodObject<{
|
|
45
|
+
SHOPIFY_STORE_DOMAIN: z.ZodString;
|
|
46
|
+
SHOPIFY_ACCESS_TOKEN: z.ZodString;
|
|
47
|
+
}, z.core.$strip>>[] | undefined>], undefined>;
|
|
48
|
+
declare const getArticle: _keystrokehq_core0.Operation<z.ZodObject<{
|
|
49
|
+
id: z.ZodString;
|
|
50
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
51
|
+
id: z.ZodString;
|
|
52
|
+
title: z.ZodString;
|
|
53
|
+
handle: z.ZodString;
|
|
54
|
+
blog: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
55
|
+
id: z.ZodString;
|
|
56
|
+
title: z.ZodString;
|
|
57
|
+
handle: z.ZodString;
|
|
58
|
+
createdAt: z.ZodOptional<z.ZodISODateTime>;
|
|
59
|
+
updatedAt: z.ZodOptional<z.ZodISODateTime>;
|
|
60
|
+
commentPolicy: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
61
|
+
templateSuffix: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
62
|
+
}, z.core.$strip>>>;
|
|
63
|
+
author: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
64
|
+
body: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
65
|
+
summary: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
66
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
67
|
+
image: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
68
|
+
url: z.ZodURL;
|
|
69
|
+
altText: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
70
|
+
}, z.core.$strip>>>;
|
|
71
|
+
publishedAt: z.ZodOptional<z.ZodNullable<z.ZodISODateTime>>;
|
|
72
|
+
updatedAt: z.ZodOptional<z.ZodISODateTime>;
|
|
73
|
+
}, z.core.$strip>, readonly [_keystrokehq_core0.CredentialSet<"shopify", z.ZodObject<{
|
|
74
|
+
SHOPIFY_STORE_DOMAIN: z.ZodString;
|
|
75
|
+
SHOPIFY_ACCESS_TOKEN: z.ZodString;
|
|
76
|
+
}, z.core.$strip>, readonly _keystrokehq_core_credential_set0.CredentialConnection<z.ZodObject<{
|
|
77
|
+
SHOPIFY_STORE_DOMAIN: z.ZodString;
|
|
78
|
+
SHOPIFY_ACCESS_TOKEN: z.ZodString;
|
|
79
|
+
}, z.core.$strip>>[] | undefined>], undefined>;
|
|
80
|
+
declare const createArticle: _keystrokehq_core0.Operation<z.ZodObject<{
|
|
81
|
+
handle: z.ZodOptional<z.ZodString>;
|
|
82
|
+
body: z.ZodOptional<z.ZodString>;
|
|
83
|
+
summary: z.ZodOptional<z.ZodString>;
|
|
84
|
+
authorName: z.ZodOptional<z.ZodString>;
|
|
85
|
+
isPublished: z.ZodOptional<z.ZodBoolean>;
|
|
86
|
+
publishDate: z.ZodOptional<z.ZodISODateTime>;
|
|
87
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
88
|
+
blogId: z.ZodString;
|
|
89
|
+
title: z.ZodString;
|
|
90
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
91
|
+
id: z.ZodString;
|
|
92
|
+
title: z.ZodString;
|
|
93
|
+
handle: z.ZodString;
|
|
94
|
+
blog: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
95
|
+
id: z.ZodString;
|
|
96
|
+
title: z.ZodString;
|
|
97
|
+
handle: z.ZodString;
|
|
98
|
+
createdAt: z.ZodOptional<z.ZodISODateTime>;
|
|
99
|
+
updatedAt: z.ZodOptional<z.ZodISODateTime>;
|
|
100
|
+
commentPolicy: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
101
|
+
templateSuffix: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
102
|
+
}, z.core.$strip>>>;
|
|
103
|
+
author: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
104
|
+
body: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
105
|
+
summary: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
106
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
107
|
+
image: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
108
|
+
url: z.ZodURL;
|
|
109
|
+
altText: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
110
|
+
}, z.core.$strip>>>;
|
|
111
|
+
publishedAt: z.ZodOptional<z.ZodNullable<z.ZodISODateTime>>;
|
|
112
|
+
updatedAt: z.ZodOptional<z.ZodISODateTime>;
|
|
113
|
+
}, z.core.$strip>, readonly [_keystrokehq_core0.CredentialSet<"shopify", z.ZodObject<{
|
|
114
|
+
SHOPIFY_STORE_DOMAIN: z.ZodString;
|
|
115
|
+
SHOPIFY_ACCESS_TOKEN: z.ZodString;
|
|
116
|
+
}, z.core.$strip>, readonly _keystrokehq_core_credential_set0.CredentialConnection<z.ZodObject<{
|
|
117
|
+
SHOPIFY_STORE_DOMAIN: z.ZodString;
|
|
118
|
+
SHOPIFY_ACCESS_TOKEN: z.ZodString;
|
|
119
|
+
}, z.core.$strip>>[] | undefined>], undefined>;
|
|
120
|
+
declare const updateArticle: _keystrokehq_core0.Operation<z.ZodObject<{
|
|
121
|
+
blogId: z.ZodOptional<z.ZodString>;
|
|
122
|
+
title: z.ZodOptional<z.ZodString>;
|
|
123
|
+
handle: z.ZodOptional<z.ZodString>;
|
|
124
|
+
body: z.ZodOptional<z.ZodString>;
|
|
125
|
+
summary: z.ZodOptional<z.ZodString>;
|
|
126
|
+
authorName: z.ZodOptional<z.ZodString>;
|
|
127
|
+
isPublished: z.ZodOptional<z.ZodBoolean>;
|
|
128
|
+
publishDate: z.ZodOptional<z.ZodISODateTime>;
|
|
129
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
130
|
+
id: z.ZodString;
|
|
131
|
+
redirectNewHandle: z.ZodOptional<z.ZodBoolean>;
|
|
132
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
133
|
+
id: z.ZodString;
|
|
134
|
+
title: z.ZodString;
|
|
135
|
+
handle: z.ZodString;
|
|
136
|
+
blog: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
137
|
+
id: z.ZodString;
|
|
138
|
+
title: z.ZodString;
|
|
139
|
+
handle: z.ZodString;
|
|
140
|
+
createdAt: z.ZodOptional<z.ZodISODateTime>;
|
|
141
|
+
updatedAt: z.ZodOptional<z.ZodISODateTime>;
|
|
142
|
+
commentPolicy: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
143
|
+
templateSuffix: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
144
|
+
}, z.core.$strip>>>;
|
|
145
|
+
author: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
146
|
+
body: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
147
|
+
summary: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
148
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
149
|
+
image: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
150
|
+
url: z.ZodURL;
|
|
151
|
+
altText: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
152
|
+
}, z.core.$strip>>>;
|
|
153
|
+
publishedAt: z.ZodOptional<z.ZodNullable<z.ZodISODateTime>>;
|
|
154
|
+
updatedAt: z.ZodOptional<z.ZodISODateTime>;
|
|
155
|
+
}, z.core.$strip>, readonly [_keystrokehq_core0.CredentialSet<"shopify", z.ZodObject<{
|
|
156
|
+
SHOPIFY_STORE_DOMAIN: z.ZodString;
|
|
157
|
+
SHOPIFY_ACCESS_TOKEN: z.ZodString;
|
|
158
|
+
}, z.core.$strip>, readonly _keystrokehq_core_credential_set0.CredentialConnection<z.ZodObject<{
|
|
159
|
+
SHOPIFY_STORE_DOMAIN: z.ZodString;
|
|
160
|
+
SHOPIFY_ACCESS_TOKEN: z.ZodString;
|
|
161
|
+
}, z.core.$strip>>[] | undefined>], undefined>;
|
|
162
|
+
declare const deleteArticle: _keystrokehq_core0.Operation<z.ZodObject<{
|
|
163
|
+
id: z.ZodString;
|
|
164
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
165
|
+
deletedArticleId: z.ZodString;
|
|
166
|
+
}, z.core.$strip>, readonly [_keystrokehq_core0.CredentialSet<"shopify", z.ZodObject<{
|
|
167
|
+
SHOPIFY_STORE_DOMAIN: z.ZodString;
|
|
168
|
+
SHOPIFY_ACCESS_TOKEN: z.ZodString;
|
|
169
|
+
}, z.core.$strip>, readonly _keystrokehq_core_credential_set0.CredentialConnection<z.ZodObject<{
|
|
170
|
+
SHOPIFY_STORE_DOMAIN: z.ZodString;
|
|
171
|
+
SHOPIFY_ACCESS_TOKEN: z.ZodString;
|
|
172
|
+
}, z.core.$strip>>[] | undefined>], undefined>;
|
|
173
|
+
//#endregion
|
|
174
|
+
export { createArticle, deleteArticle, getArticle, listArticles, updateArticle };
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { shopifyArticleSchema, shopifyConnectionNodesSchema, shopifyGlobalIdSchema, shopifyUserErrorSchema } from "./schemas.mjs";
|
|
2
|
+
import { createShopifyClient } from "./client.mjs";
|
|
3
|
+
import { t as shopifyOperation } from "./factory-DC-gY8L0.mjs";
|
|
4
|
+
import { n as omitUndefined, r as shopifyPageInputSchema, t as assertNoUserErrors } from "./operation-helpers-CKEDIx0o.mjs";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
|
|
7
|
+
//#region src/articles.ts
|
|
8
|
+
const articleSelection = `
|
|
9
|
+
id
|
|
10
|
+
title
|
|
11
|
+
handle
|
|
12
|
+
blog {
|
|
13
|
+
|
|
14
|
+
id
|
|
15
|
+
title
|
|
16
|
+
handle
|
|
17
|
+
createdAt
|
|
18
|
+
updatedAt
|
|
19
|
+
commentPolicy
|
|
20
|
+
templateSuffix
|
|
21
|
+
|
|
22
|
+
}
|
|
23
|
+
author {
|
|
24
|
+
name
|
|
25
|
+
}
|
|
26
|
+
body
|
|
27
|
+
summary
|
|
28
|
+
tags
|
|
29
|
+
image {
|
|
30
|
+
url
|
|
31
|
+
altText
|
|
32
|
+
}
|
|
33
|
+
publishedAt
|
|
34
|
+
updatedAt
|
|
35
|
+
`;
|
|
36
|
+
const articleMutationInputSchema = z.object({
|
|
37
|
+
blogId: shopifyGlobalIdSchema.optional(),
|
|
38
|
+
title: z.string().min(1).optional(),
|
|
39
|
+
handle: z.string().min(1).optional(),
|
|
40
|
+
body: z.string().optional(),
|
|
41
|
+
summary: z.string().optional(),
|
|
42
|
+
authorName: z.string().optional(),
|
|
43
|
+
isPublished: z.boolean().optional(),
|
|
44
|
+
publishDate: z.iso.datetime().optional(),
|
|
45
|
+
tags: z.array(z.string()).optional()
|
|
46
|
+
});
|
|
47
|
+
const articleResponseSchema = z.object({
|
|
48
|
+
id: shopifyGlobalIdSchema,
|
|
49
|
+
title: z.string(),
|
|
50
|
+
handle: z.string(),
|
|
51
|
+
blog: z.object({
|
|
52
|
+
id: shopifyGlobalIdSchema,
|
|
53
|
+
title: z.string(),
|
|
54
|
+
handle: z.string(),
|
|
55
|
+
createdAt: z.iso.datetime().optional(),
|
|
56
|
+
updatedAt: z.iso.datetime().optional(),
|
|
57
|
+
commentPolicy: z.string().nullable().optional(),
|
|
58
|
+
templateSuffix: z.string().nullable().optional()
|
|
59
|
+
}).nullable().optional(),
|
|
60
|
+
author: z.object({ name: z.string().nullable().optional() }).nullable().optional(),
|
|
61
|
+
body: z.string().nullable().optional(),
|
|
62
|
+
summary: z.string().nullable().optional(),
|
|
63
|
+
tags: z.array(z.string()).optional(),
|
|
64
|
+
image: z.object({
|
|
65
|
+
url: z.url(),
|
|
66
|
+
altText: z.string().nullable().optional()
|
|
67
|
+
}).nullable().optional(),
|
|
68
|
+
publishedAt: z.iso.datetime().nullable().optional(),
|
|
69
|
+
updatedAt: z.iso.datetime().optional()
|
|
70
|
+
});
|
|
71
|
+
const articleMutationPayloadSchema = z.object({
|
|
72
|
+
article: articleResponseSchema.nullable(),
|
|
73
|
+
userErrors: z.array(shopifyUserErrorSchema.extend({ code: z.string().optional() }))
|
|
74
|
+
});
|
|
75
|
+
const listArticlesResponseSchema = z.object({ articles: shopifyConnectionNodesSchema(articleResponseSchema) });
|
|
76
|
+
const getArticleResponseSchema = z.object({ article: articleResponseSchema.nullable() });
|
|
77
|
+
const deleteArticleResponseSchema = z.object({
|
|
78
|
+
deletedArticleId: shopifyGlobalIdSchema.nullable(),
|
|
79
|
+
userErrors: z.array(shopifyUserErrorSchema.extend({ code: z.string().optional() }))
|
|
80
|
+
});
|
|
81
|
+
function normalizeArticle(article) {
|
|
82
|
+
return {
|
|
83
|
+
id: article.id,
|
|
84
|
+
title: article.title,
|
|
85
|
+
handle: article.handle,
|
|
86
|
+
blog: article.blog ?? null,
|
|
87
|
+
author: article.author?.name ?? null,
|
|
88
|
+
body: article.body ?? null,
|
|
89
|
+
summary: article.summary ?? null,
|
|
90
|
+
tags: article.tags,
|
|
91
|
+
image: article.image ?? null,
|
|
92
|
+
publishedAt: article.publishedAt ?? null,
|
|
93
|
+
updatedAt: article.updatedAt
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function buildArticleInput(input) {
|
|
97
|
+
return omitUndefined({
|
|
98
|
+
blogId: input.blogId,
|
|
99
|
+
title: input.title,
|
|
100
|
+
handle: input.handle,
|
|
101
|
+
body: input.body,
|
|
102
|
+
summary: input.summary,
|
|
103
|
+
author: input.authorName ? { name: input.authorName } : void 0,
|
|
104
|
+
isPublished: input.isPublished,
|
|
105
|
+
publishDate: input.publishDate,
|
|
106
|
+
tags: input.tags
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
const listArticles = shopifyOperation({
|
|
110
|
+
id: "list_shopify_articles",
|
|
111
|
+
name: "List Shopify Articles",
|
|
112
|
+
description: "List blog articles from the connected Shopify store.",
|
|
113
|
+
input: shopifyPageInputSchema,
|
|
114
|
+
output: shopifyConnectionNodesSchema(shopifyArticleSchema),
|
|
115
|
+
run: async (input, credentials) => {
|
|
116
|
+
const response = await createShopifyClient(credentials).graphql(`
|
|
117
|
+
query ListArticles($first: Int!, $after: String, $query: String) {
|
|
118
|
+
articles(first: $first, after: $after, query: $query) {
|
|
119
|
+
nodes {
|
|
120
|
+
${articleSelection}
|
|
121
|
+
}
|
|
122
|
+
pageInfo {
|
|
123
|
+
hasNextPage
|
|
124
|
+
hasPreviousPage
|
|
125
|
+
startCursor
|
|
126
|
+
endCursor
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
`, listArticlesResponseSchema, { variables: {
|
|
131
|
+
first: input.first,
|
|
132
|
+
...input.after ? { after: input.after } : {},
|
|
133
|
+
...input.query ? { query: input.query } : {}
|
|
134
|
+
} });
|
|
135
|
+
return {
|
|
136
|
+
nodes: response.articles.nodes.map(normalizeArticle),
|
|
137
|
+
pageInfo: response.articles.pageInfo
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
const getArticle = shopifyOperation({
|
|
142
|
+
id: "get_shopify_article",
|
|
143
|
+
name: "Get Shopify Article",
|
|
144
|
+
description: "Retrieve a single Shopify article by its global ID.",
|
|
145
|
+
input: z.object({ id: shopifyGlobalIdSchema }),
|
|
146
|
+
output: shopifyArticleSchema,
|
|
147
|
+
run: async (input, credentials) => {
|
|
148
|
+
const response = await createShopifyClient(credentials).graphql(`
|
|
149
|
+
query GetArticle($id: ID!) {
|
|
150
|
+
article(id: $id) {
|
|
151
|
+
${articleSelection}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
`, getArticleResponseSchema, { variables: { id: input.id } });
|
|
155
|
+
if (!response.article) throw new Error(`Shopify article not found: ${input.id}`);
|
|
156
|
+
return normalizeArticle(response.article);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
const createArticle = shopifyOperation({
|
|
160
|
+
id: "create_shopify_article",
|
|
161
|
+
name: "Create Shopify Article",
|
|
162
|
+
description: "Create an article in the connected Shopify store.",
|
|
163
|
+
needsApproval: true,
|
|
164
|
+
input: articleMutationInputSchema.extend({
|
|
165
|
+
blogId: shopifyGlobalIdSchema,
|
|
166
|
+
title: z.string().min(1)
|
|
167
|
+
}),
|
|
168
|
+
output: shopifyArticleSchema,
|
|
169
|
+
run: async (input, credentials) => {
|
|
170
|
+
const response = await createShopifyClient(credentials).graphql(`
|
|
171
|
+
mutation CreateArticle($article: ArticleCreateInput!) {
|
|
172
|
+
articleCreate(article: $article) {
|
|
173
|
+
article {
|
|
174
|
+
${articleSelection}
|
|
175
|
+
}
|
|
176
|
+
userErrors {
|
|
177
|
+
code
|
|
178
|
+
field
|
|
179
|
+
message
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
`, z.object({ articleCreate: articleMutationPayloadSchema }), { variables: { article: buildArticleInput(input) } });
|
|
184
|
+
assertNoUserErrors(response.articleCreate.userErrors);
|
|
185
|
+
if (!response.articleCreate.article) throw new Error("Shopify did not return the created article.");
|
|
186
|
+
return normalizeArticle(response.articleCreate.article);
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
const updateArticle = shopifyOperation({
|
|
190
|
+
id: "update_shopify_article",
|
|
191
|
+
name: "Update Shopify Article",
|
|
192
|
+
description: "Update a Shopify article by its global ID.",
|
|
193
|
+
needsApproval: true,
|
|
194
|
+
input: articleMutationInputSchema.extend({
|
|
195
|
+
id: shopifyGlobalIdSchema,
|
|
196
|
+
redirectNewHandle: z.boolean().optional()
|
|
197
|
+
}),
|
|
198
|
+
output: shopifyArticleSchema,
|
|
199
|
+
run: async (input, credentials) => {
|
|
200
|
+
const response = await createShopifyClient(credentials).graphql(`
|
|
201
|
+
mutation UpdateArticle($id: ID!, $article: ArticleUpdateInput!) {
|
|
202
|
+
articleUpdate(id: $id, article: $article) {
|
|
203
|
+
article {
|
|
204
|
+
${articleSelection}
|
|
205
|
+
}
|
|
206
|
+
userErrors {
|
|
207
|
+
code
|
|
208
|
+
field
|
|
209
|
+
message
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
`, z.object({ articleUpdate: articleMutationPayloadSchema }), { variables: {
|
|
214
|
+
id: input.id,
|
|
215
|
+
article: omitUndefined({
|
|
216
|
+
...buildArticleInput(input),
|
|
217
|
+
redirectNewHandle: input.redirectNewHandle
|
|
218
|
+
})
|
|
219
|
+
} });
|
|
220
|
+
assertNoUserErrors(response.articleUpdate.userErrors);
|
|
221
|
+
if (!response.articleUpdate.article) throw new Error(`Shopify did not return the updated article: ${input.id}`);
|
|
222
|
+
return normalizeArticle(response.articleUpdate.article);
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
const deleteArticle = shopifyOperation({
|
|
226
|
+
id: "delete_shopify_article",
|
|
227
|
+
name: "Delete Shopify Article",
|
|
228
|
+
description: "Delete a Shopify article by its global ID.",
|
|
229
|
+
needsApproval: true,
|
|
230
|
+
input: z.object({ id: shopifyGlobalIdSchema }),
|
|
231
|
+
output: z.object({ deletedArticleId: shopifyGlobalIdSchema }),
|
|
232
|
+
run: async (input, credentials) => {
|
|
233
|
+
const response = await createShopifyClient(credentials).graphql(`
|
|
234
|
+
mutation DeleteArticle($id: ID!) {
|
|
235
|
+
articleDelete(id: $id) {
|
|
236
|
+
deletedArticleId
|
|
237
|
+
userErrors {
|
|
238
|
+
code
|
|
239
|
+
field
|
|
240
|
+
message
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
`, z.object({ articleDelete: deleteArticleResponseSchema }), { variables: { id: input.id } });
|
|
245
|
+
assertNoUserErrors(response.articleDelete.userErrors);
|
|
246
|
+
if (!response.articleDelete.deletedArticleId) throw new Error(`Shopify did not confirm article deletion: ${input.id}`);
|
|
247
|
+
return { deletedArticleId: response.articleDelete.deletedArticleId };
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
//#endregion
|
|
252
|
+
export { createArticle, deleteArticle, getArticle, listArticles, updateArticle };
|