Skip to content

Data sources and resource loading

Core reads data, resolves resource paths and manages loading and cleanup. It retains your data format; Vega, Cassiopeia and Home Spot own their respective visuals. Start with a real JSON file and image here, then use the Vega guide for playback. The hosted https://haneoka.org/embed/core.js runs directly in a browser without npm installation.

Open the complete promenade.json. It contains a title, background path and one text line; garden.svg is its original image. Select HTTP JSON in the application below and load to see the data. Resolve resource URL obtains the image address; Read resource bytes reads its actual content. This lines document is reader data rather than a playable Vega document; step 3 converts this specific format.

Edit the JSON parameters, then load. Modules and requests start only after a click; results include the actual response and transport log.

Language

Preparing example…

View the example source
import { createEmbedLoader, httpDataSource } from "https://haneoka.org/embed/core.js";
const loader = createEmbedLoader({
source: httpDataSource({ url: "https://docs.haneoka.org/examples/promenade.json" }),
locale: "fr",
});
const data = await loader.load();
const imageUrl = await loader.resourceUrl(data.background);
// After the consumer finishes with this address: await loader.dispose().

HTTP resources resolve against the configured JSON directory, which redirects do not change. An explicit assetsBase overrides it. resourceUrl obtains an address; resourceBytes reads the content. Panel fields select factories and resources. In particular, resourceKey/resourceField are this example’s resource-selection fields, while the complete SDK options are listed below.

Save promenade.json and garden.svg. Open the complete file reader, select the JSON and image, then click Load files. You should see Une promenade, the original garden image and the actual image byte count. Close reader removes the image before disposing its loader and Blob URL; select new files and load again.

Save all the code below as UTF-8 index.html to keep this project. The quickstart explains local HTTP opening. Its folder contains index.html, promenade.json and garden.svg. The JSON’s background is a resource key; the map binds that literal key to your selected File, including legal Unicode paths.

Select the JSON and its required resources separately. This example maps one image; larger projects map every declared resource key, including model sidecars, to a selected File.

promenade.json is this page’s data format. Vega requires an AdvStory commands array. For this particular lines structure, create this object after reading data:

const storyDocument = {
localization: { locales: [data.language], defaultLocale: data.language },
commands: [
{ command: 25, background: { url: data.background } },
...data.lines.map(line => ({ command: 2, targetName: line.speaker, text: line.text })),
],
};

Pass storyDocument as mountStory’s document and use that same resource directory as assetsBase. Vega supplies the complete browser page and load steps. If your selected JSON is already a complete AdvStory, read it directly instead of applying this lines conversion. A decoder validates or converts actual data; a TypeScript type only describes your expectation.

Choose Haneoka in the example and load its public song before resolving jacketUrl. Both reader and catalog adapter are available from the hosted entry:

import { createEmbedLoader, haneokaDataSource } from "https://haneoka.org/embed/core.js";
const loader = createEmbedLoader({
source: haneokaDataSource({ resource: "songs", id: "100070" }),
server: "intl", locale: "en",
});
const song = await loader.load();
const imageUrl = await loader.resourceUrl(song.jacketUrl);
// End the consumer before awaiting loader.dispose().

Public reads require no account or release. Server chooses the registry and locale selects the language context; the catalog retains its supplied localized fields. The source’s resource base defaults to the API origin, and loader.assetsBase can override it. Repeated load reads current data again. Use memoryDataSource for an existing object and fileDataSource for a selected File; their complete signatures and transport constraints follow.


