From 3182a5cc284455539c66de67b9403d0587d3e8a5 Mon Sep 17 00:00:00 2001 From: Jwahir Sundai Date: Tue, 24 Mar 2026 15:55:43 -0500 Subject: [PATCH 1/3] Add Standalone Activities Interactive Demo page Co-Authored-By: Claude Sonnet 4.6 --- ...standalone-activities-interactive-demo.mdx | 383 +++++++++++ .../elements/StandaloneActivityDemo.js | 602 ++++++++++++++++++ .../standalone-activity-demo.module.css | 416 ++++++++++++ src/components/index.js | 1 + 4 files changed, 1402 insertions(+) create mode 100644 docs/develop/standalone-activities-interactive-demo.mdx create mode 100644 src/components/elements/StandaloneActivityDemo.js create mode 100644 src/components/elements/standalone-activity-demo.module.css diff --git a/docs/develop/standalone-activities-interactive-demo.mdx b/docs/develop/standalone-activities-interactive-demo.mdx new file mode 100644 index 0000000000..317c88acf9 --- /dev/null +++ b/docs/develop/standalone-activities-interactive-demo.mdx @@ -0,0 +1,383 @@ +--- +id: standalone-activities-interactive-demo +title: Standalone Activities Interactive Demo +sidebar_label: Standalone Activities (Interactive) +toc_max_heading_level: 3 +keywords: + - standalone activity + - activity execution + - execute activity + - activity handle + - quickstart + - interactive demo +tags: + - Activities + - Temporal Client + - Temporal SDKs +description: An interactive quickstart for Temporal Standalone Activities — run Activities directly from a Temporal Client without a Workflow. +--- + +:::tip SUPPORT, STABILITY, and DEPENDENCY INFO + +Temporal SDK support for [Standalone Activities](/standalone-activity) is at +[Pre-release](/evaluate/development-production-features/release-stages#pre-release). + +All APIs are experimental and may be subject to backwards-incompatible changes. + +::: + +Standalone Activities let you execute an Activity directly from a Temporal Client — no Workflow +required. The Activity is durably enqueued on the Temporal Server, executed by a Worker, and its +result is returned to the caller. + +Use the interactive demo below to explore the API, experiment with failure scenarios, and see the +generated SDK code and CLI command update in real time. + +import { StandaloneActivityDemo } from '@site/src/components'; + + + +--- + +## How it works + +When you call `client.ExecuteActivity()` (or the equivalent in your SDK), the following happens: + +1. **Connect** — Your application connects to the Temporal Server. +2. **Schedule** — The Server durably persists the Activity execution on the specified Task Queue. +3. **Poll** — A Worker polling that Task Queue picks up the Activity Task. +4. **Execute** — The Worker runs your Activity function with the provided arguments. +5. **Return** — The result is stored by the Server and returned to the original caller via the + `ActivityHandle`. + +Because the Server durably persists the Activity, it survives Worker restarts and network +interruptions. If the Activity fails, the Server automatically retries it according to the Retry +Policy you configure. + +### Standalone vs Workflow Activities + +| | Workflow Activity | Standalone Activity | +|---|---|---| +| Orchestrated by | A Workflow Definition | Your application code | +| Started with | `workflow.ExecuteActivity()` | `client.ExecuteActivity()` | +| Retry policy | Configured per `ActivityOptions` | Configured per `StartActivityOptions` | +| Visibility | Workflow Event History | Activity Visibility (List/Count) | +| Use case | Complex, multi-step orchestration | Simple, independent jobs | + +The Activity function and Worker registration are **identical** for both — only the execution path +differs. + +--- + +## Setup + +### 1. Install the Temporal CLI + +Download the Standalone Activity prerelease build of the Temporal CLI: + +```bash +# macOS (Homebrew) +brew install temporal + +# Or download directly from GitHub +# https://github.com/temporalio/cli/releases/tag/v1.6.2-standalone-activity +``` + +### 2. Start a local development server + +```bash +temporal server start-dev +``` + +The server starts at `localhost:7233` and the Web UI at +[http://localhost:8233](http://localhost:8233). Standalone Activities appear under the **Activities** +nav item in the Web UI. + +### 3. Install the SDK + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + + + + +Requires **Go SDK v1.41.0** or higher. + +```bash +go get go.temporal.io/sdk@latest +``` + +Clone the samples repository to follow along: + +```bash +git clone https://github.com/temporalio/samples-go.git +cd samples-go +``` + + + + +Requires **Python SDK v1.23.0** or higher. + +```bash +uv add temporalio +# or: pip install temporalio +``` + +Clone the samples repository to follow along: + +```bash +git clone https://github.com/temporalio/samples-python.git +cd samples-python +``` + + + + +Requires **.NET SDK v1.12.0** or higher. + +```bash +dotnet add package Temporalio +``` + +Clone the samples repository to follow along: + +```bash +git clone https://github.com/temporalio/samples-dotnet.git +cd samples-dotnet +``` + + + + +### 4. Run the Worker + +The Worker registers the Activity and polls the Task Queue. Start it in a dedicated terminal: + + + + +```bash +go run standalone-activity/helloworld/worker/main.go +``` + + + + +```bash +uv run hello_standalone_activity/worker.py +``` + + + + +```bash +dotnet run --project src/StandaloneActivity worker +``` + + + + +### 5. Execute the Activity + +In a separate terminal, run the starter: + + + + +```bash +go run standalone-activity/helloworld/starter/main.go +``` + + + + +```bash +uv run hello_standalone_activity/execute_activity.py +``` + + + + +```bash +dotnet run --project src/StandaloneActivity execute-activity +``` + + + + +Or use the Temporal CLI directly: + +```bash +temporal activity execute \ + --type Activity \ + --activity-id my-activity-id \ + --task-queue my-task-queue \ + --start-to-close-timeout 10s \ + --input '"World"' +``` + +--- + +## Key API concepts + +### `ExecuteActivity` — start and wait + +The primary call. Durably enqueues the Activity, waits for execution, and returns the result: + + + + +```go +handle, err := c.ExecuteActivity(ctx, client.StartActivityOptions{ + ID: "my-activity-id", + TaskQueue: "my-task-queue", + StartToCloseTimeout: 10 * time.Second, +}, helloworld.Activity, "World") + +var result string +err = handle.Get(ctx, &result) +``` + + + + +```python +result = await client.execute_activity( + compose_greeting, + args=[ComposeGreetingInput("Hello", "World")], + id="my-activity-id", + task_queue="my-task-queue", + start_to_close_timeout=timedelta(seconds=10), +) +``` + + + + +```csharp +var result = await client.ExecuteActivityAsync( + () => MyActivities.ComposeGreetingAsync( + new ComposeGreetingInput("Hello", "World")), + new("my-activity-id", "my-task-queue") + { + StartToCloseTimeout = TimeSpan.FromSeconds(10), + }); +``` + + + + +### `StartActivity` — fire and forget + +Enqueues the Activity without waiting for it to finish. Useful when you want to kick off +long-running work and check back later: + + + + +```go +handle, err := c.ExecuteActivity(ctx, options, helloworld.Activity, "World") +// handle.Get() can be called later +``` + + + + +```python +handle = await client.start_activity( + compose_greeting, + args=[ComposeGreetingInput("Hello", "World")], + id="my-activity-id", + task_queue="my-task-queue", + start_to_close_timeout=timedelta(seconds=10), +) +# Later: +result = await handle.result() +``` + + + + +```csharp +var handle = await client.StartActivityAsync( + () => MyActivities.ComposeGreetingAsync( + new ComposeGreetingInput("Hello", "World")), + new("my-activity-id", "my-task-queue") + { + StartToCloseTimeout = TimeSpan.FromSeconds(10), + }); +// Later: +var result = await handle.GetResultAsync(); +``` + + + + +### List and Count Activities + + + + +```go +// List +resp, err := c.ListActivities(ctx, client.ListActivitiesOptions{ + Query: "TaskQueue = 'my-task-queue'", +}) +for info, err := range resp.Results { ... } + +// Count +resp, err := c.CountActivities(ctx, client.CountActivitiesOptions{ + Query: "TaskQueue = 'my-task-queue'", +}) +log.Println("Total:", resp.Count) +``` + + + + +```python +# List +async for info in client.list_activities( + query="TaskQueue = 'my-task-queue'" +): + print(info.activity_id, info.status) + +# Count +resp = await client.count_activities( + query="TaskQueue = 'my-task-queue'" +) +print("Total:", resp.count) +``` + + + + +```csharp +// List +await foreach (var info in client.ListActivitiesAsync( + "TaskQueue = 'my-task-queue'")) +{ + Console.WriteLine($"{info.ActivityId}: {info.Status}"); +} + +// Count +var resp = await client.CountActivitiesAsync( + "TaskQueue = 'my-task-queue'"); +Console.WriteLine($"Total: {resp.Count}"); +``` + + + + +The `Query` parameter uses the same [List Filter](/list-filter) syntax as Workflow Visibility. + +--- + +## Next steps + +For complete API reference and advanced usage, see the SDK-specific guides: + +- [Standalone Activities — Go SDK](/develop/go/standalone-activities) +- [Standalone Activities — Python SDK](/develop/python/standalone-activities) +- [Standalone Activities — .NET SDK](/develop/dotnet/standalone-activities) diff --git a/src/components/elements/StandaloneActivityDemo.js b/src/components/elements/StandaloneActivityDemo.js new file mode 100644 index 0000000000..91625c5952 --- /dev/null +++ b/src/components/elements/StandaloneActivityDemo.js @@ -0,0 +1,602 @@ +import CodeBlock from '@theme/CodeBlock'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; +import styles from './standalone-activity-demo.module.css'; + +// --------------------------------------------------------------------------- +// Code generation +// --------------------------------------------------------------------------- + +function generateSdkCode(language, config) { + const { activityId, taskQueue, greeting, name, timeout, timeoutType, simulateFailures, maxRetries } = config; + const expectedResult = `${greeting}, ${name}!`; + + if (language === 'go') { + const timeoutField = + timeoutType === 'start_to_close' + ? `StartToCloseTimeout: ${timeout} * time.Second,` + : `ScheduleToCloseTimeout: ${timeout} * time.Second,`; + const retryPolicy = + simulateFailures && maxRetries > 0 + ? `\n\tRetryPolicy: &temporal.RetryPolicy{\n\t\tMaximumAttempts: ${maxRetries + 1},\n\t},` + : ''; + return `activityOptions := client.StartActivityOptions{ +\tID: "${activityId}", +\tTaskQueue: "${taskQueue}", +\t${timeoutField}${retryPolicy} +} + +handle, err := c.ExecuteActivity(ctx, activityOptions, +\thelloworld.Activity, "${greeting}", "${name}") +if err != nil { +\tlog.Fatalln("Unable to execute activity", err) +} + +var result string +err = handle.Get(ctx, &result) +// result: "${expectedResult}"`; + } + + if (language === 'python') { + const timeoutField = + timeoutType === 'start_to_close' + ? `start_to_close_timeout=timedelta(seconds=${timeout}),` + : `schedule_to_close_timeout=timedelta(seconds=${timeout}),`; + const retryPolicy = + simulateFailures && maxRetries > 0 + ? `\n retry_policy=RetryPolicy(\n maximum_attempts=${maxRetries + 1},\n ),` + : ''; + return `result = await client.execute_activity( + compose_greeting, + args=[ComposeGreetingInput("${greeting}", "${name}")], + id="${activityId}", + task_queue="${taskQueue}", + ${timeoutField}${retryPolicy} +) +# result: "${expectedResult}"`; + } + + if (language === 'dotnet') { + const timeoutField = + timeoutType === 'start_to_close' + ? `StartToCloseTimeout = TimeSpan.FromSeconds(${timeout}),` + : `ScheduleToCloseTimeout = TimeSpan.FromSeconds(${timeout}),`; + const retryPolicy = + simulateFailures && maxRetries > 0 + ? `\n MaximumAttempts = ${maxRetries + 1},` + : ''; + return `var result = await client.ExecuteActivityAsync( + () => MyActivities.ComposeGreetingAsync( + new ComposeGreetingInput("${greeting}", "${name}")), + new("${activityId}", "${taskQueue}") + { + ${timeoutField}${retryPolicy} + }); +// result: "${expectedResult}"`; + } + + return ''; +} + +function generateCliCode(config) { + const { activityId, taskQueue, name, timeout, timeoutType } = config; + const timeoutFlag = + timeoutType === 'start_to_close' + ? `--start-to-close-timeout ${timeout}s` + : `--schedule-to-close-timeout ${timeout}s`; + return `temporal activity execute \\ + --type Activity \\ + --activity-id ${activityId} \\ + --task-queue ${taskQueue} \\ + ${timeoutFlag} \\ + --input '"${name}"'`; +} + +// --------------------------------------------------------------------------- +// Constants +// --------------------------------------------------------------------------- + +const LANGUAGES = [ + { id: 'go', label: 'Go' }, + { id: 'python', label: 'Python' }, + { id: 'dotnet', label: '.NET' }, +]; + +const FLOW_NODES = [ + { label: 'Client', sub: 'Your App' }, + { label: 'Server', sub: 'Temporal' }, + { label: 'Task Queue', sub: '' }, + { label: 'Worker', sub: '' }, + { label: 'Activity', sub: 'Function' }, +]; + +const IDLE_NODES = ['pending', 'pending', 'pending', 'pending', 'pending']; + +const DEFAULT_CONFIG = { + activityId: 'my-activity-id', + taskQueue: 'my-task-queue', + greeting: 'Hello', + name: 'World', + timeout: 10, + timeoutType: 'start_to_close', + simulateFailures: false, + failCount: 1, + maxRetries: 2, +}; + +// --------------------------------------------------------------------------- +// Main component +// --------------------------------------------------------------------------- + +export default function StandaloneActivityDemo() { + const [language, setLanguage] = useState('go'); + const [config, setConfig] = useState({ ...DEFAULT_CONFIG }); + + const [sim, setSim] = useState({ + running: false, + nodeStates: [...IDLE_NODES], + log: [], + status: 'idle', // 'idle' | 'running' | 'completed' | 'failed' + result: null, + }); + + const [history, setHistory] = useState([]); + + const runIdRef = useRef(0); + const logScrollRef = useRef(null); + + // Auto-scroll log to bottom + useEffect(() => { + if (logScrollRef.current) { + logScrollRef.current.scrollTop = logScrollRef.current.scrollHeight; + } + }, [sim.log]); + + const updateConfig = useCallback((key, value) => { + setConfig((prev) => ({ ...prev, [key]: value })); + }, []); + + const handleExecute = useCallback(() => { + const runId = ++runIdRef.current; + const isCancelled = () => runIdRef.current !== runId; + const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); + + const startTime = Date.now(); + const logEntries = []; + + const elapsed = () => ((Date.now() - startTime) / 1000).toFixed(2); + + /** Push a log entry and update React state atomically with nodeStates */ + const update = (nodeStates, msg, type = 'info') => { + if (isCancelled()) return; + if (msg) logEntries.push({ time: elapsed(), msg, type }); + setSim((prev) => ({ + ...prev, + running: true, + nodeStates, + log: [...logEntries], + status: 'running', + })); + }; + + /** Push a log-only entry (no node state change) */ + const addLog = (msg, type = 'info') => { + if (isCancelled()) return; + logEntries.push({ time: elapsed(), msg, type }); + setSim((prev) => ({ ...prev, log: [...logEntries] })); + }; + + // Reset UI immediately + setSim({ + running: true, + nodeStates: [...IDLE_NODES], + log: [], + status: 'running', + result: null, + }); + + (async () => { + let attempt = 1; + + // ── Step 0: connect ────────────────────────────────────────────────── + update( + ['active', 'pending', 'pending', 'pending', 'pending'], + 'Connecting to Temporal Server at localhost:7233...' + ); + await sleep(400); + if (isCancelled()) return; + + // ── Step 1: schedule ───────────────────────────────────────────────── + update( + ['completed', 'active', 'pending', 'pending', 'pending'], + `Scheduling activity "${config.activityId}" on task queue "${config.taskQueue}"...` + ); + await sleep(600); + if (isCancelled()) return; + + update(['completed', 'completed', 'pending', 'pending', 'pending'], null); + + // ── Retry loop ─────────────────────────────────────────────────────── + while (true) { + if (isCancelled()) return; + + // Step 2: worker poll + update( + ['completed', 'completed', 'active', 'pending', 'pending'], + attempt === 1 + ? 'Worker polling task queue for activity tasks...' + : `[Retry ${attempt - 1}] Worker polling task queue...` + ); + await sleep(550); + if (isCancelled()) return; + + // Step 3: execute + update( + ['completed', 'completed', 'completed', 'active', 'pending'], + `[Attempt ${attempt}] Executing Activity("${config.greeting}", "${config.name}")...` + ); + await sleep(750); + if (isCancelled()) return; + + const shouldFail = config.simulateFailures && attempt <= config.failCount; + const maxAttempts = config.maxRetries + 1; + + if (shouldFail) { + const retriesLeft = maxAttempts - attempt; + update( + ['completed', 'completed', 'pending', 'failed', 'pending'], + `[Attempt ${attempt}/${maxAttempts}] Activity failed: ApplicationError`, + 'error' + ); + await sleep(350); + if (isCancelled()) return; + + if (retriesLeft <= 0) { + logEntries.push({ + time: elapsed(), + msg: `Activity exhausted all ${maxAttempts} attempt(s). No more retries.`, + type: 'error', + }); + setSim((prev) => ({ + ...prev, + running: false, + status: 'failed', + log: [...logEntries], + })); + setHistory((prev) => + [ + { + activityId: config.activityId, + status: 'Failed', + duration: `${((Date.now() - startTime) / 1000).toFixed(1)}s`, + attempts: attempt, + result: '—', + timestamp: new Date().toLocaleTimeString(), + }, + ...prev, + ].slice(0, 10) + ); + return; + } + + addLog( + `Scheduling retry in 1s… (${retriesLeft} retry attempt${retriesLeft > 1 ? 's' : ''} remaining)`, + 'warn' + ); + await sleep(1000); + if (isCancelled()) return; + + attempt++; + continue; // jump back to worker poll + } + + // ── Success ─────────────────────────────────────────────────────── + update( + ['completed', 'completed', 'completed', 'completed', 'active'], + `[Attempt ${attempt}] Activity function returned successfully!`, + 'success' + ); + await sleep(400); + if (isCancelled()) return; + + const result = `${config.greeting}, ${config.name}!`; + logEntries.push({ time: elapsed(), msg: `Result: "${result}"`, type: 'success' }); + + setSim({ + running: false, + nodeStates: ['completed', 'completed', 'completed', 'completed', 'completed'], + log: [...logEntries], + status: 'completed', + result, + }); + + setHistory((prev) => + [ + { + activityId: config.activityId, + status: 'Completed', + duration: `${((Date.now() - startTime) / 1000).toFixed(1)}s`, + attempts: attempt, + result, + timestamp: new Date().toLocaleTimeString(), + }, + ...prev, + ].slice(0, 10) + ); + return; + } + })(); + }, [config]); + + const codeLanguage = language === 'dotnet' ? 'csharp' : language; + + const failureNote = + config.simulateFailures + ? config.failCount > config.maxRetries + ? `⚠ All ${config.maxRetries + 1} attempt(s) will fail — failCount exceeds maxRetries.` + : `ℹ Activity will fail ${config.failCount} time(s), then succeed on attempt ${config.failCount + 1}.` + : null; + + return ( +
+ {/* Language tabs */} +
+ {LANGUAGES.map(({ id, label }) => ( + + ))} +
+ +
+ {/* ── Left column: configure + code ── */} +
+
+

Configure Activity

+
+ updateConfig('activityId', v)} + /> + updateConfig('taskQueue', v)} + /> + updateConfig('greeting', v)} + /> + updateConfig('name', v)} + /> + updateConfig('timeout', Number(v))} + /> +
+ + +
+
+
+ +
+

