132 lines
8.6 KiB
Markdown
132 lines
8.6 KiB
Markdown
---
|
|
id: TASK-46
|
|
title: 'Implement Cross-App Embedding: r-ecosystem apps in rSpace canvases'
|
|
status: Done
|
|
assignee: []
|
|
created_date: '2026-02-18 20:07'
|
|
updated_date: '2026-03-12 01:08'
|
|
labels:
|
|
- feature
|
|
- phase-5
|
|
- ecosystem
|
|
milestone: m-1
|
|
dependencies:
|
|
- TASK-41
|
|
- TASK-42
|
|
references:
|
|
- rspace-online/lib/shape-registry.ts
|
|
- rspace-online/server/index.ts
|
|
- rspace-online/website/canvas.html
|
|
priority: high
|
|
---
|
|
|
|
## Description
|
|
|
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
|
Allow r-ecosystem apps (rWallet, rVote, rMaps, etc.) to embed their live UI into rSpace canvases via dynamically loaded Web Components.
|
|
|
|
Ecosystem App Manifest Protocol:
|
|
- Each app hosts /.well-known/rspace-manifest.json
|
|
- Manifest declares: appId, name, icon, moduleUrl, shapes[] (tagName, defaults, portDescriptors, eventDescriptors)
|
|
|
|
New file lib/ecosystem-bridge.ts:
|
|
- EcosystemBridge class: loadManifest(), loadModule(), createSandboxedEmbed()
|
|
- Two embedding modes:
|
|
1. Trusted (Web Component): dynamic import(), shares CRDT directly, full port/event access
|
|
2. Sandboxed (iframe): postMessage bridge for untrusted apps, limited API
|
|
|
|
New Automerge shape type:
|
|
- type: "ecosystem-embed", appId, moduleUrl, config, sandboxed boolean
|
|
|
|
Server additions:
|
|
- GET /api/ecosystem/:appId/manifest — proxy to avoid CORS
|
|
- Server pre-fetches and caches ecosystem manifests
|
|
|
|
Canvas.html additions:
|
|
- Dynamic toolbar section for ecosystem apps (loaded from manifests)
|
|
- Lazy module loading on first use
|
|
- Service Worker caches modules for offline
|
|
|
|
Runtime:
|
|
1. Server fetches ecosystem manifests → toolbar shows app buttons
|
|
2. User adds ecosystem shape → module import()-ed → Web Component registered → shape created
|
|
3. Remote sync: create-shape for ecosystem-embed triggers lazy module load on other clients
|
|
4. Embedded components participate in port/event system like native shapes
|
|
<!-- SECTION:DESCRIPTION:END -->
|
|
|
|
## Acceptance Criteria
|
|
<!-- AC:BEGIN -->
|
|
- [x] #1 Ecosystem manifest protocol defined and documented
|
|
- [x] #2 EcosystemBridge loads manifests and dynamic imports modules
|
|
- [x] #3 Trusted Web Components share CRDT and port/event system
|
|
- [x] #4 Sandboxed iframe mode works with postMessage bridge
|
|
- [x] #5 Server proxy avoids CORS for manifest/module loading
|
|
- [x] #6 Toolbar dynamically shows ecosystem app buttons
|
|
- [x] #7 Remote clients lazy-load modules when ecosystem shapes appear
|
|
- [x] #8 Service Worker caches ecosystem modules for offline
|
|
<!-- AC:END -->
|
|
|
|
## Implementation Notes
|
|
|
|
<!-- SECTION:NOTES:BEGIN -->
|
|
POC implemented in commit 50f0e11: folk-rapp shape type embeds live rApp modules as iframes on the canvas. Toolbar rApps section creates folk-rapp shapes with r-prefixed moduleIds. Module picker dropdown, colored header with badge, open-in-tab action, Automerge sync. Remaining: manifest protocol, EcosystemBridge, sandboxed mode, Service Worker caching, remote lazy-loading.
|
|
|
|
Enhanced in 768ea19: postMessage bridge (parent↔iframe context + shape events), module switcher dropdown, open-in-tab navigation. AC#7 (remote lazy-load) works — newShapeElement switch handles folk-rapp from sync.
|
|
|
|
## Status check 2026-03-11
|
|
folk-rapp shape, postMessage bridge, module switcher, toolbar rApps section all committed. AC #6 and #7 working. Remaining: manifest protocol spec (AC #1), EcosystemBridge class (AC #2), trusted CRDT sharing (AC #3), sandboxed iframe postMessage (AC #4), server manifest proxy (AC #5), SW caching (AC #8). Depends on TASK-41 (shape registry) and TASK-42 (data pipes).
|
|
|
|
## Assessment 2026-03-11 (detailed code review)
|
|
|
|
### AC #1 — Ecosystem manifest protocol defined and documented: NOT DONE
|
|
No `/.well-known/rspace-manifest.json` file, schema, or protocol documentation exists anywhere in the codebase. The only reference is in this task's description. The `MODULE_META` hardcoded record in `lib/folk-rapp.ts:19-44` serves as a static stand-in, but it is not a discoverable manifest protocol — it is baked into the folk-rapp component.
|
|
|
|
### AC #2 — EcosystemBridge loads manifests and dynamic imports modules: NOT DONE
|
|
No `lib/ecosystem-bridge.ts` file exists. No `EcosystemBridge` class anywhere. The `folk-rapp` component (`lib/folk-rapp.ts`) uses same-origin iframe embedding (line 845-873) and a hardcoded `MODULE_META` lookup (line 19-44) instead of dynamic manifest loading + `import()`. No dynamic import logic for external ecosystem modules.
|
|
|
|
### AC #3 — Trusted Web Components share CRDT and port/event system: PARTIALLY DONE
|
|
- **Port system**: `folk-rapp` does NOT implement `portDescriptors`, `getPort()`, `setPortValue()`, or `onEventReceived()`. It does not participate in the typed data pipe system from `lib/data-types.ts` / `lib/folk-arrow.ts`.
|
|
- **Event bus**: `folk-rapp` does not subscribe to `CanvasEventBus` channels.
|
|
- **CRDT sharing**: The `community-sync.ts` `#postMessageToParent()` (line 1281-1297) broadcasts `shape-updated` events to parent frames, and `folk-rapp` receives them via `#handleMessage()` (line 722-750). This is a one-way data bridge (iframe -> parent) via postMessage — NOT direct CRDT sharing.
|
|
- **Verdict**: The postMessage bridge works for shape update forwarding, but there is no direct CRDT doc sharing, no port/event participation. NOT DONE per the AC's intent of "shares CRDT and port/event system".
|
|
|
|
### AC #4 — Sandboxed iframe mode works with postMessage bridge: PARTIALLY DONE
|
|
- `folk-rapp` has a working postMessage protocol (lines 12-16 doc comment):
|
|
- Parent -> iframe: `{ source: 'rspace-parent', type: 'context', shapeId, space, moduleId }` (line 756)
|
|
- iframe -> parent: `{ source: 'rspace-canvas', type: 'shape-updated' }` (line 733)
|
|
- iframe -> parent: `{ source: 'rspace-rapp', type: 'navigate', moduleId }` (line 747)
|
|
- However, this is same-origin iframe embedding of internal rApps only. There is no sandbox attribute, no origin validation (uses `'*'` for postMessage target), and no structured API bridge for untrusted third-party apps. The AC envisions a security-conscious sandboxed mode for untrusted ecosystem apps — that does not exist yet.
|
|
- **Verdict**: Basic postMessage works for internal rApps. The sandboxed-for-untrusted-apps mode is NOT DONE.
|
|
|
|
### AC #5 — Server proxy avoids CORS for manifest/module loading: NOT DONE
|
|
No `/api/ecosystem/:appId/manifest` route exists in `server/index.ts`. No proxy endpoint for fetching external ecosystem manifests. The `server/landing-proxy.ts` `buildEcosystemMap()` function (line 124) is for rewriting standalone domain links in landing pages, not for proxying manifests.
|
|
|
|
### AC #8 — Service Worker caches ecosystem modules for offline: PARTIALLY DONE
|
|
- `website/sw.ts` has a `PrecacheManifest` system (lines 18-23) that caches `core` and `modules` arrays from `/precache-manifest.json`.
|
|
- The activate handler lazy-caches module bundles (lines 64-96).
|
|
- `vite.config.ts` generates the precache manifest at build time (line 1172-1183).
|
|
- However, this caches the built-in rSpace module bundles (Vite output), NOT external ecosystem module URLs loaded at runtime. There is no mechanism to dynamically add ecosystem module URLs to the SW cache.
|
|
- **Verdict**: SW caching infrastructure exists for internal modules. Ecosystem-specific module caching is NOT DONE.
|
|
|
|
## Implementation 2026-03-12
|
|
|
|
### Files created:
|
|
- `shared/ecosystem-manifest.ts` — TypeScript types for the ecosystem manifest protocol (EcosystemManifest, EcosystemShapeDescriptor, EventDescriptor, ResolvedManifest, ECOSYSTEM_PROTOCOL_VERSION)
|
|
- `lib/ecosystem-bridge.ts` — EcosystemBridge class (singleton) with loadManifest(), loadModule(), registerShapes(), createSandboxedEmbed(), SW cache notification, origin-validated postMessage handling
|
|
|
|
### Files modified:
|
|
- `server/index.ts` — Added /.well-known/rspace-manifest.json (self-manifest), GET /api/ecosystem/:appId/manifest (proxy with cache), GET /api/ecosystem/:appId/module (JS proxy)
|
|
- `lib/folk-rapp.ts` — Added portDescriptors (data-in, data-out, trigger-in, trigger-out), initPorts(), onEventReceived(), sandbox attribute on iframe, origin-validated postMessage (AC#3+#4), forward port-value-changed to iframe, handle ecosystem-embed messages
|
|
- `website/sw.ts` — Added ECOSYSTEM_CACHE, message handler for cache-ecosystem-module and clear-ecosystem-cache, preserved ecosystem cache during version cleanup
|
|
|
|
### Summary of remaining work:
|
|
| AC | Status | Blocking? |
|
|
|----|--------|----------|
|
|
| #1 Manifest protocol | Not done | Yes — foundation for #2, #5 |
|
|
| #2 EcosystemBridge | Not done | Yes — core feature |
|
|
| #3 CRDT + port/event sharing | Not done | Depends on #2 |
|
|
| #4 Sandboxed iframe | Partial (internal postMessage works) | Needs security hardening |
|
|
| #5 Server proxy | Not done | Depends on #1 |
|
|
| #8 SW caching | Partial (infra exists, not ecosystem-aware) | Depends on #2 |
|
|
<!-- SECTION:NOTES:END -->
|