@redeemid/emdash 0.0.29 → 0.0.31

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 CHANGED
@@ -27,7 +27,7 @@ Selling a membership is ordinary Shopify: a product, a checkout, and a Flow work
27
27
  - **For shoppers:** product pages with options, subscriptions and Product structured data, a product sitemap, collections with Shopify's filters and sorting, product search, recommendations, a cart page and cart drawer, discount codes, local prices by market, and a checkout that opens signed in for members. It all works without JavaScript.
28
28
  - **For members:** sign in, Account and Sign out without ever seeing the word "Shopify"; a member card with levels, renewal and end dates; preferences saved on their customer record; download or delete what the site keeps about them.
29
29
  - **For editors:** Paywall break, Members only, Sold on its own, Plan, Product, Collection, Product search, Contribution and Preferences blocks; an Access panel on every entry; teasers, a monthly or weekly meter, drip, badges and protected file downloads.
30
- - **For staff:** Access, Members, Webhooks, App events, Reports, Setup, Status, Access test and Privacy pages in the EmDash admin. New content types and taxonomies appear on their own, so access never needs a code change.
30
+ - **For staff:** an Overview, then Members, Access (with Map and Test tabs), Reports, Privacy and Connection (Status, Setup, Webhooks, Health and App events tabs) in the EmDash admin, and a dashboard card. New content types and taxonomies appear on their own, so access never needs a code change.
31
31
  - **For developers:** Astro components for every piece, `canRead()` and friends for your own code, a storefront from the Storefront API with a cart and a signed-in checkout, a browser client with analytics events, signed outbound events, MCP tools for AI agents and a `redeemid-emdash` command.
32
32
 
33
33
  ## Requirements
@@ -75,7 +75,7 @@ import Content from "@redeemid/emdash/components/Content.astro";
75
75
  <Content value={post.data.content} collection="posts" terms={post.data.terms} id={post.id} title={post.data.title} />
