Repository navigation
test: Artifact Apps (#14306) rebased on main with authorization coverage - #14616
danny-avila wants to merge 18 commits into
Conversation
…data-service Adds shared Artifact Apps contracts (PLAN §6/§8/§9): status/visibility/runtime enums, publish/update/version request schemas, response types, query keys, API endpoint builders, and data-service calls. Registers ARTIFACT_APP in the ResourceType/AccessRoleIds enums and permission-bit mapping. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds ArtifactApp and ArtifactVersion schemas, models, and DB methods (PLAN §6.1/§6.2): atomic app+version creation, tenant-scoped queries, version lifecycle (draft/release/activate/withdraw), SHA-256 source hashing, and an idempotent index migration (§6.4). Includes unit tests against an in-memory MongoDB covering CRUD, tenant isolation, snapshot integrity, and immutability. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ctions Wires ARTIFACT_APP into the existing ACL system (PLAN §7): seeds Owner/Editor/Viewer access roles, adds READ/MANAGE_ARTIFACT_APPS capabilities with ResourceCapabilityMap bypass, and registers artifact audit actions. Makes the share middleware permission map partial so capability-governed resources need no PermissionTypes entry. Updates role/capability tests. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add the /api/artifact-apps surface (WP3/WP4): typed publishing, CRUD, and version-lifecycle (create/release/activate/withdraw) handlers in packages/api, backed by the existing ACL system and audit log. Actor identity and tenant are derived strictly from the authenticated session per §8.7. Full-source snapshots are immutable once released. Includes mongodb-memory-server integration tests. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…(WIP WP5/WP6) Partial frontend work: standalone app view, app renderer wrapper, react-query queries/mutations for artifact-apps endpoints, i18n keys. Committed to preserve progress before credit exhaustion.
Documents what is done (WP0-WP4 backend + partial WP5/WP6 frontend) and what remains (build verification, WP5 routing, WP6 publish UI, combined PR) so a resuming agent can pick up without re-discovery.
- ResourceType.ARTIFACT_APP was missing from the exhaustive Record<ResourceType, ...> maps main added since this branch's base (RESOURCE_CONFIGS, accessDescriptions); adds the com_ui_artifact_app key. - makeReq test helper constrained body to the chat-shaped ServerRequest['body']; handlers zod-parse it, so the mock body is unknown.
- Adds canAccessArtifactAppResource.spec.js, the only middleware in accessResources/ that had no spec. Exercises real ACL entries: owner allowed, no-grant denied, view-only grant denied for edit/delete, unknown app 404, and a grant under a different resourceType not leaking across. - Covers update/remove/listVersions/getVersion/withdrawVersion, which had no tests, plus the §8.7 patch guarantee and cross-app version-id isolation. handlers.ts: 44% -> 71% statements, 36% -> 58% branches, 70% -> 95% funcs.
🚨 Unused i18next Keys DetectedThe following translation keys are defined in
|
There was a problem hiding this comment.
🟡 Not ready to approve
Multiple verified functional/API mismatches and integration gaps (standalone renderer type mapping, non-transaction create inconsistency, sharing policy support) need to be addressed before safe approval.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Pull request overview
Adds end-to-end “Artifact Apps” plumbing across data-schemas, API routes/handlers, data-provider, and client UI, and introduces new test coverage for the authorization surface so CI can validate the feature after rebasing on main.
Changes:
- Introduces Artifact App + Version persistence (schemas/models/methods/migrations) and audit/capability taxonomy wiring.
- Adds Artifact Apps HTTP handlers/routes, access-resource middleware, and client-side routes/UI + data-provider queries/mutations.
- Adds/extends Jest coverage for handlers and authorization middleware.
File summaries
| File | Description |
|---|---|
| packages/data-schemas/src/types/index.ts | Re-export Artifact App types. |
| packages/data-schemas/src/types/artifactApp.ts | Adds Artifact App/Version TS interfaces & inputs. |
| packages/data-schemas/src/types/admin.ts | Extends audit categories/actions for artifacts. |
| packages/data-schemas/src/schema/index.ts | Exports artifact schemas. |
| packages/data-schemas/src/schema/artifactVersion.ts | Adds ArtifactVersion Mongoose schema + indexes. |
| packages/data-schemas/src/schema/artifactApp.ts | Adds ArtifactApp Mongoose schema + indexes. |
| packages/data-schemas/src/models/index.ts | Registers ArtifactApp/ArtifactVersion models. |
| packages/data-schemas/src/models/artifactVersion.ts | Model factory + tenant isolation for versions. |
| packages/data-schemas/src/models/artifactApp.ts | Model factory + tenant isolation for apps. |
| packages/data-schemas/src/migrations/index.ts | Exports artifact index migration. |
| packages/data-schemas/src/migrations/artifactAppIndexes.ts | Ensures artifact collection indexes exist. |
| packages/data-schemas/src/methods/index.ts | Wires ArtifactAppMethods into methods aggregate. |
| packages/data-schemas/src/methods/artifactApp.ts | Implements artifact app/version CRUD + lifecycle. |
| packages/data-schemas/src/methods/artifactApp.spec.ts | Adds unit tests for artifact app methods. |
| packages/data-schemas/src/methods/accessRole.ts | Seeds default roles for ARTIFACT_APP. |
| packages/data-schemas/src/methods/accessRole.spec.ts | Extends tests for new role IDs. |
| packages/data-schemas/src/index.ts | Re-exports artifact methods/constants from package. |
| packages/data-schemas/src/admin/capabilities.ts | Adds system capabilities + resource mapping for apps. |
| packages/data-schemas/rollup.fallback.mjs | Adds rollup fallback build config for data-schemas. |
| packages/data-provider/src/keys.ts | Adds React Query keys for artifact apps. |
| packages/data-provider/src/index.ts | Exports artifact apps module. |
| packages/data-provider/src/data-service.ts | Adds client API functions for artifact apps. |
| packages/data-provider/src/artifactApps.ts | Adds shared Zod schemas + DTO types. |
| packages/data-provider/src/api-endpoints.ts | Adds endpoint builders for artifact apps routes. |
| packages/data-provider/src/accessPermissions.ts | Adds ResourceType/AccessRoleIds for ARTIFACT_APP. |
| packages/data-provider/rollup.fallback.mjs | Adds rollup fallback build config for data-provider. |
| packages/api/src/middleware/share.ts | Adjusts share mapping typing for new resource type. |
| packages/api/src/index.ts | Exports artifact apps package entry. |
| packages/api/src/artifactApps/index.ts | Exports handler factory/types. |
| packages/api/src/artifactApps/handlers.ts | Implements artifact apps REST handlers + auditing. |
| packages/api/src/artifactApps/tests/handlers.spec.ts | Adds handler test coverage (publish/update/remove/version ops). |
| docs/artifact-apps/WP0-exploration.md | Documents repo exploration supporting feature plan. |
| docs/artifact-apps/adr-0002-mcp-bridge.md | Design ADR for future MCP bridge. |
| docs/artifact-apps/adr-0001-snapshot-storage.md | Design ADR for snapshot storage approach. |
| client/src/utils/resources.ts | Adds ARTIFACT_APP to resource config mapping. |
| client/src/routes/index.tsx | Adds /apps/* routes for standalone viewer/list. |
| client/src/locales/en/translation.json | Adds i18n keys for artifact apps. |
| client/src/hooks/Nav/useSideNavLinks.ts | Adds “Apps” link/panel entry. |
| client/src/data-provider/index.ts | Exports ArtifactApps client data-provider module. |
| client/src/data-provider/ArtifactApps/queries.ts | Adds artifact apps React Query hooks. |
| client/src/data-provider/ArtifactApps/mutations.ts | Adds artifact app mutation hooks + invalidation. |
| client/src/data-provider/ArtifactApps/index.ts | Barrel export for ArtifactApps hooks. |
| client/src/components/Sharing/PublicSharingToggle.tsx | Adds ARTIFACT_APP to public sharing toggle labels. |
| client/src/components/Artifacts/Artifacts.tsx | Adds publish button to artifact header actions. |
| client/src/components/ArtifactApps/StandaloneAppView.tsx | Standalone artifact app view + version selector. |
| client/src/components/ArtifactApps/PublishArtifactButton.tsx | Adds publish icon button for eligible artifacts. |
| client/src/components/ArtifactApps/PublishAppDialog.tsx | Adds multi-step publish dialog UI. |
| client/src/components/ArtifactApps/index.ts | Barrel export for ArtifactApps components. |
| client/src/components/ArtifactApps/ArtifactAppsList.tsx | Implements list UI for published apps. |
| client/src/components/ArtifactApps/AppRenderer.tsx | Renders app snapshot via existing artifact preview. |
| api/server/routes/index.js | Registers artifactApps router. |
| api/server/routes/artifactApps.js | Adds legacy /api/artifact-apps router wiring. |
| api/server/middleware/accessResources/index.js | Exports canAccessArtifactAppResource middleware. |
| api/server/middleware/accessResources/canAccessArtifactAppResource.spec.js | Adds authz middleware test coverage. |
| api/server/middleware/accessResources/canAccessArtifactAppResource.js | Adds artifact app-specific ACL middleware wrapper. |
| api/server/index.js | Mounts /api/artifact-apps route. |
Review details
Suppressed comments (1)
packages/api/src/middleware/share.ts:52
resourceToPermissionTypehas no entry forResourceType.ARTIFACT_APP, so share policy requests for artifact apps will return 400 "Unsupported resource type". This effectively blocks the generic sharing endpoints from supporting the new resource type, which undermines the Artifact Apps sharing workflow.
const resourceToPermissionType: Partial<Record<ResourceType, PermissionTypes>> = {
[ResourceType.AGENT]: PermissionTypes.AGENTS,
[ResourceType.PROMPTGROUP]: PermissionTypes.PROMPTS,
[ResourceType.MCPSERVER]: PermissionTypes.MCP_SERVERS,
[ResourceType.REMOTE_AGENT]: PermissionTypes.REMOTE_AGENTS,
[ResourceType.SKILL]: PermissionTypes.SKILLS,
[ResourceType.SHARED_LINK]: PermissionTypes.SHARED_LINKS,
};
- Files reviewed: 56/56 changed files
- Comments generated: 10
- Review effort level: Lite
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
| const artifact: Artifact = { | ||
| id: version.artifactVersionId, | ||
| lastUpdateTime: Date.now(), | ||
| type: version.artifactType, | ||
| content: version.sourceSnapshot, | ||
| title, | ||
| }; |
| const version: TArtifactVersion | null | undefined = versionId | ||
| ? versionQuery.data | ||
| : appQuery.data?.version; | ||
|
|
| const publishableTypes = [ | ||
| 'application/vnd.react', | ||
| 'application/vnd.ant.react', | ||
| 'text/html', | ||
| 'application/vnd.code-html', | ||
| ]; | ||
| if (!artifact.type || !publishableTypes.includes(artifact.type)) { | ||
| return null; |
| <input | ||
| id="pub-title" | ||
| className="w-full rounded-lg border border-border-medium bg-surface-secondary px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-ring" | ||
| {...register('title')} | ||
| /> | ||
| {errors.title && ( |
| <nav aria-label="Progress" className="mb-6 flex items-center gap-2"> | ||
| {STEPS.map((s, i) => ( | ||
| <div key={s} className="flex items-center gap-2"> | ||
| <div | ||
| className={`flex h-6 w-6 items-center justify-center rounded-full text-xs font-bold transition-colors ${ | ||
| i < stepIndex | ||
| ? 'bg-green-500 text-white' | ||
| : i === stepIndex | ||
| ? 'bg-blue-500 text-white' | ||
| : 'bg-surface-secondary text-text-secondary' | ||
| }`} | ||
| > | ||
| {i < stepIndex ? '✓' : i + 1} | ||
| </div> | ||
| <span | ||
| className={`text-xs ${ | ||
| i === stepIndex ? 'font-semibold text-text-primary' : 'text-text-secondary' | ||
| }`} | ||
| > | ||
| {stepLabel[s]} | ||
| </span> | ||
| {i < STEPS.length - 1 && <div className="h-px w-6 bg-border-light" />} |
| function canonicalize(value: unknown): unknown { | ||
| if (Array.isArray(value)) { | ||
| return value.map(canonicalize); | ||
| } | ||
| if (value && typeof value === 'object') { | ||
| const source = value as Record<string, unknown>; | ||
| return Object.keys(source) | ||
| .sort() | ||
| .reduce<Record<string, unknown>>((acc, key) => { | ||
| acc[key] = canonicalize(source[key]); | ||
| return acc; | ||
| }, {}); | ||
| } | ||
| return value; | ||
| } |
| const runtimeConfigSchema = new Schema( | ||
| { | ||
| dependencies: { type: Schema.Types.Mixed, default: undefined }, | ||
| entryPoint: { type: String }, | ||
| renderMode: { type: String }, | ||
| }, | ||
| { _id: false }, | ||
| ); |
| const result = await deleteArtifactApp({ artifactAppId: id }); | ||
| if (!result.deletedApp) { | ||
| return res.status(404).json({ error: 'Artifact app not found' }); | ||
| } | ||
| audit({ | ||
| tenantId: user.tenantId, | ||
| action: 'artifact_app.archived', | ||
| actor: { type: 'user', id: user.id as string, name: user.name ?? user.username ?? '' }, | ||
| target: { type: ResourceType.ARTIFACT_APP, id }, | ||
| metadata: { deletedVersions: result.deletedVersions }, | ||
| }); | ||
| return res.status(200).json({ success: true }); |
| if (!useTransaction) { | ||
| const [app] = await ArtifactApp.create([appDoc]); | ||
| const [version] = await ArtifactVersion.create([versionSeed]); | ||
| app.activeVersionId = version.artifactVersionId; | ||
| await app.save(); | ||
| return { app, version }; | ||
| } |
| export interface TArtifactAppWithVersion { | ||
| app: TArtifactApp; | ||
| version: TArtifactVersion; | ||
| } |
|
Hi @danny-avila, I created an extension for this PR and I will need your review on it |
Companion to #14306 — all original work authored by @dsheine (Daniel Schätzl); every one of that PR's 14 commits is preserved here as an ancestor, with authorship intact. This branch adds a
mainmerge plus three commits on top.Opened as a draft for one reason: #14306 has never been tested. Zero check-runs on all 14 of its commits (fork PR, workflows unapproved), zero reviews, zero comments since 2026-07-17. Pushing to a branch in this repo is the only way to get CI to actually look at it.
What this adds on top of @dsheine's work
Merged latest
main(was 126 commits behind). Zero textual conflicts, but two semantic breaks:ResourceType.ARTIFACT_APPwas missing from the exhaustiveRecord<ResourceType, …>mapsmainhas since added inclient/src/utils/resources.tsandPublicSharingToggle.tsx. Clean merge, two type errors. Added the entries plus acom_ui_artifact_appkey.makeReqin the handlers spec constrainedbodyto the chat-shapedServerRequest['body'], producing 11 type errors. These were pre-existing, not merge damage — the same 11 appear at the pre-merge head. The handlers zod-parsereq.body, so the mock body is nowunknown.Test coverage for the authorization surface, which had none:
canAccessArtifactAppResource.spec.js(12 tests) — this was the only middleware inaccessResources/without a spec. Same real-AclEntry+mongodb-memory-serverpattern as itscanAccessMCPServerResourcesibling. Covers owner allowed, no-grant 403, view-only grant denied for both EDIT and DELETE, unknown app 404, customresourceIdParam, and a grant under a differentresourceTypeon the same ObjectId not leaking across.update,remove,listVersions,getVersion,withdrawVersion), taking that suite 10 → 24 tests.handlers.tsgoes 44% → 71% statements, 36% → 58% branches, 70% → 95% functions.Two of those are the ones worth having before review, and both pass: PATCH drops client-supplied
createdBy/tenantId(the §8.7 guarantee, previously only pinned onpublish), and a version id from one app does not resolve under another app's id.Not addressed here
Deliberately no client component tests. The plan is to move
AppRendereroff Sandpack onto the sandboxedsrcdocrenderer from #13540, which rewrites the path those tests would pin.Two things this surfaced that are design questions for @dsheine, not defects:
routes/artifactApps.jsappliesrequireJwtAuthto the whole router, so there is no anonymous path — yet the schema carriesallowAnonymousViewand Enhancement: Standalone Artifact Publishing and Sharing #13374's motivating use case is sharing with people who don't have accounts.toolPolicyat WP1 as immutable-per-version so the later bridge needs no migration. 📡 feat: Live Artifacts: Sandboxed HTML That Calls MCP Tools #13540 already implements a working bridge with a different allowlist shape (file.metadata.mcpTools). Whichever lands second should conform, and right now the schema would be frozen before the working implementation is consulted.Not for merge as-is. It exists so CI runs and so the authz layer has evidence behind it.