From 610fd4ffa2150f1b8310e728424ad704c134be58 Mon Sep 17 00:00:00 2001 From: MechaCat02 Date: Tue, 2 Jun 2026 22:26:07 +0200 Subject: [PATCH] feat(v1.1.3-modules): dashboard kind dropdown + scripts-list and detail badges - `Script` type gains `kind: 'endpoint' | 'module'`. `CreateScriptInput` + `UpdateScriptInput` carry an optional `kind` field. - App page's script-create form grows a kind dropdown next to Name + Description. Selecting "module" surfaces a hint that modules cannot bind to routes / triggers. - Scripts list renders a small badge after the version: blue "endpoint" or purple "module". - Script detail page renders the same badge next to the H1. `npm run check` passes (0 errors, 0 warnings). Co-Authored-By: Claude Opus 4.7 (1M context) --- dashboard/src/lib/api.ts | 8 +++ dashboard/src/routes/apps/[slug]/+page.svelte | 53 ++++++++++++++++++- .../src/routes/scripts/[id]/+page.svelte | 32 ++++++++++- 3 files changed, 91 insertions(+), 2 deletions(-) diff --git a/dashboard/src/lib/api.ts b/dashboard/src/lib/api.ts index eb894a2..62bf0f1 100644 --- a/dashboard/src/lib/api.ts +++ b/dashboard/src/lib/api.ts @@ -21,6 +21,8 @@ export interface ScriptSandbox { max_expr_depth?: number; } +export type ScriptKind = 'endpoint' | 'module'; + export interface Script { id: string; app_id: string; @@ -28,6 +30,8 @@ export interface Script { description: string | null; version: number; source: string; + /** v1.1.3 — 'endpoint' (default) handles routes/triggers; 'module' is imported by other scripts. */ + kind: ScriptKind; timeout_seconds: number; memory_limit_mb: number; sandbox: ScriptSandbox; @@ -173,6 +177,8 @@ export interface CreateScriptInput { name: string; description?: string | null; source: string; + /** Defaults to 'endpoint' server-side if omitted. v1.1.3. */ + kind?: ScriptKind; timeout_seconds?: number; memory_limit_mb?: number; } @@ -184,6 +190,8 @@ export interface UpdateScriptInput { timeout_seconds?: number; memory_limit_mb?: number; sandbox?: ScriptSandbox; + /** v1.1.3 — endpoint→module rejected if routes/triggers reference the script. */ + kind?: ScriptKind; } export interface DeadLetterRow { diff --git a/dashboard/src/routes/apps/[slug]/+page.svelte b/dashboard/src/routes/apps/[slug]/+page.svelte index afc87b2..34e4a73 100644 --- a/dashboard/src/routes/apps/[slug]/+page.svelte +++ b/dashboard/src/routes/apps/[slug]/+page.svelte @@ -63,6 +63,10 @@ let createScriptName = $state(''); let createScriptDescription = $state(''); let createScriptSource = $state(SAMPLE_SOURCE); + // v1.1.3: endpoint (default — handles routes/triggers) vs module + // (library imported by other scripts). Modules cannot be bound to + // routes or used as trigger targets. + let createScriptKind = $state<'endpoint' | 'module'>('endpoint'); let creatingScript = $state(false); let createScriptError = $state(null); @@ -201,12 +205,14 @@ app_id: app.id, name: createScriptName.trim(), description: createScriptDescription.trim() || null, - source: createScriptSource + source: createScriptSource, + kind: createScriptKind }); showCreateScript = false; createScriptName = ''; createScriptDescription = ''; createScriptSource = SAMPLE_SOURCE; + createScriptKind = 'endpoint'; await loadScripts(app.id); } catch (e) { createScriptError = e instanceof Error ? e.message : String(e); @@ -473,6 +479,13 @@ Name + + {#if createScriptKind === 'module'} +

+ Modules expose fn and const declarations to other + scripts via import "name" as alias;. They cannot be bound to + routes or used as trigger targets. +

+ {/if} {#if createScriptError}
{createScriptError}
{/if} @@ -503,6 +523,11 @@
{script.name} v{script.version} + {#if script.kind === 'module'} + module + {:else} + endpoint + {/if}
{script.description ?? '—'}
@@ -1154,4 +1179,30 @@ display: flex; justify-content: flex-end; } + + .kind-badge { + display: inline-block; + padding: 0 0.45rem; + margin-left: 0.5rem; + border-radius: 0.25rem; + font-size: 0.7rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; + line-height: 1.5; + } + + .kind-endpoint { + background: #1e3a5f; + color: #93c5fd; + } + + .kind-module { + background: #3f2e7d; + color: #c4b5fd; + } + + .small { + font-size: 0.85rem; + } diff --git a/dashboard/src/routes/scripts/[id]/+page.svelte b/dashboard/src/routes/scripts/[id]/+page.svelte index 934933a..5f41150 100644 --- a/dashboard/src/routes/scripts/[id]/+page.svelte +++ b/dashboard/src/routes/scripts/[id]/+page.svelte @@ -433,7 +433,14 @@ {script.name} {/if} -

{script.name}

+

+ {script.name} + {#if script.kind === 'module'} + module + {:else} + endpoint + {/if} +

v{script.version} · timeout {script.timeout_seconds}s · {script.description ?? 'no description'}

@@ -1323,4 +1330,27 @@ margin: 0.25rem 0 0 4rem; font-size: 0.75rem; } + + .kind-badge { + display: inline-block; + padding: 0 0.45rem; + margin-left: 0.5rem; + border-radius: 0.25rem; + font-size: 0.65rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; + line-height: 1.5; + vertical-align: middle; + } + + .kind-endpoint { + background: #1e3a5f; + color: #93c5fd; + } + + .kind-module { + background: #3f2e7d; + color: #c4b5fd; + }