FieldType/required/defaultEffect, combinations and errors
sourcerequired DataSource<T>Must supply load(context); loader calls it for each load. Optional source assetsBase supplies the resource directory, optional dispose runs once. Choose the source appropriate to the consuming host’s format.
serveroptional string; undefinedPassed unchanged to custom sources/resolvers. Haneoka adapter defaults it to intl and validates the registry slug through the public client. Language never chooses a server.
localeoptional string; enPassed unchanged; author locales such as fr/pt-BR are valid. Haneoka sends it as Accept-Language for supported-language negotiation.
assetsBaseoptional string; source.assetsBase, then noneOverrides source base. Absolute HTTP(S) directory, no query/fragment; trailing slash added. Invalid base throws TypeError at construction. Relative keys without a base fail URL construction.
resolveResourceoptional (key:string,context:DataContext)=>string|URL|Blob|Promise<…>; key unchangedCompletely maps a resource key. Result URL uses assetsBase when relative. Resolver rejection rejects that resource operation; missing local Map entry does not fall through to HTTP.
fetcheroptional (Request)=>Promise<Response>; fetchReceives cors Request, caller headers, configured credentials and signal. Both document and resource reads use it. A downstream media element has its own transport. Throwing rejects the operation.
credentialsoptional omit/same-origin/include; omitApplies to loader transport. Credentialed cross-origin services must allow the embedding origin and credentials. Invalid values are rejected by native Request.
headersoptional HeadersInit; noneShared transport headers; a source’s per-request headers override matching names. Haneoka adapter sets Accept-Language from locale. Headers validation follows Fetch.
maxBytesoptional positive safe integer; 67108864Per decoded JSON/resource-byte response budget, not a total scene/cache/URL-resolution budget. Nonpositive/unsafe values throw RangeError at construction; oversized reads throw RangeError (Haneoka wraps response_too_large).
onListenerErroroptional (error:unknown)=>void; ignoredReceives observer exceptions. Its own exception is isolated, and never changes the operation result.

A loader has no constructor signal, timeout, decoder or automatic retry/cache option. Supply a per-operation signal, a source decoder, or a transport/source policy. Resolving a URL alone does not download bytes or apply maxBytes to the eventual media element.

FactoryRequired argumentsOptional arguments/defaultsResource origin and validation
memoryDataSource(data)any T, including an authored documentnoneReturns the exact original value/reference; no default asset directory or cloning/validation.
httpDataSource({url,decode?})absolute HTTP(S) string or URLdecode(unknown)→T; default parsed JSONFactory validates protocol/URL. Base is configured URL’s directory, not redirect destination. Loader maxBytes/signal apply; JSON parse/decode failures reject load.
fileDataSource(file,decode?)browser File or Blob, including a host-created Blobsame decoder; default parsed JSONNo network base. Reads actual Blob bytes through loader budget/progress; invalid JSON/decoder rejects load.
fileResourceResolver(files)ReadonlyMap<string,Blob>noneExact Unicode key lookup, no normalization. Missing key throws Error. URL calls create owned Blob URLs; byte calls read Blob directly.
haneokaDataSource({resource,id?,view?,apiBase?,decode?})resource stringdetails belowCurrent public catalog adapter; no account or release parameter.

Haneoka factory fields: resource is a manifest name with public-client resource syntax; id?:string selects an entity (omitted→index); view?:string selects a manifest projection; apiBase?:string defaults https://haneoka.org/api/v1/ and must be an absolute HTTP(S) directory without query/fragment; decode? validates/converts the index/entity (default JSON value). Syntax/absent data errors follow client rules during load. Source asset base is the API origin root; a loader assetsBase explicitly overrides it. Server and locale belong to the loader context rather than this factory.

Complete custom source and resolver context

Section titled “Complete custom source and resolver context”

A DataSource<T> declares load(context): T|Promise<T>, optional assetsBase?:string, and optional dispose():void|Promise<void>. The loader invokes cleanup once during disposal and waits for it; cleanup rejection rejects the shared disposal promise. Use a fresh owned source when its cleanup releases resources used by another loader.

DataContext contains these six public fields:

FieldTypeMeaning
serveroptional stringConfigured source registry, unchanged.
localestringConfigured/default author language.
signalAbortSignalThat operation’s cancellation signal; use it in your work.
fetcher(Request)→Promise<Response>Loader’s configured transport.
maxBytesnumberEffective decoded-byte budget; custom parsing should honor it.
progress({loaded:number,total?:number})→voidReports actual operation bytes; omit total when unknown.

