Documentation
Browse documentation

DocumentationReference

SDK reference

@overcrow/sdk 1.0 is the API of the logic of a PlayerVox OverCrow widget: the state, the data of the host, services, timers, drawing, the options menu, messages, logs and formatting. This page lists every export of the package. The logic and services and permissions explain how to use them.

The SDK is on npm, and the command below adds it to a project. A project made by overcrow-widget init already lists it: run npm install there.

SH
npm install --save-dev --save-exact @overcrow/sdk@1.0.0
TSwidgets/clock/logic.ts
import {
  formatDate,
  formatTime,
  initState,
  onHost,
  option,
  t,
  timers,
  type DateOrder,
  type Timer,
} from "@overcrow/sdk";

The SDK is bundled into the widget's logic.js when it is packaged, and runs inside the widget's VM; loading it anywhere else throws an error. Unused parts are removed from the bundle. It is a convenience, not a security boundary: PlayerVox OverCrow checks every call, parameter and value that leaves the VM.

ExportKindSummary
SDK_VERSIONconstant"1.0.0".
RUNTIME_SURFACEconstant"0.1", the version of the VM's built-in interface that this SDK needs.

State and view

ExportKindSummary
stateconstantThe widget state object. Change it; after each turn the VM evaluates the view again and sends only the differences.
WidgetStateinterfaceType of state; declare its members by module augmentation. Undeclared members are unknown.
initState(initial)functionCopies initial into state and returns state typed as initial. Call it once, while the module loads.
registerView(table)functionRegisters the compiled expressions and handlers of the view. The command-line tool generates this call: a widget never writes it.
ViewTabletypeThe table that registerView takes.
Expressiontype(state, scope) => value: a compiled expression of the view.
Handlertype(state, scope, event) => void: a compiled handler of the view.
NodeEvent<E>interface{ type, detail } of an event; detail is EventDetailMap[E].
ScopeinterfaceThe names in scope of an expression: for items, component properties.
TStemplates/chart/logic.ts
declare module "@overcrow/sdk" {
  interface WidgetState {
    values: number[];
  }
}

const state = initState({ values: [50] as number[] });

Host data

ExportKindSummary
hostconstantFrozen object whose members always read the latest values sent by PlayerVox OverCrow.
HostDatainterfaceType of host: locale, messages, theme, region, scale (in thousandths), viewport, mode, visible, options, grants.
onHost(listener)functionCalls listener with the names of the host members that PlayerVox OverCrow just sent (options after a menu change, visible when shown or hidden, region…), before the view is evaluated again. Returns { cancel() }.
HostKeytypeThe name of a member of HostData.
HostListenertypeA listener of onHost.
Viewportinterface{ width, height }, in logical pixels.
hasGrant(capability)functionWhether the user granted a capability.
option(id, fallback)functionThe stored value of a row of the options menu, or fallback when there is none or it has another type.
MenuValuetypeboolean | number | string.
Widened<T>typeThe result type of option: a false fallback gives boolean.
TSwidgets/clock/logic.ts
onHost((changed) => {
  if (changed.includes("options")) {
    const values = menuValues();
    const unitChanged = values.seconds !== state.seconds;
    Object.assign(state, values);
    if (unitChanged) {
      start();
    }
  }
  // A new UTC offset (time zone or summer time) shows at once.
  if (changed.includes("region")) {
    tick();
  }
});

Services

Every service answers through PlayerVox OverCrow, which checks its permission or capability, its parameters and the user action at each call.

