Skip to content

Examples in your docs

createRunner runs a code example in the page against a region, so docs can show the code a reader would write for AWS and run it as shown. How the code is displayed is up to you: pass the runner a string, and it hands back what the code printed.

import { createRunner } from 'pocket-region/browser';
const runner = createRunner();
const result = await runner.run(code, {
onOutput: ({ stream, text }) => panel.append(`${text}\n`),
});
if (!result.ok) panel.append(`${result.error}\n`);

A snippet doesn’t need Pocket Region’s setup. The runner runs it against the region you pass, or boots one on the first run and empties it before every run after that unless reset says otherwise, and fills in what AWS code gets from its environment: SDK clients it imports default to us-east-1, throwaway credentials, and the runner’s region. So this runs as shown:

import { S3Client, CreateBucketCommand, ListBucketsCommand } from '@aws-sdk/client-s3';
const s3 = new S3Client({});
await s3.send(new CreateBucketCommand({ Bucket: 'photos' }));
const { Buckets } = await s3.send(new ListBucketsCommand({}));
console.log(Buckets.map((bucket) => bucket.Name));

A snippet’s own region and credentials are kept. A requestHandler it passes is not, so no request leaves the tab. A credential provider such as fromCognitoIdentityPool builds its own client, which the runner doesn’t reach, and would call AWS.

A snippet can’t import pocket-region itself: it is AWS code, and the region is the runner’s.

Pass language: 'python' to run a snippet with Python, where boto3 reaches the same region. The runner fills in a region, throwaway credentials, and the request hook, so this runs as shown against the region a JavaScript snippet on the same page uses.

import boto3
s3 = boto3.client('s3')
s3.create_bucket(Bucket='photos')
print([bucket['Name'] for bucket in s3.list_buckets()['Buckets']])

A snippet runs as a script: what it prints is all it prints. With echo: true it also prints what an interpreter session would have, the value of each expression statement as its repr, and nothing for None. That’s how a page runs an example written as a >>> session: the prompts are the page’s format, so the page removes them, drops the expected output between them, and runs the statements it’s left with. This page blanks the expected output rather than dropping it, so every line keeps its number, runs the session below as one snippet with echo: true, and puts each result under the statement whose line it came from.

>>> import boto3
>>> s3 = boto3.client('s3')
>>> s3.create_bucket(Bucket='photos')['ResponseMetadata']['HTTPStatusCode']
>>> [bucket['Name'] for bucket in s3.list_buckets()['Buckets']]

The first Python run downloads Pyodide again for the snippet’s own interpreter, which keeps a snippet that never ends out of the region. It loads the same boto3 wheels, at the same pinned version, as the region’s Python functions. Name anything else a snippet imports in python.packages.

Option Type Default
region Region none A region from createRegion to run every snippet against. The runner never stops it, and empties it only when reset says so, so your page can set it up before a run and look at it after. It can’t be combined with boot. A snippet’s imports load from where that region’s resolve says, and a Python snippet’s Pyodide from where the region’s own came from. See Setting up a region.
boot BrowserRegionOptions none Without region, options for the region the runner boots itself. See The region. A Python snippet’s own Pyodide comes from its indexURL too, and a JavaScript snippet’s imports of anything but pocket-region from its resolve, as the region’s Lambda handlers’ do.
reset 'each-run' | 'never' 'each-run', or 'never' with region When the runner empties the region. 'each-run' empties it before every run, so each example starts clean. The runner’s own region is empty when it boots, so its first run skips the reset. 'never' keeps what earlier runs made, for examples that build on each other, such as a tutorial whose second step writes to the bucket its first step created: the region’s state, a Python run’s names, and what a JavaScript run put on globalThis. A run after a reset starts with none of them.
setup string | { language, code } | (region) => Promise<void> none What runs on the region whenever it’s empty: after a boot, after each reset, and on the first run against a region you passed in. Code in the same form as a snippet, JavaScript when a string, or a function of the region for a setup the reader isn’t shown. See Setting up a region.
python { packages?: string[] } { packages: [] } What micropip installs from PyPI before the first Python run, beside the region’s boto3, as requirement strings such as 'pynamodb==6.1.0'. Pin them, so a page runs the same code every time. Packages must be pure Python, or built for Pyodide. boto3 and its dependencies stay at the region’s versions.

When every example needs the same resources, such as the table a library’s quick start writes to, give the runner setup. It runs before a snippet whenever the region is empty, so with the default reset each run starts from the same state, and with 'never' it runs once.