76
76
  ```
77
77
 
78
- Set the secrets, open **Plugins → RedeemID → Setup** in the admin, and work down the steps. [Getting started](docs/getting-started.md) walks through all of it.
78
+ Set the secrets, open **Plugins → RedeemID → Connection → Setup** in the admin, and work down the steps. [Getting started](docs/getting-started.md) walks through all of it.
79
79
 
80
80
  ## Documentation
81
81
 
package/docs/access.md CHANGED
@@ -47,11 +47,13 @@ The rest of a level is how it looks and where it leads:
47
47
 
48
48
  Everything about who reads what can be set in the EmDash admin, so a new content type, taxonomy or level never needs a code change. Under **Plugins → RedeemID → Access**:
49
49
 
50
- - **Levels.** Add, rename and remove levels and the Shopify tags that grant them: one group per line, commas between tags, and `all:` before a line that needs every tag on it. Tick the levels each one includes, and set its plan products, members' home and badge colour. Name them anything; the slug is made from the name. A level that rules, features or folders still use can't be removed; removing one drops it from other levels' includes.
51
- - **Site rules.** The site default, how term rules combine, the meter and the wall's join link.
52
- - **Content types.** Who can read each collection, with drip, the meter and the teaser. Every collection on the site is listed, including ones made after RedeemID was set up.
53
- - **Taxonomies.** Rules for terms in every taxonomy, for one collection or every collection the taxonomy is on.
54
- - **Features** and **Protected files.**
50
+ - **Map** (first tab). One row per reader: everyone, any signed-in member, then each level (the ones others include first). Columns give who is in it (tags), members holding it now, the levels it includes, and how many features, content types, terms, entries and folders it opens, with what it gets through an included level counted as "via". Pick a row to list everything it opens, each with a link to where it's changed. The map shows which settings name a reader; the Test tab says which rule wins on one post.
51
+ - **Levels.** **Add a level** (or Edit in the table) opens a dialog: the name, the Shopify tags that grant it (one group per line, commas between tags, and `all:` before a line that needs every tag on it) and the levels it includes. **More options** holds its wall text, join link, plan products, members' home and badge colour. Name levels anything; the slug is made from the name. A level that rules, features or folders still use can't be removed; removing one drops it from other levels' includes.
52
+ - **Features.** Perks held through a level or a tag, added and edited in a dialog. A level says what a member is and decides what they can read; a feature is a switch the site's code checks (`<Feature>`, `hasFeature`). Both tabs end with **Use it in your site**: who grants it (Shopify), the code that checks it, and a link to the developer API. Each Edit dialog shows the same code with that slug filled in.
53
+ - **Rules.** The site default and how term rules combine; the meter and the wall's join link (a form of their own); and who can read each content type, with drip, the meter and the teaser. Every collection on the site is listed, including ones made after RedeemID was set up.
54
+ - **Terms.** Rules for terms in every taxonomy, for one collection or every collection the taxonomy is on, added in a dialog.
55
+ - **Files.** Protected media folders, added in a dialog.
56
+ - **Test.** What an entry needs and whether a reader with some tags passes. Nothing is saved.
55
57
 
56
58
  The first save of a setting copies its value from your code config, so nothing is lost, and from then on the admin's value is used. **Use the code's setting** removes the admin's copy. Saved settings are kept in the site's database. Each server keeps the last ones it read and checks one small record per request to see whether anyone saved since, so the whole set is read again only after a save. If the database can't be read, the settings last read stand; a server that has read none yet locks every entry (and refuses files in any media folder) until it can, rather than falling back to the code's settings and opening what the admin locked.
57
59
 
package/docs/content.md CHANGED
@@ -81,4 +81,4 @@ text: {
81
81
 
82
82
  Each key is a string's original English and its value is the new text. Only the strings you name change: there is no find-and-replace, so replacing "Sign in" leaves "Sign in to download this file." alone. `STRINGS` in `@redeemid/emdash/text` lists every string, grouped by where it shows.
83
83
 
84
- A replacement may use its original's placeholders (such as `{level}`, or `{signIn}` for a link) in any order, or not at all, but no others. Text is plain, so HTML shows as typed. A replacement with an unknown placeholder, or for a string RedeemID doesn't show, is ignored and listed on the Status page with the reason. Props you pass (`label`, `title`, Notices `messages`) win over `text`. Staff screens in the admin aren't covered.
84
+ A replacement may use its original's placeholders (such as `{level}`, or `{signIn}` for a link) in any order, or not at all, but no others. Text is plain, so HTML shows as typed. A replacement with an unknown placeholder, or for a string RedeemID doesn't show, is ignored and listed on Connection → Status with the reason. Props you pass (`label`, `title`, Notices `messages`) win over `text`. Staff screens in the admin aren't covered.
@@ -56,7 +56,7 @@ Under `/_emdash/api/auth/redeemid/`. Same-origin only: EmDash checks `Origin` on
56
56
  - `api/state` (GET): the visitor's own state.
57
57
 
58
58
  ```json
