@crvouga/mockingbird-service-shopify 0.0.0-stage → 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/CHANGELOG.md +5 -0
- package/DISCOVERY.md +107 -0
- package/README.md +169 -2
- package/SUPPORT.md +11 -0
- package/dist/chunk-DQ2XG467.js +67 -0
- package/dist/chunk-DQ2XG467.js.map +7 -0
- package/dist/chunk-GSVXSSVL.js +2041 -0
- package/dist/chunk-GSVXSSVL.js.map +7 -0
- package/dist/cli.js +18 -0
- package/dist/cli.js.map +7 -0
- package/dist/index.d.ts +1612 -0
- package/dist/index.js +47 -0
- package/dist/index.js.map +7 -0
- package/dist/server.d.ts +1024 -0
- package/dist/server.js +12 -0
- package/dist/server.js.map +7 -0
- package/openapi.yaml +183 -0
- package/package.json +127 -4
package/CHANGELOG.md
ADDED
package/DISCOVERY.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# @crvouga/mockingbird-service-shopify discovery
|
|
2
|
+
|
|
3
|
+
This is the installed-package index for coding agents and tooling. All relative links resolve
|
|
4
|
+
inside `node_modules/@crvouga/mockingbird-service-shopify/`; no repository checkout is needed to discover the emulator's
|
|
5
|
+
supported surface or documented behavior.
|
|
6
|
+
|
|
7
|
+
## Run it in a fleet
|
|
8
|
+
|
|
9
|
+
The recommended way to run this emulator is in a fleet, next to the other emulators the
|
|
10
|
+
application calls: one `mockingbird.json`, one process, each emulator on its own port.
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
npm install -D @crvouga/mockingbird-fleet @crvouga/mockingbird-service-shopify
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
`mockingbird.json` (add one entry per installed emulator; `"port": 0` picks a free port):
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"services": {
|
|
21
|
+
"shopify": { "port": 0 }
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
npx mockingbird-fleet serve --config mockingbird.json --dashboard --ready-file ready.json
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Read this emulator's URL from `services.<name>.url` in `ready.json` and point the client at it.
|
|
31
|
+
`adminBase` in the same file is the fleet controller, which resets, snapshots and restores every
|
|
32
|
+
emulator in one call, and `dashboard` is one page with every HTTP emulator's admin UI and the
|
|
33
|
+
notifications the application sent. The guide: <https://mockingbird.chrisvouga.dev/docs/fleets>. The README covers running this
|
|
34
|
+
emulator alone or in-process.
|
|
35
|
+
|
|
36
|
+
## Capability and behavior sources
|
|
37
|
+
|
|
38
|
+
| Question | Authoritative file | What it contains |
|
|
39
|
+
| --- | --- | --- |
|
|
40
|
+
| Behaviour and integration | [`README.md`](README.md) | Routes, state transitions, auth, webhooks, controls, presets and deliberate omissions. |
|
|
41
|
+
| Exact capabilities | [`SUPPORT.md`](SUPPORT.md) | Supported, unsupported and parity-covered operations or commands, including reasons for gaps. |
|
|
42
|
+
| Wire contract | [`openapi.yaml`](openapi.yaml) | Machine-readable paths, methods, schemas, responses and parity annotations. |
|
|
43
|
+
| Public API | [`dist/index.d.ts`](dist/index.d.ts) | The installed package's exact TypeScript exports and signatures. |
|
|
44
|
+
| Package metadata | [`package.json`](package.json) | Runtime/entry-point claims, vendor links, parity scope/tier and `mockingbird.discovery`. |
|
|
45
|
+
|
|
46
|
+
Read these together: the contract/capability matrix says *what* is available, while the README
|
|
47
|
+
defines stateful behavior, lifecycle rules, test controls, and intentional oracle differences.
|
|
48
|
+
If prose and an executable surface disagree, report a parity mismatch instead of adding a
|
|
49
|
+
consumer-side workaround.
|
|
50
|
+
|
|
51
|
+
## Parity and oracle
|
|
52
|
+
|
|
53
|
+
- Declared parity surface: **Admin GraphQL orders, metafields, and webhook subscriptions**.
|
|
54
|
+
- Parity tier: **cold** (the repository controls when live checks run).
|
|
55
|
+
- Oracle: **Live vendor API or sandbox**.
|
|
56
|
+
- Repository command: `bun run parity:service -- shopify`.
|
|
57
|
+
- Evidence model: Run from a Mockingbird checkout; credentials come only from .env.local or GitHub Actions secrets. Missing credentials exit 2.
|
|
58
|
+
|
|
59
|
+
The npm package contains evidence summaries and the exact contract, not credentials or the
|
|
60
|
+
repository-only parity harness. Self-parity/property and acceptance tests run in the Mockingbird
|
|
61
|
+
repository; live parity is an additional oracle check, not a substitute for the packaged matrix.
|
|
62
|
+
|
|
63
|
+
## Runtime introspection
|
|
64
|
+
|
|
65
|
+
- `GET /__admin/health`
|
|
66
|
+
- `GET /__admin`
|
|
67
|
+
- `GET /__admin/controls`
|
|
68
|
+
- `GET /__admin/state`
|
|
69
|
+
- `GET /__admin/requests`
|
|
70
|
+
- `GET /__admin/metrics`
|
|
71
|
+
- `GET /__admin/faults/presets`
|
|
72
|
+
- `GET /__admin/ui`
|
|
73
|
+
|
|
74
|
+
For HTTP services, use `x-mockingbird-namespace` (or the documented credential/path carrier) so
|
|
75
|
+
parallel tests do not share state. Admin state, journal, metrics and fault-preset endpoints are
|
|
76
|
+
designed for assertions and diagnosis by consuming test suites.
|
|
77
|
+
|
|
78
|
+
## Admin controls
|
|
79
|
+
|
|
80
|
+
Every HTTP emulator takes the same controls, and each means the same thing at boot
|
|
81
|
+
(`createRuntime({ adminControls })`, `serve --admin-controls`), over `/__admin`, and in a request header:
|
|
82
|
+
|
|
83
|
+
| Control | Admin API | Request header | Interface |
|
|
84
|
+
| --- | --- | --- | --- |
|
|
85
|
+
| Isolate state by key | `GET /__admin/namespaces` | `x-mockingbird-namespace: <key>` | [Namespaces](https://mockingbird.chrisvouga.dev/docs/admin-controls/namespaces) |
|
|
86
|
+
| Reset to boot | `POST /__admin/reset` | `x-mockingbird-reset: true` | [Reset](https://mockingbird.chrisvouga.dev/docs/admin-controls/reset) |
|
|
87
|
+
| Snapshot and restore | `POST /__admin/snapshots`, `POST /__admin/snapshots/<id>/restore` | `x-mockingbird-snapshot: <name>`, `x-mockingbird-restore: <name>` | [Snapshot and restore](https://mockingbird.chrisvouga.dev/docs/admin-controls/snapshot-and-restore) |
|
|
88
|
+
| Move the clock | `POST /__admin/clock` | `x-mockingbird-time: <instant>`, `x-mockingbird-advance: <duration>` | [Clock](https://mockingbird.chrisvouga.dev/docs/admin-controls/clock) |
|
|
89
|
+
| Read or branch from old state | `GET /__admin/timeline` | `x-mockingbird-at: <checkpoint>`, `x-mockingbird-branch: <name>` | [Checkpoints and branches](https://mockingbird.chrisvouga.dev/docs/admin-controls/checkpoints-and-branches) |
|
|
90
|
+
| Inject a fault | `POST /__admin/faults` | `x-mockingbird-fault: <fault>` | [Faults](https://mockingbird.chrisvouga.dev/docs/admin-controls/faults) |
|
|
91
|
+
| Latency and timeouts | `PUT /__admin/latency` | `x-mockingbird-delay: <duration>`, `x-mockingbird-timeout: <duration>` | [Latency and timeouts](https://mockingbird.chrisvouga.dev/docs/admin-controls/latency-and-timeouts) |
|
|
92
|
+
| Replay from a seed | `PUT /__admin/controls` | `x-mockingbird-seed: <seed>` | [Seed and determinism](https://mockingbird.chrisvouga.dev/docs/admin-controls/seed-and-determinism) |
|
|
93
|
+
| See what happened | `GET /__admin/requests`, `GET /__admin/state` | | [Journal, metrics and state](https://mockingbird.chrisvouga.dev/docs/admin-controls/journal-and-state) |
|
|
94
|
+
|
|
95
|
+
`GET /__admin` lists every route and header with a one-line summary, and `GET /__admin/controls`
|
|
96
|
+
is the namespace's current control document. The same seed and the same requests give the same
|
|
97
|
+
bytes. Setting controls as an emulator starts is [Boot](https://mockingbird.chrisvouga.dev/docs/admin-controls/boot); every route and header is in
|
|
98
|
+
the [Reference](https://mockingbird.chrisvouga.dev/docs/admin-controls/reference). The full guide: <https://mockingbird.chrisvouga.dev/docs/admin-controls>.
|
|
99
|
+
|
|
100
|
+
## Report a mismatch or missing capability
|
|
101
|
+
|
|
102
|
+
Follow the [agent reporting contract](https://github.com/crvouga/mockingbird/blob/main/docs/REPORTING_ISSUES.md). Include package version,
|
|
103
|
+
operation/command, a minimal redacted request, actual emulator result, expected oracle result or vendor
|
|
104
|
+
documentation, and whether the mismatch appears in the matrix. Never include keys, tokens,
|
|
105
|
+
customer data, prompts, PHI, card data, or unredacted recordings.
|
|
106
|
+
|
|
107
|
+
Service key: `shopify`.
|
package/README.md
CHANGED
|
@@ -1,3 +1,170 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @crvouga/mockingbird-service-shopify
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Local emulators. Real API contracts. Part of [Mockingbird](https://github.com/crvouga/mockingbird).
|
|
4
|
+
|
|
5
|
+
Stateful emulator of the Shopify Admin GraphQL API for a backend that fulfils orders through
|
|
6
|
+
Shopify: it creates orders (directly or through draft orders), reads their fulfilment status and
|
|
7
|
+
tracking, looks up product variants, sets metafields and manages webhook subscriptions. Order and
|
|
8
|
+
fulfilment webhooks go to the store's own subscriptions, signed with `X-Shopify-Hmac-Sha256`.
|
|
9
|
+
Status: **wip**.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install -D @crvouga/mockingbird-service-shopify
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
ESM only. Node >= 22 or Bun >= 1.2. Serve with `npx mockingbird-shopify serve`, `createServer`
|
|
18
|
+
from `./server`, or mount `createRuntime()` in any Fetch-compatible process.
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
Shopify's official [`@shopify/admin-api-client`](https://www.npmjs.com/package/@shopify/admin-api-client)
|
|
23
|
+
always calls `https://<storeDomain>`, so point its `customFetchApi` at the emulator. Any non-empty
|
|
24
|
+
`X-Shopify-Access-Token` authenticates unless `PUT /__admin/settings {"accessTokens": [...]}`
|
|
25
|
+
restricts it.
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { createAdminApiClient } from "@shopify/admin-api-client"
|
|
29
|
+
import { createServer } from "@crvouga/mockingbird-service-shopify/server"
|
|
30
|
+
|
|
31
|
+
const server = await createServer()
|
|
32
|
+
const storeDomain = "mockingbird-test.myshopify.com"
|
|
33
|
+
|
|
34
|
+
// The application's own client. Only the transport changes: requests to the store go to the
|
|
35
|
+
// emulator instead of Shopify.
|
|
36
|
+
const shopify = createAdminApiClient({
|
|
37
|
+
storeDomain,
|
|
38
|
+
apiVersion: "2026-10",
|
|
39
|
+
accessToken: "shpat_test",
|
|
40
|
+
customFetchApi: (url, init) => fetch(url.replace(`https://${storeDomain}`, server.url), init),
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
const { data } = await shopify.request(
|
|
44
|
+
`mutation Create($order: OrderCreateOrderInput!) {
|
|
45
|
+
orderCreate(order: $order) {
|
|
46
|
+
order { id name displayFulfillmentStatus }
|
|
47
|
+
userErrors { field message code }
|
|
48
|
+
}
|
|
49
|
+
}`,
|
|
50
|
+
{
|
|
51
|
+
variables: {
|
|
52
|
+
order: {
|
|
53
|
+
email: "buyer@example.test",
|
|
54
|
+
lineItems: [{ variantId: "gid://shopify/ProductVariant/41000000001", quantity: 1 }],
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
)
|
|
59
|
+
console.log(data.orderCreate.order)
|
|
60
|
+
// → {id: "gid://shopify/Order/5000000001", name: "#1001", displayFulfillmentStatus: "UNFULFILLED"}
|
|
61
|
+
|
|
62
|
+
// Test setup: mark the order shipped, as the warehouse would.
|
|
63
|
+
await fetch(`${server.url}/__admin/orders/5000000001/fulfill`, {
|
|
64
|
+
method: "POST",
|
|
65
|
+
headers: { "content-type": "application/json" },
|
|
66
|
+
body: JSON.stringify({ trackingCompany: "UPS", trackingNumber: "1Z0000000000000001" }),
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
await server.close()
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
With plain `fetch`, call `POST ${server.url}/admin/api/2026-10/graphql.json` with
|
|
73
|
+
`X-Shopify-Access-Token` and a `{query, variables}` body.
|
|
74
|
+
|
|
75
|
+
### GraphQL surface
|
|
76
|
+
|
|
77
|
+
`POST /admin/api/{version}/graphql.json` executes any document that validates against the subset
|
|
78
|
+
schema (`SHOPIFY_SDL`, with Shopify's own type, field, enum and input names) using the reference
|
|
79
|
+
`graphql` implementation, so aliases, fragments, variables and validation errors behave like the
|
|
80
|
+
real API. `version` is a quarterly version (`2026-10`) or `unstable`.
|
|
81
|
+
|
|
82
|
+
| Field | Behaviour |
|
|
83
|
+
| --- | --- |
|
|
84
|
+
| `shop` | The store's name, `myshopifyDomain` and currency (settings). |
|
|
85
|
+
| `orderCreate(order, options)` | Line items reference a `variantId` (price from the variant unless `priceSet` overrides it) or are custom (`title` + `priceSet`). Stores email, phone, note, tags, currency, `financialStatus` (default `PAID`), shipping address and metafields; the order starts `UNFULFILLED`, named `#1001`, `#1002`, …. Problems come back as `userErrors { field message code }` (`["order", "lineItems", "0", "variantId"]` / `NOT_FOUND`, an empty or non-positive line `BLANK`/`INVALID`) and nothing is stored. Announces `orders/create`. |
|
|
86
|
+
| `draftOrderCreate(input)` | An `OPEN` draft (`#D1`, …) with variant or custom (`originalUnitPrice`) lines and metafields. |
|
|
87
|
+
| `draftOrderComplete(id, paymentPending, sourceName)` | Turns the draft into an order (`PAID`, or `PENDING` with `paymentPending: true`), sets `status: COMPLETED` and `order`. Completing twice, or an unknown draft, is a `userError`. |
|
|
88
|
+
| `order(id)` / `draftOrder(id)` | `displayFulfillmentStatus`, `displayFinancialStatus`, `cancelledAt`, `cancelReason`, prices (`totalPriceSet`), `lineItems`, `fulfillments { status displayStatus trackingInfo { company number url } }`, `shippingAddress`, `metafield(namespace, key)` and `metafields`. A malformed gid is a GraphQL error (`Invalid global id`); an unknown one is `null`. |
|
|
89
|
+
| `productVariants(first/last, after/before, query, reverse)` / `productVariant(id)` | The seeded catalogue. `query` takes `sku:`, `product_id:`, `title:`, `barcode:`, `id:` and free text; another search field is an explicit `MOCKINGBIRD_UNSUPPORTED` GraphQL error. |
|
|
90
|
+
| `metafieldsSet(metafields)` | Upserts by owner + namespace + key on orders, draft orders, products and variants. At most 25 per call. Validates `single_line_text_field`, `multi_line_text_field`, `number_integer`, `number_decimal`, `boolean`, `date`, `date_time`, `url` and `json` values; any error (with `elementIndex`) rejects the whole call. A missing namespace is the app-reserved `app--<appId>`. |
|
|
91
|
+
| `webhookSubscriptionCreate(topic, webhookSubscription)` / `webhookSubscriptionDelete(id)` / `webhookSubscriptions(…)` / `webhookSubscription(id)` | Topics `ORDERS_CREATE`, `ORDERS_FULFILLED`, `ORDERS_CANCELLED`, `FULFILLMENTS_CREATE`, `FULFILLMENTS_UPDATE`. `callbackUrl` (or `uri`) must be an http(s) URL; the same topic and address twice answers `Address for this topic has already been taken`. `includeFields` trims the delivered body. Filters `topics` and `callbackUrl`. |
|
|
92
|
+
|
|
93
|
+
Connections require `first` or `last` (at most 250) and page with opaque cursors. Every response
|
|
94
|
+
carries `extensions.cost` (`requestedQueryCost`, `actualQueryCost`, `throttleStatus`).
|
|
95
|
+
|
|
96
|
+
A missing or unknown token answers HTTP 401
|
|
97
|
+
`{"errors": "[API] Invalid API key or access token (unrecognized login or wrong password)"}`; a
|
|
98
|
+
body without `query` answers 400 `{"errors": {"query": "Required parameter missing or invalid"}}`.
|
|
99
|
+
|
|
100
|
+
Seed fixtures: products `gid://shopify/Product/7000000001` (Mockingbird Tee; variants
|
|
101
|
+
`…/ProductVariant/41000000001` `MB-TEE-S` and `…/41000000002` `MB-TEE-M`, 25.00) and
|
|
102
|
+
`…/Product/7000000002` (Mockingbird Mug; variant `…/41000000003` `MB-MUG`, 12.50).
|
|
103
|
+
|
|
104
|
+
### Webhooks
|
|
105
|
+
|
|
106
|
+
Webhooks are delivered to the subscriptions the application creates with
|
|
107
|
+
`webhookSubscriptionCreate`, as Shopify's REST JSON (`snake_case` order or fulfilment bodies with
|
|
108
|
+
`admin_graphql_api_id`). Each delivery carries `X-Shopify-Topic`, `X-Shopify-Hmac-Sha256` (base64
|
|
109
|
+
HMAC-SHA256 of the raw body under the app's client secret, setting `apiSecret`, default
|
|
110
|
+
`mockingbird_shopify_api_secret`), `X-Shopify-Shop-Domain`, `X-Shopify-API-Version` (the version
|
|
111
|
+
the subscription was created with), `X-Shopify-Webhook-Id`, `X-Shopify-Event-Id` and
|
|
112
|
+
`X-Shopify-Triggered-At`.
|
|
113
|
+
|
|
114
|
+
| Topic | When |
|
|
115
|
+
| --- | --- |
|
|
116
|
+
| `orders/create` | `orderCreate`, `draftOrderComplete` |
|
|
117
|
+
| `fulfillments/create`, then `orders/fulfilled` | `POST /__admin/orders/:id/fulfill` |
|
|
118
|
+
| `fulfillments/update` | `POST /__admin/fulfillments/:id/update` |
|
|
119
|
+
| `orders/cancelled` | `POST /__admin/orders/:id/cancel` |
|
|
120
|
+
|
|
121
|
+
An endpoint added with `PUT /__admin/webhook-endpoints` also receives a copy of each delivery.
|
|
122
|
+
`GET /__admin/webhooks/events`, replay, flush and the delivery-fault presets work as for every
|
|
123
|
+
emulator.
|
|
124
|
+
|
|
125
|
+
### Admin controls
|
|
126
|
+
|
|
127
|
+
| Route | Effect |
|
|
128
|
+
| --- | --- |
|
|
129
|
+
| `POST /__admin/orders/:id/fulfill` `{trackingCompany?, trackingNumber?, trackingUrl?}` | Fulfils every line (`displayFulfillmentStatus: FULFILLED`), records a `SUCCESS` fulfilment with its tracking. `:id` is the numeric id or the URL-encoded gid. 409 when cancelled or already fulfilled. |
|
|
130
|
+
| `POST /__admin/fulfillments/:id/update` `{displayStatus?, trackingCompany?, trackingNumber?, trackingUrl?}` | Moves the shipment (`IN_TRANSIT`, `OUT_FOR_DELIVERY`, `DELIVERED`, …) or replaces its tracking. |
|
|
131
|
+
| `POST /__admin/orders/:id/cancel` `{reason?}` | Cancels an unfulfilled order (`CUSTOMER`, `DECLINED`, `FRAUD`, `INVENTORY`, `STAFF`, `OTHER`; default `OTHER`). |
|
|
132
|
+
| `GET /__admin/orders/:id` | The order as its webhook body renders it. |
|
|
133
|
+
| `POST /__admin/fail-next-mutation` `{"kind": "userErrors", message?, field?, code?}` or `{"kind": "throttled"}` | The next mutation document (queries are unaffected) answers that `userError` on every mutation field, or the `THROTTLED` error, without changing state. |
|
|
134
|
+
| `GET/PUT /__admin/settings` | `accessTokens`, `apiSecret`, `shopName`, `shopDomain`, `currency`, `appId`. |
|
|
135
|
+
|
|
136
|
+
Presets (`POST /__admin/faults {"preset": "<name>", "count"?: n}`): `throttled` (HTTP 200,
|
|
137
|
+
`errors[{message: "Throttled", extensions: {code: "THROTTLED"}}]`, `actualQueryCost: null`),
|
|
138
|
+
`invalid_access_token` (401), `server_error` (500), `webhook_duplicate`, `webhook_reorder` and
|
|
139
|
+
`webhook_drop`, plus the standard ones.
|
|
140
|
+
|
|
141
|
+
Namespaces use `x-mockingbird-namespace`, `/__admin/ns/<name>/admin/api/…`, or the access token
|
|
142
|
+
through `PUT /__admin/credentials {"credentials": {"<token>": "<namespace>"}}`.
|
|
143
|
+
|
|
144
|
+
## API
|
|
145
|
+
|
|
146
|
+
The main entry exports `ShopifyAPI`, `ShopifyState`, `createRuntime`, `SHOPIFY_NAMESPACE`,
|
|
147
|
+
`SHOPIFY_PRESETS`, `SHOPIFY_SDL`, `shopifySchema`, `DEFAULT_SETTINGS`, `ACCESS_TOKEN_HEADER`,
|
|
148
|
+
`THROTTLE_STATUS`, `accessTokenOf`, `shopifyTimestamp`, `signWebhook`, `gid`, `parseGid`,
|
|
149
|
+
`restOrder`, `restFulfillment`, `restTopic`, the generated `document`, `operationIds` and
|
|
150
|
+
`supportedOperationIds`, and the record, settings and option types.
|
|
151
|
+
`@crvouga/mockingbird-service-shopify/server` exports `createServer`, `serveTarget`,
|
|
152
|
+
`DEFAULT_PORT`, and the server option/result types.
|
|
153
|
+
|
|
154
|
+
## Deliberately not modelled
|
|
155
|
+
|
|
156
|
+
- Every behaviour above is a **best guess from Shopify's Admin GraphQL documentation, not
|
|
157
|
+
oracle-verified** against a development store: the exact `userErrors` messages and codes, the
|
|
158
|
+
default `financialStatus` of `orderCreate`, and the query cost numbers in particular.
|
|
159
|
+
- Query cost calculation and the leaky bucket: the cost is 1 per query field and 10 per mutation
|
|
160
|
+
field, and the bucket is never drained; use the `throttled` preset or
|
|
161
|
+
`POST /__admin/fail-next-mutation` to exercise throttling.
|
|
162
|
+
- Payments, transactions, refunds, inventory reservation, taxes, discounts, shipping lines,
|
|
163
|
+
customers, and fulfilment orders or fulfilment services; fulfilments are created only through
|
|
164
|
+
the admin control, never by a mutation.
|
|
165
|
+
- Webhook topics other than the five above, XML webhooks (an explicit `MOCKINGBIRD_UNSUPPORTED`
|
|
166
|
+
error), Pub/Sub and EventBridge endpoints, and HMAC verification of the app's own OAuth flow.
|
|
167
|
+
Shopify requires `https` callback addresses; the emulator also accepts `http` so a local
|
|
168
|
+
receiver works.
|
|
169
|
+
- The REST Admin API, the Storefront API, bulk operations and GraphQL subscriptions.
|
|
170
|
+
- Metafield definitions, `compareDigest`, and list or reference metafield types.
|
package/SUPPORT.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Shopify Admin GraphQL API (Mockingbird subset) — operation support
|
|
2
|
+
|
|
3
|
+
Generated from `openapi.yaml`; do not edit by hand.
|
|
4
|
+
|
|
5
|
+
- operations in spec: **1**
|
|
6
|
+
- supported by the emulator: **1**
|
|
7
|
+
- parity enabled: **1**
|
|
8
|
+
|
|
9
|
+
| operationId | route | emulator | parity | notes |
|
|
10
|
+
| --- | --- | --- | --- | --- |
|
|
11
|
+
| `AdminGraphql` | `POST /admin/api/{version}/graphql.json` | ✅ supported | ⚠️ unsafe (opt-in) | Creates orders and webhook subscriptions in the store |
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createRuntime
|
|
3
|
+
} from "./chunk-GSVXSSVL.js";
|
|
4
|
+
|
|
5
|
+
// src/server.ts
|
|
6
|
+
import { listen } from "@crvouga/mockingbird-runtime/adapter-node";
|
|
7
|
+
var DEFAULT_PORT = 12159;
|
|
8
|
+
var createServer = async (options = {}) => {
|
|
9
|
+
const { port, host, ...rest } = options;
|
|
10
|
+
const runtime = createRuntime(rest);
|
|
11
|
+
const listening = await listen(runtime, {
|
|
12
|
+
port: port ?? 0,
|
|
13
|
+
...host !== void 0 ? { host } : {}
|
|
14
|
+
});
|
|
15
|
+
return { ...listening, runtime };
|
|
16
|
+
};
|
|
17
|
+
var text = (value) => typeof value === "string" ? value : void 0;
|
|
18
|
+
var serveTarget = {
|
|
19
|
+
name: "shopify",
|
|
20
|
+
defaultPort: DEFAULT_PORT,
|
|
21
|
+
options: {
|
|
22
|
+
"access-token": {
|
|
23
|
+
type: "string",
|
|
24
|
+
value: "<token>",
|
|
25
|
+
description: "The only X-Shopify-Access-Token to accept (default: any non-empty token)"
|
|
26
|
+
},
|
|
27
|
+
"api-secret": {
|
|
28
|
+
type: "string",
|
|
29
|
+
value: "<secret>",
|
|
30
|
+
description: "The app's client secret: the X-Shopify-Hmac-Sha256 key for webhooks"
|
|
31
|
+
},
|
|
32
|
+
"shop-domain": {
|
|
33
|
+
type: "string",
|
|
34
|
+
value: "<shop>.myshopify.com",
|
|
35
|
+
description: "Sent as X-Shopify-Shop-Domain and returned by shop { myshopifyDomain }"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
create: (values, common) => {
|
|
39
|
+
const token = text(values["access-token"]);
|
|
40
|
+
const secret = text(values["api-secret"]);
|
|
41
|
+
const domain = text(values["shop-domain"]);
|
|
42
|
+
return createRuntime({
|
|
43
|
+
settings: {
|
|
44
|
+
...token ? { accessTokens: [token] } : {},
|
|
45
|
+
...secret ? { apiSecret: secret } : {},
|
|
46
|
+
...domain ? { shopDomain: domain } : {}
|
|
47
|
+
},
|
|
48
|
+
...common.adminPrefix !== void 0 ? { adminPrefix: common.adminPrefix } : {},
|
|
49
|
+
...common.adminKey !== void 0 ? { adminKey: common.adminKey } : {},
|
|
50
|
+
...common.adminControls !== void 0 ? { adminControls: common.adminControls } : {},
|
|
51
|
+
...common.seed !== void 0 ? { seed: common.seed } : {},
|
|
52
|
+
...common.onLog ? { onLog: common.onLog } : {}
|
|
53
|
+
});
|
|
54
|
+
},
|
|
55
|
+
banner: () => [
|
|
56
|
+
"GraphQL: POST /admin/api/<version>/graphql.json with X-Shopify-Access-Token",
|
|
57
|
+
"webhooks: webhookSubscriptionCreate, signed with X-Shopify-Hmac-Sha256 (--api-secret)",
|
|
58
|
+
"namespaces: x-mockingbird-namespace, /__admin/ns/<name>/admin/api/\u2026, or PUT /__admin/credentials {<token>: <ns>}"
|
|
59
|
+
]
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export {
|
|
63
|
+
DEFAULT_PORT,
|
|
64
|
+
createServer,
|
|
65
|
+
serveTarget
|
|
66
|
+
};
|
|
67
|
+
//# sourceMappingURL=chunk-DQ2XG467.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/server.ts"],
|
|
4
|
+
"sourcesContent": ["/// <reference types=\"node\" />\nimport { type Listening, listen, type ServeTarget } from \"@crvouga/mockingbird-adapter-node\"\nimport { createRuntime, type ShopifyRuntime, type ShopifyRuntimeOptions } from \"./runtime.js\"\n\n/** Port `mockingbird-shopify serve` listens on when none is given. */\nexport const DEFAULT_PORT = 12159\n\nexport type ShopifyServerOptions = ShopifyRuntimeOptions & {\n /** Default `0`: the OS picks a free port. */\n port?: number\n /** Default `127.0.0.1`. */\n host?: string\n}\n\nexport type ShopifyServer = Listening & { runtime: ShopifyRuntime }\n\n/** Serve the Shopify Admin GraphQL emulator over `node:http`. */\nexport const createServer = async (options: ShopifyServerOptions = {}): Promise<ShopifyServer> => {\n const { port, host, ...rest } = options\n const runtime = createRuntime(rest)\n const listening = await listen(runtime, {\n port: port ?? 0,\n ...(host !== undefined ? { host } : {}),\n })\n return { ...listening, runtime }\n}\n\nconst text = (value: string | boolean | undefined) =>\n typeof value === \"string\" ? value : undefined\n\n/** How `serve` (and `serve --config`) builds the Shopify emulator from flags. */\nexport const serveTarget: ServeTarget = {\n name: \"shopify\",\n defaultPort: DEFAULT_PORT,\n options: {\n \"access-token\": {\n type: \"string\",\n value: \"<token>\",\n description: \"The only X-Shopify-Access-Token to accept (default: any non-empty token)\",\n },\n \"api-secret\": {\n type: \"string\",\n value: \"<secret>\",\n description: \"The app's client secret: the X-Shopify-Hmac-Sha256 key for webhooks\",\n },\n \"shop-domain\": {\n type: \"string\",\n value: \"<shop>.myshopify.com\",\n description: \"Sent as X-Shopify-Shop-Domain and returned by shop { myshopifyDomain }\",\n },\n },\n create: (values, common) => {\n const token = text(values[\"access-token\"])\n const secret = text(values[\"api-secret\"])\n const domain = text(values[\"shop-domain\"])\n return createRuntime({\n settings: {\n ...(token ? { accessTokens: [token] } : {}),\n ...(secret ? { apiSecret: secret } : {}),\n ...(domain ? { shopDomain: domain } : {}),\n },\n ...(common.adminPrefix !== undefined ? { adminPrefix: common.adminPrefix } : {}),\n ...(common.adminKey !== undefined ? { adminKey: common.adminKey } : {}),\n ...(common.adminControls !== undefined ? { adminControls: common.adminControls } : {}),\n ...(common.seed !== undefined ? { seed: common.seed } : {}),\n ...(common.onLog ? { onLog: common.onLog } : {}),\n })\n },\n banner: () => [\n \"GraphQL: POST /admin/api/<version>/graphql.json with X-Shopify-Access-Token\",\n \"webhooks: webhookSubscriptionCreate, signed with X-Shopify-Hmac-Sha256 (--api-secret)\",\n \"namespaces: x-mockingbird-namespace, /__admin/ns/<name>/admin/api/\u2026, or PUT /__admin/credentials {<token>: <ns>}\",\n ],\n}\n"],
|
|
5
|
+
"mappings": ";;;;;AACA,SAAyB,cAAgC;AAIlD,IAAM,eAAe;AAYrB,IAAM,eAAe,OAAO,UAAgC,CAAC,MAA8B;AAChG,QAAM,EAAE,MAAM,MAAM,GAAG,KAAK,IAAI;AAChC,QAAM,UAAU,cAAc,IAAI;AAClC,QAAM,YAAY,MAAM,OAAO,SAAS;AAAA,IACtC,MAAM,QAAQ;AAAA,IACd,GAAI,SAAS,SAAY,EAAE,KAAK,IAAI,CAAC;AAAA,EACvC,CAAC;AACD,SAAO,EAAE,GAAG,WAAW,QAAQ;AACjC;AAEA,IAAM,OAAO,CAAC,UACZ,OAAO,UAAU,WAAW,QAAQ;AAG/B,IAAM,cAA2B;AAAA,EACtC,MAAM;AAAA,EACN,aAAa;AAAA,EACb,SAAS;AAAA,IACP,gBAAgB;AAAA,MACd,MAAM;AAAA,MACN,OAAO;AAAA,MACP,aAAa;AAAA,IACf;AAAA,IACA,cAAc;AAAA,MACZ,MAAM;AAAA,MACN,OAAO;AAAA,MACP,aAAa;AAAA,IACf;AAAA,IACA,eAAe;AAAA,MACb,MAAM;AAAA,MACN,OAAO;AAAA,MACP,aAAa;AAAA,IACf;AAAA,EACF;AAAA,EACA,QAAQ,CAAC,QAAQ,WAAW;AAC1B,UAAM,QAAQ,KAAK,OAAO,cAAc,CAAC;AACzC,UAAM,SAAS,KAAK,OAAO,YAAY,CAAC;AACxC,UAAM,SAAS,KAAK,OAAO,aAAa,CAAC;AACzC,WAAO,cAAc;AAAA,MACnB,UAAU;AAAA,QACR,GAAI,QAAQ,EAAE,cAAc,CAAC,KAAK,EAAE,IAAI,CAAC;AAAA,QACzC,GAAI,SAAS,EAAE,WAAW,OAAO,IAAI,CAAC;AAAA,QACtC,GAAI,SAAS,EAAE,YAAY,OAAO,IAAI,CAAC;AAAA,MACzC;AAAA,MACA,GAAI,OAAO,gBAAgB,SAAY,EAAE,aAAa,OAAO,YAAY,IAAI,CAAC;AAAA,MAC9E,GAAI,OAAO,aAAa,SAAY,EAAE,UAAU,OAAO,SAAS,IAAI,CAAC;AAAA,MACrE,GAAI,OAAO,kBAAkB,SAAY,EAAE,eAAe,OAAO,cAAc,IAAI,CAAC;AAAA,MACpF,GAAI,OAAO,SAAS,SAAY,EAAE,MAAM,OAAO,KAAK,IAAI,CAAC;AAAA,MACzD,GAAI,OAAO,QAAQ,EAAE,OAAO,OAAO,MAAM,IAAI,CAAC;AAAA,IAChD,CAAC;AAAA,EACH;AAAA,EACA,QAAQ,MAAM;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|