ExportKindSummary
storagenamespaceget({ key }), set({ key, value }), remove({ key }), keys(). Permission storage.
httpnamespacefetch(url, { as, method?, body?, contentType? }). Permission network.
clipboardnamespacewriteText({ text }), during a user action. Permission clipboardWrite.
gameEventsnamespacesubscribe(listener). Permission gameEvents.
sessionnamespacesubscribe(listener). Capability session.read.
telemetrynamespacesubscribe(listener). Capability telemetry.read.
fpsnamespacesubscribe(listener). Capability fps.read.
medianamespacesubscribe({ cover? }, listener); previous, playPause, next({ player? }) during a user action.
stopwatchnamespacesubscribe(listener); toggle(), reset() during a user action.
notesnamespacesubscribe(listener); create(), select({ note }), setItem({ note, item, checked }), delete({ note }) during a user action.
playervoxnamespacescore.subscribe, rating.subscribe, reviews.subscribe, reviews.page({ page?, followedOnly? }).
journalnamespacesubscribe(listener); page({ cursor? }); delete({ session }) during a user action, after the user's confirmation.
twitchnamespacechat.subscribe(listener); chat.join({ channel }), chat.leave(), chat.favorite({ channel, favorite }) during a user action.
call(service, params?)functionCalls any service that answers once, by its name.
subscribe(service, params, listener)functionSubscribes to any subscription service, by its name.
ServiceErrorclassThe rejection of a failed call: code is a ServiceErrorCode.
Listener<T>type(update: SubscriptionUpdate<T>) => void.
SubscriptionUpdate<T>type{ ok: true, value, final } or { ok: false, error, final: true }.
Subscriptioninterfacecancel(); calling it again does nothing.
ServiceParams<N>typeThe parameters of service N.
ServiceResult<N>typeThe result, or the update, of service N.
FetchOptions<A>interfaceThe options of http.fetch.
FetchResponse<A>type{ status, contentType, body }, or { status, asset } for as: "image".
BodyTypetype"json" | "text" | "bytes" | "image".
BodyTypesinterfaceThe decoded body of each body type.
HttpMethodtypeThe methods of network rules.
HttpServicesinterfaceType of http.

The listener always comes last, and a service without parameters takes none:

TSwidgets/fps/logic.ts
  fps.subscribe((update) => {
    Object.assign(state, reading(update));
  });
TSwidgets/media/logic.ts
  const current = media.subscribe({ cover: state.showCover }, (update) => {
    if (update.ok) {
      state.media = update.value;
      state.unavailable = false;
      return;
    }
    // The subscription ended: the host's source failed. Show it and ask
    // again later; hidden, the timer waits until the widget shows.
    if (subscription === current) {
      subscription = null;
      state.media = null;
      state.unavailable = true;
      retry = timers.after(RETRY_MS, subscribe);
    }
  });

http.fetch sends a string body as text/plain and any other body as JSON, unless contentType says otherwise, and decodes the answer as as asks. A response without a body gives null, "" or an empty ArrayBuffer.

The types of the other namespaces are generated from the schema: ClipboardServices, FpsServices, GameEventsServices, JournalServices, MediaServices, NotesServices, PlayervoxServices, SessionServices, StopwatchServices, StorageServices, TelemetryServices and TwitchServices. Each service is described in the service reference.

Timers

ExportKindSummary
timersnamespaceafter(ms, callback), every(ms, callback), atEach(unit, callback).
Timerinterfacecancel(); calling it again does nothing.
  • At most 16 timers, never shorter than 100 ms: a shorter interval is raised to it.
  • No timer ticks while the widget is hidden. A repeating timer skips the hidden ticks; a one-shot timer that fell due while hidden ticks once when the widget is shown again.
  • atEach("second" | "minute" | "hour" | "day", callback) calls callback at each boundary of the user's local time, and once when the widget is shown again. It also wakes when the user's UTC offset changes.
TSwidgets/media/logic.ts
      retry = timers.after(RETRY_MS, subscribe);

Drawing, menu, messages and log

ExportKindSummary
draw(ref, commands)functionReplaces the commands of the canvas whose ref is given.
onMenu(handler)functionReceives the ID of each action row of the options menu that the user chooses.
t(key, params?)functionThe message of the active language, with each {name} replaced by params.name; the key itself when there is no message.
MessageParamstypeRecord<string, string | number>.
lognamespacedebug, info, warn, error(text): shown by overcrow-widget dev, dropped for an installed widget.

The draw commands are typed tuples, listed in drawing.

TSdocs/content/examples/countdown/logic.ts
    dots.push(["circle", 6 + index * 14, 6, 4], ["fill", "var(--color-accent)"]);

Time, dates and numbers

The VM has no Intl, and its local time is UTC: Date never gives the user's time. These helpers format from host.region. Numbers follow the user's number format (1,234.5, 1 234,5 or 1.234,5), whatever the interface language; dates are numeric, in the user's date order; times are 24-hour. Each helper also accepts its region values as options, so it runs in a unit test outside the VM.

