@assembly-js/app-foundation 0.0.49 → 0.0.50
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
CHANGED
|
@@ -14,6 +14,9 @@ A foundation update reads the sections between an app's version and the target:
|
|
|
14
14
|
from every bullet except `[guidance]`, agent work from the check scripts those `[action]`
|
|
15
15
|
bullets ship. Write `[fix]`, `[behavior]` and `[action]` bullets for the app's owner.
|
|
16
16
|
|
|
17
|
+
## 0.0.50
|
|
18
|
+
- [guidance] The build agent is no longer told to reinstall packages that come pre-installed, offered a toast library the platform bans, or shown a secret type the manifest rejects, and its header guidance no longer contradicts itself when the app owns its header. It is also told why the dashboard and client surfaces take separate routes and to detect the viewer with `getSessionIdentity()`.
|
|
19
|
+
|
|
17
20
|
## 0.0.49
|
|
18
21
|
- [fix] Scheduled-work wiring, proxy, and session-token problems are now caught when the platform updates the app, not only when the app's own change touches them.
|
|
19
22
|
|
package/CLAUDE.md
CHANGED
|
@@ -41,7 +41,9 @@ Assembly-specific gotchas live in `.claude/skills/<name>/SKILL.md`. Invoke them
|
|
|
41
41
|
- `assembly-automations` — REQUIRED when the request asks for a platform-side effect in reaction to an in-app event (a message, a CRM field, a contract, a form, a file — not the app's own tables), even if the user never says "automation": declare the app's API contract in `.assembly/api-resources.json` (a separate file from `app.json`) — domain events that become automation triggers + workspace webhooks, and staff-invokable operations that become automation actions. Declared events must also be emitted at runtime — inside `after()`, reporting the acting user on the documented headers — declaring alone never fires anything. Read it before adding either.
|
|
42
42
|
<!-- /feature:api-resources -->
|
|
43
43
|
- `assembly-app-surfaces` — REQUIRED before writing `.assembly/app.json`: declare which surfaces (dashboard, client portal, notification) the app exposes; omitting the client surface keeps the app off client portal nav entirely
|
|
44
|
+
<!-- !feature:app-owned-header -->
|
|
44
45
|
- `assembly-app-bridge-header` — breadcrumbs (don't include app name), CTAs
|
|
46
|
+
<!-- /feature:app-owned-header -->
|
|
45
47
|
- `assembly-app-bridge-toast` — show toast notifications in the parent dashboard
|
|
46
48
|
<!-- feature:deep-link-bridge-nav -->
|
|
47
49
|
- `assembly-platform-links` — link platform records (client, company, invoice, contract, form, file) to their portal detail page via `linkTo()`; never hand-build a host URL. Also links between workspace apps by install id. Check the app's installed app-bridge first (see the skill) — `navigateTo` doesn't exist on older peers — and only link staff-only destinations (client, company, form, etc.) from the dashboard surface, never the client portal.
|
|
@@ -457,9 +459,9 @@ Because the app runs in an iframe inside the parent dashboard, several normal Ne
|
|
|
457
459
|
- **Cookies must be `sameSite: 'none'` and `secure: true`.** Anything else is dropped silently in cross-origin iframes.
|
|
458
460
|
- **No top-level navigation.** Don't use `window.location = '...'` to go to external URLs — it'll either break out of the iframe or be blocked. For external links use `<a target="_blank" rel="noreferrer">`.
|
|
459
461
|
- **No `window.open(..., '_self')`.** Same issue.
|
|
462
|
+
<!-- !feature:app-owned-header -->
|
|
460
463
|
- **The portal owns the chrome (header, sidebar).** Don't render your own breadcrumbs or page titles via DOM. Use `@assembly-js/app-bridge` to set them.
|
|
461
464
|
- **Never put the app name in breadcrumbs.** The parent portal *automatically* prepends the app's name as breadcrumb 0. If you also pass `{ label: 'MyApp' }` as the first entry of `setBreadcrumbs`, it renders twice ("MyApp / MyApp / Reports"). For a flat one-page app pass `[]` (or skip `setBreadcrumbs` entirely). For a multi-level app pass only the sub-pages: `setBreadcrumbs([{ label: 'Reports' }, { label: 'Q4 2026' }])`. Read `.claude/skills/assembly-app-bridge-header/SKILL.md` for full examples.
|
|
462
|
-
<!-- !feature:app-owned-header -->
|
|
463
465
|
- **Action buttons go in the header — set via the `useAssemblyHeader` hook.** Submit, Save, Edit, Create — any primary page-level action MUST live in the header (`primaryCta`), not a `<Button>` in the page body. Secondary actions use `secondaryCta`; overflow/tertiary actions use `actionsMenu`. Drive all of it through the `useAssemblyHeader` hook (`app/components/use-assembly-header.ts`) — **never call `AssemblyBridge.header.set*` from your own `useEffect`** (that hand-rolling is what makes header buttons vanish after a re-render). See `.claude/skills/assembly-app-bridge-header/SKILL.md` for the full API.
|
|
464
466
|
**Exception: the notification surface (`/n`).** The Notification Center embeds the `/n` route in a plain `<iframe>` without app bridge header support. `useAssemblyHeader` calls are silently ignored — header CTAs never appear. On the notification surface, render primary actions as inline `<Button>` elements in the page body. Detect the notification surface by the route — code under `app/n/` IS the surface; for a shared component pass an explicit prop from the `/n` page rather than inferring from `payload.notificationId`, which is best-effort under the session-exchange flow. This exception applies ONLY to the `/n` route; all other surfaces MUST use the header. **Do not reuse a component that calls `useAssemblyHeader` on the `/n` surface** — if it also has an inline action button, both the header CTA and the inline button will render on non-notification surfaces (duplicate CTAs). Either create a separate component for `/n` or accept a prop that switches between header and inline CTAs.
|
|
465
467
|
<!-- /feature:app-owned-header -->
|
|
@@ -528,7 +530,7 @@ Two paths, decided by whether an `assembly-integrations` skill is present and, i
|
|
|
528
530
|
"key": "SENDGRID_API_KEY",
|
|
529
531
|
"label": "SendGrid API key",
|
|
530
532
|
"description": "Used to send ticket-submitted emails. Create one under SendGrid Settings > API Keys.",
|
|
531
|
-
"type": "
|
|
533
|
+
"type": "api_key",
|
|
532
534
|
"docsUrl": "https://docs.sendgrid.com/ui/account-and-settings/api-keys"
|
|
533
535
|
}
|
|
534
536
|
]
|
|
@@ -642,9 +644,11 @@ If you forget this, `bun`/`bunx` commands will fail with "command not found". Th
|
|
|
642
644
|
### Install (use bun)
|
|
643
645
|
|
|
644
646
|
```bash
|
|
645
|
-
export PATH="$HOME/.bun/bin:$PATH" && bun add
|
|
647
|
+
export PATH="$HOME/.bun/bin:$PATH" && bun add <extra-package>
|
|
646
648
|
```
|
|
647
649
|
|
|
650
|
+
`@assembly-js/node-sdk` and `@assembly-js/app-bridge` are already installed, so don't `bun add` them. Use `bun add` only for an extra package the task needs.
|
|
651
|
+
|
|
648
652
|
### Inner verification loop — type-check, don't full-build
|
|
649
653
|
|
|
650
654
|
Use `bunx tsc --noEmit` to verify your changes while iterating. It runs in 2–3s and gives you the full, untruncated type errors. Reserve `bun run build` (~15–20s for a full Next build) for the **final** check at the end of the task, not after every edit. Running `bun run build` between trivial edits adds ~15s of dead time per cycle.
|
|
@@ -807,7 +811,7 @@ This app uses the **Assembly UI** component registry (`@assembly-ui` namespace i
|
|
|
807
811
|
|
|
808
812
|
**shadcn drop-in replacements** — same composition patterns, same sub-component names, same imports. Built on `@base-ui/react` — root props differ from Radix/shadcn (see documented differences above; check `components/ui/<name>.tsx` if you hit a type error):
|
|
809
813
|
|
|
810
|
-
accordion, alert, alert-dialog, avatar, badge, breadcrumb, button, button-group, calendar, card, checkbox, collapsible, combobox, dialog, dropdown-menu, field, input, input-group, item, label, popover, progress, radio-group, select, separator, skeleton, slider,
|
|
814
|
+
accordion, alert, alert-dialog, avatar, badge, breadcrumb, button, button-group, calendar, card, checkbox, collapsible, combobox, dialog, dropdown-menu, field, input, input-group, item, label, popover, progress, radio-group, select, separator, skeleton, slider, spinner, switch, table, tabs, textarea, toggle, toggle-group, tooltip
|
|
811
815
|
|
|
812
816
|
**Custom Assembly components** — these are NOT shadcn drop-ins. They have their own unique APIs and do not follow standard shadcn composition patterns. Read the usage instructions below before using them:
|
|
813
817
|
|
package/package.json
CHANGED
|
@@ -75,5 +75,5 @@ interface ToastShowPayload {
|
|
|
75
75
|
## Rules
|
|
76
76
|
|
|
77
77
|
- **Client components only.** `AssemblyBridge` posts messages to the parent window — call it in `'use client'` components only.
|
|
78
|
-
- **Don't build toast UI inside the iframe.** The parent dashboard renders the toast in its own chrome; your iframe should have no toast component
|
|
78
|
+
- **Don't build toast UI inside the iframe.** The parent dashboard renders the toast in its own chrome; your iframe should have no toast component, so don't install or import `sonner` or `react-hot-toast`.
|
|
79
79
|
- **Don't stack multiple toasts for one action.** Show one toast per user action.
|
|
@@ -26,7 +26,7 @@ If you are unsure, default to **internal-only**. Do not add a client surface unl
|
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
- `internal` is **required** — every Vibe app must have a dashboard surface. `client` is optional; omit it for internal-only apps. `notification` is optional; declare it when opening a notification should land on a specific record (see below).
|
|
29
|
-
- `path` is the route within your app for that surface. Must start with `/`. Use `/` for the surface's root; use distinct sub-
|
|
29
|
+
- `path` is the route within your app for that surface. Must start with `/`. Use `/` for the surface's root; use a distinct sub-path (`/c` for the client surface) for each other surface. Two routes, because the same path can't tell the dashboard viewer from the client viewer without branching on the token, and each surface needs its own layout and its own access rules.
|
|
30
30
|
- Generate the corresponding routes in your app code. E.g. if you declare `client.path = "/c"`, you must have a `/c` page in the app.
|
|
31
31
|
|
|
32
32
|
<!-- !feature:notification-settings -->
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: assembly-client-users
|
|
3
|
-
description: Use when the app needs to read, list, create, or update client users — the customers of the business that owns the Assembly workspace. Clients sign into the branded Client Portal and see only their own data. Detect via
|
|
3
|
+
description: Use when the app needs to read, list, create, or update client users — the customers of the business that owns the Assembly workspace. Clients sign into the branded Client Portal and see only their own data. Detect via getSessionIdentity().
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Client users are **customers of the business** that owns the Assembly workspace. They sign into the branded Client Portal at the workspace's domain. They see only their own data — their own messages, their own files, their own invoices.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: assembly-internal-users
|
|
3
|
-
description: Use when the app reads/lists internal users, gates a feature to workspace admins (role is "admin" or "staff"), or an internal viewer browses clients/companies in the Dashboard. Covers Client Access (isClientAccessLimited, companyAccessList) scoping. Internal users sign into the Dashboard only. Detect via
|
|
3
|
+
description: Use when the app reads/lists internal users, gates a feature to workspace admins (role is "admin" or "staff"), or an internal viewer browses clients/companies in the Dashboard. Covers Client Access (isClientAccessLimited, companyAccessList) scoping. Internal users sign into the Dashboard only. Detect via getSessionIdentity().
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Internal users are the **business team** running the Assembly workspace. They sign into the Dashboard at the workspace's domain, never the branded Client Portal. They have full read/write power over clients, companies, files, etc. — **within their Client Access scope** (see below).
|