@on-belay/sdk 2.2.1 → 3.0.0
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 +246 -0
- package/README.md +43 -6
- package/dist/index.d.mts +9928 -3408
- package/dist/index.d.ts +9928 -3408
- package/dist/index.js +13242 -5520
- package/dist/index.mjs +13171 -5520
- package/package.json +3 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# Changelog — `@on-belay/sdk`
|
|
2
|
+
|
|
3
|
+
## 3.0.0 — 2026-08-23
|
|
4
|
+
|
|
5
|
+
**A major version by semver, with near-zero real migration work.** Two exported functions
|
|
6
|
+
are gone and 24 change arity, which is unambiguously major — but the 23 are helpers that
|
|
7
|
+
**404 on every call in 2.2.1**, so no working code depends on their current shape. If your
|
|
8
|
+
fieldset compiles against 2.2.1 today and does not call an Acumatica detail helper, it
|
|
9
|
+
compiles against 3.0.0 unchanged.
|
|
10
|
+
|
|
11
|
+
### Why this release exists
|
|
12
|
+
|
|
13
|
+
`scripts/generate-integration-helpers.ts` imported only `prisma/operation-defs-part1` and
|
|
14
|
+
`-part2`, by name, while the operation catalog had grown to `part15` — so 330 operations
|
|
15
|
+
across 15 integrations were unreachable by construction. On top of that, the generator had
|
|
16
|
+
not been run in full since **2026-05-11** (SDK 2.2.0), so even operations added to part1 and
|
|
17
|
+
part2 after that date never reached the package. `shopmonkey.listInventoryParts` below is one
|
|
18
|
+
of those: it was catalogued on 2026-08-20 in *part2*, a file the generator did read.
|
|
19
|
+
|
|
20
|
+
Both gaps were invisible for the same reason: nothing compared the generated output to the
|
|
21
|
+
catalog. The only changes the generated directory received in those three months were two
|
|
22
|
+
single-file **hand-edits** (#825, #1059) to files whose header says
|
|
23
|
+
*"AUTO-GENERATED — do not edit by hand"*. Both happen to survive this regeneration — the
|
|
24
|
+
`get_video_analytics` deletion because its catalog row was deleted too, and the
|
|
25
|
+
`ShopifySlug` narrowing because it was folded into the generator — but that was luck, and
|
|
26
|
+
nothing was checking.
|
|
27
|
+
|
|
28
|
+
| | 2.2.1 | 3.0.0 |
|
|
29
|
+
|---|---:|---:|
|
|
30
|
+
| Operations exposed | 941 | **1,547** |
|
|
31
|
+
| Integration modules | 75 | **110** |
|
|
32
|
+
|
|
33
|
+
### Added — 608 helpers, 35 new integration modules
|
|
34
|
+
|
|
35
|
+
`air`, `airtable`, `amazon-ads`, `applovin`, `box`, `celigo`, `cloudflare`, `cloudinary`,
|
|
36
|
+
`feishu`, `gemini`, `gooddata`, `google-business-profile`, `impact`, `instagram-insights`,
|
|
37
|
+
`instagram-public`, `judge-me`, `knocommerce`, `linkedin-pages`, `magento`, `microsoft-ads`,
|
|
38
|
+
`mixpanel`, `okta`, `openai-ads`, `pinterest-organic`, `railway`, `rippling`, `salsify`,
|
|
39
|
+
`semrush`, `sharepoint`, `shopmy`, `smile-io`, `teachable`, `tiktok-organic`,
|
|
40
|
+
`transcriptapi`, `web-search`, `windsor`.
|
|
41
|
+
|
|
42
|
+
440 of the 606 net-new helpers are in those 35 modules; the other 166 landed on modules that
|
|
43
|
+
already existed. Two examples of what the gap cost in practice:
|
|
44
|
+
|
|
45
|
+
- **`shopmonkey.listInventoryParts`** (`/v3/inventory_part`). A production fieldset
|
|
46
|
+
hand-rolled a raw `executeProxyCall` around this, with the comment *"the SDK exposes only
|
|
47
|
+
a generic `listInventory` (→ /v3/inventory); the spec's op is `list_inventory_parts`
|
|
48
|
+
(→ /v3/inventory_part)"* — `owl-shopmonkey-import/src/lib/inventory-resolve.ts:233`. It is
|
|
49
|
+
a typed helper now, and that workaround can be deleted.
|
|
50
|
+
- **`amazonSeller.pullConnector`** (`/amazon_sp`). The only Amazon operation that returns
|
|
51
|
+
real Amazon data. The SDK previously exposed ten Amazon operations that the proxy silently
|
|
52
|
+
rewrites to Windsor aggregates, and not this one.
|
|
53
|
+
|
|
54
|
+
### Fixed — 23 Acumatica helpers built a malformed URL and 404'd on every call
|
|
55
|
+
|
|
56
|
+
`analyzePathPattern` converts a regex `pathPattern` into a template literal. A trailing bare
|
|
57
|
+
character class with no quantifier — Acumatica's `[^?/]` — matched none of its substitutions,
|
|
58
|
+
so `split("/")` sheared the class and the emitted path ended in the literal text `[^`.
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
2.2.1 /entity/Default/${defaultId}/SalesOrder/[^
|
|
62
|
+
3.0.0 /entity/Default/${endpointVersion}/SalesOrder/${salesOrderId}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
This shipped: `npm unpack @on-belay/sdk@2.2.1` shows the leaked `[^` in `dist/index.js`.
|
|
66
|
+
Affected every detail fetch/update/delete across `SalesOrder`, `Customer`, `Vendor`,
|
|
67
|
+
`StockItem`, `NonStockItem`, `PurchaseOrder`, `Invoice`, `Payment`, `Contact`, `Employee`,
|
|
68
|
+
`GeneralLedgerTransaction`, `Account`, `Bill`.
|
|
69
|
+
|
|
70
|
+
**Each of these helpers now takes one more argument** — the resource key that used to be
|
|
71
|
+
`[^`. That is the breaking change, and it is the fix.
|
|
72
|
+
|
|
73
|
+
Also fixed: `sharepoint.searchDrive` emitted `/root/searchq='[^']*'` instead of
|
|
74
|
+
`/root/search(q='${param1}')`. New in this release, so nothing depended on the broken form.
|
|
75
|
+
|
|
76
|
+
### Fixed — 14 Google helpers dropped the `:action` suffix from the path
|
|
77
|
+
|
|
78
|
+
`ga4.run_report.*`, `gemini.generateContent`, `gemini.streamGenerateContent`,
|
|
79
|
+
`gemini.embedContent`, `gemini.countTokens` and others emitted the resource path with the
|
|
80
|
+
action suffix **missing**:
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
2.2.1 /v1beta/properties/${propertyId}
|
|
84
|
+
3.0.0 /v1beta/properties/${propertyId}:runReport
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
A path segment that merely *contained* the parameter placeholder was being collapsed down
|
|
88
|
+
to the placeholder, so anything else in that segment was thrown away. Unlike the Acumatica
|
|
89
|
+
case this leaves nothing regex-shaped behind, so it is invisible to a "did any regex
|
|
90
|
+
punctuation survive?" check — the path is simply, silently, a different endpoint. Our own
|
|
91
|
+
proxy rejects it too, because the proxy validates the request path against the same
|
|
92
|
+
`pathPattern`.
|
|
93
|
+
|
|
94
|
+
The same collapse was dropping the literal `v` from version segments, so 27 Google Ads and
|
|
95
|
+
Salesforce helpers emitted `/${version}/…` where the API needs `/v18/…`. Now
|
|
96
|
+
`/v${version}/…`.
|
|
97
|
+
|
|
98
|
+
### Fixed — a regex quantifier ate a parameter
|
|
99
|
+
|
|
100
|
+
`judgeMe.getProduct` emitted the literal path `/products/-`. Its pattern is
|
|
101
|
+
`^/products/-?\d+`; the parameter was substituted, then the query-string strip cut from the
|
|
102
|
+
`-?` quantifier onward and took the parameter with it. Now `/products/${productId}` — and
|
|
103
|
+
the helper takes the `productId` argument it always should have.
|
|
104
|
+
|
|
105
|
+
### Fixed — 10 helpers were unparseable TypeScript
|
|
106
|
+
|
|
107
|
+
44 catalog keys are dotted (`shopify.themes.list`, `ga4.run_report.overall`). The generator
|
|
108
|
+
left the dots in the function name, emitting `export async function shopify.themes.list<T>(`.
|
|
109
|
+
Ten such keys were already reachable, so the generator produced a file that would not parse.
|
|
110
|
+
Identifiers are now sanitized (`shopifyThemesList`); the dotted string is unchanged as the
|
|
111
|
+
operation key sent to the proxy.
|
|
112
|
+
|
|
113
|
+
### Not changed — Shopify REST paths still omit `.json`
|
|
114
|
+
|
|
115
|
+
Unchanged from 2.2.1. Recorded because it was changed and reverted twice during review, and
|
|
116
|
+
the reasoning is worth having in one place.
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
2.2.1 and 3.0.0 /admin/api/{version}/orders
|
|
120
|
+
catalog pattern ^/admin/api/[^/]+/orders\.json ← requires .json
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Your calls are not failing.** Shopify accepts both forms — 154 json-less `/admin/api/`
|
|
124
|
+
calls over 30 days, **every one status 200**, zero failures. And the platform check that
|
|
125
|
+
*would* reject a json-less path is unreachable from a fieldset: it runs only for
|
|
126
|
+
group-access callers with an enforcing grant, and fieldset tokens never take that branch.
|
|
127
|
+
|
|
128
|
+
⚠️ **The argument for the other choice, stated fairly:** all 17 of the catalog's
|
|
129
|
+
hand-curated Shopify `/admin/api/` `examplePath` values carry `.json`. That is real, and it
|
|
130
|
+
is why this flipped once. It was judged **documentation** divergence rather than evidence of
|
|
131
|
+
breakage — a 55-path behaviour change needs a functional reason, and there is none for this
|
|
132
|
+
package's consumers. Those 17 appear in `PATH-DIVERGENCE.json` as expected entries.
|
|
133
|
+
|
|
134
|
+
### Changed — caller-supplies-the-path operations now take that path as a parameter
|
|
135
|
+
|
|
136
|
+
Three patterns are bare prefixes rather than fixed endpoints. `shopify.admin.fetch` says so
|
|
137
|
+
in its own description — *"The path is chosen by the caller, so this operation is broad"* —
|
|
138
|
+
and its example is `/admin/api/2026-01/shop.json`. The generator emitted the prefix with no
|
|
139
|
+
parameter, so the helper could not reach any resource:
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
2.2.1 shopifyAdminFetch(orgId, slug, options) → /admin/api/2024-01
|
|
143
|
+
3.0.0 shopifyAdminFetch(orgId, slug, resourcePath, …) → /admin/api/2026-01/${resourcePath}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
5 operations: `shopify.admin.fetch`, github `get_file_contents` / `create_update_file`,
|
|
147
|
+
cloudinary `get_asset` / `delete_asset`. All five are new in 3.0.0 except
|
|
148
|
+
`shopify.admin.fetch`, whose 2.2.1 form could not be used for anything.
|
|
149
|
+
|
|
150
|
+
### Known — 5 operations emit a path their pattern rejects, and are still shipped
|
|
151
|
+
|
|
152
|
+
`gmail.searchMessages`, `youtube.getVideo`, `googleDrive.downloadFile`,
|
|
153
|
+
`microsoftTeams.listTeams`, `yotpo.getProductReviews`. Their patterns bake a
|
|
154
|
+
**caller-supplied query value** into the path (`\?q=`, `\?.*id=`, `\?alt=media`, `\?.*resourceProvisioningOptions`), which a path
|
|
155
|
+
template cannot express.
|
|
156
|
+
|
|
157
|
+
**They are shipped, deliberately.** `youtube.getVideo` emits `/youtube/v3/videos` and that
|
|
158
|
+
exact path has **3 live 200s** in the last 90 days — pass the id in `queryParams`. An earlier
|
|
159
|
+
draft of this release omitted several of these; that was reverted. The patterns are
|
|
160
|
+
over-specified, and loosening them is a catalog change tracked separately.
|
|
161
|
+
|
|
162
|
+
All divergences are listed in `src/integrations/PATH-DIVERGENCE.json`.
|
|
163
|
+
|
|
164
|
+
### Fixed — Shopify Admin API version was `2024-01`
|
|
165
|
+
|
|
166
|
+
Over a year past Shopify's end-of-life for that version. Now `2026-01`, read from the
|
|
167
|
+
platform's own `SHOPIFY_API_VERSION` constant at generate time so the two cannot drift.
|
|
168
|
+
|
|
169
|
+
### Breaking changes — complete list
|
|
170
|
+
|
|
171
|
+
**27 changes require a caller to change code. 24 of them are path fixes.**
|
|
172
|
+
|
|
173
|
+
| Change | Count | Source |
|
|
174
|
+
|---|---:|---|
|
|
175
|
+
| Acumatica detail helpers take one more argument | 22 | **This release** — the 404 fix |
|
|
176
|
+
| `github.getFileContents` / `createUpdateFile` take a `resourcePath` argument | 2 | **This release** — the bare-prefix fix |
|
|
177
|
+
| `shopify.getAnalyticsReport`: `options.queryParams` → `options.body` | 1 | Catalog |
|
|
178
|
+
| `shopmonkey.getOrderFees` removed | 1 | Catalog |
|
|
179
|
+
| `shopmonkey.listInventory` removed | 1 | Catalog |
|
|
180
|
+
|
|
181
|
+
The last three are **catalog-driven, not authored in the SDK** — upstream operation
|
|
182
|
+
definitions changed and the SDK had simply not been regenerated since. `getAnalyticsReport`
|
|
183
|
+
moved because Shopify **deleted** the REST `reports.json` resource, so the operation is a
|
|
184
|
+
GraphQL POST now. Both `shopmonkey` keys were deleted from the catalog because the endpoints
|
|
185
|
+
no longer exist; `/v3/order/{id}/fee` 404s.
|
|
186
|
+
|
|
187
|
+
**41 parameters were renamed. These are not breaking for positional calls** — arity and
|
|
188
|
+
types are unchanged, and every helper is called positionally. They are corrections to names
|
|
189
|
+
a developer would otherwise trust and get wrong:
|
|
190
|
+
|
|
191
|
+
- `defaultId` → `endpointVersion` (Acumatica). The segment after `/entity/Default/` is the
|
|
192
|
+
endpoint *version* — `Default` is the endpoint *name* — so the old name read as an
|
|
193
|
+
identifier of something called Default.
|
|
194
|
+
- `salesorderId` → `salesOrderId`, `inventoryitemId` → `inventoryItemId`,
|
|
195
|
+
`generalledgertransactionId` → `generalLedgerTransactionId`, `localpostId` → `localPostId`,
|
|
196
|
+
and similar across `acumatica`, `netsuite`, `linkedin-ads`, `gooddata`,
|
|
197
|
+
`google-business-profile`. Names were being derived from a lowercased string, collapsing
|
|
198
|
+
the word boundaries.
|
|
199
|
+
- `accountId` → `adAccountId` on six `redditAds` helpers (catalog-driven).
|
|
200
|
+
|
|
201
|
+
### Known affected consumers: none
|
|
202
|
+
|
|
203
|
+
`owl-shopmonkey-import` is the only external consumer. It pins `^2.2.0`, so it will **not**
|
|
204
|
+
pick up 3.0.0 automatically, and it calls none of the removed or changed helpers — verified
|
|
205
|
+
against its source: it uses `shopify.listInventoryLevels` and raw `executeProxyCall`.
|
|
206
|
+
|
|
207
|
+
### Guardrails added, so this cannot recur silently
|
|
208
|
+
|
|
209
|
+
- The generator reads `prisma/operation-defs.ts` — the same aggregate seeded into the live
|
|
210
|
+
`IntegrationOperation` table — and **fails loudly** if any `operation-defs-part*.ts` file
|
|
211
|
+
on disk holds operations the aggregate does not carry. A future `part16` that nobody wires
|
|
212
|
+
in aborts the run and names the missing keys.
|
|
213
|
+
- **A path-divergence report.** After conversion, a value is substituted for every
|
|
214
|
+
`${param}` and the concrete path is tested against the source `pathPattern`. Mismatches
|
|
215
|
+
are written to a committed `src/integrations/PATH-DIVERGENCE.json` and the set is **pinned
|
|
216
|
+
by the test suite**, so it cannot grow unnoticed. Current run: **1,547 of 1,547 emitted,
|
|
217
|
+
0 omitted, 64 divergences recorded.**
|
|
218
|
+
🚫 It is deliberately **not** a gate. It was one, briefly, and as a gate it removed 55
|
|
219
|
+
working Shopify helpers plus `youtube.getVideo` — whose "broken" path has three live
|
|
220
|
+
200s. A divergence says the template and the pattern disagree; it does not say which is
|
|
221
|
+
wrong.
|
|
222
|
+
- Colliding helper names, module filenames, or barrel namespaces abort the run instead of
|
|
223
|
+
silently shadowing.
|
|
224
|
+
- CI asserts the generated output matches the catalog exactly, in both directions, plus
|
|
225
|
+
identifier validity, barrel uniqueness, regression pins on each non-trivial path idiom,
|
|
226
|
+
and the Shopify version mirror. The staleness that hid these operations for three months
|
|
227
|
+
is now a red build, not a silence.
|
|
228
|
+
- ⚠️ **The divergence check replaced an earlier "no regex punctuation survived" check,
|
|
229
|
+
which was blind in both directions** and is the reason three defect classes in this
|
|
230
|
+
release shipped in 2.2.1 at all. A *dropped* literal leaves nothing regex-shaped behind
|
|
231
|
+
(`:runReport` vanishing passed it cleanly), and `?` is both regex punctuation and an
|
|
232
|
+
ordinary URL character (so google_drive's correct path was refused). The round-trip
|
|
233
|
+
subsumes it and needs no list of known idioms, so a shape nobody has seen yet is still
|
|
234
|
+
caught.
|
|
235
|
+
- **The examplePath check is the primary path signal.** 616 ops carry a hand-curated
|
|
236
|
+
`examplePath`; the test asserts every emitted template can produce its own. It caught what
|
|
237
|
+
nothing else could — `shopify.admin.fetch`'s bare prefix passed the pattern round-trip,
|
|
238
|
+
because a prefix pattern matches trivially. Three blind spots in sequence: punctuation
|
|
239
|
+
missed deletions, the round-trip missed under-specification, the example caught it.
|
|
240
|
+
**17 known mismatches**, pinned: 15 shopify + 1 yotpo are the deliberate json-less form
|
|
241
|
+
above, and 1 is `cloudinary.uploadAssetRemote`, where the pattern is a 4-way alternation
|
|
242
|
+
(`^/(image|video|auto|raw)/upload`) collapsed to the first option.
|
|
243
|
+
- Real path defects are pinned by tests that encode what the **upstream API** requires,
|
|
244
|
+
not agreement with a regex: the `:action` suffix and `v` version literal (28,142 live
|
|
245
|
+
google_ads calls, 100% carrying both), acumatica's sheared character class, and
|
|
246
|
+
judge_me's `/products/-`. Evidence about the API is the authority — a pattern is not.
|
package/README.md
CHANGED
|
@@ -375,7 +375,7 @@ import { executeProxyCall } from "@on-belay/sdk"
|
|
|
375
375
|
const result = await executeProxyCall<{ products: Array<{ id: number; title: string }> }>(
|
|
376
376
|
orgId,
|
|
377
377
|
"shopify",
|
|
378
|
-
"
|
|
378
|
+
"list_products",
|
|
379
379
|
"/admin/api/2024-01/products.json?limit=1",
|
|
380
380
|
{ method: "GET" },
|
|
381
381
|
{ fieldsetSlug: "my-fieldset" },
|
|
@@ -395,6 +395,25 @@ console.log(result.data.products[0]?.title)
|
|
|
395
395
|
|
|
396
396
|
The org must have the integration connected; your fieldset's `requiredOperations` must include `operationKey`; the org must be enrolled in your fieldset. Each of those gates returns a different `ProxyErrorCode`.
|
|
397
397
|
|
|
398
|
+
> **`operationKey` is the operation's own catalog key, matched by exact string
|
|
399
|
+
> equality.** `list_products`, `create_contact`, `shopify.themes.assets.update` —
|
|
400
|
+
> passed alongside a separate `integrationSlug` argument. It is **not** a
|
|
401
|
+
> resource-and-verb path: `"shopify.products.list"` is not a catalog key on anything
|
|
402
|
+
> and is refused. There is no normalization, aliasing or wildcarding: the key you
|
|
403
|
+
> pass must be byte-identical to the one in your `requiredOperations`, which must in
|
|
404
|
+
> turn be a real key on that integration. Some catalog keys contain dots of their
|
|
405
|
+
> own; paste those exactly as written. The
|
|
406
|
+
> [typed integration helpers](#typed-integration-helpers) are generated from the
|
|
407
|
+
> catalog, so using them removes this class of mistake entirely.
|
|
408
|
+
>
|
|
409
|
+
> On the **application form** the pair is written as one string,
|
|
410
|
+
> `integrationSlug.operationKey` — e.g. `shopify.list_products`. A few catalog keys
|
|
411
|
+
> are themselves slug-prefixed, so an entry like `reddit_ads.get_report` reads two
|
|
412
|
+
> ways (the key `reddit_ads.get_report`, or the key `get_report` on `reddit_ads` —
|
|
413
|
+
> two different operations, with different path patterns). Those are rejected rather
|
|
414
|
+
> than guessed; write them with a colon — `reddit_ads:get_report` — which is never
|
|
415
|
+
> ambiguous.
|
|
416
|
+
|
|
398
417
|
### `getOrgContext`
|
|
399
418
|
|
|
400
419
|
```ts
|
|
@@ -669,7 +688,7 @@ class OnbelayClient {
|
|
|
669
688
|
isEnrolled(orgId: string): Promise<boolean>
|
|
670
689
|
getEnrolledOrgs(): Promise<string[]>
|
|
671
690
|
|
|
672
|
-
shopify(orgId: string
|
|
691
|
+
shopify(orgId: string): ShopifySubClient
|
|
673
692
|
// + one accessor per integration (hubspot, klaviyo, linear, …)
|
|
674
693
|
}
|
|
675
694
|
```
|
|
@@ -727,7 +746,7 @@ The full type surface re-exported from the package root:
|
|
|
727
746
|
| `ContentType` | Free-form string alias used by `recordPublish`. |
|
|
728
747
|
| `PublishResult` | `recordPublish` return shape. |
|
|
729
748
|
| `LogActionResult` | `logAction` return shape (`{ ok: boolean }`). |
|
|
730
|
-
| `ShopifySlug` | `"shopify"
|
|
749
|
+
| `ShopifySlug` | `"shopify"` — accepted by the Shopify integration helpers. |
|
|
731
750
|
| `WebhookPayload` | Inbound webhook body. See [§8](#8-webhook-payload-contract). |
|
|
732
751
|
| `WebhookHandlerOptions`, `WebhookHandlerContext`, `WebhookResult` | `createOnbelayWebhookHandler` shapes. |
|
|
733
752
|
| `WebhookVerifyOptions` | Options for `validateWebhookSignature` / handler. |
|
|
@@ -820,9 +839,27 @@ The full webhook contract is specified in `qa-brightline-dod-external-fieldset-s
|
|
|
820
839
|
If you ship an embedded UI, the platform renders it as an iframe at `/dashboard/fieldsets/[slug]` with `sandbox="allow-scripts allow-forms"`. The handshake:
|
|
821
840
|
|
|
822
841
|
1. Iframe loads. It posts `{ type: "onbelay:ready" }` to `https://app.onbelay.ai`.
|
|
823
|
-
2. Platform issues a
|
|
842
|
+
2. Platform issues a **1-hour** HS256 JWT and posts `{ type: "onbelay:context", token, proxyUrl, fieldsetSlug }` back to the iframe. There is **no top-level `orgId` or `userId`** — both are inside the JWT and are returned by `validateDashboardToken`. `proxyUrl` is a **base** URL (e.g. `https://app.onbelay.ai`), the same shape as `ONBELAY_PROXY_URL` and `OnbelayConfig.proxyUrl` — the SDK appends `/api/sdk/proxy` itself, so do **not** append it yourself or you will request `.../api/sdk/proxy/api/sdk/proxy`.
|
|
824
843
|
3. Iframe sends `token` to **its own backend**, which calls `validateDashboardToken(token, ONBELAY_DASHBOARD_SECRET)` and then performs proxy calls scoped to the validated `orgId`.
|
|
825
|
-
4.
|
|
844
|
+
4. The platform re-issues a fresh token on `visibilitychange` **and** on a ~10-minute timer, so your listener must tolerate repeated `onbelay:context` messages — each supersedes the last.
|
|
845
|
+
|
|
846
|
+
> ⚠️ **Do not write the guard yourself — use `isOnbelayContextMessage`.**
|
|
847
|
+
>
|
|
848
|
+
> ```ts
|
|
849
|
+
> import { isOnbelayContextMessage } from "@on-belay/sdk"
|
|
850
|
+
>
|
|
851
|
+
> window.addEventListener("message", (event) => {
|
|
852
|
+
> if (event.origin !== "https://app.onbelay.ai") return
|
|
853
|
+
> if (!isOnbelayContextMessage(event.data)) return
|
|
854
|
+
> // event.data is narrowed: { type, token, proxyUrl, fieldsetSlug }
|
|
855
|
+
> })
|
|
856
|
+
> ```
|
|
857
|
+
>
|
|
858
|
+
> A hand-written guard of the shape `if (!d.token || !d.orgId) return` rejects
|
|
859
|
+
> **every** message, because `orgId` is not at the top level. The symptom is not an
|
|
860
|
+
> error — the UI renders and every API call becomes a silent no-op. This is a real
|
|
861
|
+
> incident, not a hypothetical: it is what happened on `owl-shopmonkey-import`
|
|
862
|
+
> (`docs/fieldsets/owl-shopmonkey-import/specs/qa-test-pass.md:81`).
|
|
826
863
|
|
|
827
864
|
```ts
|
|
828
865
|
// Inside your iframe (browser):
|
|
@@ -853,7 +890,7 @@ if (!ctx) return new Response(JSON.stringify({ error: "invalid_token" }), { stat
|
|
|
853
890
|
const result = await executeProxyCall(
|
|
854
891
|
ctx.orgId,
|
|
855
892
|
"shopify",
|
|
856
|
-
"
|
|
893
|
+
"list_products",
|
|
857
894
|
"/admin/api/2024-01/products.json",
|
|
858
895
|
undefined,
|
|
859
896
|
{ fieldsetSlug: ctx.fieldsetSlug },
|