This document details the JavaScript API available to Synura extensions.
The core object for interacting with the Synura application.
-
synura.open(options, [context], [callback])- Opens a new view. Optionally connects to the view if
callbackis provided. - Arguments:
options(Object): Configuration for the view.view(String): The type of view to open (e.g.,'/views/list').styles(Object): Styles configuration.models(Object): Models data.
context(Object, Optional): Arbitrary data passed back in events. Can be omitted if only 2 arguments are passed and the 2nd is the callback.callback(Function, Optional): Called when an event occurs. If provided,synura.connectis called automatically.
- Returns: An object
{ success: boolean, viewId: number, viewName: string, error?: string }.
- Opens a new view. Optionally connects to the view if
-
synura.update(viewId, data)- Updates an existing view.
- Arguments:
viewId(Number): The ID of the view to update.data(Object): Newstylesormodelsto merge.
- Special Model Keys:
- For
'/views/list',data.models.appendappends new list items to the existingcontentsinstead of replacing them. This is useful for pagination. - For
'/views/chat',data.models.appendappends new chat messages.
- For
- Returns: An object
{ success: boolean, viewId: number, viewName: string, error?: string }.
-
synura.connect(viewId, context, callback)- Listens for events from a view.
- Arguments:
viewId(Number): The ID of the view.context(Object): Arbitrary data passed back in events (useful for tracking state).callback(Function): Called when an event occurs. Receives aneventobject.
- Returns: An object
{ success: boolean, viewId: number, error?: string }. - Note: Only one callback can be active per view. Calling
connectagain will overwrite the previous callback.
-
synura.close(viewId)- Closes a specific view.
- Arguments:
viewId(Number): The ID of the view to close.
- Returns: An object
{ success: boolean, viewId: number, viewName: string, error?: string }.
-
synura.parse(type, element): Parses a DOM element into a structured object based on the specified type.type: A string specifying the parsing logic (e.g.,'post').element: The DOM element to parse.- Returns: An array of objects representing the parsed content (e.g.,
[{type: 'text', value: '...'}, {type: 'image', value: '...'}]).
A custom synchronous HTTP client (unlike Web API, it does not return a Promise).
Network failures, invalid URLs, denied permissions, timeouts, cancellation, and oversized responses throw an Error. HTTP error statuses such as 404 still return a response with ok: false. Invalid request headers throw a TypeError.
Host restriction: By default, extension requests must target
SYNURA.domain. If an extension needs sibling subdomains or path-scoped hosts, declareSYNURA.host_permissions.synurartand the real Synura app use the same rule.
Network declarations (domain, host_permissions, and bypass) are validated and captured when the extension is loaded. Changing the JavaScript package object later does not change these settings. SYNURA.host_permissions is optional. When it is omitted or empty, the effective permission uses the host portion of SYNURA.domain:
[
"http://<host>/*",
"https://<host>/*"
]When it is nonempty, it replaces the default fetch scope. Each pattern must be one of:
http://host/path
https://host/path
*://host/path
The host can be exact (api.example.com) or a left-most wildcard (*.example.com). The path can contain *. Omitting the path means /, not /*. Query strings and fragments are not allowed in permission patterns.
Examples:
var SYNURA = {
domain: "github.io",
host_permissions: [
"https://*.github.io/*"
]
};Security boundaries:
-
Permission hosts must stay in the same registrable domain family as
SYNURA.domain. -
Wildcards cannot target ICANN public suffixes such as
*.io,*.com, or*.co.kr. -
Private/public-suffix service domains such as
github.ioare handled by the public suffix list. For example,*.github.iois allowed only when the extension identity is scoped togithub.io; auser.github.ioextension cannot broaden to sibling pages. -
For raw IPs and localhost, explicit permission ports must match the extension's domain declaration. A portless pattern, including the implicit host scope, accepts any port. To restrict a non-default port, include it in
host_permissions, for examplehttp://127.0.0.1:8080/*. Requests normalize default ports, so omit:80and:443from their respective HTTP/HTTPS patterns. -
Options:
method:'GET'|'POST'headers: Object{ "Key": "Value" }. Values must be strings;nulland arrays are not header objects.body: String. Non-string JSON values are serialized as JSON;nullmeans an empty body.onProgress: Synura Custom Option. Function(current, total) => { ... }. Download positions are collected and delivered synchronously beforefetch()returns.currentandtotalare in bytes;totalis -1 if unknown (e.g., chunked encoding). Only the final response in a redirect chain reports progress. Callback exceptions are ignored.
-
Returns: A
Responseobject with:status: (Number) The HTTP status code (e.g., 200, 404).statusText: (String) The status description (e.g., "200 OK", "404 Not Found"). Usestatusfor comparisons; older runtimes may omit the numeric prefix.ok: (Boolean)trueif the status code is in the range 200-299.url: (String) The response URL visible to the runtime. If a runtime follows redirects internally, this can be the final URL. If a3xxresponse is exposed to the extension, this remains the requested URL.headers: (Object) Response header properties plus case-insensitiveget(name)(string orundefined),getAll(name)(array), andentries()(array of all[name, value]pairs). Duplicate values are joined with", "for property access andget(), exceptSet-Cookie, which uses newlines. UsegetAll("set-cookie")to read individual cookies without splittingExpiresdates.text(): Decodes the body as UTF-8, replacing invalid byte sequences and retaining a leading BOM. It does not inspect the Content-Type charset or guess a language. Extensions handle other encodings explicitly witharrayBuffer()andTextDecoder(label).arrayBuffer(): Returns the raw response body bytes as anArrayBuffer.json(): Parses the same string astext(). Empty or invalid JSON throws aSyntaxError.dom(mimeType): Synura Custom Function. Parses the current response string as HTML (only'text/html'supported). For non-UTF-8 pages, decode bytes first and usenew DOMParser().parseFromString(...).
Redirects and limits: Fetch follows
3xxresponses with a Location header, preserving method/body and checking each destination, up to 10 requests.followRedirectsandredirectare not supported options. The response body limit is 8 MiB. Matching redirect cookies last for that fetch call only, and an explicit Cookie header takes precedence. Cookie names replace earlier names case-insensitively; this temporary redirect store does not process expiry attributes.
const response = fetch("https://example.com/page");
const raw = response.arrayBuffer();
const html = new TextDecoder("euc-kr").decode(raw);
const utf8Bytes = new TextEncoder().encode(html);const response = fetch("https://" + SYNURA.domain + "/some/path");
if (response.status >= 300 && response.status < 400) {
const location = (response.headers && (response.headers["Location"] || response.headers["location"])) || "";
console.log("redirect", response.url, location);
}Objects returned by response.dom(), new DOMParser().parseFromString(...), or element query methods provide a subset of the standard DOM API.
-
Properties:
tagName(String): The tag name (upper case).id(String): The element's ID.className(String): The class attribute.classList(DOMTokenList):add(className): Adds a class.remove(className): Removes a class.toggle(className): Toggles a class.contains(className): Checks if a class exists.
dataset(DOMStringMap):- Read/Write data attributes (e.g.
el.dataset.foomaps todata-foo).
- Read/Write data attributes (e.g.
textContent(String): The text content of the element and its descendants.innerText(String): The rendered text content of the element and its descendants.childNodes(NodeList): List of child nodes.firstChild,lastChild(Element): First/Last child element.nextSibling(Element): Next sibling element.nodeType(Number):1(Element) or3(Text).
-
Methods:
getAttribute(name): Returns the attribute value, ornullif the attribute does not exist. For HTML documents, attribute name matching is case-insensitive.hasAttribute(name): Returnstrueif the attribute exists,falseotherwise.querySelector(selector): Returns the first matching child element.querySelectorAll(selector): Returns a NodeList of matching child elements.remove(): Removes the element from its parent.cloneNode(deep): Returns a clone of the node. Ifdeepis true, the descendants are also cloned.
querySelector(selector)querySelectorAll(selector)
console.log(...args): Logs messages to the debug console (and logcat on Android).
navigator.language: The current locale exposed by the runtime as a BCP 47 language tag. Insynurart, this defaults toen-USand can be changed with thelocale <tag>command.
Persistent key-value storage backed by a database.
localStorage.setItem(key, value)- Saves a key-value pair. Both must be strings (like Web API).
localStorage.getItem(key)- Returns the value (string) for the key, or
nullif not found.
- Returns the value (string) for the key, or
localStorage.removeItem(key)- Removes the item with the specified key.
localStorage.clear()- Removes all items.
Transient, in-memory key-value storage (LRU cache).
sessionStorage.setItem(key, value)- Saves a key-value pair. Value can be any JavaScript type (unlike Web API).
sessionStorage.getItem(key)- Returns the value for the key, or
nullif not found.
- Returns the value for the key, or
sessionStorage.removeItem(key)- Removes the item with the specified key.
sessionStorage.clear()- Removes all items.
localStorage.setItem("token", "12345");
const token = localStorage.getItem("token");
sessionStorage.setItem("cache", {
lastPage: 1,
items: []
});
const cache = sessionStorage.getItem("cache");Query-string helpers available in the app and synurart: append, delete, get, getAll, has, set, keys, values, entries, and toString.
The constructor accepts a query string, an iterable of pairs, or an object. set preserves the first matching parameter's position and removes duplicate values.
Use for...of to iterate. forEach and sort are not available.
for (const [name, value] of new URLSearchParams("q=hello&page=2")) {
console.log(name, value);
}Browser-like UTF-8 encoder.
new TextEncoder()encoding: Always"utf-8".encode(input): Returns aUint8Arraycontaining UTF-8 bytes for the input string.encodeInto(input, destination): Writes UTF-8 bytes into a destinationUint8Arrayand returns{ read, written }.
Browser-like byte decoder for supported charset labels.
new TextDecoder(label)encoding: Canonical encoding label resolved by the runtime (for example"utf-8"or"euc-kr").fatal: Alwaysfalse.ignoreBOM: Alwaysfalse.decode(input): Decodes anArrayBufferor typed array into a JavaScript string.
Common use cases include:
new TextDecoder("utf-8")new TextDecoder("euc-kr")
Note: The API shape matches the Web Encoding API, but Synura's
fetch()is synchronous. Methods likeresponse.text()andresponse.arrayBuffer()return values directly instead of Promises.
Data passed to views via the models object follows a specific structure used by the Synura protocol.
The native model object consists of four optional fields:
message(String): Text content or JSON-serialized data.code(Integer): Numeric data (counts, IDs, etc.).value(Double): Decimal numeric data (ratings, percentages, etc.).details(Array): A list of items (used for lists, comments, content blocks).
{
"message": "Hello World",
"code": 123,
"value": 123.45,
"details": [...]
}To simplify development, you can pass standard JavaScript types, and Synura will automatically map them to the canonical structure:
| JavaScript Type | Maps To | Example |
|---|---|---|
| String | { message: "..." } |
title: "Hello" |
| Number | { value: 123.45, code: 123 } |
rating: 4.5 |
| Array | { details: [...] } |
contents: [item1, item2] |
| Generic Object | { message: JSON.stringify(...) } |
user: { name: "Bob", id: 1 } |
Important: If you pass an object that contains keys other than
message,code,value, ordetails(e.g.,{ message: "Hello", user: "Bob" }), it will be treated as a Generic Object and the entire object will be JSON-stringified into themessagefield. To use the explicit wrapper format, the object must only contain the allowed keys.
Views are opened via synura.open({ view: '/views/<type>', models: ..., styles: ... }).
- List View (
/views/list): Lists, grids, galleries. - Post View (
/views/post): Articles, posts, comments. - Chat View (
/views/chat): Messaging interface. - Settings View (
/views/settings): Forms and configuration. - Editor View (
/views/editor): Text editor with attachments. - Browser View (
/views/browser): In-app web browser. - Simple View (
/views/simple): Basic text display. - Markdown View (
/views/markdown): Markdown rendering. - Source View (
/views/source): Code viewer with syntax highlighting.
Dialogs are opened via synura.open({ view: '/dialogs/<type>', ... }). Unlike views, dialogs appear as modal overlays and do not navigate away from the current screen.
- Input Dialog (
/dialogs/input): Modal form input. - Confirmation Dialog (
/dialogs/confirmation): Simple alert/confirm.
Handle events in the synura.connect callback, or the optional callback passed to synura.open.
{
eventId: "CLICK" | "LOAD" | "REFRESH" | "SCROLL_TO_END" | "MENU_CLICK" | "SUBMIT",
context: {
...
},
data: {
...
}
}LOAD/REFRESH: Triggered when the view loads or user pulls to refresh.CLICK: User clicked an item.datacontains the item object (e.g.,{ link: "...", index: 0 }).SCROLL_TO_END: User scrolled to the bottom (List View). Fetch next page here.MENU_CLICK: User clicked a menu item.data.menucontains the menu text.AUTHOR_CLICK: User clicked the author.datacontainsauthorand optionallylink.SUBMIT: User submitted a form (Settings/Dialog).datacontains form values.
- Maximum Script Size: 128KB (131,072 bytes). Extensions exceeding this size will fail to install.
Synura uses a passive router architecture to enable content pre-fetching and instant navigation.
See Router Pattern for full documentation on:
- Router Handler (
router(url)) - Threading Model
- State Sharing
- Caching Strategy