Use sendRequestToBitfieldTarget(...) when app code needs to ask a named Bitfield target to do work.
What this is
A target is a public callable name. Your app sends a payload to that name. Runtime Kit routes the payload to the package code registered for that target. Your app receives reply bytes.import { sendRequestToBitfieldTarget } from '@bitfield/runtime-kit';
The mental model
Your app should not care how the work is performed. It should care about the public action name and the payload it sends.If a user clicks a search button, your app can call product.search. The package behind that target can change later. Your button can stay the same as long as the action reply shape stays the same.That is the value: app code calls the public target name, not the private implementation.How it works technically
app action
-> sendRequestToBitfieldTarget({ target, payload })
-> Runtime Kit encodes payload as bytes
-> named target receives the request
-> target replies with bytes
-> app decodes bytes according to the public action reply shape
readuseBitfieldData(...) reads values already prepared for the surface.
|
actsendRequestToBitfieldTarget(...) sends bytes to a named target.
→
replyThe app decodes only what the action reply shape promises.
The request shape is:type BitfieldTargetRequest = {
target: string;
payload?: unknown;
};
type BitfieldTargetReply = {
payload: Uint8Array;
};
For the exact request type, payload conversion table, reply type, cancellation behavior, and invalid examples, read Runtime Kit API.Send JSON-like data
const reply = await sendRequestToBitfieldTarget({
target: 'product.search',
payload: { query: 'blue jacket' },
});
const text = new TextDecoder().decode(reply.payload);
const result = JSON.parse(text);
Use JSON-like payloads when your action reply shape is ordinary app data.Send text
await sendRequestToBitfieldTarget({
target: 'note.analyze',
payload: 'Look for missing details.',
});
Runtime Kit encodes strings as bytes before sending.Send bytes
const reply = await sendRequestToBitfieldTarget({
target: 'image.thumbnail',
payload: imageBytes,
});
If payload is already a Uint8Array, Runtime Kit sends those bytes.Understand payload encoding
| Payload value | Public behavior |
|---|
Uint8Array | Sent as bytes. |
string | Encoded as text bytes. |
| object, array, number, or boolean | Serialized as JSON bytes. |
null or omitted payload | Sent as empty bytes. |
The target decides what those bytes mean. Your app and target should agree on a public contract, such as JSON in and JSON out.If a target needs JSON with a null value, send an object that contains the null field:await sendRequestToBitfieldTarget({
target: 'settings.update',
payload: { value: null },
});
Decode the reply
The reply is always bytes because targets are not forced into one format.const text = new TextDecoder().decode(reply.payload);
For JSON replies:const data = JSON.parse(new TextDecoder().decode(reply.payload));
For binary replies, keep the Uint8Array:const thumbnailBytes = reply.payload;
Cancel a request
Use an AbortController when the user leaves a screen, changes a search query, or cancels work.let currentSearch: AbortController | null = null;
async function runSearch(query: string) {
currentSearch?.abort();
currentSearch = new AbortController();
return sendRequestToBitfieldTarget(
{ target: 'product.search', payload: { query } },
currentSearch.signal,
);
}
Handle errors
Wrap user-triggered requests so the UI can recover.try {
const reply = await sendRequestToBitfieldTarget({
target: 'product.search',
payload: { query },
});
return JSON.parse(new TextDecoder().decode(reply.payload));
} catch (error) {
return { error: error instanceof Error ? error.message : 'Request failed' };
}
An error means the public request did not complete. It does not mean the component should learn the private target implementation.Before / after
| Before | After |
|---|
| Button imports the feature implementation directly. | Button calls a named target. |
| Replacing the feature requires rewiring app imports. | Replacing the package keeps the target name stable. |
| App code imports implementation details. | App code uses only the payload and reply contract. |
| A caller has to understand private execution code. | A caller sends a request to a public target name. |
Common mistakes
Using target names as storage addressesproduct.search is a callable target name. It is not a storage path.Parsing every reply the same wayThe reply is bytes. Decode according to the action reply shape. Do not assume every target returns JSON unless that target says it does.Calling private implementation code directlyIf a component imports the target implementation, the request boundary has been bypassed.Forgetting cancellationSearch boxes, typeahead, and long tasks should use an abort signal so stale work can stop.Quick reference
const reply = await sendRequestToBitfieldTarget({
target: 'target.name',
payload: { ok: true },
});
With cancellation:const controller = new AbortController();
await sendRequestToBitfieldTarget(request, controller.signal);
Decode JSON:const json = JSON.parse(new TextDecoder().decode(reply.payload));
Now build the bigger version
Write the action reply shape before the button.type SearchRequest = {
query: string;
};
type SearchReply = {
items: Array<{ id: string; name: string }>;
};
Then make the button use only that contract:async function search(query: string): Promise<SearchReply> {
const reply = await sendRequestToBitfieldTarget({
target: 'product.search',
payload: { query } satisfies SearchRequest,
});
return JSON.parse(new TextDecoder().decode(reply.payload)) as SearchReply;
}
For binary work, keep the same shape. The target name stays public, the payload contract stays explicit, and the reply stays bytes:const reply = await sendRequestToBitfieldTarget({
target: 'image.thumbnail',
payload: imageBytes,
});
const thumbnailBytes = reply.payload;