Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
4.4 KiB
4.4 KiB
Sample Extension Template (Runner + Iframe UI)
This template outlines a minimal v2 extension composed of:
- Server handlers compiled to WASM and executed by the Runner
- An iframe UI built with React using the Alga UI kit and SDK
- A Manifest v2 that declares endpoints and the UI entry
Core rules:
- All server calls go through the Gateway:
/api/ext/[extensionId]/[[...path]]→ RunnerPOST /v1/execute - UI assets are served by the Runner at
${RUNNER_PUBLIC_BASE}/ext-ui/{extensionId}/{content_hash}/[...] - Iframe src is constructed via buildExtUiSrc() and initialized with bootstrapIframe()
- Gateway scaffold reference: server/src/app/api/ext/[extensionId]/[[...path]]/route.ts
File Structure
my-extension/
├── src/
│ ├── http/
│ │ ├── list_agreements.ts
│ │ └── sync.ts
│ └── ui/
│ ├── index.html
│ └── src/
│ ├── main.tsx
│ └── components/
├── dist/
│ ├── handlers/http/list_agreements.wasm # built artifact(s)
│ └── ui/ # built iframe assets
├── manifest.json
├── SIGNATURE # generated by CI
├── package.json
└── README.md
Manifest v2
{
"name": "com.example.agreements",
"publisher": "Example, Inc.",
"version": "1.0.0",
"runtime": "wasm-js@1",
"capabilities": ["http.fetch", "storage.kv"],
"ui": { "type": "iframe", "entry": "ui/index.html" },
"api": {
"endpoints": [
{ "method": "GET", "path": "/agreements", "handler": "dist/handlers/http/list_agreements" },
{ "method": "POST", "path": "/agreements/sync", "handler": "dist/handlers/http/sync" }
]
},
"assets": ["ui/**/*"]
}
WASM Handler (conceptual)
// src/http/list_agreements.ts
export async function list_agreements(ctx) {
const items = await ctx.storage.list({ namespace: 'agreements' });
return { status: 200, headers: { 'content-type': 'application/json' }, body: { data: items } };
}
Build to dist/handlers/http/list_agreements.wasm via your language toolchain.
Iframe UI (React)
// ui/src/main.tsx
import React, { useEffect, useState } from 'react';
import { createRoot } from 'react-dom/client';
import { useExtension, BridgeProvider } from '@alga/extension-iframe-sdk';
import { DataTable } from '@alga/ui-kit';
function App() {
const { context } = useExtension();
const [rows, setRows] = useState<any[]>([]);
useEffect(() => {
async function load() {
const res = await fetch(`/api/ext/${context.extensionId}/agreements`, { headers: context.authHeaders });
const data = await res.json();
setRows(Array.isArray(data) ? data : data.data ?? []);
}
load();
}, [context.extensionId, context.authHeaders]);
return <DataTable columns={[{ key: 'name', header: 'Name' }]} rows={rows} />;
}
createRoot(document.getElementById('root')!).render(
<BridgeProvider>
<App />
</BridgeProvider>
);
Packaging & Signing
- Produce bundle with
manifest.json, WASM artifacts underdist/, UI assets underui/ - Compute SHA256 over the canonical bundle
- Sign using your publisher certificate; write
SIGNATURE - Publish to the Registry (CI step)
See: security_signing.md
Install & Use
- Tenant admin installs a specific version and grants capabilities
- UI entry is served by the Runner at
${RUNNER_PUBLIC_BASE}/ext-ui/{extensionId}/{content_hash}/index.html - Client/UI code calls handlers via
/api/ext/{extensionId}/...(Gateway proxies to Runner)
Notes
- Do not rely on server filesystem paths; artifacts are fetched by content hash
- Keep responses small; paginate lists
- Use SDK‑provided headers for authentication; do not forward end‑user tokens
- Validate inputs; return structured errors
Related references:
- Gateway route scaffold: server/src/app/api/ext/[extensionId]/[[...path]]/route.ts
- Iframe bridge: server/src/lib/extensions/ui/iframeBridge.ts
- Runner overview: runner.md
- Manifest schema: manifest_schema.md