@assembly-js/app-foundation 0.4.1 → 0.4.3
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 +6 -0
- package/CLAUDE.md +2 -2
- package/dist/assembly.d.ts +1 -2
- package/dist/assembly.js +1 -2
- package/dist/codegen.js +4 -4
- package/package.json +1 -1
- package/skills/assembly-app-clients/SKILL.md +6 -4
package/CHANGELOG.md
CHANGED
|
@@ -19,6 +19,12 @@ A patch release never changes what an app's build requires; a minor release may
|
|
|
19
19
|
not by hand; see `changes/README.md`. CI fails a PR that changes a check script without a
|
|
20
20
|
`bump: minor` change file.
|
|
21
21
|
|
|
22
|
+
## 0.4.3
|
|
23
|
+
- [guidance] The build agent now knows `viewerClient` works for cross-app calls made by a client contact, and keeps `installerClient` for team members, cron, webhooks and background work.
|
|
24
|
+
|
|
25
|
+
## 0.4.2
|
|
26
|
+
- [guidance] The build agent's main guide now says a client contact's cross-app calls use `viewerClient`, while team members and background work keep `installerClient`.
|
|
27
|
+
|
|
22
28
|
## 0.4.1
|
|
23
29
|
- [guidance] Build agents append to `requiredSecrets` instead of rewriting it, and remove an entry only when the user asks to disconnect that integration.
|
|
24
30
|
|
package/CLAUDE.md
CHANGED
|
@@ -52,7 +52,7 @@ Assembly-specific gotchas live in `.claude/skills/<name>/SKILL.md`. Invoke them
|
|
|
52
52
|
- `assembly-app-inner-location` — mount `<LocationReporter />` so the host mirrors the app's inner page in its address bar; deep-link a notification or link to a specific record with `linkTo({ to: 'app', installId, path })`, never to the app root. Never hand-write location reporting.
|
|
53
53
|
<!-- /feature:deep-link-app-inner-location -->
|
|
54
54
|
<!-- feature:cross-app-apis -->
|
|
55
|
-
- `assembly-app-clients` — calling another workspace app through its generated client in `generated/apps/<appId>/` (`installerClient
|
|
55
|
+
- `assembly-app-clients` — calling another workspace app through its generated client in `generated/apps/<appId>/` (`viewerClient` for a client contact, `installerClient` for team members and background work, by verb and path), and rendering an empty state that names the app when the call is refused instead of a 500.
|
|
56
56
|
<!-- /feature:cross-app-apis -->
|
|
57
57
|
- `assembly-ai` — AI features, including speech-to-text, via Assembly's OpenAI proxy (no API keys needed)
|
|
58
58
|
- `assembly-storage` — app-private file uploads/downloads (NOT the portal Files module). The storage helpers come from `@assembly-js/app-foundation/server` — import them, don't rewrite them. POST only `{ fileName }` (a bare name) and store the key the platform returns; never build the S3 key yourself.
|
|
@@ -462,7 +462,7 @@ Relationships:
|
|
|
462
462
|
If the user names another app, read its file in `.assembly/docs/apps/` and follow the call recipe at the top of that file. If that folder is absent, the app is not callable (it may still need the Security update). Cross-app calls from pages, server actions and routes use `getInstallerAssembly()`; cron and webhooks use `getOfflineAssembly()`. Never return another app's response to a client unfiltered — scope any client data you forward. While a person is using the app, those calls use the installer client, because the platform does not yet prove which person the call is for. Once it can, use the viewer client for that person's data and keep the installer client for staff-wide reads.
|
|
463
463
|
<!-- /feature:api-resources -->
|
|
464
464
|
<!-- feature:cross-app-apis -->
|
|
465
|
-
When `generated/apps/<appId>/` exists for that app, call it through the generated client instead (see the `assembly-app-clients` skill); the docs recipe is the fallback for an app with no folder there.
|
|
465
|
+
When `generated/apps/<appId>/` exists for that app, call it through the generated client instead (see the `assembly-app-clients` skill); the docs recipe is the fallback for an app with no folder there. The generated client supersedes the installer-client note above: a client contact's own calls use `viewerClient`, which the platform scopes to that contact; team members and background work use `installerClient`.
|
|
466
466
|
<!-- /feature:cross-app-apis -->
|
|
467
467
|
|
|
468
468
|
---
|
package/dist/assembly.d.ts
CHANGED
|
@@ -115,8 +115,7 @@ export interface AppProxyOptions {
|
|
|
115
115
|
*
|
|
116
116
|
* `installer` calls as this app, with the same per-request mint as
|
|
117
117
|
* getInstallerAssembly. `viewer` forwards the signed-in user's verified bearer,
|
|
118
|
-
* which the platform
|
|
119
|
-
* person a cross-app call is for.
|
|
118
|
+
* which the platform scopes to that contact and refuses for a team member.
|
|
120
119
|
*/
|
|
121
120
|
export declare function appProxyOptions(appId: string, caller: AppProxyCaller): AppProxyOptions;
|
|
122
121
|
export declare function getAssemblyForRequest(req: NextRequest): Promise<AssemblyAPI | NextResponse>;
|
package/dist/assembly.js
CHANGED
|
@@ -389,8 +389,7 @@ export async function getInstallerAssembly() {
|
|
|
389
389
|
*
|
|
390
390
|
* `installer` calls as this app, with the same per-request mint as
|
|
391
391
|
* getInstallerAssembly. `viewer` forwards the signed-in user's verified bearer,
|
|
392
|
-
* which the platform
|
|
393
|
-
* person a cross-app call is for.
|
|
392
|
+
* which the platform scopes to that contact and refuses for a team member.
|
|
394
393
|
*/
|
|
395
394
|
export function appProxyOptions(appId, caller) {
|
|
396
395
|
const origin = (trustedBaseUrl ?? OpenAPI.BASE).replace(/\/+$/, '');
|
package/dist/codegen.js
CHANGED
|
@@ -131,10 +131,10 @@ import type { paths } from './types.js';
|
|
|
131
131
|
|
|
132
132
|
export const APP_ID = ${JSON.stringify(appId)};
|
|
133
133
|
|
|
134
|
-
// Calls as this app. Use it
|
|
134
|
+
// Calls as this app. Use it for team members, cron, webhooks and background work.
|
|
135
135
|
export const installerClient = createClient<paths>(appProxyOptions(APP_ID, 'installer'));
|
|
136
136
|
|
|
137
|
-
// Calls as the signed-in
|
|
137
|
+
// Calls as the signed-in client contact. Refused for a team member.
|
|
138
138
|
export const viewerClient = createClient<paths>(appProxyOptions(APP_ID, 'viewer'));
|
|
139
139
|
`;
|
|
140
140
|
}
|
|
@@ -155,8 +155,8 @@ const { data, error } = await installerClient.GET('/projects/{id}', {
|
|
|
155
155
|
});
|
|
156
156
|
\`\`\`
|
|
157
157
|
|
|
158
|
-
- \`installerClient\` calls as this app. Use it
|
|
159
|
-
- \`viewerClient\` calls as the signed-in
|
|
158
|
+
- \`installerClient\` calls as this app. Use it for pages and actions a team member runs, and from cron, webhooks and background work. Filter anything you show to a client.
|
|
159
|
+
- \`viewerClient\` calls as the signed-in client contact. The platform scopes the call to that contact, so do not filter what it returns. Use it for pages and actions a contact runs; it is refused for a team member.
|
|
160
160
|
|
|
161
161
|
## Operations
|
|
162
162
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<!-- feature:cross-app-apis -->
|
|
2
2
|
---
|
|
3
3
|
name: assembly-app-clients
|
|
4
|
-
description: Use when the app calls another workspace app and `generated/apps/<appId>/` exists for it. Covers reading the generated README and types, calling `installerClient` by verb and path, and rendering an empty state that names the app when the call is refused (app_not_found, operation_not_found, viewer_not_allowed, callee_error) instead of throwing a 500.
|
|
4
|
+
description: Use when the app calls another workspace app and `generated/apps/<appId>/` exists for it. Covers reading the generated README and types, calling `viewerClient` or `installerClient` by verb and path, and rendering an empty state that names the app when the call is refused (app_not_found, operation_not_found, viewer_not_allowed, callee_error) instead of throwing a 500.
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
## Where the client lives
|
|
@@ -31,9 +31,11 @@ Call it from server code only: pages, server actions, route handlers, cron and w
|
|
|
31
31
|
|
|
32
32
|
## Installer or viewer
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
`viewerClient` calls as the signed-in client contact. The platform scopes the call to that contact, so do not filter or double-check what it returns. Use it for pages and actions a contact runs. The platform refuses it for a team member.
|
|
35
35
|
|
|
36
|
-
`
|
|
36
|
+
`installerClient` calls as this app, not as a person. Use it for pages and actions a team member runs, and from cron, webhooks and other background work. Filter anything you show to a client.
|
|
37
|
+
|
|
38
|
+
If an operation can be called by a contact, it must not make platform calls about clients, companies or the workspace as the caller. Contacts are refused on those routes and the call surfaces as a generic failure. Look things up with the app's own access, or store the display names on the row when the item is assigned.
|
|
37
39
|
|
|
38
40
|
## When the other app cannot answer
|
|
39
41
|
|
|
@@ -43,7 +45,7 @@ The other app can be uninstalled, can drop an operation, or can fail on its own.
|
|
|
43
45
|
|---|---|---|
|
|
44
46
|
| `app_not_found` | The app is not installed in this workspace, or this app may not call it. | Empty state: "<App name> is not available in this workspace." |
|
|
45
47
|
| `operation_not_found` | The app no longer offers this operation. | Empty state: "<App name> no longer provides this." |
|
|
46
|
-
| `viewer_not_allowed` | The call used `viewerClient
|
|
48
|
+
| `viewer_not_allowed` | The platform refused the call for this person: a team member used `viewerClient`, or a contact could not be resolved. | For a team member, call with `installerClient`. For a contact, render an empty or permission state naming the app and never retry with `installerClient`. |
|
|
47
49
|
| `callee_error` | The other app answered with its own failure. The most common case at runtime. | Error state: "<App name> could not load this right now." |
|
|
48
50
|
|
|
49
51
|
Branch on the result and render a state that names the app:
|