Skip to content

test: Artifact Apps (#14306) rebased on main with authorization coverage - #14616

Draft
danny-avila wants to merge 18 commits into
mainfrom
danny-avila/artifact-apps-update
Draft

danny-avila wants to merge 18 commits into
mainfrom
danny-avila/artifact-apps-update

Conversation

@danny-avila

Copy link
Copy Markdown
Collaborator

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 main merge 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_APP was missing from the exhaustive Record<ResourceType, …> maps main has since added in client/src/utils/resources.ts and PublicSharingToggle.tsx. Clean merge, two type errors. Added the entries plus a com_ui_artifact_app key.
  • makeReq in the handlers spec constrained body to the chat-shaped ServerRequest['body'], producing 11 type errors. These were pre-existing, not merge damage — the same 11 appear at the pre-merge head. The handlers zod-parse req.body, so the mock body is now unknown.

Test coverage for the authorization surface, which had none:

  • canAccessArtifactAppResource.spec.js (12 tests) — this was the only middleware in accessResources/ without a spec. Same real-AclEntry + mongodb-memory-server pattern as its canAccessMCPServerResource sibling. Covers owner allowed, no-grant 403, view-only grant denied for both EDIT and DELETE, unknown app 404, custom resourceIdParam, and a grant under a different resourceType on the same ObjectId not leaking across.
  • The five handlers with no tests (update, remove, listVersions, getVersion, withdrawVersion), taking that suite 10 → 24 tests. handlers.ts goes 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 on publish), 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 AppRenderer off Sandpack onto the sandboxed srcdoc renderer from #13540, which rewrites the path those tests would pin.

Two things this surfaced that are design questions for @dsheine, not defects:

  1. routes/artifactApps.js applies requireJwtAuth to the whole router, so there is no anonymous path — yet the schema carries allowAnonymousView and Enhancement: Standalone Artifact Publishing and Sharing #13374's motivating use case is sharing with people who don't have accounts.
  2. ADR-0002 fixes toolPolicy at 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.

Daniel Schätzl and others added 18 commits July 14, 2026 13:46
…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.
Copilot AI review requested due to automatic review settings August 3, 2026 23:14
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

🚨 Unused i18next Keys Detected

The following translation keys are defined in translation.json but are not used in the codebase:

  • com_ui_artifact_app_publish_success
  • com_ui_artifact_app_review
  • com_ui_artifact_app_sharing
  • com_ui_artifact_app_step_tools

⚠️ Please remove these unused keys to keep the translation files clean.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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

  • resourceToPermissionType has no entry for ResourceType.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.

Comment on lines +19 to +25
const artifact: Artifact = {
id: version.artifactVersionId,
lastUpdateTime: Date.now(),
type: version.artifactType,
content: version.sourceSnapshot,
title,
};
Comment on lines +64 to +67
const version: TArtifactVersion | null | undefined = versionId
? versionQuery.data
: appQuery.data?.version;

Comment on lines +18 to +25
const publishableTypes = [
'application/vnd.react',
'application/vnd.ant.react',
'text/html',
'application/vnd.code-html',
];
if (!artifact.type || !publishableTypes.includes(artifact.type)) {
return null;
Comment on lines +181 to +186
<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 && (
Comment on lines +145 to +166
<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" />}
Comment on lines +38 to +52
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;
}
Comment on lines +4 to +11
const runtimeConfigSchema = new Schema(
{
dependencies: { type: Schema.Types.Mixed, default: undefined },
entryPoint: { type: String },
renderMode: { type: String },
},
{ _id: false },
);
Comment on lines +392 to +403
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 });
Comment on lines +168 to +174
if (!useTransaction) {
const [app] = await ArtifactApp.create([appDoc]);
const [version] = await ArtifactVersion.create([versionSeed]);
app.activeVersionId = version.artifactVersionId;
await app.save();
return { app, version };
}
Comment on lines +182 to +185
export interface TArtifactAppWithVersion {
app: TArtifactApp;
version: TArtifactVersion;
}
@romy651

romy651 commented Sep 18, 2026

Copy link
Copy Markdown

Hi @danny-avila, I created an extension for this PR and I will need your review on it
#16019

romy651 pushed a commit to fdj-united/Fdj-LibreChat that referenced this pull request Oct 6, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants