@drawbridge/drawbridge-agents 0.1.12 → 0.1.16
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/claude/CLAUDE.md
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# app-web gate helpers
|
|
2
|
+
|
|
3
|
+
Applies to `drawbridge-app-web` only.
|
|
4
|
+
|
|
5
|
+
UI gating goes through the shared helpers in `@/lib/helpers` — never inline the
|
|
6
|
+
underlying object walks:
|
|
7
|
+
|
|
8
|
+
- `hasCapability( organization, key, level = 'manage' )` — capability checks by
|
|
9
|
+
dot-path (`'campaigns.settings'`, `'brands'`). Never write
|
|
10
|
+
`organization?.capabilities?.x === 'manage'` inline; a missed optional chain
|
|
11
|
+
in that pattern has crashed pages before.
|
|
12
|
+
- `hasConnection( organization, slug )` — "org has an ACTIVE connection for
|
|
13
|
+
this slug". Reads `connectionItems` from the org payload (see the
|
|
14
|
+
organization-payload contract in `cross-repo-contracts.md`); never fetch
|
|
15
|
+
`/organization/:id/connections` just to gate UI.
|
|
16
|
+
- `hasRestriction( object, key )` — plan-limit restrictions on an org or
|
|
17
|
+
campaign.
|
|
18
|
+
|
|
19
|
+
Compose extras (`getPlanFeature(...).granted`, totals checks) explicitly at the
|
|
20
|
+
call site; the helpers stay single-purpose. Pass the org from the
|
|
21
|
+
`[ 'organization', id ]` query cache (or an `enabled : false` `useQuery` on
|
|
22
|
+
that key when the component must re-render as it refreshes).
|
|
@@ -26,6 +26,12 @@ skill) so it stops living only in tribal memory.
|
|
|
26
26
|
`queue/index.js` together.
|
|
27
27
|
- BullMQ jobs are keyed on the change-stream resume token as `jobId` for HA dedup across sync
|
|
28
28
|
replicas. Reusing a static `jobId` with `removeOnComplete` makes re-enqueues no-op silently.
|
|
29
|
+
- Scraped-asset file reuse keys on `organization + meta.origin + meta.element` **plus
|
|
30
|
+
`meta.render` for pipeline-rendered marks** (sync `resolveAsset` writes/queries it; the field
|
|
31
|
+
and its index live in drawbridge-api `schema/file.js`). `origin` identifies the source asset,
|
|
32
|
+
`render` content-hashes the rendered output — drop `render` from either side and a rendering
|
|
33
|
+
fix never ships: reuse serves the first-ever render forever (the onemethod
|
|
34
|
+
black-on-black-logo-twice bug).
|
|
29
35
|
|
|
30
36
|
## Billing / metering
|
|
31
37
|
|
|
@@ -46,7 +52,26 @@ skill) so it stops living only in tribal memory.
|
|
|
46
52
|
worker retries it into the update path. The merge path must delete duplicate contacts BEFORE
|
|
47
53
|
`$set`-ing the survivor (the expanded `leads` array collides with rows about to be deleted).
|
|
48
54
|
Drop the index → duplicate contacts, doubled segment members, double-billed `contact.create` +
|
|
49
|
-
`segment.join`; reorder the merge → every merge aborts E11000 and duplicates never heal.
|
|
55
|
+
`segment.join`; reorder the merge → every merge aborts E11000 and duplicates never heal. The
|
|
56
|
+
merge path also reverses each merged-away contact's **current-period** `action` rows (matched by
|
|
57
|
+
`meta.contact`, in the billing transaction) so the survivor isn't billed once per duplicate.
|
|
58
|
+
- The `action` ledger's **`{ organization : 1, createdAt : 1 }` index** (drawbridge-api
|
|
59
|
+
`schema/action.js`) is a shared dependency: sync's `computeCycleActions` (billing, by `createdAt`
|
|
60
|
+
window) AND api's date-ranged reporting (`route/organization-usage.js`) both match on it. Drop it
|
|
61
|
+
→ COLLSCAN on every invoice + usage query.
|
|
62
|
+
- The `action` ledger is **permanent — no TTL** (drawbridge-api `schema/action.js`): the
|
|
63
|
+
proof-of-billing record AND the source for date-ranged usage reporting, both read as raw rows by
|
|
64
|
+
`createdAt`. Re-adding a TTL shorter than any billing interval silently under-bills long cycles
|
|
65
|
+
and blanks reporting past the window — any retention change must clear billing + reporting.
|
|
66
|
+
|
|
67
|
+
## Reporting (api ↔ app-web)
|
|
68
|
+
|
|
69
|
+
- Every reporting request (usage/revenue/redemptions/analytics, org + admin) carries the viewer's
|
|
70
|
+
IANA `timezone` (app-web sends `Intl.DateTimeFormat().resolvedOptions().timeZone`); drawbridge-api
|
|
71
|
+
buckets the date range in it via `lib/report-window.js` (half-open UTC window, defaults to UTC when
|
|
72
|
+
absent). app-web omitting it → ranges bucket by UTC not the user's calendar days (boundary events
|
|
73
|
+
land on the wrong day). The param rides each endpoint's existing query shape (top-level vs
|
|
74
|
+
`filters`) — do NOT normalise the shapes.
|
|
50
75
|
|
|
51
76
|
## `@drawbridge/*` package coordination
|
|
52
77
|
|
|
@@ -75,3 +100,19 @@ skill) so it stops living only in tribal memory.
|
|
|
75
100
|
- The `write_resource_feedbacks` and `read_publications` scopes live in drawbridge-shopify-app's
|
|
76
101
|
`shopify.app.*.toml` but power other repos: sync's product ResourceFeedback sends and the
|
|
77
102
|
embedded app's publishing section. Removing either → silent Shopify userErrors, no crash.
|
|
103
|
+
- `SHOPIFY_REQUIRED_SCOPES` (`@drawbridge/shopify` `lib/constants.js`) must stay in lockstep with
|
|
104
|
+
`[access_scopes]` in drawbridge-shopify-app's `shopify.app.*.toml` (both files). drawbridge-api
|
|
105
|
+
diffs each store's granted scopes against it to flag "update permissions" on the connections
|
|
106
|
+
page. A scope added to the toml but not the constant silently escapes the check; one added to
|
|
107
|
+
the constant but not the toml flags every store forever.
|
|
108
|
+
|
|
109
|
+
## Organization payload (api ↔ app-web)
|
|
110
|
+
|
|
111
|
+
- `connectionItems` on the org payload comes only from drawbridge-api
|
|
112
|
+
`extend.organization({ connections : true })` (org GET + org menu opt in via
|
|
113
|
+
`hasOrganizationAccess`); app-web's `hasConnection` helper (`lib/helpers.js`) and api's
|
|
114
|
+
`lib/tasks.js connectionErrors` read it. Two invariants: the lookup's `$project`
|
|
115
|
+
(id/slug/status/title/shop/errors) is a **security boundary** — the org response spreads to the
|
|
116
|
+
browser, so adding a credential field ships connection tokens to every dashboard user; and api
|
|
117
|
+
deploys before app-web — the gate treats a missing field as "no connection" and silently hides
|
|
118
|
+
connection-gated UI (e.g. the campaign Shop product columns row).
|
package/package.json
CHANGED