Skip to main content

Release v0.0.71

Β· 11 min read
vNext Team
Burgan Tech Engineering

Overview​

This release makes vNext event-driven and query-friendly. Event-driven workflows let an external pub/sub message start a new instance (workflow-level event) or advance an existing one through a triggerType: 3 transition, correlated by an IEventMapping script (#86). A fluent instance query language (InstanceQuery) replaces hand-written GraphQL filter JSON in script mappings β€” one builder powers event selectors, GetInstancesTask filter specs, and raw list-endpoint query strings. A new GetInstance task (TaskType = 19) returns a full single-instance projection (#806), and function result caching serves a repeat function call from a Dapr state store with a single cache read. Instance start/transition and function endpoints now accept application/x-www-form-urlencoded bodies (#825); output scripts can set the content-type response header and async operations return 202 Accepted (#819). The State function response gains a master schema HREF backed by a new master schema function (#821) and an always-present interaction directive object for long-poll clients. The component schema also ships a new data-vocab vocabulary (x-context-source / x-context-target) for schema-driven client context binding. This release runs on component schema 0.0.50.


Features​

Event-driven workflows (#86)​

A workflow can now react to external pub/sub events in two independent ways:

ActionWhat it doesWhere the event is declaredDelivered with
startCreates a new instanceOn the workflow (attributes.event)?action=start
transitionAdvances an active instanceOn a transition (transition.event, requires "triggerType": 3)?action=transition&transitionKey=<key>
{
"key": "abort-order",
"target": "aborted",
"triggerType": 3,
"event": {
"mapping": { "location": "./src/AbortEventMapping.csx", "code": "<base64>" }
}
}

The mapping script implements the new IEventMapping interface and returns an EventMappingResult: an InstanceKey + Body, or β€” when the payload carries no key β€” a Selector built with the fluent InstanceQuery and terminated with First()/Last(), which the runtime resolves against instance columns and instance-data JSON.

Behavior details:

  • Event transitions are supported on state transitions and shared transitions only (triggerType: 3 requires an event definition); start/cancel/exit/updateData transitions stay manual.
  • Delivery infrastructure is domain-owned: one generic runtime endpoint (POST .../instances/events?action=...) receives everything; topics, Dapr Subscription YAMLs, and pub/sub components live with the domain β€” shipping a new event needs no runtime redeploy.
  • CloudEvent envelopes are unwrapped before the mapping runs; selectors are automatically scoped to the target workflow's flow; a non-matching event returns 200 by design so it is not redelivered forever.
  • The vnext helm chart gains global.pubsubComponents and global.subscriptionComponents values that render Dapr Component / Subscription resources scoped to the orchestrator sidecar.

Reference: issue #86, helm PR vnext-helm-charts #25 β€” see also the new Event-Driven Workflows guide and Interfaces β†’ IEventMapping.

Fluent instance query helpers (InstanceQuery)​

Script mappings no longer need to hand-concatenate GraphQL filter JSON. The new fluent builder in BBT.Workflow.Filtering (part of the script engine's default imports) describes every instance query in the platform:

var query = InstanceQuery.Create()
.Where("currentState", f => f.Eq("active"))
.Where("attributes.amount", f => f.Ge(1000).Lt(5000))
.OrGroup(
q => q.Where("attributes.city", f => f.Eq("London")),
q => q.Where("attributes.city", f => f.Eq("Paris")))
.OrderByDescending("createdAt");

How you end the chain decides what you get:

TerminalProducesUsed for
.First() / .Last()A single-instance resolverEvent correlation (EventMappingResult.Selector)
.Build()An InstanceQuerySpec list/report queryGetInstancesTask.SetFilterSpec(...), or wire strings for DaprServiceTask
  • Full operator set (Eq, Ne, Gt/Ge/Lt/Le, Like, StartsWith/EndsWith, In/NotIn, Between, IsNull, Includes) emitting the existing GraphQL wire JSON.
  • GroupBy + Count/Sum/Avg/Min/Max aggregations for report queries.
  • Build-time guardrails: whitelisted column names, no unfiltered single-resolve, list-only features rejected on First()/Last().
  • GetInstancesTask.SetFilterSpec(spec) is the recommended path β€” same-domain queries run in-process with no HTTP/Dapr hop; spec.ToFilterJson()/ToSortJson()/ToQueryString() serve raw DaprServiceTask integrations.

Reference: see Instance Filtering β†’ Fluent InstanceQuery Builder and GetInstances Task.

GetInstance task β€” full single-instance projection (#806)​

A new trigger-family task type, GetInstance (TaskType = 19), is the task-level equivalent of GET /api/v1/{domain}/workflows/{workflow}/instances/{instance}. It returns the full instance projection β€” metadata and data β€” complementing GetInstances (list, type 15) and GetInstanceData (data only, type 13).

{
"attributes": {
"type": "19",
"config": {
"domain": "sales",
"flow": "order-workflow",
"key": "ORDER-001",
"extensions": []
}
}
}

Same-domain execution runs in-process through the instance query gateway; cross-domain execution calls the same REST endpoint over HTTP or Dapr. Both paths surface an identical output shape to the script context, so one mapping works regardless of where the instance lives.

Reference: issue #806 β€” see also the new GetInstance Task component page.

Function result caching​

A custom function can now declare an optional cache block that caches its entire response in a Dapr state store. On a hit the response is served with a single cache read β€” tasks are skipped entirely (a real-world config-evaluation function dropped from ~230ms to ~93ms). Opt-in per function, and intended only for side-effect-free (read) functions.

{
"attributes": {
"scope": "I",
"task": { /* ... */ },
"cache": {
"keyExpression": { "location": "dynamicExpresso", "code": "\"config:\" + Instance.Key + \":\" + Instance.Version" },
"ttlInSeconds": 300,
"consistency": "Eventual",
"bypassOnCacheError": true
}
}
}
  • The cache key comes from a Dynamic Expresso keyExpression (evaluated against the script context) or a static key; storeName defaults to the runtime's DAPR_STATE_STORE_NAME.
  • bypassOnCacheError: true (default) falls back to executing the function when the cache is unavailable.
  • Generation-namespace invalidation: generationKey / generationKeyExpression folds a generation stamp (held in the state store) into the cache key, so bumping the stamp invalidates a whole family of entries without deletes. Instance.Version is available in key expressions so a new config version self-invalidates.

Reference: see Custom Functions β†’ Cache.

Form-urlencoded request bodies (#825)​

The public Orchestration endpoints for workflow start, transition execution, and function invocation now accept application/x-www-form-urlencoded bodies alongside JSON. Form keys use bracket paths and are normalized into the same JSON tree before the existing payload-mode pipeline runs:

Form inputJSON result
attributes[customer][name]=Alinested objects
tags[]=a&tags[]=b (or repeated tags=a&tags=b)scalar array
items[0][name]=A&items[1][name]=Barray of objects
  • Scalar values use JSON-literal semantics in payload data (30, true, null become typed values; "00123" stays a string); the standard envelope fields key, stage, and tags always remain strings.
  • Ambiguous shapes (items[][name]=A, sparse/negative indices, scalar/container collisions) are rejected with HTTP 400 β€” no partially normalized payload is passed downstream.
  • Payload mode resolution is unchanged: x-vnext-payload-mode header overrides auto-detection.

Reference: issue #825 β€” see also REST API β†’ Instance Endpoints.

User-controlled Content-Type and 202 Accepted (#819)​

Two response-mapping changes for instance and function endpoints:

  • content-type unblocked: function and instance output scripts can now set the content-type response header for integration scenarios (previously stripped). The default remains application/json when the script does not set one.
  • 202 Accepted for async operations: instance start and transition now return 202 Accepted instead of 200 for sync=false success, reflecting that the work is queued for durable background processing. sync=true, error results, and the custom output-response path are unchanged.

Reference: issue #819 β€” see also Async / Sync.

Master schema function and State response HREF (#821)​

A new master system function resolves the flow-level master schema an instance is bound to (Workflow.Schema), forwarding to the active subflow instance when one is present. The State function response now carries a master HREF alongside the existing data/view/schema links, so clients can fetch the instance's master schema without knowing the workflow definition.

  • Query-role denial returns 403; a flow without a master schema returns 404.

Reference: issue #821 β€” see also Built-in Functions.

State interaction directives for long-poll clients​

The State function response's interaction object is now emitted whenever the state declares interaction.longPoll (subject to role grants) β€” regardless of the terminate value:

{
"interaction": {
"terminateLongPoll": false,
"fallbackTimeoutSeconds": 600
}
}
  • terminateLongPoll reflects the state's interaction.longPoll.terminate value. When true, the client terminates its long-poll, renders the entered state, and acknowledges via the included ack HREF (fallback resumes the pipeline automatically if not acknowledged within fallbackTimeoutSeconds).
  • When false, the client restarts the long-poll request if it has stopped β€” independent of the instance status β€” and keeps retrying within the fallbackTimeoutSeconds window.
  • ack is present only when terminateLongPoll is true.

Reference: see Workflow component β†’ State Interaction and Async / Sync.

Data Context Vocabulary β€” data-vocab​

The component schema package ships a new vocabulary, data-vocab.json, defining two backwards-compatible JSON Schema annotations for schema-driven client context binding:

  • x-context-source (on a transition-input schema property): marks the property as client-resolved β€” from a literal (const), a context-store slot (context), or the client identity (identity: subject|user) β€” instead of a rendered form field.
  • x-context-target (on a workflow master schema): maps instance-data field paths to context-store slots, applied on every instance read so reusable outputs (tokens, device ids, certificates) propagate to the client context-store automatically.

Slot keys support {instance} / {subject} templating; standard validators ignore the annotations, so unannotated schemas behave exactly as before.

Reference: see Schema component β†’ Data Context Vocabulary.


Configuration Updates​

Configuration for v0.0.71:

{
"runtimeVersion": "0.0.71",
"schemaVersion": "0.0.50"
}

Note: Schema version advances to 0.0.50 (from 0.0.49). The bump adds the CacheAside task type "18" and GetInstance task type "19" (#806), the function attributes.cache block, the workflow/transition event definition (#86), and the new data-vocab.json vocabulary. Update @burgan-tech/vnext-schema in your domain project before validating against this runtime.

Container images: published at tag 0.0.71 under ghcr.io/burgan-tech/vnext/*, Cosign-signed (keyless OIDC) with SBOM + provenance. Immutable digests are listed in the GitHub release.


Issues Referenced​

  • vnext #86 β€” Event-driven workflow start and transitions with IEventMapping correlation.
  • vnext #806 β€” GetInstance task type for full single-instance projection.
  • vnext #825 β€” application/x-www-form-urlencoded support on start/transition/function endpoints.
  • vnext #819 β€” User-controlled content-type response header and 202 Accepted for async instance operations.
  • vnext #821 β€” Master schema function and master HREF in the State function response.
  • vnext-helm-charts #25 β€” Dapr resiliency policies + values-driven pub/sub and subscription components.

Summary​

  • Event-driven workflows: attributes.event starts instances, triggerType: 3 transitions advance them; IEventMapping correlates by InstanceKey or a fluent Selector; delivery stays domain-owned via Dapr Subscriptions (helm support included).
  • InstanceQuery: one fluent builder for event selectors (First()/Last()), GetInstancesTask filter specs (Build() + SetFilterSpec), and raw list-endpoint wire strings β€” with GroupBy/aggregations and build-time guardrails.
  • GetInstance task (Type 19) returns the full instance projection (metadata + data), in-process for same-domain calls.
  • Function result caching: opt-in cache block with Dynamic Expresso keys, TTL, and generation-namespace invalidation.
  • Form-urlencoded bodies are accepted on start/transition/function endpoints with strict bracket-path β†’ JSON normalization.
  • Response shaping: output scripts may set content-type; async start/transition return 202 Accepted.
  • State response: new master schema HREF and an always-present interaction long-poll directive (terminateLongPoll, fallbackTimeoutSeconds).
  • data-vocab: x-context-source / x-context-target annotations for schema-driven client context binding.
  • Schema is 0.0.50.

vNext Runtime Platform Team Released July 21, 2026