const runner = createRunner({
setup: `
import { DynamoDBClient, CreateTableCommand } from '@aws-sdk/client-dynamodb';
await new DynamoDBClient({}).send(new CreateTableCommand({ TableName: 'books', ... }));
`,
});

What setup prints is hidden unless it fails. A failed setup fails the run with an error starting setup failed:, the snippet doesn’t run, and the next run empties the region and tries setup again, even with reset: 'never'. Setup prepares the region, not the snippet: a name it defines isn’t there when the snippet runs.

Show setup on the page as a fence when the reader should see it: it’s the same text the runner runs. When the setup is your page’s business rather than the reader’s, pass a function instead. It runs on the page with the region, at the same moments and with the same failure handling, and what it prints goes to the page’s console. clientConfig(region) gives its SDK clients what a snippet’s clients get.

import { clientConfig, createRunner } from 'pocket-region/browser';
import { DynamoDBClient, CreateTableCommand } from '@aws-sdk/client-dynamodb';
const runner = createRunner({
async setup(region) {
await new DynamoDBClient(clientConfig(region)).send(new CreateTableCommand({ TableName: 'books', ... }));
},
});

With a region you pass in, the runner leaves it as it is unless you say otherwise: reset defaults to 'never', and setup runs once, on the first run, on whatever the region already holds. Ask for reset: 'each-run' to have the runner empty it before every run, including the first, and run setup on it each time:

const region = await createRegion();
const runner = createRunner({ region, reset: 'each-run', setup: createBooksTable });

The runner waits for the previous run before it empties the region, so your page needn’t serialise runs itself. If your page sets the region up in its own code instead, do it before the first run, or between runs once the previous one has finished, since a reset during a run empties the region under it.

type Runner = {
supported: boolean;
run(code: string, options?: { language?, echo?, reset?, onOutput?, onStatus? }): Promise<RunResult>;
stop(): Promise<void>;
};
type RunResult = { ok: true; durationMs: number } | { ok: false; durationMs: number; error: unknown };
  • supported is false in a browser without WebAssembly JSPI, where no region can boot. Show the code without a Run button there.
  • run(code) resolves once the code finishes, and never rejects: what the code throws or raises comes back as error, with its name, message, stack, and the line of the snippet it came from when that could be told. A Python error’s stack is its traceback. Runs wait for each other, since they share the region.
    • language is 'javascript' unless it says 'python'.
    • echo prints each expression statement’s value as an interpreter session would, for Python only.
    • reset is this run’s, in place of the runner’s reset: 'never' continues from the previous run, keeping the region and, for Python, the names the previous run defined, so a page can run a session one statement at a time.
    • onOutput(output) gets each line the snippet prints, with language, stream, and text. Python output is what print or a session’s echo wrote, one line at a time. In both languages, line is the 1-based line of the snippet whose call made the output: the console call, print, or echoed statement, even when a library did the calling. It’s absent when nothing of the snippet was on the stack, such as a rejection reported after the run. JavaScript output adds method and values: it gets each console call, with method naming it. log, info, debug, table, and dir go to 'stdout', and warn and error to 'stderr', along with any rejected promise nothing handles, as an error. text is the call as one string, with values that aren’t strings as JSON. For table, text is the table drawn in box characters, and for dir it’s the first argument alone. values holds the call’s arguments themselves, copied to the page, so you can render them your own way. A Set, Map, Date, or typed array arrives as one. An argument that can’t be copied, such as an object with a method, arrives as its text. The page’s own console isn’t touched.
    • onStatus({ phase }) gets 'booting' before the region’s first boot, which downloads about 15 MB on a first visit, 'resetting' before a later run that empties the region, 'setting-up' while setup runs, then 'running'. The first Python run loads its interpreter and packages during whichever phase comes first.
  • stop() ends the current run at once, however stuck it is: its result is ok: false with an error saying stopped. The region the runner booted itself stops with it, and a later run boots again. A region you passed in is left alone.

A snippet can be TypeScript. The runner removes the types before running it, without type checking, and leaves plain JavaScript as it is.

A JavaScript snippet runs as the body of an async function, so top-level await works. It can import with import … from '…' at the start of a line and import('…') with a literal specifier, but it can’t export. A Python snippet runs as a module, with top-level await allowed, in a fresh namespace each run. Either runs in a Web Worker, so it can’t reach the page or its DOM, and a snippet that loops forever freezes nothing but itself.