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
127 lines
4.4 KiB
Markdown
127 lines
4.4 KiB
Markdown
# 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]]` → Runner `POST /v1/execute`
|
||
- UI assets are served by the Runner at `${RUNNER_PUBLIC_BASE}/ext-ui/{extensionId}/{content_hash}/[...]`
|
||
- Iframe src is constructed via [buildExtUiSrc()](../../../server/src/lib/extensions/ui/iframeBridge.ts:38) and initialized with [bootstrapIframe()](../../../server/src/lib/extensions/ui/iframeBridge.ts:45)
|
||
- Gateway scaffold reference: [server/src/app/api/ext/[extensionId]/[[...path]]/route.ts](../../../server/src/app/api/ext/%5BextensionId%5D/%5B%5B...path%5D%5D/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
|
||
|
||
```json
|
||
{
|
||
"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)
|
||
|
||
```ts
|
||
// 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)
|
||
|
||
```tsx
|
||
// 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 under `dist/`, UI assets under `ui/`
|
||
- Compute SHA256 over the canonical bundle
|
||
- Sign using your publisher certificate; write `SIGNATURE`
|
||
- Publish to the Registry (CI step)
|
||
|
||
See: [security_signing.md](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](../../../server/src/app/api/ext/%5BextensionId%5D/%5B%5B...path%5D%5D/route.ts)
|
||
- Iframe bridge: [server/src/lib/extensions/ui/iframeBridge.ts](../../../server/src/lib/extensions/ui/iframeBridge.ts:38)
|
||
- Runner overview: [runner.md](runner.md)
|
||
- Manifest schema: [manifest_schema.md](manifest_schema.md)
|