Failure Simulation

+
+
+ +
+ {config.simulateFailures && ( + <> + updateConfig('failCount', Number(v))} + /> + updateConfig('maxRetries', Number(v))} + /> + + )} +
+ {failureNote &&

{failureNote}

} +
+ +
+

SDK Code

+ {generateSdkCode(language, config)} +
+ +
+

CLI Command

+ {generateCliCode(config)} +
+
+ + {/* ── Right column: simulation ── */} +
+
+ +
+ +
+

Execution Flow

+
+ {FLOW_NODES.map((node, i) => ( + +
+
{node.label}
+ {node.sub && ( +
{node.sub}
+ )} +
+ {i < FLOW_NODES.length - 1 && ( +
+ › +
+ )} +
+ ))} +
+
+ +
+

Activity Log

+
+ {sim.log.length === 0 ? ( +
+ Click "Execute Activity" to run the simulation +
+ ) : ( + sim.log.map((entry, i) => ( +
+ [{entry.time}s] + {entry.msg} +
+ )) + )} +
+ + {sim.status === 'completed' && sim.result && ( +
+ ✅ Result: "{sim.result}" +
+ )} + {sim.status === 'failed' && ( +
+ ❌ Activity failed after exhausting all retry attempts +
+ )} +
+ + {history.length > 0 && ( +
+

+ Activity History{' '} + ({history.length}) +

+
+ + + + + + + + + + + + {history.map((h, i) => ( + + + + + + + + ))} + +
Activity IDStatusDurationAttemptsResult
{h.activityId} + + {h.status} + + {h.duration}{h.attempts}{h.result}
+
+
+ )} +
+
+
+ ); +} + +// --------------------------------------------------------------------------- +// Sub-components +// --------------------------------------------------------------------------- + +function ConfigField({ label, value, onChange, type = 'text', min, max }) { + return ( +
+ + onChange(e.target.value)} + /> +
+ ); +} diff --git a/src/components/elements/standalone-activity-demo.module.css b/src/components/elements/standalone-activity-demo.module.css new file mode 100644 index 0000000000..4b4db563d7 --- /dev/null +++ b/src/components/elements/standalone-activity-demo.module.css @@ -0,0 +1,416 @@ +/* ── Root ───────────────────────────────────────────────────────────────── */ + +.demo { + font-family: var(--ifm-font-family-base); +} + +/* ── Language tabs ──────────────────────────────────────────────────────── */ + +.languageTabs { + display: flex; + gap: 6px; + margin-bottom: 16px; + border-bottom: 2px solid var(--ifm-color-emphasis-200); + padding-bottom: 10px; +} + +.langTab { + padding: 5px 18px; + border: 1px solid var(--ifm-color-emphasis-300); + border-radius: 4px; + background: var(--ifm-background-color); + color: var(--ifm-font-color-base); + cursor: pointer; + font-size: 0.875rem; + font-weight: 500; + transition: background 0.15s, color 0.15s, border-color 0.15s; +} + +.langTab:hover { + background: var(--ifm-color-emphasis-100); +} + +.langTabActive { + background: var(--ifm-color-primary); + color: #fff; + border-color: var(--ifm-color-primary); +} + +/* ── Two-column layout ──────────────────────────────────────────────────── */ + +.columns { + display: flex; + gap: 24px; + align-items: flex-start; +} + +.leftCol, +.rightCol { + flex: 1; + min-width: 0; +} + +@media (max-width: 900px) { + .columns { + flex-direction: column; + } +} + +/* ── Section ────────────────────────────────────────────────────────────── */ + +.section { + margin-bottom: 20px; +} + +.sectionTitle { + font-size: 0.95rem; + font-weight: 600; + margin: 0 0 8px; + color: var(--ifm-font-color-base); +} + +/* ── Config form ────────────────────────────────────────────────────────── */ + +.configGrid { + border: 1px solid var(--ifm-color-emphasis-200); + border-radius: 6px; + overflow: hidden; +} + +.configRow { + display: flex; + align-items: center; + padding: 7px 12px; + border-bottom: 1px solid var(--ifm-color-emphasis-200); + background: var(--ifm-background-surface-color); +} + +.configRow:last-child { + border-bottom: none; +} + +.configLabel { + flex: 1; + font-size: 0.83rem; + color: var(--ifm-font-color-secondary); + user-select: none; +} + +.configInput, +.configSelect { + width: 150px; + padding: 4px 8px; + border: 1px solid var(--ifm-color-emphasis-300); + border-radius: 4px; + background: var(--ifm-background-color); + color: var(--ifm-font-color-base); + font-size: 0.83rem; +} + +.checkbox { + margin-right: 6px; + cursor: pointer; +} + +.simNote { + font-size: 0.8rem; + color: var(--ifm-font-color-secondary); + margin: 8px 0 0; + padding: 8px 12px; + background: var(--ifm-color-emphasis-100); + border-radius: 4px; + line-height: 1.4; +} + +/* ── Execute button ─────────────────────────────────────────────────────── */ + +.executeBtn { + width: 100%; + padding: 12px 24px; + background: var(--ifm-color-primary); + color: #fff; + border: none; + border-radius: 6px; + font-size: 1rem; + font-weight: 600; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + transition: background 0.2s; +} + +.executeBtn:hover:not(.executeBtnDisabled) { + background: var(--ifm-color-primary-dark); +} + +.executeBtnDisabled { + opacity: 0.65; + cursor: not-allowed; +} + +/* ── Spinner ────────────────────────────────────────────────────────────── */ + +@keyframes spin { + to { + transform: rotate(360deg); + } +} + +.spinner { + display: inline-block; + width: 14px; + height: 14px; + border: 2px solid rgba(255, 255, 255, 0.35); + border-top-color: #fff; + border-radius: 50%; + animation: spin 0.75s linear infinite; + flex-shrink: 0; +} + +/* ── Flow diagram ───────────────────────────────────────────────────────── */ + +.flowDiagram { + display: flex; + align-items: center; + justify-content: space-between; + padding: 14px 12px; + background: var(--ifm-background-surface-color); + border: 1px solid var(--ifm-color-emphasis-200); + border-radius: 6px; + overflow-x: auto; + gap: 4px; +} + +.flowNode { + display: flex; + flex-direction: column; + align-items: center; + padding: 8px 10px; + border-radius: 6px; + border: 2px solid var(--ifm-color-emphasis-300); + background: var(--ifm-background-color); + min-width: 68px; + text-align: center; + transition: border-color 0.3s, background 0.3s, opacity 0.3s; +} + +.flowNodeLabel { + font-size: 0.75rem; + font-weight: 600; + white-space: nowrap; + transition: color 0.3s; +} + +.flowNodeSub { + font-size: 0.63rem; + color: var(--ifm-font-color-secondary); + margin-top: 2px; + white-space: nowrap; +} + +/* Node states */ + +.flowNode_pending { + opacity: 0.38; +} + +@keyframes nodePulse { + 0%, + 100% { + box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.45); + } + 50% { + box-shadow: 0 0 0 7px rgba(59, 130, 246, 0); + } +} + +.flowNode_active { + border-color: #3b82f6; + background: rgba(59, 130, 246, 0.08); + animation: nodePulse 1.4s ease-in-out infinite; +} + +.flowNode_active .flowNodeLabel { + color: #3b82f6; +} + +.flowNode_completed { + border-color: #22c55e; + background: rgba(34, 197, 94, 0.08); +} + +.flowNode_completed .flowNodeLabel { + color: #15803d; +} + +.flowNode_failed { + border-color: #ef4444; + background: rgba(239, 68, 68, 0.08); +} + +.flowNode_failed .flowNodeLabel { + color: #dc2626; +} + +/* Arrows */ + +.flowArrow { + font-size: 1.6rem; + color: var(--ifm-color-emphasis-300); + flex-shrink: 0; + line-height: 1; + transition: color 0.3s; + user-select: none; +} + +.flowArrowLit { + color: #22c55e; +} + +/* ── Activity log ───────────────────────────────────────────────────────── */ + +.log { + height: 190px; + overflow-y: auto; + background: var(--ifm-background-surface-color); + border: 1px solid var(--ifm-color-emphasis-200); + border-radius: 6px; + padding: 10px 12px; + font-family: var(--ifm-font-family-monospace); + font-size: 0.78rem; + line-height: 1.55; +} + +.logPlaceholder { + color: var(--ifm-font-color-secondary); + font-style: italic; + padding: 4px 0; +} + +.logLine { + display: flex; + gap: 8px; + padding: 1px 0; +} + +.logTime { + color: var(--ifm-font-color-secondary); + flex-shrink: 0; +} + +.logLine_info .logMsg { + color: var(--ifm-font-color-base); +} + +.logLine_success .logMsg { + color: #15803d; + font-weight: 600; +} + +.logLine_error .logMsg { + color: #dc2626; +} + +.logLine_warn .logMsg { + color: #b45309; +} + +/* Result banners */ + +.resultSuccess { + margin-top: 8px; + padding: 9px 14px; + background: rgba(34, 197, 94, 0.1); + border: 1px solid rgba(34, 197, 94, 0.5); + border-radius: 6px; + color: #15803d; + font-size: 0.88rem; +} + +.resultFailed { + margin-top: 8px; + padding: 9px 14px; + background: rgba(239, 68, 68, 0.08); + border: 1px solid rgba(239, 68, 68, 0.45); + border-radius: 6px; + color: #dc2626; + font-size: 0.88rem; +} + +/* ── Activity history table ─────────────────────────────────────────────── */ + +.historyCount { + font-weight: 400; + color: var(--ifm-font-color-secondary); + font-size: 0.85rem; +} + +.historyWrapper { + overflow-x: auto; + border: 1px solid var(--ifm-color-emphasis-200); + border-radius: 6px; +} + +.historyTable { + width: 100%; + border-collapse: collapse; + font-size: 0.82rem; +} + +.historyTable th { + padding: 7px 12px; + text-align: left; + background: var(--ifm-background-surface-color); + border-bottom: 1px solid var(--ifm-color-emphasis-200); + font-weight: 600; + color: var(--ifm-font-color-secondary); + font-size: 0.72rem; + text-transform: uppercase; + letter-spacing: 0.05em; + white-space: nowrap; +} + +.historyTable td { + padding: 7px 12px; + border-bottom: 1px solid var(--ifm-color-emphasis-100); + vertical-align: middle; +} + +.historyTable tr:last-child td { + border-bottom: none; +} + +.historyId { + font-family: var(--ifm-font-family-monospace); + font-size: 0.78rem; +} + +.historyResult { + font-family: var(--ifm-font-family-monospace); + font-size: 0.78rem; + color: var(--ifm-font-color-secondary); +} + +.badgeSuccess { + display: inline-block; + padding: 2px 8px; + background: rgba(34, 197, 94, 0.12); + color: #15803d; + border-radius: 9999px; + font-size: 0.74rem; + font-weight: 600; + white-space: nowrap; +} + +.badgeFailed { + display: inline-block; + padding: 2px 8px; + background: rgba(239, 68, 68, 0.1); + color: #dc2626; + border-radius: 9999px; + font-size: 0.74rem; + font-weight: 600; + white-space: nowrap; +} diff --git a/src/components/index.js b/src/components/index.js index 07b4ad9ef6..df840f56ae 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -1,5 +1,6 @@ // Website components export { default as RetrySimulator } from './elements/RetrySimulator'; +export { default as StandaloneActivityDemo } from './elements/StandaloneActivityDemo'; export { default as HomePageHero } from './elements/HomePageHero'; export { SdkLogos } from './elements/SdkLogos'; export { SdkLogosAsBlocks } from './elements/SdkLogosAsBlocks'; From 4400836ac540f507d72c3410219f67119c9c58a1 Mon Sep 17 00:00:00 2001 From: Jwahir Sundai Date: Thu, 28 May 2026 08:47:49 -0500 Subject: [PATCH 2/3] Shorten standalone activities demo page and edits --- ...standalone-activities-interactive-demo.mdx | 360 ++---------------- sidebars.js | 8 + .../elements/StandaloneActivityDemo.js | 74 ++-- .../standalone-activity-demo.module.css | 26 +- 4 files changed, 101 insertions(+), 367 deletions(-) diff --git a/docs/develop/standalone-activities-interactive-demo.mdx b/docs/develop/standalone-activities-interactive-demo.mdx index 317c88acf9..7ae27940fd 100644 --- a/docs/develop/standalone-activities-interactive-demo.mdx +++ b/docs/develop/standalone-activities-interactive-demo.mdx @@ -14,21 +14,24 @@ tags: - Activities - Temporal Client - Temporal SDKs -description: An interactive quickstart for Temporal Standalone Activities — run Activities directly from a Temporal Client without a Workflow. +description: An interactive overview of Temporal Standalone Activities that lets you run Activities directly from a Temporal Client without a Workflow. --- :::tip SUPPORT, STABILITY, and DEPENDENCY INFO -Temporal SDK support for [Standalone Activities](/standalone-activity) is at -[Pre-release](/evaluate/development-production-features/release-stages#pre-release). +[Standalone Activities](/standalone-activity) are available as a +[Public Preview](/evaluate/development-production-features/release-stages#public-preview) feature +in the Go, Python, and .NET SDKs, and as a +[Pre-release](/evaluate/development-production-features/release-stages#pre-release) feature in the +Java and TypeScript SDKs. -All APIs are experimental and may be subject to backwards-incompatible changes. +Standalone Activities require Temporal CLI v1.7.0 or higher and Temporal Server v1.31.0 or higher. ::: -Standalone Activities let you execute an Activity directly from a Temporal Client — no Workflow -required. The Activity is durably enqueued on the Temporal Server, executed by a Worker, and its -result is returned to the caller. +Standalone Activities let you execute an Activity directly from a Temporal Client without +requiring a Workflow Definition, since the Activity is durably enqueued on the Temporal Server, +executed by a Worker, and returned to the caller through an Activity handle. Use the interactive demo below to explore the API, experiment with failure scenarios, and see the generated SDK code and CLI command update in real time. @@ -43,12 +46,16 @@ import { StandaloneActivityDemo } from '@site/src/components'; When you call `client.ExecuteActivity()` (or the equivalent in your SDK), the following happens: -1. **Connect** — Your application connects to the Temporal Server. -2. **Schedule** — The Server durably persists the Activity execution on the specified Task Queue. -3. **Poll** — A Worker polling that Task Queue picks up the Activity Task. -4. **Execute** — The Worker runs your Activity function with the provided arguments. -5. **Return** — The result is stored by the Server and returned to the original caller via the - `ActivityHandle`. +1. **Connect**: Your application opens a connection to the Temporal Server using a Temporal Client + configured with your namespace and credentials. +2. **Schedule**: The Server durably persists the Activity Task on the specified Task Queue so that + the request survives Worker restarts and network interruptions. +3. **Poll**: A Worker that is polling that Task Queue picks up the Activity Task and prepares to + execute it. +4. **Execute**: The Worker runs your Activity function with the provided arguments and reports the + outcome back to the Server. +5. **Return**: The Server stores the result and returns it to the original caller through the + `ActivityHandle` that was created when the Activity was scheduled. Because the Server durably persists the Activity, it survives Worker restarts and network interruptions. If the Activity fails, the Server automatically retries it according to the Retry @@ -58,319 +65,14 @@ Policy you configure. | | Workflow Activity | Standalone Activity | |---|---|---| -| Orchestrated by | A Workflow Definition | Your application code | -| Started with | `workflow.ExecuteActivity()` | `client.ExecuteActivity()` | -| Retry policy | Configured per `ActivityOptions` | Configured per `StartActivityOptions` | -| Visibility | Workflow Event History | Activity Visibility (List/Count) | -| Use case | Complex, multi-step orchestration | Simple, independent jobs | +| Orchestrated by | A Workflow Definition | Your application code (via the Temporal Client) | +| Started with | SDK-specific (for example, `workflow.ExecuteActivity()` in Go) | SDK-specific (for example, `client.ExecuteActivity()` in Go, `client.StartActivityAsync()` in .NET) | +| Retry policy | Set in `ActivityOptions` inside your Workflow | Set in `StartActivityOptions` when calling the client | +| Visibility | Shown in the Workflow's Event History | Shown in the Activity list and count views | +| Use case | Multi-step orchestration with multiple Activities | Single, independent jobs like sending an email or processing a webhook | -The Activity function and Worker registration are **identical** for both — only the execution path -differs. - ---- - -## Setup - -### 1. Install the Temporal CLI - -Download the Standalone Activity prerelease build of the Temporal CLI: - -```bash -# macOS (Homebrew) -brew install temporal - -# Or download directly from GitHub -# https://github.com/temporalio/cli/releases/tag/v1.6.2-standalone-activity -``` - -### 2. Start a local development server - -```bash -temporal server start-dev -``` - -The server starts at `localhost:7233` and the Web UI at -[http://localhost:8233](http://localhost:8233). Standalone Activities appear under the **Activities** -nav item in the Web UI. - -### 3. Install the SDK - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - - - - -Requires **Go SDK v1.41.0** or higher. - -```bash -go get go.temporal.io/sdk@latest -``` - -Clone the samples repository to follow along: - -```bash -git clone https://github.com/temporalio/samples-go.git -cd samples-go -``` - - - - -Requires **Python SDK v1.23.0** or higher. - -```bash -uv add temporalio -# or: pip install temporalio -``` - -Clone the samples repository to follow along: - -```bash -git clone https://github.com/temporalio/samples-python.git -cd samples-python -``` - - - - -Requires **.NET SDK v1.12.0** or higher. - -```bash -dotnet add package Temporalio -``` - -Clone the samples repository to follow along: - -```bash -git clone https://github.com/temporalio/samples-dotnet.git -cd samples-dotnet -``` - - - - -### 4. Run the Worker - -The Worker registers the Activity and polls the Task Queue. Start it in a dedicated terminal: - - - - -```bash -go run standalone-activity/helloworld/worker/main.go -``` - - - - -```bash -uv run hello_standalone_activity/worker.py -``` - - - - -```bash -dotnet run --project src/StandaloneActivity worker -``` - - - - -### 5. Execute the Activity - -In a separate terminal, run the starter: - - - - -```bash -go run standalone-activity/helloworld/starter/main.go -``` - - - - -```bash -uv run hello_standalone_activity/execute_activity.py -``` - - - - -```bash -dotnet run --project src/StandaloneActivity execute-activity -``` - - - - -Or use the Temporal CLI directly: - -```bash -temporal activity execute \ - --type Activity \ - --activity-id my-activity-id \ - --task-queue my-task-queue \ - --start-to-close-timeout 10s \ - --input '"World"' -``` - ---- - -## Key API concepts - -### `ExecuteActivity` — start and wait - -The primary call. Durably enqueues the Activity, waits for execution, and returns the result: - - - - -```go -handle, err := c.ExecuteActivity(ctx, client.StartActivityOptions{ - ID: "my-activity-id", - TaskQueue: "my-task-queue", - StartToCloseTimeout: 10 * time.Second, -}, helloworld.Activity, "World") - -var result string -err = handle.Get(ctx, &result) -``` - - - - -```python -result = await client.execute_activity( - compose_greeting, - args=[ComposeGreetingInput("Hello", "World")], - id="my-activity-id", - task_queue="my-task-queue", - start_to_close_timeout=timedelta(seconds=10), -) -``` - - - - -```csharp -var result = await client.ExecuteActivityAsync( - () => MyActivities.ComposeGreetingAsync( - new ComposeGreetingInput("Hello", "World")), - new("my-activity-id", "my-task-queue") - { - StartToCloseTimeout = TimeSpan.FromSeconds(10), - }); -``` - - - - -### `StartActivity` — fire and forget - -Enqueues the Activity without waiting for it to finish. Useful when you want to kick off -long-running work and check back later: - - - - -```go -handle, err := c.ExecuteActivity(ctx, options, helloworld.Activity, "World") -// handle.Get() can be called later -``` - - - - -```python -handle = await client.start_activity( - compose_greeting, - args=[ComposeGreetingInput("Hello", "World")], - id="my-activity-id", - task_queue="my-task-queue", - start_to_close_timeout=timedelta(seconds=10), -) -# Later: -result = await handle.result() -``` - - - - -```csharp -var handle = await client.StartActivityAsync( - () => MyActivities.ComposeGreetingAsync( - new ComposeGreetingInput("Hello", "World")), - new("my-activity-id", "my-task-queue") - { - StartToCloseTimeout = TimeSpan.FromSeconds(10), - }); -// Later: -var result = await handle.GetResultAsync(); -``` - - - - -### List and Count Activities - - - - -```go -// List -resp, err := c.ListActivities(ctx, client.ListActivitiesOptions{ - Query: "TaskQueue = 'my-task-queue'", -}) -for info, err := range resp.Results { ... } - -// Count -resp, err := c.CountActivities(ctx, client.CountActivitiesOptions{ - Query: "TaskQueue = 'my-task-queue'", -}) -log.Println("Total:", resp.Count) -``` - - - - -```python -# List -async for info in client.list_activities( - query="TaskQueue = 'my-task-queue'" -): - print(info.activity_id, info.status) - -# Count -resp = await client.count_activities( - query="TaskQueue = 'my-task-queue'" -) -print("Total:", resp.count) -``` - - - - -```csharp -// List -await foreach (var info in client.ListActivitiesAsync( - "TaskQueue = 'my-task-queue'")) -{ - Console.WriteLine($"{info.ActivityId}: {info.Status}"); -} - -// Count -var resp = await client.CountActivitiesAsync( - "TaskQueue = 'my-task-queue'"); -Console.WriteLine($"Total: {resp.Count}"); -``` - - - - -The `Query` parameter uses the same [List Filter](/list-filter) syntax as Workflow Visibility. +The Activity function and Worker registration are **identical** for both approaches, and only the +execution path that triggers the Activity differs between them. --- @@ -378,6 +80,8 @@ The `Query` parameter uses the same [List Filter](/list-filter) syntax as Workfl For complete API reference and advanced usage, see the SDK-specific guides: -- [Standalone Activities — Go SDK](/develop/go/standalone-activities) -- [Standalone Activities — Python SDK](/develop/python/standalone-activities) -- [Standalone Activities — .NET SDK](/develop/dotnet/standalone-activities) +- [Standalone Activities with the Go SDK](/develop/go/activities/standalone-activities) +- [Standalone Activities with the Java SDK](/develop/java/activities/standalone-activities) +- [Standalone Activities with the Python SDK](/develop/python/activities/standalone-activities) +- [Standalone Activities with the TypeScript SDK](/develop/typescript/activities/standalone-activities) +- [Standalone Activities with the .NET SDK](/develop/dotnet/activities/standalone-activities) diff --git a/sidebars.js b/sidebars.js index e606b87475..b9136644c0 100644 --- a/sidebars.js +++ b/sidebars.js @@ -1661,6 +1661,14 @@ module.exports = { 'web-ui', ], }, + { + type: 'category', + label: 'Interactive Demos', + collapsed: true, + items: [ + 'develop/standalone-activities-interactive-demo', + ], + }, 'glossary', 'with-ai', // { diff --git a/src/components/elements/StandaloneActivityDemo.js b/src/components/elements/StandaloneActivityDemo.js index 91625c5952..b6348dce7b 100644 --- a/src/components/elements/StandaloneActivityDemo.js +++ b/src/components/elements/StandaloneActivityDemo.js @@ -6,27 +6,33 @@ import styles from './standalone-activity-demo.module.css'; // Code generation // --------------------------------------------------------------------------- +const GREETING = 'Hello'; + function generateSdkCode(language, config) { - const { activityId, taskQueue, greeting, name, timeout, timeoutType, simulateFailures, maxRetries } = config; - const expectedResult = `${greeting}, ${name}!`; + const { activityId, taskQueue, name, timeout, timeoutType, simulateFailures, maxRetries } = config; + const expectedResult = `${GREETING}, ${name}!`; if (language === 'go') { const timeoutField = timeoutType === 'start_to_close' ? `StartToCloseTimeout: ${timeout} * time.Second,` : `ScheduleToCloseTimeout: ${timeout} * time.Second,`; + const retryImport = + simulateFailures && maxRetries > 0 + ? '// import "go.temporal.io/sdk/temporal"\n' + : ''; const retryPolicy = simulateFailures && maxRetries > 0 ? `\n\tRetryPolicy: &temporal.RetryPolicy{\n\t\tMaximumAttempts: ${maxRetries + 1},\n\t},` : ''; - return `activityOptions := client.StartActivityOptions{ + return `${retryImport}activityOptions := client.StartActivityOptions{ \tID: "${activityId}", \tTaskQueue: "${taskQueue}", \t${timeoutField}${retryPolicy} } handle, err := c.ExecuteActivity(ctx, activityOptions, -\thelloworld.Activity, "${greeting}", "${name}") +\thelloworld.Activity, "${name}") if err != nil { \tlog.Fatalln("Unable to execute activity", err) } @@ -41,13 +47,17 @@ err = handle.Get(ctx, &result) timeoutType === 'start_to_close' ? `start_to_close_timeout=timedelta(seconds=${timeout}),` : `schedule_to_close_timeout=timedelta(seconds=${timeout}),`; + const retryImport = + simulateFailures && maxRetries > 0 + ? '# from temporalio.common import RetryPolicy\n' + : ''; const retryPolicy = simulateFailures && maxRetries > 0 ? `\n retry_policy=RetryPolicy(\n maximum_attempts=${maxRetries + 1},\n ),` : ''; - return `result = await client.execute_activity( + return `${retryImport}result = await client.execute_activity( compose_greeting, - args=[ComposeGreetingInput("${greeting}", "${name}")], + args=[ComposeGreetingInput("${GREETING}", "${name}")], id="${activityId}", task_queue="${taskQueue}", ${timeoutField}${retryPolicy} @@ -60,13 +70,17 @@ err = handle.Get(ctx, &result) timeoutType === 'start_to_close' ? `StartToCloseTimeout = TimeSpan.FromSeconds(${timeout}),` : `ScheduleToCloseTimeout = TimeSpan.FromSeconds(${timeout}),`; + const retryImport = + simulateFailures && maxRetries > 0 + ? '// using Temporalio.Common;\n' + : ''; const retryPolicy = simulateFailures && maxRetries > 0 - ? `\n MaximumAttempts = ${maxRetries + 1},` + ? `\n RetryPolicy = new() { MaximumAttempts = ${maxRetries + 1} },` : ''; - return `var result = await client.ExecuteActivityAsync( + return `${retryImport}var result = await client.ExecuteActivityAsync( () => MyActivities.ComposeGreetingAsync( - new ComposeGreetingInput("${greeting}", "${name}")), + new ComposeGreetingInput("${GREETING}", "${name}")), new("${activityId}", "${taskQueue}") { ${timeoutField}${retryPolicy} @@ -77,18 +91,32 @@ err = handle.Get(ctx, &result) return ''; } -function generateCliCode(config) { +function generateCliCode(language, config) { const { activityId, taskQueue, name, timeout, timeoutType } = config; const timeoutFlag = timeoutType === 'start_to_close' ? `--start-to-close-timeout ${timeout}s` : `--schedule-to-close-timeout ${timeout}s`; + + let activityType; + let inputFlag; + if (language === 'go') { + activityType = 'Activity'; + inputFlag = `--input '"${name}"'`; + } else if (language === 'python') { + activityType = 'compose_greeting'; + inputFlag = `--input '{"greeting": "${GREETING}", "name": "${name}"}'`; + } else { + activityType = 'ComposeGreeting'; + inputFlag = `--input '{"Greeting": "${GREETING}", "Name": "${name}"}'`; + } + return `temporal activity execute \\ - --type Activity \\ + --type ${activityType} \\ --activity-id ${activityId} \\ --task-queue ${taskQueue} \\ ${timeoutFlag} \\ - --input '"${name}"'`; + ${inputFlag}`; } // --------------------------------------------------------------------------- @@ -114,7 +142,6 @@ const IDLE_NODES = ['pending', 'pending', 'pending', 'pending', 'pending']; const DEFAULT_CONFIG = { activityId: 'my-activity-id', taskQueue: 'my-task-queue', - greeting: 'Hello', name: 'World', timeout: 10, timeoutType: 'start_to_close', @@ -232,7 +259,7 @@ export default function StandaloneActivityDemo() { // Step 3: execute update( ['completed', 'completed', 'completed', 'active', 'pending'], - `[Attempt ${attempt}] Executing Activity("${config.greeting}", "${config.name}")...` + `[Attempt ${attempt}] Executing Activity("${config.name}")...` ); await sleep(750); if (isCancelled()) return; @@ -298,7 +325,7 @@ export default function StandaloneActivityDemo() { await sleep(400); if (isCancelled()) return; - const result = `${config.greeting}, ${config.name}!`; + const result = `${GREETING}, ${config.name}!`; logEntries.push({ time: elapsed(), msg: `Result: "${result}"`, type: 'success' }); setSim({ @@ -332,8 +359,8 @@ export default function StandaloneActivityDemo() { const failureNote = config.simulateFailures ? config.failCount > config.maxRetries - ? `⚠ All ${config.maxRetries + 1} attempt(s) will fail — failCount exceeds maxRetries.` - : `ℹ Activity will fail ${config.failCount} time(s), then succeed on attempt ${config.failCount + 1}.` + ? `All ${config.maxRetries + 1} attempt(s) will fail because failCount exceeds maxRetries.` + : `Activity will fail ${config.failCount} time(s), then succeed on attempt ${config.failCount + 1}.` : null; return ( @@ -367,11 +394,6 @@ export default function StandaloneActivityDemo() { value={config.taskQueue} onChange={(v) => updateConfig('taskQueue', v)} /> - updateConfig('greeting', v)} - />

CLI Command

- {generateCliCode(config)} + {generateCliCode(language, config)} @@ -462,7 +484,7 @@ export default function StandaloneActivityDemo() { Running… ) : ( - '▶ Execute Activity' + 'Execute Activity' )} @@ -523,12 +545,12 @@ export default function StandaloneActivityDemo() { {sim.status === 'completed' && sim.result && (
- ✅ Result: "{sim.result}" + Result: "{sim.result}"
)} {sim.status === 'failed' && (
- ❌ Activity failed after exhausting all retry attempts + Activity failed after exhausting all retry attempts
)} diff --git a/src/components/elements/standalone-activity-demo.module.css b/src/components/elements/standalone-activity-demo.module.css index 4b4db563d7..53f360ab0a 100644 --- a/src/components/elements/standalone-activity-demo.module.css +++ b/src/components/elements/standalone-activity-demo.module.css @@ -17,7 +17,7 @@ .langTab { padding: 5px 18px; border: 1px solid var(--ifm-color-emphasis-300); - border-radius: 4px; + border-radius: 0; background: var(--ifm-background-color); color: var(--ifm-font-color-base); cursor: pointer; @@ -73,7 +73,7 @@ .configGrid { border: 1px solid var(--ifm-color-emphasis-200); - border-radius: 6px; + border-radius: 0; overflow: hidden; } @@ -101,7 +101,7 @@ width: 150px; padding: 4px 8px; border: 1px solid var(--ifm-color-emphasis-300); - border-radius: 4px; + border-radius: 0; background: var(--ifm-background-color); color: var(--ifm-font-color-base); font-size: 0.83rem; @@ -118,7 +118,7 @@ margin: 8px 0 0; padding: 8px 12px; background: var(--ifm-color-emphasis-100); - border-radius: 4px; + border-radius: 0; line-height: 1.4; } @@ -130,7 +130,7 @@ background: var(--ifm-color-primary); color: #fff; border: none; - border-radius: 6px; + border-radius: 0; font-size: 1rem; font-weight: 600; cursor: pointer; @@ -178,7 +178,7 @@ padding: 14px 12px; background: var(--ifm-background-surface-color); border: 1px solid var(--ifm-color-emphasis-200); - border-radius: 6px; + border-radius: 0; overflow-x: auto; gap: 4px; } @@ -188,7 +188,7 @@ flex-direction: column; align-items: center; padding: 8px 10px; - border-radius: 6px; + border-radius: 0; border: 2px solid var(--ifm-color-emphasis-300); background: var(--ifm-background-color); min-width: 68px; @@ -276,7 +276,7 @@ overflow-y: auto; background: var(--ifm-background-surface-color); border: 1px solid var(--ifm-color-emphasis-200); - border-radius: 6px; + border-radius: 0; padding: 10px 12px; font-family: var(--ifm-font-family-monospace); font-size: 0.78rem; @@ -324,7 +324,7 @@ padding: 9px 14px; background: rgba(34, 197, 94, 0.1); border: 1px solid rgba(34, 197, 94, 0.5); - border-radius: 6px; + border-radius: 0; color: #15803d; font-size: 0.88rem; } @@ -334,7 +334,7 @@ padding: 9px 14px; background: rgba(239, 68, 68, 0.08); border: 1px solid rgba(239, 68, 68, 0.45); - border-radius: 6px; + border-radius: 0; color: #dc2626; font-size: 0.88rem; } @@ -350,7 +350,7 @@ .historyWrapper { overflow-x: auto; border: 1px solid var(--ifm-color-emphasis-200); - border-radius: 6px; + border-radius: 0; } .historyTable { @@ -398,7 +398,7 @@ padding: 2px 8px; background: rgba(34, 197, 94, 0.12); color: #15803d; - border-radius: 9999px; + border-radius: 0; font-size: 0.74rem; font-weight: 600; white-space: nowrap; @@ -409,7 +409,7 @@ padding: 2px 8px; background: rgba(239, 68, 68, 0.1); color: #dc2626; - border-radius: 9999px; + border-radius: 0; font-size: 0.74rem; font-weight: 600; white-space: nowrap; From 31a7e1d293ad9bbe8fcaa3a7443a6b909b94c38a Mon Sep 17 00:00:00 2001 From: Jwahir Sundai Date: Thu, 28 May 2026 08:54:48 -0500 Subject: [PATCH 3/3] Edit metadata and approachable intro --- ...standalone-activities-interactive-demo.mdx | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/docs/develop/standalone-activities-interactive-demo.mdx b/docs/develop/standalone-activities-interactive-demo.mdx index 7ae27940fd..5750c3b3ed 100644 --- a/docs/develop/standalone-activities-interactive-demo.mdx +++ b/docs/develop/standalone-activities-interactive-demo.mdx @@ -1,20 +1,15 @@ --- id: standalone-activities-interactive-demo -title: Standalone Activities Interactive Demo -sidebar_label: Standalone Activities (Interactive) +title: Standalone Activities Demo +sidebar_label: Standalone Activities toc_max_heading_level: 3 keywords: - standalone activity - - activity execution - - execute activity - - activity handle - - quickstart - interactive demo tags: - - Activities - - Temporal Client + - Standalone Activities - Temporal SDKs -description: An interactive overview of Temporal Standalone Activities that lets you run Activities directly from a Temporal Client without a Workflow. +description: An interactive overview of Temporal Standalone Activities. --- :::tip SUPPORT, STABILITY, and DEPENDENCY INFO @@ -29,12 +24,13 @@ Standalone Activities require Temporal CLI v1.7.0 or higher and Temporal Server ::: -Standalone Activities let you execute an Activity directly from a Temporal Client without -requiring a Workflow Definition, since the Activity is durably enqueued on the Temporal Server, -executed by a Worker, and returned to the caller through an Activity handle. +Standalone Activities let you run a single Activity straight from your application without +writing a Workflow. Your code calls the Temporal Client, the Server durably enqueues the request +for a Worker to pick up, and the result comes back through a handle that your code can wait on or +check later. -Use the interactive demo below to explore the API, experiment with failure scenarios, and see the -generated SDK code and CLI command update in real time. +Try the demo below to walk through the full flow, tweak the retry and timeout settings, and watch +the SDK code and CLI command update as you go. import { StandaloneActivityDemo } from '@site/src/components';