@assembly-js/app-foundation 0.0.8-rc.0 → 0.0.8
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.md +4 -0
- package/dist/client.d.ts +1 -0
- package/dist/client.js +3 -0
- package/dist/parent-origin.d.ts +17 -0
- package/dist/parent-origin.js +57 -0
- package/package.json +1 -1
- package/skills/assembly-custom-fields/SKILL.md +0 -19
package/CLAUDE.md
CHANGED
|
@@ -10,6 +10,10 @@ In user-facing chat, never mention ASSEMBLY_ENV, COPILOT_ENV, or any other inter
|
|
|
10
10
|
|
|
11
11
|
**If an SDK auth warning appears mid-build** — for example, a "You must set ASSEMBLY_ENV=local" message from the SDK initializer — treat it as expected transient state. The sandbox resolves it automatically as the build progresses. Do not surface this message in chat and do not ask the user to set any environment variables.
|
|
12
12
|
|
|
13
|
+
When you describe a feature or a change, describe what the user and their clients will **experience** — who can open the app, what each audience sees or can do. The mechanics that produce it are plumbing too: keep manifest keys (`surfaces`, `app.json`), file names, SDK method names, and identity fields like `clientId`/`internalUserId` out of chat unless the user asks how it works. "Clients will see only their own record" is something they can act on; "add a client surface to the manifest and branch on `identity.clientId`" is not.
|
|
14
|
+
|
|
15
|
+
When a request implies a scope or permission change, apply it and report the product effect — don't stop to ask the user to approve the mechanics. "When a client views this, show them their own info" already means "make this client-facing," so make the change and say what happened ("clients now see just their own record; your team still sees everyone"). Ask first only on a genuine product choice the user can weigh — who should see the app, what each audience sees — phrased in those terms, never as a manifest or surface decision.
|
|
16
|
+
|
|
13
17
|
---
|
|
14
18
|
|
|
15
19
|
## Skills
|
package/dist/client.d.ts
CHANGED
package/dist/client.js
CHANGED
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
// also runs in the Edge middleware runtime that proxy.ts uses.
|
|
8
8
|
export * from './session-exchange.js';
|
|
9
9
|
export * from './network-capture.js';
|
|
10
|
+
// Self-running on client module load (see the module comment): registers the
|
|
11
|
+
// framing origin with the app-bridge allowlist so custom-domain portals work.
|
|
12
|
+
export * from './parent-origin.js';
|
|
10
13
|
export * from './client-companies.js';
|
|
11
14
|
export * from './pagination.js';
|
|
12
15
|
export * from './theme.js';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the origin of the window framing this app.
|
|
3
|
+
*
|
|
4
|
+
* Prefers `location.ancestorOrigins` (browser-attested, immune to referrer
|
|
5
|
+
* policy; Chromium/WebKit) and falls back to `document.referrer` (Firefox).
|
|
6
|
+
* Returns null outside an iframe or when neither source is available —
|
|
7
|
+
* e.g. a restrictive Referrer-Policy on the parent in Firefox.
|
|
8
|
+
*/
|
|
9
|
+
export declare function resolveParentOrigin(): string | null;
|
|
10
|
+
/**
|
|
11
|
+
* Add the framing origin to the bridge's postMessage allowlist.
|
|
12
|
+
*
|
|
13
|
+
* Idempotent (the bridge holds origins in a Set) and restricted to https
|
|
14
|
+
* parents — local development origins are already on the default allowlist,
|
|
15
|
+
* so an http parent never needs registering.
|
|
16
|
+
*/
|
|
17
|
+
export declare function registerParentOriginWithBridge(): void;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// Registers the app's real framing origin with the app-bridge postMessage
|
|
2
|
+
// allowlist, so the bridge works on workspaces with custom portal domains.
|
|
3
|
+
//
|
|
4
|
+
// The bridge SDK ships a hardcoded origin allowlist (localhost + assembly
|
|
5
|
+
// domains) and silently drops every outgoing and incoming message when the
|
|
6
|
+
// parent origin isn't on it. On a custom-domain portal that kills header CTAs,
|
|
7
|
+
// session-token refresh, and theme sync. Trusting the framing origin here is
|
|
8
|
+
// sound because the proxy already gates who may frame the app at all: its CSP
|
|
9
|
+
// frame-ancestors directive allows only the static assembly domains plus this
|
|
10
|
+
// workspace's own portal URL, and the browser enforces that before any of this
|
|
11
|
+
// bundle runs. Inside the frame, the browser-reported parent origin is
|
|
12
|
+
// therefore always one the platform trusts.
|
|
13
|
+
import { AssemblyBridge } from '@assembly-js/app-bridge';
|
|
14
|
+
/**
|
|
15
|
+
* Resolve the origin of the window framing this app.
|
|
16
|
+
*
|
|
17
|
+
* Prefers `location.ancestorOrigins` (browser-attested, immune to referrer
|
|
18
|
+
* policy; Chromium/WebKit) and falls back to `document.referrer` (Firefox).
|
|
19
|
+
* Returns null outside an iframe or when neither source is available —
|
|
20
|
+
* e.g. a restrictive Referrer-Policy on the parent in Firefox.
|
|
21
|
+
*/
|
|
22
|
+
export function resolveParentOrigin() {
|
|
23
|
+
if (typeof window === 'undefined')
|
|
24
|
+
return null;
|
|
25
|
+
if (window.parent === window)
|
|
26
|
+
return null;
|
|
27
|
+
const ancestor = window.location.ancestorOrigins?.[0];
|
|
28
|
+
if (ancestor)
|
|
29
|
+
return ancestor;
|
|
30
|
+
try {
|
|
31
|
+
const referrer = document.referrer;
|
|
32
|
+
if (referrer)
|
|
33
|
+
return new URL(referrer).origin;
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// Unparseable referrer — fall through to null.
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Add the framing origin to the bridge's postMessage allowlist.
|
|
42
|
+
*
|
|
43
|
+
* Idempotent (the bridge holds origins in a Set) and restricted to https
|
|
44
|
+
* parents — local development origins are already on the default allowlist,
|
|
45
|
+
* so an http parent never needs registering.
|
|
46
|
+
*/
|
|
47
|
+
export function registerParentOriginWithBridge() {
|
|
48
|
+
const origin = resolveParentOrigin();
|
|
49
|
+
if (!origin || !origin.startsWith('https://'))
|
|
50
|
+
return;
|
|
51
|
+
AssemblyBridge.configure({ additionalOrigins: [origin] });
|
|
52
|
+
}
|
|
53
|
+
// Run on module load: every app's client bundle imports this package (authFetch,
|
|
54
|
+
// network capture), and module evaluation precedes hydration — so the origin is
|
|
55
|
+
// registered before any component's first header/session-token call. Baked app
|
|
56
|
+
// files never call this module directly, which is exactly why it self-runs.
|
|
57
|
+
registerParentOriginWithBridge();
|
package/package.json
CHANGED
|
@@ -84,22 +84,3 @@ Send the structured object — a bare string is accepted for backwards compatibi
|
|
|
84
84
|
- **Clearing a value**: empty array `[]`, not `null` or `undefined`.
|
|
85
85
|
- **Either `key` or `id` works** as the inner option value — use `key` by convention.
|
|
86
86
|
- Same shape applies to `createClient`, `createCompany`, `updateCompany`.
|
|
87
|
-
|
|
88
|
-
<!-- feature:platform-mcp -->
|
|
89
|
-
## Provisioning fields at build time (platform MCP)
|
|
90
|
-
|
|
91
|
-
You have a scoped platform MCP server (tool prefix `mcp__platform__`). When the app you are building depends on a custom field the workspace does not have yet — the user asked for "call clients at their phone number" and no Phone-type client field exists — provision it yourself at build time instead of shipping an app that gates on missing workspace config:
|
|
92
|
-
|
|
93
|
-
```
|
|
94
|
-
mcp__platform__custom_fields with action "list" — check what exists first
|
|
95
|
-
mcp__platform__custom_fields with action "create" — create the missing field
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
Rules:
|
|
99
|
-
|
|
100
|
-
- **Check before creating** (`list`, match case-insensitively by name) — never create a duplicate of a field that already exists under a slightly different name.
|
|
101
|
-
- **Creating a field does not replace graceful handling.** The app's runtime code must still handle the field being absent or renamed later (discover by name, render a setup hint) — you provision the field the app *starts* with; you don't get to assume it forever.
|
|
102
|
-
- **Never delete fields.** Deletion is not available to you by policy: fields carry data on real client records.
|
|
103
|
-
- **This tool is for workspace configuration only.** Do not reach for `mcp__platform__*` to create clients, send messages/notifications/invoices, or touch any client-facing data — those denials are enforced server-side and the attempt wastes a turn.
|
|
104
|
-
- Tell the user in chat, in product terms, when you create a field ("I added a Phone number field to your client records so the app can call them").
|
|
105
|
-
<!-- /feature:platform-mcp -->
|