Skip to content

Captured handler producers

The current Me captured-execution profile excludes producers and virtual joins loaded from live Realm files. Owner-authored definitions remain available. Producer execution and cache reuse retain World and declaration checks; shared cache partitions include both. The legacy Wasm producer example demonstrates the compatibility route, not captured resource admission. Me also accepts the following captured handler producer profile. Each flat producers/*.yml or .yaml file contains one version-1 object:

producers/movie-records.yml
version: 1
name: movie-records
handler: movie.records
keyArgument: keys
joins:
- targetLabel: MovieRecord
anchorLabel: Person
relationship: HAS_RECORD
keyField: id
recordKeyField: personId

The binding retains an approved handler from the same installation. The handler receives a JSON object whose keyArgument contains an array of string keys and returns an array of record objects. Declare the target type and identity separately; recordKeyField links records to their matching anchor keys. The host owns graph scope and overlay metadata; records cannot supply userId, worldId, workspaceId, visibleTo or __vc* properties. Sharing uses an explicit host operation. Owner producer names take precedence and suppress conflicting captured joins.

This profile has no caching. A producer may declare filter pushdown so its handler filters at the source, and the separate paging profile to walk a handler’s own cursor across multiple calls within one fetch.

producers/incidents-by-service.yml
version: 1
name: incidents-by-service
handler: status.incidents
keyArgument: serviceIds
joins:
- {targetLabel: Incident, anchorLabel: Service, relationship: HAS_INCIDENT,
keyField: serviceId, recordKeyField: serviceId}
page: {argument: cursor, maxPages: 4}
pushdown:
- {property: impact, argument: impact}
- {property: status, argument: status}
wasm/handlers.ts
export async function incidents(input, ctx) {
// input.serviceIds: string[]; input.cursor: absent on the first page
// input.impact / input.status: string[], present only when the query pinned them
const page = await ctx.gateway.statuspage.listIncidents({
services: input.serviceIds.join(","),
impact: input.impact?.join(","),
status: input.status?.join(","),
after: input.cursor,
});
return { rows: page.items, next: page.nextCursor ?? null };
}

A query reaches the producer only by traversing one of its joins from a bound anchor, as in Virtual Cypher §2. A producer’s target may anchor another producer’s join, so producers chain. Declare a pushdown rule for every property the handler can filter on, since a filter left undeclared makes the handler fetch everything the key allows. TypeScript realms shows the same producer written with defineRealm. Me limits each fetch to 256 keys, 2,048 characters per key, 64 KiB of encoded arguments, 1 MiB of output and 1,024 rows. It accepts 32 flat declaration files, 8 KiB per file, 64 KiB total and eight joins per binding. page and pushdown are optional; every other field shown is required. Unknown fields, duplicate names or joins, aliases, tags and undeclared handlers are refused. These are host profile limits, independent of backend placement.

A handler grant does not grant API or datasource access. Each host call still needs its resource approval. Me supports an explicitly approved owned-data query profile: gateway.cypher.query({cypher, params}) returns {rows, warnings, coverage} and invokes only same-installation captured producers. Statements require named owned nodes and a final literal LIMIT from 1 to 512. See hosted execution for limits, owner approval and excluded host operations.