progress’s argument has its own loaded/optional total fields. The resolver returns a string, URL or Blob synchronously/asynchronously. Absolute HTTP/HTTPS/Blob/data URLs keep their origin; relative strings use the effective assetsBase and / starts at its origin root. Other protocols throw TypeError. The loader never invents a browser/SSR page base.

const source = {
assetsBase: "https://author.example/project/",
async load(context) {
context.signal.throwIfAborted();
const response = await context.fetcher(new Request("https://author.example/project/story.json", { signal: context.signal }));
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json(); // use your format-specific bounded decoder here
},
dispose() { /* release resources owned by this source */ },
};
const loader = createEmbedLoader({ source, locale: "pt-BR", resolveResource: key => key });
MemberArguments/defaults and resultLifetime behavior
localereadonly stringEffective locale.
serverreadonly string or undefinedExplicit server, before an adapter applies its fallback.
disposedreadonly booleanBecomes true immediately when dispose starts.
load(options={})optional {signal?:AbortSignal}; Promise<T>New independent source operation, key $data; repeated loads call source again.
resourceUrl(key,options={})required string + optional signal; Promise<string>Resolves key, no byte fetch. One cached URL per owned Blob until dispose.
resourceBytes(key,options={})required string + optional signal; Promise<Uint8Array>Resolves key and reads bytes with status/budget/progress.
subscribe(listener)required (event:EmbedEvent)=>void; returns unsubscribe functionReceives future events; does not replay state. Throws if already disposed.
cancel()no arguments; voidAborts all current operations; leaves loader reusable.
dispose()no arguments; shared Promise<void>Immediately disallows work, aborts active calls, revokes owned URLs, emits disposed, clears observers and invokes source cleanup once. Idempotent; disposed loader cannot be reused.

Per-call signal cancellation rejects promptly, including when a custom source ignores its signal. The loader suppresses late ready/progress. Source work should still observe context.signal to stop its underlying activity. cancel() preserves later calls; dispose() ends the loader. Release consumers before revoking URLs; URLs authored by your own code remain your responsibility.

Event typeFieldsWhen
start / readyoperation:number, key:stringBegin / successfully finish an operation.
progressoperation,key,loaded:number,total?:numberBytes actually read. Memory data/URL-only resolution have no automatic byte progress. Compression/unknown Content-Length omit total.
error / cancelledoperation,key,error:unknownFailure / cancellation.
disposedtype onlyLoader disposal; cancellation failures are suppressed after disposal.

IDs increase independently per loader; key is $data or the literal resource key. Listener errors do not reject operations. Concurrent operations are independent; there is no data-level deduplication or retry. The small demo retains at most32 displayed records while the SDK’s event stream remains unchanged.

EmbedHttpError(status,url) extends Error with name/message/status/url for a non-success HTTP/resource read. Invalid URLs/bases/resolved protocols throw TypeError, exceeded byte budgets throw RangeError, invalid JSON throws SyntaxError, and authored decoder/source/resolver errors remain theirs. Haneoka API failures retain ApiClientError; its response budget reports code response_too_large, HTTP status and nonretryable default. Abort rejects with the signal reason, usually AbortError/TimeoutError. Calls after dispose reject Error; source cleanup failure rejects dispose.

createHaneokaBranding(document,options={}) is exported by the hosted core facade and the package /branding entry. Required document is the host DOM Document; corner?:"top-left"|"top-right"|"bottom-left"|"bottom-right" defaults top-left. It returns an HTMLAnchorElement, makes no image request, and does not append itself. Append it as a translucent overlay to a positioned full-size host, preserving its48px touch target, and remove it during cleanup. The href is fixed https://haneoka.org/, target _blank with noopener; original inline SVG/48px touch target/focus are supplied. Visual embed hosts manage this helper automatically. Importing data-only core creates no DOM.