ExportKindSummary
localTime(ms, offsetMinutes?)functionThe LocalTime fields of an instant in the user's UTC offset, or in the offsetMinutes of a service timestamp.
formatTime(ms, options?)function14:08, or 14:08:42 with seconds: true.
formatDate(ms, options?)function17/07/2026 (dmy), 07/17/2026 (mdy), 2026-07-17 (ymd).
formatNumber(value, options?)functionThe separators of the user's number format; minimumFractionDigits, maximumFractionDigits (3 by default), grouping.
formatDuration(ms, options?)function04:05, 1:02:03, 04:05.67 with hundredths: true; truncated, as a stopwatch shows it.
delayToNext(unit, nowMs, region?)functionThe milliseconds to the next boundary of local time, never past the next change of UTC offset.
LocalTimeinterfaceyear, month (1 to 12), day, weekday (0 is Sunday), hour, minute, second, millisecond.
TimeUnittype"second" | "minute" | "hour" | "day".
TimeOptionsinterfaceoffsetMinutes.
DateOptionsinterfaceoffsetMinutes, order.
ClockOptionsinterfaceoffsetMinutes, seconds.
NumberOptionsinterfaceThe options of formatNumber.
DurationOptionsinterfacehours, hundredths, format.
TSwidgets/clock/logic.ts
export function clockDate(now: number, order: DateOrder): string {
  return formatDate(now, { order });
}

Month and weekday names, 12-hour clocks and plural rules are not part of 1.0.

Generated types

These types are generated from the widget schema, so they always match what PlayerVox OverCrow accepts.

ExportSummary
ServiceName, CallServiceName, SubscribeServiceName, GestureServiceNameThe service names: all of them, those that answer once, the subscriptions, and those that need a user action.
ServiceParamsMap, ServiceResultMapThe parameters and the result of each service.
ServiceErrorCode, ServiceErrorPayloadThe error codes, and the { code } value that carries one.
Session, Telemetry, Fps, Stopwatch, Media, Notes, Note, NoteItem, CreatedNote, Score, Rating, RatingState, Review, ReviewsState, ReviewsPage, JournalState, JournalPage, JournalSession, TwitchChat, ChatMessage, ChatFragment, GameEvent, HttpResponseThe result shapes.
Permission, Capability, SensitiveCapability, CapabilityServices, PermissionServicesThe permissions and capabilities of the manifest, and the services each one allows.
Locale, Theme, Mode, Region, NumberFormat, DateOrderThe values of host.
EventName, GestureEventName, EventDetailMap, NamedKeyThe events of the view, those that are user actions, their details, and the named keys of keydown.
DrawCommand, DrawCommandName, ColorThe canvas commands and colours.
TokenName, ColorToken, LengthToken, FontSizeToken, FontFamilyToken, TimeToken, ShadowTokenThe design tokens.
IconNameThe names of the icons.
MenuRowType, HostFeatureThe row types of the options menu, and the data sources of requires.
JsonValue, AssetHandle, ImageSourceAny JSON value; an image handle given by a service; an image of the package or such a handle.

Limit constants

The limits a widget's logic works against are exported as constants, so the code never repeats a number: MAX_TIMERS, MIN_TIMER_INTERVAL_MS, MAX_SERVICE_CALLS_IN_FLIGHT, MAX_SUBSCRIPTIONS, MAX_STORAGE_KEYS, MAX_STORAGE_KEY_BYTES, MAX_STORAGE_VALUE_BYTES, STORAGE_QUOTA_BYTES, MAX_REQUEST_URL_BYTES, MAX_HTTP_REQUEST_BYTES, MAX_HTTP_RESPONSE_BYTES, MAX_HTTP_DECLARED_RESPONSE_BYTES, MAX_CLIPBOARD_BYTES, MAX_OBJECT_ID_BYTES, MAX_NOTES, MAX_NOTE_ITEMS, MAX_NOTE_TITLE_BYTES, MAX_NOTE_BODY_BYTES, MAX_NOTE_ITEM_BYTES, MAX_REVIEW_CHARS, MAX_CHAT_CHANNEL_BYTES, MAX_CHAT_MESSAGE_CHARS, MAX_CHAT_FRAGMENTS, MAX_CHAT_FAVORITES, MAX_DRAW_COMMANDS, MAX_DRAW_STATE_DEPTH, MAX_PATH_POINTS, MAX_PATCH_BYTES, MAX_SCENE_NODES, MAX_CHILDREN, MAX_NODE_TEXT_BYTES, MAX_ATTRIBUTE_TEXT_BYTES, MAX_LABEL_BYTES, MAX_FIELD_TEXT_BYTES, MAX_LOCALE_ENTRIES, MAX_LOG_BYTES, VM_HEAP_BYTES, VM_MAX_HEAP_BYTES, VM_TURN_BUDGET_MS, VM_JOBS_PER_TURN and VM_MESSAGES_PER_TURN.

Unit tests: @overcrow/sdk/testing

A second entry point, never bundled into a widget, replaces PlayerVox OverCrow in a unit test of the logic: installRuntime() gives virtual time, settles the service calls and pushes the subscription values. See unit tests.