59
- { "apiVersion": "1.5.0", "version": "0.0.29", "signedIn": true, "linked": true,
59
+ { "apiVersion": "1.5.0", "version": "0.0.31", "signedIn": true, "linked": true,
60
60
  "levels": ["gold"], "features": ["ad-free"], "cart": { "count": 2 },
61
61
  "meter": { "used": 1, "limit": 3, "period": "month" } }
62
62
  ```
@@ -12,7 +12,7 @@ From an empty EmDash site to a locked post that opens for a tagged customer.
12
12
 
13
13
  ## 1. Set up Shopify
14
14
 
15
- Follow [Connect a store](shopify-setup.md). It gives you a Customer Account API client, Storefront API tokens and a webhook app. Once the package is installed, **Plugins → RedeemID → Setup** walks through the same steps for your site, with its own callback URI filled in, and says which are done.
15
+ Follow [Connect a store](shopify-setup.md). It gives you a Customer Account API client, Storefront API tokens and a webhook app. Once the package is installed, **Plugins → RedeemID → Connection → Setup** walks through the same steps for your site, with its own callback URI filled in, and says which are done.
16
16
 
17
17
  ## 2. Install
18
18
 
@@ -68,7 +68,7 @@ REDEEMID_SHOPIFY_ADMIN_APP_CLIENT_SECRET=… # verifies webhooks
68
68
  REDEEMID_SHOPIFY_STOREFRONT_PRIVATE_TOKEN=… # only to sell on the site: plans, shop and checkout
69
69
  ```
70
70
 
71
- The [secrets reference](reference.md#secrets) lists the rest. The Setup page prints the command for each secret its steps need.
71
+ The [secrets reference](reference.md#secrets) lists the rest. Connection → Setup prints the command for each secret its steps need.
72
72
 
73
73
  ## 5. Render content through RedeemID
74
74
 
@@ -84,11 +84,11 @@ import Notices from "@redeemid/emdash/components/Notices.astro";
84
84
  <Content value={post.data.content} collection="posts" terms={post.data.terms} id={post.id} title={post.data.title} />
85
85
  ```
86
86
 
87
- Use `<Content>` in place of `<PortableText>` for every body that may be locked, because plain `<PortableText>` shows everything. You don't have to find them by hand: `redeemidGuard()` scans the site's `.astro` files at build time, and the Status page names each template, with its line, that renders a gated collection's body with `<PortableText>`. While any rule is on, it also lists templates whose collection it can't tell (such as a component given the body as a prop), so you can check them.
87
+ Use `<Content>` in place of `<PortableText>` for every body that may be locked, because plain `<PortableText>` shows everything. You don't have to find them by hand: `redeemidGuard()` scans the site's `.astro` files at build time, and Connection → Status names each template, with its line, that renders a gated collection's body with `<PortableText>`. While any rule is on, it also lists templates whose collection it can't tell (such as a component given the body as a prop), so you can check them.
88
88
 
89
89
  ## 6. Check it
90
90
 
91
91
  1. Finish EmDash setup as staff first. Store customers can't create the first account.
92
- 2. Open **Plugins → RedeemID → Setup**. Work down the steps until it says RedeemID is set up (it registers the webhooks for you). **Status** lists every setting as Set or Missing.
92
+ 2. Open **Plugins → RedeemID → Connection → Setup**. Work down the steps until it says RedeemID is set up (it registers the webhooks for you). **Status** lists every setting as Set or Missing.
93
93
  3. Tag a test customer `member` in Shopify, open a locked post, and sign in as that customer. The post opens.
94
94
  4. Remove the tag in Shopify. The post locks again once the webhook arrives.
@@ -31,7 +31,7 @@ Both caching helpers add the tag `redeemid` (`CACHE_TAG`). Saving on the Access
31
31
 
32
32
  A cached page is the same for everyone, signed-in members included, since nothing runs on a hit. Keep member-aware parts of cached pages, such as the header's Account link or a cart count, in the browser with [`window.redeemid`](developer-api.md), or leave those pages out of the cache.
33
33
 
34
- The Status page lists templates that call `Astro.cache.set()` on pages of a gated collection.
34
+ Connection → Status lists templates that call `Astro.cache.set()` on pages of a gated collection.
35
35
 
36
36
  ## Rate limits
37
37
 
@@ -45,8 +45,8 @@ Each action counts separately per visitor (by `CF-Connecting-IP`), and Cloudflar
45
45
 
46
46
  ## Troubleshooting
47
47
 
48
- - **A setting shows Missing on the Status page.** Check the name: the Status page lists any `REDEEMID_` variable it doesn't read. Set it as a runtime secret, not a build variable. On Workers, a deploy can remove plain-text dashboard variables, so use encrypted secrets.
48
+ - **A setting shows Missing on Connection → Status.** Check the name: Connection → Status lists any `REDEEMID_` variable it doesn't read. Set it as a runtime secret, not a build variable. On Workers, a deploy can remove plain-text dashboard variables, so use encrypted secrets.
49
49
  - **Sign-in comes back with a notice.** The code says why; see [notices](reference.md#notices). `sign_in_unavailable` means store discovery failed, usually a wrong `shop`. `not_configured` means the client ID or `shop` is missing.
50
50
  - **Register webhooks names a refused topic.** The app lacks a scope, such as `read_customer_merge` for `customers/merge`. Add it to the app, have the store accept the new version, and press Register again.
51
- - **Tags changed in Shopify but the site didn't follow.** Check Webhooks → Received and Health, and App events with Problems only. Resync the member from their page, or ask them to press Refresh membership.
51
+ - **Tags changed in Shopify but the site didn't follow.** Check Connection → Webhooks and Health, and App events with Problems only. Resync the member from their page, or ask them to press Refresh membership.
52
52
  - **A member sees the visitor's page.** A cache is serving it. See [caching](#caching).
package/docs/reference.md CHANGED
@@ -6,7 +6,7 @@ Pass one object to both `redeemid(config)` and `redeemidPlugin(config)`. `levels
6
6
 
7
7
  | Option | Type | What it does |
8
8
  | --- | --- | --- |
9
- | `shop` | `string` | The store's `*.myshopify.com` domain. A custom domain works for sign-in, but the Admin API (staff tools, webhook registration, tag reads) answers only on myshopify.com, and the Status page warns when the app is set up with a custom domain. Leave it out to read `REDEEMID_SHOPIFY_STORE_DOMAIN`. |
9
+ | `shop` | `string` | The store's `*.myshopify.com` domain. A custom domain works for sign-in, but the Admin API (staff tools, webhook registration, tag reads) answers only on myshopify.com, and Connection → Status warns when the app is set up with a custom domain. Leave it out to read `REDEEMID_SHOPIFY_STORE_DOMAIN`. |
10
10
  | `label` | `string` | The sign-in button's label. Default "Sign in". |
11
11
  | `levels` | `{ slug, name, tags?, match?, groups?, includes?, wall?, joinUrl?, products?, home?, badge? }[]` | Levels from customer tags. See [levels](access.md#levels). |
12
12
  | `rules` | `{ collection, taxonomy?, term?, levels, open?, drip?, meter? }[]` | What needs which levels. See [rules](access.md#rules). |
@@ -42,7 +42,7 @@ Pass one object to both `redeemid(config)` and `redeemidPlugin(config)`. `levels
42
42
  | `REDEEMID_SHOPIFY_STORE_DOMAIN` | Discovery | The store's domain, only when `shop` isn't in the config |
43
43
  | `REDEEMID_EVENTS_SECRET` | Events | Signs outbound events. Set it with `events.url`. |
44
44
 
45
- The Status page shows each one as Set or Missing, and lists any `REDEEMID_` variable that is set but not read, by name only, so a misspelt name stands out. Authorization, token and logout endpoints need no setting: RedeemID reads them from the store's discovery documents.
45
+ Connection → Status shows each one as Set or Missing, and lists any `REDEEMID_` variable that is set but not read, by name only, so a misspelt name stands out. Authorization, token and logout endpoints need no setting: RedeemID reads them from the store's discovery documents.
46
46
 
47
47
  ## Components
48
48
 
@@ -110,7 +110,7 @@ The full developer API (stable exports, JSON routes, the browser client and its
110
110
 
111
111
  Under `/_emdash/api/auth/redeemid/`: `login`, `callback`, `logout`, `webhook`, `buy`, `cart`, `cart-edit`, `account`, `preferences` and `privacy`, and the JSON routes `api/state`, `api/cart` and `api/search`. Staff-only routes for the admin, agents and the command line are under `/_emdash/api/plugins/redeemid/`.
112
112
 
113
- In the Customer Account API client, set the callback URI to `https://<site>/_emdash/api/auth/redeemid/callback`, the logout URI to `https://<site>/`, and (public clients) the JavaScript origin to `https://<site>`. Point webhooks at `https://<site>/_emdash/api/auth/redeemid/webhook`: `customers/create`, `customers/update`, `customers/delete`, `customers/disable`, `customers/enable`, `customers/merge`, `customer.tags_added` and `customer.tags_removed` (the Setup page registers these for you). The compliance topics `customers/data_request`, `customers/redact` and `shop/redact` go to the same URL, set in the app's own configuration.
113
+ In the Customer Account API client, set the callback URI to `https://<site>/_emdash/api/auth/redeemid/callback`, the logout URI to `https://<site>/`, and (public clients) the JavaScript origin to `https://<site>`. Point webhooks at `https://<site>/_emdash/api/auth/redeemid/webhook`: `customers/create`, `customers/update`, `customers/delete`, `customers/disable`, `customers/enable`, `customers/merge`, `customer.tags_added` and `customer.tags_removed` (Connection → Setup registers these for you). The compliance topics `customers/data_request`, `customers/redact` and `shop/redact` go to the same URL, set in the app's own configuration.
114
114
 
115
115
  ## Notices
116
116
 
@@ -63,8 +63,8 @@ After a purchase, **Refresh membership** on the member card reads their tags aga
63
63
 
64
64
  ## Check a recipe
65
65
 
66
- 1. Under **Plugins → RedeemID → Webhooks → Setup**, make sure `customer.tags_added` and `customer.tags_removed` show as Registered. The Setup page registers them for you.
66
+ 1. Under **Plugins → RedeemID → Connection → Status → Webhook subscriptions**, make sure `customer.tags_added` and `customer.tags_removed` show as Registered. The Setup tab registers them for you.
67
67
  2. Buy the product with a test customer, using Shopify's Bogus Gateway or a 100% discount code.
68
68
  3. In Flow, open the workflow's **Recent runs**. The run should show the tag added.
69
- 4. Open **Plugins → RedeemID → Members**, then the customer. The tag and level should be there.
69
+ 4. Open **Plugins → RedeemID → Members**, then the customer. The level should be on top and the tag under **Tags**.
70
70
  5. Refund the order. The tag and the level should both go.
@@ -2,7 +2,7 @@
2
2
 
3
3
  What to set up in Shopify, and on the site, to run RedeemID on an EmDash site. One store can serve several sites: each site gets its own Headless storefront for sign-in, and each receives webhooks.
4
4
 
5
- Once the package is installed, **Plugins → RedeemID → Setup** in the EmDash admin walks through these same steps with your site's own addresses filled in, checks each one, and registers the webhooks for you.
5
+ Once the package is installed, **Plugins → RedeemID → Connection → Setup** in the EmDash admin walks through these same steps with your site's own addresses filled in, checks each one, and registers the webhooks for you.
6
6
 
7
7
  ## 1. Sign-in: a Headless storefront
8
8
 
@@ -27,7 +27,7 @@ The store must use the new customer accounts (Settings → Customer accounts). C
27
27
 
28
28
  RedeemID keeps tags and status current from webhooks sent by a Shopify app on the store (for example a Dev Dashboard app). Shopify signs every webhook with that app's **client secret**, which is the site's webhook secret.
29
29
 
30
- Give the app the `read_customers` and `write_customers` Admin API scopes (`write_customers` alone is enough, since a write scope includes its read scope), plus `read_customer_merge` for `customers/merge`, and set its **client ID** on the site as well. Then the Setup page (or **Webhooks → Setup**) registers these topics at `https://<site>/_emdash/api/auth/redeemid/webhook` with one button:
30
+ Give the app the `read_customers` and `write_customers` Admin API scopes (`write_customers` alone is enough, since a write scope includes its read scope), plus `read_customer_merge` for `customers/merge`, and set its **client ID** on the site as well. Then Connection → Setup (or the Webhook subscriptions on Connection → Status) registers these topics at `https://<site>/_emdash/api/auth/redeemid/webhook` with one button:
31
31
 
32
32
  `customers/create`, `customers/update`, `customers/delete`, `customers/disable`, `customers/enable`, `customers/merge`, `customer.tags_added`, `customer.tags_removed`
33
33
 
package/docs/staff.md CHANGED
@@ -4,11 +4,11 @@
4
4
 
5
5
  With `REDEEMID_SHOPIFY_ADMIN_APP_CLIENT_ID` set, RedeemID gets an Admin API token for the webhook app with the client credentials grant. The app must be in the same Shopify organization as the store, as Dev Dashboard apps are, and needs `read_customers` and `write_customers`. Then, under **Plugins → RedeemID**:
6
6
 
7
- - **Member detail.** Levels, tags and features; resync from Shopify; add and remove tags. Tag edits are made in Shopify and read back. Nothing is granted on the site.
7
+ - **Member detail.** Account, levels and tag count on top, then tabs: Membership (levels, features, membership dates and level history), Tags (add in a dialog, remove), Account (email, consent, last sync and the site user) and Privacy. Resync from Shopify, and on a myshopify.com store, open the customer in Shopify. Tag edits are made in Shopify and read back. Nothing is granted on the site.
8
8
  - **Site user.** Signing in links a customer to the site user with the same verified email. When the emails differ, staff link the customer to an existing Subscriber by that user's email on the member's page (never a staff account, never a user linked to another customer), or unlink them. This part needs no Admin API app.
9
9
  - **Bulk resync.** Reads the 25 members synced longest ago, few enough for the Workers free limit of 50 database queries a request. Press it again for the next 25. It never runs on its own.
10
- - **Webhook subscriptions.** Webhooks → Setup lists the store's subscriptions to this site and registers any that are missing. A topic Shopify refuses, such as `customers/merge` without `read_customer_merge`, is named with the reason and doesn't stop the others. `customers/redact` stays in the app's own settings.
11
- - **Access test.** A collection, an entry, terms and a reader's tags in; the verdict out.
10
+ - **Webhook subscriptions.** Connection → Status → Webhook subscriptions lists the store's subscriptions to this site and registers any that are missing. A topic Shopify refuses, such as `customers/merge` without `read_customer_merge`, is named with the reason and doesn't stop the others. `customers/redact` stays in the app's own settings.
11
+ - **Access → Test.** A collection, an entry, terms and a reader's tags in; the verdict out.
12
12
 
13
13
  Resync updates email, name, tags, email marketing consent and the membership dates. Enabled or disabled status comes only from the enable and disable webhooks. With the app set up, two more things read the customer from the Admin API as it is now: every sign-in (and Refresh membership), and every `customer.tags_added` or `customer.tags_removed` webhook. Those topics carry only the change and can arrive out of order, so reading the current tags keeps the mirror right. Without the app, or when the read fails, the webhook's change is applied as sent.
14
14
 
@@ -25,14 +25,24 @@ A subscription app or Shopify Flow can keep these customer metafields, as dates
25
25
 
26
26
  RedeemID reads them with the Admin API (resync, sign-in, tag webhooks), so they need the Admin API app. They're for display only: access still comes from tags alone, and nothing on the site ends access on these dates. Time-boxed access is a Flow that waits and removes the tag (see [Shopify Flow recipes](shopify-flow.md)).
27
27
 
28
- ## Webhooks, app events and reports
28
+ ## Overview, dashboard card and Connection
29
+
30
+ **Plugins → RedeemID → Overview** is the first page: anything that needs doing (setup steps left, webhook problems, open privacy requests), cards for sign-in, members, webhooks, access, privacy and setup with links to each, members by level, recent app events and links to the docs, the store's customers and Flow. The **RedeemID** card on the EmDash dashboard shows the same alerts and counts with links to Overview, Members, Access and Connection's Webhooks tab (or its Setup tab while something is missing).
31
+
32
+ **Connection** holds everything about the store link in five tabs: Status, Setup, Webhooks, Health and App events. It opens on Setup until every step is done, then on Status. Each tab has its own address, such as `/_emdash/admin/plugins/redeemid/connection/webhooks`, which the dashboard card and Overview link to.
33
+
34
+ **Status** shows the addresses to paste into Shopify (callback URI, JavaScript origin, logout URI, webhook URL) and the store domain as read-only fields with copy buttons, every setting as Set or Missing with the environment variable it comes from, the template checks and the webhook subscriptions.
35
+
36
+ The admin pages are built as Block Kit on the server. The package's admin entry (`@redeemid/emdash/admin`, loaded by EmDash from the plugin descriptor) renders them in the browser and adds what Block Kit has no block for: copy fields, dialogs and linked cards. It needs `react`, `@cloudflare/kumo` and `@emdash-cms/blocks`, which every EmDash site already has through `emdash`.
37
+
38
+ ## Webhooks, health, app events and reports
29
39
 
30
40
  Members gain and lose access only when Shopify sends tag changes, so RedeemID shows what arrived and warns when deliveries stop.
31
41
 
32
- - **Webhooks → Received.** Each delivery's topic, what became of it (processed, skipped, older than what's stored, not used) and the customer ID, with counts of deliveries turned away (bad signature) and failed (storage errors; Shopify sends those again).
33
- - **Webhooks → Health.** Once a day RedeemID registers again any subscription Shopify dropped (with the Admin API app), and warns when no webhook has come in for a number of days (3 by default, 0 turns it off), or when deliveries were turned away or failed in the last day. **Check now** runs it at once. Warnings show on the page and the dashboard widget. If you enter an address on the Health tab and the site has an EmDash email provider, they also go by email: once when problems start or change, then once a week while they last.
34
- - **Webhooks → Setup.** The endpoint, whether signatures can be checked, and the subscriptions.
35
- - **App events.** What RedeemID did and what came of it: webhooks received, turned away and failed, sign-ins and refusals (by reason), Admin API calls (by operation, with timings), resyncs, tag edits, webhook registrations, settings saves, outbound events that failed, metered reads and the health checks. Filter by event or show problems only. Entries hold ids, labels and outcomes, never bodies, tokens or emails. Failed outbound events also keep the event body (ids and levels, never an email or a name) so they can be sent again.
42
+ - **Connection → Webhooks.** Each delivery's topic, what became of it (processed, skipped, older than what's stored, not used) and the customer ID, with counts of deliveries turned away (bad signature) and failed (storage errors; Shopify sends those again).
43
+ - **Connection → Health.** Once a day RedeemID registers again any subscription Shopify dropped (with the Admin API app), and warns when no webhook has come in for a number of days (3 by default, 0 turns it off), or when deliveries were turned away or failed in the last day. **Check now** runs it at once. Warnings show on the page and the dashboard widget. If you enter an address on the Health tab and the site has an EmDash email provider, they also go by email: once when problems start or change, then once a week while they last.
44
+ - **Webhook subscriptions** (on Connection → Status). The endpoint, whether signatures can be checked, and the subscriptions.
45
+ - **Connection → App events.** What RedeemID did and what came of it: webhooks received, turned away and failed, sign-ins and refusals (by reason), Admin API calls (by operation, with timings), resyncs, tag edits, webhook registrations, settings saves, outbound events that failed, metered reads and the health checks. Filter by event or show problems only. Entries hold ids, labels and outcomes, never bodies, tokens or emails. Failed outbound events also keep the event body (ids and levels, never an email or a name) so they can be sent again.
36
46
  - **Reports.** Members on each level, who joined and left in the last 30 days and by week for 12 weeks (from each member's tag dates), sign-ins and metered reads.
37
47
 
38
48
  App events and delivery records are kept 30 days. The daily check runs from EmDash's scheduler: on Node it runs by itself; on Cloudflare Workers, add a Cron Trigger to the site's Worker as EmDash's deployment guide describes. The plugin asks for `email:send` only to send these warnings.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redeemid/emdash",
3
- "version": "0.0.29",
3
+ "version": "0.0.31",
4
4
  "description": "Shopify commerce for EmDash sites: storefront, cart, checkout, customer accounts, memberships and paywalls. Shopify runs commerce; EmDash runs content.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -26,6 +26,7 @@
26
26
  "./text": "./src/text.ts",
27
27
  "./contract": "./src/contract.ts",
28
28
  "./plugin": "./src/plugin.ts",
29
+ "./admin": "./src/admin.ts",
29
30
  "./blocks": "./src/blocks/index.ts",
30
31
  "./components/*": "./src/components/*",
31
32
  "./routes/*": "./src/routes/*"
@@ -58,18 +59,26 @@
58
59
  "e2e": "node tests/e2e/run.mjs"
59
60
  },
60
61
  "peerDependencies": {
62
+ "@cloudflare/kumo": ">=2.6.0",
61
63
  "@emdash-cms/auth": ">=1.1.0",
64
+ "@emdash-cms/blocks": ">=1.4.0",
62
65
  "astro": ">=6",
63
- "emdash": ">=1.1.0"
66
+ "emdash": ">=1.4.0",
67
+ "react": ">=19"
64
68
  },
65
69
  "dependencies": {
66
70
  "jose": "^6.1.3"
67
71
  },
68
72
  "devDependencies": {
73
+ "@cloudflare/kumo": "^2.6.0",
74
+ "@emdash-cms/admin": "^1.4.0",
69
75
  "@emdash-cms/auth": "^1.4.0",
76
+ "@emdash-cms/blocks": "^1.4.0",
77
+ "@types/react": "^19.2.0",
70
78
  "astro": "^7.3.2",
71
79
  "emdash": "^1.4.0",
72
80
  "kysely": "^0.29.0",
81
+ "react": "19.2.4",
73
82
  "typescript": "^6.0.3",
74
83
  "vitest": "4.1.10"
75
84
  },