From 9791d314b534b156dd72505732a4a00eb8cda7f0 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Sun, 3 May 2026 20:12:05 +0530 Subject: [PATCH] feat: add rust function runtime template support --- bun.lock | 4 +- package.json | 2 +- src/lib/stores/runtimes.ts | 16 +- .../functions/create-function/+page.svelte | 42 +++--- .../create-function/deploy/+page.svelte | 9 +- .../create-function/manual/+page.svelte | 2 +- .../repository-[repository]/+page.svelte | 2 +- .../template-[template]/+page.svelte | 139 ++++++++++-------- src/routes/(public)/functions/deploy/+page.ts | 15 +- static/icons/dark/color/rust.svg | 1 + static/icons/dark/grayscale/rust.svg | 1 + static/icons/light/color/rust.svg | 1 + static/icons/light/grayscale/rust.svg | 1 + 13 files changed, 141 insertions(+), 94 deletions(-) create mode 100644 static/icons/dark/color/rust.svg create mode 100644 static/icons/dark/grayscale/rust.svg create mode 100644 static/icons/light/color/rust.svg create mode 100644 static/icons/light/grayscale/rust.svg diff --git a/bun.lock b/bun.lock index 3e2c925ae5..616c2964a3 100644 --- a/bun.lock +++ b/bun.lock @@ -6,7 +6,7 @@ "name": "@appwrite/console", "dependencies": { "@ai-sdk/svelte": "^1.1.24", - "@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@33968aa", + "@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@93d2dfa", "@appwrite.io/pink-icons": "0.25.0", "@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@bfe7ce3", "@appwrite.io/pink-legacy": "^1.0.3", @@ -124,7 +124,7 @@ "@analytics/type-utils": ["@analytics/type-utils@0.6.4", "", {}, "sha512-Ou1gQxFakOWLcPnbFVsrPb8g1wLLUZYYJXDPjHkG07+5mustGs5yqACx42UAu4A6NszNN6Z5gGxhyH45zPWRxw=="], - "@appwrite.io/console": ["@appwrite.io/console@https://pkg.vc/-/@appwrite/@appwrite.io/console@33968aa", { "dependencies": { "json-bigint": "1.0.0" } }], + "@appwrite.io/console": ["@appwrite.io/console@https://pkg.vc/-/@appwrite/@appwrite.io/console@93d2dfa", { "dependencies": { "json-bigint": "1.0.0" } }], "@appwrite.io/pink-icons": ["@appwrite.io/pink-icons@0.25.0", "", {}, "sha512-0O3i2oEuh5mWvjO80i+X6rbzrWLJ1m5wmv2/M3a1p2PyBJsFxN8xQMTEmTn3Wl/D26SsM7SpzbdW6gmfgoVU9Q=="], diff --git a/package.json b/package.json index a18b34eaa6..99b100b0af 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ }, "dependencies": { "@ai-sdk/svelte": "^1.1.24", - "@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@33968aa", + "@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@93d2dfa", "@appwrite.io/pink-icons": "0.25.0", "@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@bfe7ce3", "@appwrite.io/pink-legacy": "^1.0.3", diff --git a/src/lib/stores/runtimes.ts b/src/lib/stores/runtimes.ts index c42f8b1f73..c30d0ae04a 100644 --- a/src/lib/stores/runtimes.ts +++ b/src/lib/stores/runtimes.ts @@ -1,5 +1,7 @@ -export function getIconFromRuntime(runtime: string) { +export function getIconFromRuntime(runtime?: string) { switch (true) { + case !runtime: + return undefined; case runtime.includes('node'): return 'node'; case runtime.includes('php'): @@ -18,6 +20,18 @@ export function getIconFromRuntime(runtime: string) { return 'deno'; case runtime.includes('flutter'): return 'flutter'; + case runtime.includes('dotnet'): + return 'dotnet'; + case runtime.includes('java'): + return 'java'; + case runtime.includes('swift'): + return 'swift'; + case runtime.includes('kotlin'): + return 'kotlin'; + case runtime.includes('cpp'): + return 'cpp'; + case runtime.includes('rust'): + return 'rust'; default: return undefined; } diff --git a/src/routes/(console)/project-[region]-[project]/functions/create-function/+page.svelte b/src/routes/(console)/project-[region]-[project]/functions/create-function/+page.svelte index 42be5ca748..a9c2e00cd3 100644 --- a/src/routes/(console)/project-[region]-[project]/functions/create-function/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/functions/create-function/+page.svelte @@ -21,6 +21,7 @@ import Wizard from '$lib/layout/wizard.svelte'; import { Link } from '$lib/elements'; import { Button } from '$lib/elements/forms'; + import { getIconFromRuntime } from '$lib/stores/runtimes'; import { regionalConsoleVariables } from '../../store'; export let data; @@ -36,17 +37,17 @@ const starterTemplate = data.templates.find((template) => template.id === 'starter'); - const baseRuntimesList = [ - ...new Map( - data.runtimesList.runtimes.map((runtime) => { - const base = runtime.name.split('-')[0]; - return [base, runtime]; - }) - ).values() - ]; - - const starterTemplateRuntimes = starterTemplate.runtimes.filter((r) => - baseRuntimesList.some((base) => base.$id === r.name) + const latestRuntimesByKey = new Map(); + for (const runtime of data.runtimesList.runtimes) { + latestRuntimesByKey.set(runtime.key, runtime); + } + + const starterTemplateRuntimeIds = new Set( + starterTemplate?.runtimes.map((runtime) => runtime.name) ?? [] + ); + + const starterTemplateRuntimes = [...latestRuntimesByKey.values()].filter((runtime) => + starterTemplateRuntimeIds.has(runtime.$id) ); function connect(e: Models.ProviderRepository) { @@ -131,11 +132,8 @@ Quick start - {#each starterTemplateRuntimes.slice(0, 6) as template} - {@const iconName = template.name.split('-')[0]} - {@const runtimeDetail = baseRuntimesList.find( - (runtime) => runtime.$id === template.name - )} + {#each starterTemplateRuntimes as runtime} + {@const iconName = getIconFromRuntime(runtime.key)} + ) + `?runtime=${runtime.$id}`}> - - + + {#if iconName} + + {/if} - {runtimeDetail?.name} + {runtime.name} diff --git a/src/routes/(console)/project-[region]-[project]/functions/create-function/deploy/+page.svelte b/src/routes/(console)/project-[region]-[project]/functions/create-function/deploy/+page.svelte index 26410e3e69..e0e389e912 100644 --- a/src/routes/(console)/project-[region]-[project]/functions/create-function/deploy/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/functions/create-function/deploy/+page.svelte @@ -59,13 +59,16 @@ data.runtimesList?.runtimes?.map((r) => ({ value: r.$id, label: `${r.name} - ${r.version}`, - leadingHtml: `` + leadingHtml: `` })) || [] ); onMount(() => { - const runtimeParam = data.runtime || page.url.searchParams.get('runtime') || 'node-18.0'; - runtime = runtimeParam as Runtime; + const runtimeParam = data.runtime || page.url.searchParams.get('runtime'); + const runtimeOption = data.runtimesList.runtimes.find( + (runtime) => runtime.$id === runtimeParam + ); + runtime = (runtimeOption?.$id ?? data.runtimesList.runtimes[0]?.$id) as Runtime; entrypoint = page.url.searchParams.get('entrypoint') || ''; diff --git a/src/routes/(console)/project-[region]-[project]/functions/create-function/manual/+page.svelte b/src/routes/(console)/project-[region]-[project]/functions/create-function/manual/+page.svelte index c1452020db..d0e10cdf4b 100644 --- a/src/routes/(console)/project-[region]-[project]/functions/create-function/manual/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/functions/create-function/manual/+page.svelte @@ -32,7 +32,7 @@ const runtimeOptions = data.runtimesList.runtimes.map((runtime) => { const { $id: value, name, version, key } = runtime; const label = `${name} - ${version}`; - const iconName = getIconFromRuntime(key); + const iconName = getIconFromRuntime(key) ?? 'empty'; const iconSrc = iconFor(iconName, 'color'); const leadingHtml = `${iconName}`; diff --git a/src/routes/(console)/project-[region]-[project]/functions/create-function/repository-[repository]/+page.svelte b/src/routes/(console)/project-[region]-[project]/functions/create-function/repository-[repository]/+page.svelte index 58d29ad202..7b19202d0f 100644 --- a/src/routes/(console)/project-[region]-[project]/functions/create-function/repository-[repository]/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/functions/create-function/repository-[repository]/+page.svelte @@ -37,7 +37,7 @@ return { value: runtime.$id, label: `${runtime.name} - ${runtime.version}`, - leadingHtml: `` + leadingHtml: `` }; }); diff --git a/src/routes/(console)/project-[region]-[project]/functions/create-function/template-[template]/+page.svelte b/src/routes/(console)/project-[region]-[project]/functions/create-function/template-[template]/+page.svelte index 8aef379d1a..cfb1bcd802 100644 --- a/src/routes/(console)/project-[region]-[project]/functions/create-function/template-[template]/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/functions/create-function/template-[template]/+page.svelte @@ -11,7 +11,7 @@ import { installation, repository } from '$lib/stores/vcs'; import { Fieldset, Layout, Icon, Divider, Empty, Typography } from '@appwrite.io/pink-svelte'; import { IconGithub } from '@appwrite.io/pink-icons-svelte'; - import { onMount } from 'svelte'; + import { onMount, untrack } from 'svelte'; import { writable } from 'svelte/store'; import ProductionBranch from '$lib/components/git/productionBranchFieldset.svelte'; import Configuration from './configuration.svelte'; @@ -37,64 +37,83 @@ import { Dependencies } from '$lib/constants'; import { getIconFromRuntime } from '$lib/stores/runtimes'; import { regionalConsoleVariables } from '$routes/(console)/project-[region]-[project]/store'; + import type { PageData } from './$types'; - export let data; + let { data }: { data: PageData } = $props(); - const specificationOptions = (data.specificationsList?.specifications ?? []).map((size) => ({ - label: - `${size.cpus} CPU, ${size.memory} MB RAM` + - (!size.enabled ? ` (Upgrade to use this)` : ''), - value: size.slug, - disabled: !size.enabled - })); + const specificationOptions = $derived( + (data.specificationsList?.specifications ?? []).map((size) => ({ + label: + `${size.cpus} CPU, ${size.memory} MB RAM` + + (!size.enabled ? ` (Upgrade to use this)` : ''), + value: size.slug, + disabled: !size.enabled + })) + ); + + let showExitModal = $state(false); + let isCreatingRepository = $state(false); + + let formComponent = $state
(); + let isSubmitting = $state(writable(false)); + + let name = $state(untrack(() => data.template.name)); + let id = $state(null); + let runtime = $state(); + let branch = $state('main'); + let rootDir = $state('./'); + let connectBehaviour = $state<'now' | 'later'>('now'); + let repositoryBehaviour = $state<'new' | 'existing'>('new'); + let repositoryName = $state(); + let repositoryPrivate = $state(true); + let selectedInstallationId = $state(''); + let selectedRepository = $state(''); + let showConfig = $state(false); + let silentMode = $state(false); + let entrypoint = $state(''); + let selectedScopes = $state([]); + let execute = $state(true); + let variables = $state[]>([]); + let specification = $state(untrack(() => specificationOptions[0]?.value || '')); + + const availableRuntimes: Models.Runtime[] = $derived( + data.runtimesList.runtimes.filter((runtime) => + data.template.runtimes.some((templateRuntime) => templateRuntime.name === runtime.$id) + ) + ); - let showExitModal = false; - let isCreatingRepository = false; + function sortRuntimesByVersionDesc(a: Models.Runtime, b: Models.Runtime) { + return b.version.localeCompare(a.version, undefined, { numeric: true }); + } + + function selectInitialRuntime() { + const runtimeParam = page.url.searchParams.get('runtime'); + const requestedRuntime = availableRuntimes.find((runtime) => runtime.$id === runtimeParam); + + if (requestedRuntime) { + return requestedRuntime.$id as Runtime; + } - let formComponent: Form; - let isSubmitting = writable(false); + const runtimeById = new Map( + data.runtimesList.runtimes.map((runtime) => [runtime.$id, runtime]) + ); + const preferredRuntime = data.template.runtimes + .map((runtime) => runtimeById.get(runtime.name)) + .find(Boolean); + const preferredRuntimes = preferredRuntime + ? availableRuntimes.filter((runtime) => runtime.key === preferredRuntime.key) + : []; + const runtimes = preferredRuntimes.length ? preferredRuntimes : availableRuntimes; - let name = data.template.name; - let id: string | null = null; - let runtime: Runtime; - let branch = 'main'; - let rootDir = './'; - let connectBehaviour: 'now' | 'later' = 'now'; - let repositoryBehaviour: 'new' | 'existing' = 'new'; - let repositoryName = undefined; - let repositoryPrivate = true; - let selectedInstallationId = ''; - let selectedRepository = ''; - let showConfig = false; - let silentMode = false; - let entrypoint = ''; - let selectedScopes: Scopes[] = []; - let execute = true; - let variables: Partial[] = []; - let specification = specificationOptions[0]?.value || ''; + return [...runtimes].sort(sortRuntimesByVersionDesc)[0]?.$id as Runtime; + } onMount(async () => { if (!$installation?.$id) { $installation = data.installations.installations[0]; } selectedInstallationId = $installation?.$id; - if (data.template.runtimes && data.template.runtimes.length > 0) { - const targetRuntime = data.template.runtimes[0].name; - const matchingRuntimes = Object.values(Runtime).filter((r) => - r.startsWith(targetRuntime.split('-')[0]) - ); - - matchingRuntimes.sort((a, b) => { - const versionA = a.split('-')[1]; - const versionB = b.split('-')[1]; - return versionB.localeCompare(versionA, undefined, { numeric: true }); - }); - if (page.url.searchParams.has('runtime')) { - runtime = page.url.searchParams.get('runtime') as Runtime; - } else { - runtime = matchingRuntimes[0] as Runtime; - } - } + runtime = selectInitialRuntime(); }); async function createRepository() { @@ -210,18 +229,18 @@ } } - $: if (repositoryBehaviour === 'new') { - selectedInstallationId ??= $installation?.$id; - repositoryName ??= name.split(' ').join('-').toLowerCase(); - } - - $: if (connectBehaviour === 'later') { - selectedRepository = null; - } + $effect(() => { + if (repositoryBehaviour === 'new') { + selectedInstallationId ??= $installation?.$id; + repositoryName ??= name.split(' ').join('-').toLowerCase(); + } + }); - $: availableRuntimes = data.runtimesList.runtimes.filter((runtime) => - data.template.runtimes.some((templateRuntime) => templateRuntime.name === runtime.$id) - ); + $effect(() => { + if (connectBehaviour === 'later') { + selectedRepository = null; + } + }); @@ -259,7 +278,7 @@ return { value: runtime.$id, label: `${runtime.name} - ${runtime.version}`, - leadingHtml: `` + leadingHtml: `` }; })} diff --git a/src/routes/(public)/functions/deploy/+page.ts b/src/routes/(public)/functions/deploy/+page.ts index 929de33ec1..28defa6778 100644 --- a/src/routes/(public)/functions/deploy/+page.ts +++ b/src/routes/(public)/functions/deploy/+page.ts @@ -31,6 +31,16 @@ export const load: PageLoad = async ({ parent, url }) => { const envParam = url.searchParams.get('env'); const envKeys = envParam ? envParam.split(',').map((key: string) => key.trim()) : []; + // Get available runtimes + const runtimesList = await sdk.forConsole.functions.listRuntimes(); + + const runtimeOption = runtimesList.runtimes.find((option) => option.$id === runtime); + const runtimeId = runtimeOption?.$id ?? runtimesList.runtimes[0]?.$id; + + if (!runtimeId) { + redirect(302, base + '/'); + } + const deploymentData: { type: 'repo'; repository: { @@ -48,12 +58,9 @@ export const load: PageLoad = async ({ parent, url }) => { rootDirectory: null }, name: name || '', - runtime: runtime || 'node-18.0' + runtime: runtimeId }; - // Get available runtimes - const runtimesList = await sdk.forConsole.functions.listRuntimes(); - const info = getRepositoryInfo(repository); if (!info) { redirect(302, base + '/'); diff --git a/static/icons/dark/color/rust.svg b/static/icons/dark/color/rust.svg new file mode 100644 index 0000000000..7364d9ede6 --- /dev/null +++ b/static/icons/dark/color/rust.svg @@ -0,0 +1 @@ + diff --git a/static/icons/dark/grayscale/rust.svg b/static/icons/dark/grayscale/rust.svg new file mode 100644 index 0000000000..af2f8dd014 --- /dev/null +++ b/static/icons/dark/grayscale/rust.svg @@ -0,0 +1 @@ + diff --git a/static/icons/light/color/rust.svg b/static/icons/light/color/rust.svg new file mode 100644 index 0000000000..7364d9ede6 --- /dev/null +++ b/static/icons/light/color/rust.svg @@ -0,0 +1 @@ + diff --git a/static/icons/light/grayscale/rust.svg b/static/icons/light/grayscale/rust.svg new file mode 100644 index 0000000000..9c3a86e0ca --- /dev/null +++ b/static/icons/light/grayscale/rust.svg @@ -0,0 +1 @@ +