en Async Interpret - chiba233/yume-dsl-token-walker GitHub Wiki
Interpret (sync) | Exports | Home
The async API is a full mirror of the synchronous Interpret API. Same semantics, same safety guarantees — but interpret can await, and interpretChildren returns AsyncIterable.
When to use instead of sync:
- Your handler needs to
fetchremote content - Your handler queries a database
- Your handler calls an async renderer (e.g. Shiki
codeToHtml)
If none of your handlers are async, use the sync API — it's simpler and has zero async overhead.
import { createParser, createSimpleInlineHandlers } from "yume-dsl-rich-text";
import { interpretTextAsync, collectNodesAsync } from "yume-dsl-token-walker";
const parser = createParser({
handlers: createSimpleInlineHandlers(["bold"]),
});
const html = (
await collectNodesAsync(
interpretTextAsync("Hello $$bold(world)$$", parser, {
createText: (text) => text,
interpret: async (token, helpers) => {
if (token.type === "bold") {
return {
type: "nodes",
nodes: (async function* () {
yield "<b>";
yield* helpers.interpretChildren(token.value);
yield "</b>";
})(),
};
}
return { type: "unhandled" };
},
}, undefined),
)
).join("");
// → "Hello <b>world</b>"import type { AsyncInterpretRuleset } from "yume-dsl-token-walker";
import { interpretTokensAsync, collectNodesAsync } from "yume-dsl-token-walker";
const ruleset: AsyncInterpretRuleset<string, void> = {
createText: (text) => text,
interpret: async (token, helpers) => {
if (token.type === "embed") {
const url = typeof token.url === "string" ? token.url : "";
const response = await fetch(url);
const html = await response.text();
return { type: "text", text: `<div class="embed">${html}</div>` };
}
if (token.type === "bold") {
return {
type: "nodes",
nodes: (async function* () {
yield "<b>";
yield* helpers.interpretChildren(token.value);
yield "</b>";
})(),
};
}
return { type: "unhandled" };
},
onUnhandled: "flatten",
};
const nodes = await collectNodesAsync(
interpretTokensAsync(tokens, ruleset, undefined),
);| Aspect | Sync | Async |
|---|---|---|
interpret return |
InterpretResult<TNode> |
Awaitable<AsyncInterpretResult<TNode>> — can return a Promise |
interpretChildren return |
Iterable<TNode> |
AsyncIterable<TNode> — consume with for await or yield*
|
nodes in result |
Iterable<TNode> |
Iterable<TNode> | AsyncIterable<TNode> — plain arrays and async generators both work |
createText |
Synchronous | Still synchronous — text wrapping is a pure operation |
onUnhandled function |
Returns ResolvedResult
|
Returns Awaitable<AsyncResolvedResult>
|
| Error handling, recursion detection | Same | Same |
| Helper | Description |
|---|---|
fromAsyncHandlerMap(handlers) |
Build an async interpret function from Record<type, asyncHandler>. Unmatched tokens return { type: "unhandled" }
|
wrapAsyncHandlers(handlers, wrap) |
Wrap every async handler with shared logic. The wrap callback receives the awaited result |
collectNodesAsync(iterable) |
Collect an AsyncIterable<TNode> into an array. Equivalent to a for await loop |
async function* interpretTextAsync<TNode, TEnv>(
input: string,
parser: ParserLike,
ruleset: AsyncInterpretRuleset<TNode, TEnv>,
env: TEnv,
): AsyncGenerator<TNode>;async function* interpretTokensAsync<TNode, TEnv>(
tokens: TextToken[],
ruleset: AsyncInterpretRuleset<TNode, TEnv>,
env: TEnv,
): AsyncGenerator<TNode>;interface AsyncInterpretRuleset<TNode, TEnv = unknown> {
createText: (text: string) => TNode; // always sync
interpret: (
token: TextToken,
helpers: AsyncInterpretHelpers<TNode, TEnv>,
) => Awaitable<AsyncInterpretResult<TNode>>;
onUnhandled?: AsyncUnhandledStrategy<TNode, TEnv>; // default: "flatten"
onError?: (context: {
error: Error;
phase: "interpret" | "flatten" | "traversal" | "internal";
token?: TextToken;
position?: SourceSpan;
env: TEnv;
}) => void;
}interface AsyncInterpretHelpers<TNode, TEnv = unknown> {
interpretChildren: (value: string | TextToken[]) => AsyncIterable<TNode>;
flattenText: (value: string | TextToken[]) => string; // still sync
env: TEnv;
}type AsyncInterpretResult<TNode> =
| { type: "nodes"; nodes: Iterable<TNode> | AsyncIterable<TNode> }
| { type: "text"; text: string }
| { type: "flatten" }
| { type: "unhandled" }
| { type: "drop" };type AsyncUnhandledStrategy<TNode, TEnv = unknown> =
| "throw"
| "flatten"
| "drop"
| ((
token: TextToken,
helpers: AsyncInterpretHelpers<TNode, TEnv>,
) => Awaitable<AsyncResolvedResult<TNode>>);type Awaitable<T> = T | Promise<T>;Used throughout async API signatures so you can return either a plain value or a Promise.