@tavora/sdk 0.0.0-stage → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -0
- package/LICENSE +21 -0
- package/README.md +68 -3
- package/dist/index.d.ts +173 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +267 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol.d.ts +640 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +123 -0
- package/dist/protocol.js.map +1 -0
- package/package.json +43 -3
- package/src/index.ts +491 -0
- package/src/protocol.ts +539 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
What changed in `@tavora/sdk` and the bridge protocol it speaks, newest first. Before Tavora 1.0 the SDK may still change in ways that break modules; each such change raises the minor version (0.2 → 0.3) and is marked **Breaking** here. Anything else raises the patch version. To make sure players have a Tavora that knows what your module uses, set [`minTavora`](https://docs.mytavora.app/reference/manifest) in its manifest.
|
|
4
|
+
|
|
5
|
+
## 0.2.2
|
|
6
|
+
|
|
7
|
+
- `MIN_TAVORA`: the oldest Tavora that has everything this SDK version offers, for `minTavora` in your manifest.
|
|
8
|
+
- Panel types: a manifest may list `panels`, named tools such as Inventory and Build planner that players add, dock and copy on their own, all sharing the module's saved content and permissions. `panel.type` says which one a page runs as; the handshake carries it as `panel.type` (Tavora 0.17.12). Set `minTavora` to `0.17.12` or newer when you use them: an older Tavora refuses the `panels` field.
|
|
9
|
+
|
|
10
|
+
## 0.2.1
|
|
11
|
+
|
|
12
|
+
- `ui.showMenu(request)`: a menu Tavora draws above every panel, so it is never cut off at the panel's edge. Items with an optional colour dot, check or radio mark, hint and danger style; separators; headings; one level of submenus. Only right after a click, right-click or key press in the panel; resolves with the chosen id or null (Tavora 0.17.9).
|
|
13
|
+
|
|
14
|
+
## 0.2.0
|
|
15
|
+
|
|
16
|
+
Needs Tavora 0.17.8 or newer for `minTavora`; everything else works from the Tavora version named next to it.
|
|
17
|
+
|
|
18
|
+
- `minTavora` in the manifest: the oldest Tavora a module works with. An older Tavora refuses to install or load it and says why (Tavora 0.17.8).
|
|
19
|
+
- `connectors.discord`: the Discord app on the player's PC through Tavora's Discord sign-in: `state()`, `connect()`, `allowControl()`, `command()` for a fixed list of RPC commands, `subscribe()` and `onEvent()` for voice events, and `image()` for avatars and server icons. Needs `"discord"` in `permissions.connectors` and the player's consent (Tavora 0.16.11).
|
|
20
|
+
- `connectors.aiUsage`: the usage of the player's Claude and Codex accounts, without ever seeing a login. Needs `"ai-usage"` in `permissions.connectors` (Tavora 0.16.10).
|
|
21
|
+
- `storage.app`: small settings shared by every game, next to `storage` for the current game (Tavora 0.16.10).
|
|
22
|
+
- `network.fetch`: web requests to the hosts in `permissions.network`, after the player allowed them; HTTPS only, no cookies (Tavora 0.16.8).
|
|
23
|
+
- `clipboard.writeImage(png)`: puts a PNG on the clipboard right after a click or key press in the panel (Tavora 0.14.1).
|
|
24
|
+
- `blobs`: pictures a module keeps for a game, served from the module's own address (Tavora 0.12.0).
|
|
25
|
+
- `links.open(url)`: opens a web address in the game's Browser panel, right after a click or key press (Tavora 0.11.0).
|
|
26
|
+
- `ui.getLanguage()` and `ui.onLanguageChange()`: the language Tavora shows its texts in (Tavora 0.5.9).
|
|
27
|
+
|
|
28
|
+
## 0.1.0
|
|
29
|
+
|
|
30
|
+
The first version: `connect()`, the current game, `storage`, `documents` with revisions and change events, panel state, the panel's size, the theme and its changes, and the suspend, resume and dispose lifecycle. Bridge protocol v1.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 wellbrained
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,68 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
# @tavora/sdk
|
|
2
|
+
|
|
3
|
+
Typed client for [Tavora](https://mytavora.app) modules. A module is a web page that runs in a sandboxed panel inside Tavora Desktop; this package is how it talks to the host.
|
|
4
|
+
|
|
5
|
+
**Status:** bridge protocol v1, still before 1.0: a minor version can change things, and the [changelog](./CHANGELOG.md) says what and which Tavora a version needs.
|
|
6
|
+
|
|
7
|
+
Start a new module with `npm create @tavora/module` (or `pnpm create @tavora/module`), or add the SDK to your own project with `npm install @tavora/sdk`.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { connect, TavoraError } from "@tavora/sdk";
|
|
11
|
+
|
|
12
|
+
const tavora = await connect();
|
|
13
|
+
const game = await tavora.game.getCurrent(); // { id: "poe2", displayName: "Path of Exile 2" }
|
|
14
|
+
|
|
15
|
+
// Small settings, private to this module in this game.
|
|
16
|
+
const count = (await tavora.storage.get<number>("count")) ?? 0;
|
|
17
|
+
await tavora.storage.set("count", count + 1);
|
|
18
|
+
|
|
19
|
+
// Documents are shared by all panels of this module in this game.
|
|
20
|
+
const plan = await tavora.documents.get<{ steps: string[] }>("plan");
|
|
21
|
+
try {
|
|
22
|
+
await tavora.documents.put({ id: "plan", title: "Plan", data: { steps: ["Act 1"] }, ifRevision: plan?.revision ?? 0 });
|
|
23
|
+
} catch (error) {
|
|
24
|
+
if (error instanceof TavoraError && error.code === "CONFLICT") {
|
|
25
|
+
// Another panel saved first: reload instead of overwriting.
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
tavora.documents.onChange(({ id }) => console.log(`${id} changed in another panel`));
|
|
29
|
+
|
|
30
|
+
// State for this one panel (open tab, scroll position). Removed when the panel closes.
|
|
31
|
+
await tavora.panel.setState({ tab: "gear" });
|
|
32
|
+
|
|
33
|
+
tavora.workspace.onResize(({ width, height }) => {});
|
|
34
|
+
tavora.ui.onThemeChange((theme) => {});
|
|
35
|
+
tavora.lifecycle.onSuspend(() => {}); // hidden: pause timers and animations
|
|
36
|
+
tavora.lifecycle.onResume(() => {});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`connect()` can be called any number of times and always returns the same connection. It rejects with a `TavoraError` when the page runs outside Tavora (`HOST_UNAVAILABLE`) or the host speaks another protocol version (`INCOMPATIBLE`).
|
|
40
|
+
|
|
41
|
+
Every failed call rejects with a `TavoraError`. Check `error.code`, not the message. The codes, limits and the wire format are in the [bridge protocol reference](https://docs.mytavora.app/reference/protocol); the manifest every module needs is in the [manifest reference](https://docs.mytavora.app/reference/manifest).
|
|
42
|
+
|
|
43
|
+
Values you store must be JSON. Type parameters such as `get<number>()` are not checked at runtime.
|
|
44
|
+
|
|
45
|
+
## More from the host
|
|
46
|
+
|
|
47
|
+
| API | What it does |
|
|
48
|
+
| --- | --- |
|
|
49
|
+
| `tavora.storage.app` | The same small settings, shared by every game: for choices the player expects to stay the same from game to game. |
|
|
50
|
+
| `tavora.ui.getLanguage()`, `onLanguageChange` | The language of Tavora's own texts (`"en"`, `"de"`). Show your module in it, or in English when you have no translation. |
|
|
51
|
+
| `tavora.blobs` | Pictures too big for a document: up to 50 MB each and 500 MB per module and game. `put` a file, show it with `url(id)`. See [Files](https://docs.mytavora.app/reference/protocol#files-blobs). |
|
|
52
|
+
| `tavora.clipboard.writeImage(png)` | Puts a PNG on the system clipboard. Call it from the click or key press that asked for it. See [Clipboard](https://docs.mytavora.app/reference/protocol#clipboard). |
|
|
53
|
+
| `tavora.links.open(url)` | Opens a web address in the game's Browser panel, from a click or key press. |
|
|
54
|
+
| `tavora.network.fetch(url, init)` | A web request to a host listed in `permissions.network`, once the player granted it. See [Network](https://docs.mytavora.app/reference/protocol#network). |
|
|
55
|
+
| `tavora.connectors.aiUsage` | The usage of the player's Claude and Codex accounts, with the `ai-usage` connector. |
|
|
56
|
+
| `tavora.connectors.discord` | The Discord app on this PC through Tavora's Discord sign-in, with the `discord` connector: voice channels, mute and deafen, voice events. |
|
|
57
|
+
|
|
58
|
+
What needs the player's permission (`permissions.network` and `permissions.connectors` in the [manifest](https://docs.mytavora.app/reference/manifest)) works only once the player has granted it: they are asked when they add the module and can take it back in Settings > Integrations. Until then the call fails with `PERMISSION_DENIED`; `tavora.host.capabilities` lists what was granted when the module connected. See [Capabilities](https://docs.mytavora.app/reference/protocol#capabilities) and [Connectors](https://docs.mytavora.app/reference/protocol#connectors).
|
|
59
|
+
|
|
60
|
+
## Examples
|
|
61
|
+
|
|
62
|
+
The same small module built three ways: [vanilla TypeScript](https://github.com/wellbrained/tavora/tree/main/examples/vanilla-module), [React](https://github.com/wellbrained/tavora/tree/main/examples/react-module) and [Vue](https://github.com/wellbrained/tavora/tree/main/examples/vue-module).
|
|
63
|
+
|
|
64
|
+
## Building a module
|
|
65
|
+
|
|
66
|
+
- Bundle everything. A module's page has no network access, so no CDN scripts, web fonts or remote images. To reach a web API, list its host in `permissions.network` and use `tavora.network.fetch` once the player has granted it.
|
|
67
|
+
- Use relative asset paths (Vite: `base: "./"`). The page is served from `tavora-module://<module-id>/`.
|
|
68
|
+
- Point the manifest's `entry` at the built HTML file.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { type AiUsageAccountInfo, type AiUsageReading, type BlobInfo, type BlobUsage, type Capability, type DiscordCommand, type DiscordCommands, type DiscordEvent, type DiscordEventName, type DiscordResult, type DiscordState, type DocumentChange, type DocumentSummary, type ErrorCode, type ErrorPayload, type HandshakeResult, type JsonValue, type MenuRequest, type NetworkRequest, type NetworkResponse, type PutDocumentParams, type TavoraDocument, type TavoraGame, type TavoraTheme, type TavoraViewport } from "./protocol.js";
|
|
2
|
+
export type { AiUsageAccountInfo, AiUsageReading, BlobInfo, BlobType, BlobUsage, Capability, DiscordCommand, DiscordCommands, DiscordEvent, DiscordEventName, DiscordResult, DiscordState, DocumentChange, DocumentSummary, ErrorCode, HandshakeResult, JsonValue, MenuEntry, MenuRequest, NetworkRequest, NetworkResponse, PutDocumentParams, TavoraDocument, TavoraGame, TavoraTheme, TavoraViewport, ThemeToken, } from "./protocol.js";
|
|
3
|
+
export { BLOB_ID, BLOB_TYPES, DISCORD_COMMANDS, DISCORD_EVENTS, LIMITS, PROTOCOL_VERSION, THEME_TOKENS } from "./protocol.js";
|
|
4
|
+
/** Version of this SDK package, sent to the host during the handshake. */
|
|
5
|
+
export declare const SDK_VERSION = "0.2.2";
|
|
6
|
+
/**
|
|
7
|
+
* The oldest Tavora that has everything this SDK version offers. Put it in your manifest as
|
|
8
|
+
* `minTavora`, so an older Tavora says it needs an update instead of loading a module it cannot run
|
|
9
|
+
* (the starter, `npm create @tavora/module`, does). The changelog names it for every version.
|
|
10
|
+
*/
|
|
11
|
+
export declare const MIN_TAVORA = "0.17.12";
|
|
12
|
+
export declare class TavoraError extends Error {
|
|
13
|
+
readonly code: ErrorCode;
|
|
14
|
+
/** Set for PERMISSION_DENIED. */
|
|
15
|
+
readonly capability: Capability | undefined;
|
|
16
|
+
/** Set for CONFLICT: the document's current revision. */
|
|
17
|
+
readonly revision: number | undefined;
|
|
18
|
+
constructor(payload: ErrorPayload);
|
|
19
|
+
}
|
|
20
|
+
type Unsubscribe = () => void;
|
|
21
|
+
/** Small key/value settings: at most 5 MB of keys and values together, values plain JSON. */
|
|
22
|
+
export interface KeyValueStore {
|
|
23
|
+
/** T is not checked at runtime; the host only guarantees the value is JSON. */
|
|
24
|
+
get<T = JsonValue>(key: string): Promise<T | null>;
|
|
25
|
+
set(key: string, value: JsonValue): Promise<void>;
|
|
26
|
+
delete(key: string): Promise<void>;
|
|
27
|
+
keys(): Promise<string[]>;
|
|
28
|
+
}
|
|
29
|
+
export interface TavoraSDK {
|
|
30
|
+
/** What the host reported when this module connected. */
|
|
31
|
+
readonly host: HandshakeResult;
|
|
32
|
+
game: {
|
|
33
|
+
getCurrent(): Promise<TavoraGame>;
|
|
34
|
+
};
|
|
35
|
+
/** Small key/value settings, private to this module in the current game. */
|
|
36
|
+
storage: KeyValueStore & {
|
|
37
|
+
/** The same, shared by every game: settings that should not change from game to game. */
|
|
38
|
+
app: KeyValueStore;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Named documents (builds, plans, checklists), private to this module in
|
|
42
|
+
* the current game and shared by all of its panels.
|
|
43
|
+
*/
|
|
44
|
+
documents: {
|
|
45
|
+
list(): Promise<DocumentSummary[]>;
|
|
46
|
+
get<T = JsonValue>(id: string): Promise<(TavoraDocument & {
|
|
47
|
+
data: T;
|
|
48
|
+
}) | null>;
|
|
49
|
+
/** Creates or replaces a document. Pass ifRevision to avoid overwriting another panel's change. */
|
|
50
|
+
put(document: PutDocumentParams): Promise<DocumentSummary>;
|
|
51
|
+
delete(id: string, options?: {
|
|
52
|
+
ifRevision?: number;
|
|
53
|
+
}): Promise<void>;
|
|
54
|
+
/** Fires when another panel of this module changes a document. */
|
|
55
|
+
onChange(fn: (change: DocumentChange) => void): Unsubscribe;
|
|
56
|
+
};
|
|
57
|
+
/** UI state for this one panel (selection, scroll, open tab). Cleared when the panel is closed. */
|
|
58
|
+
panel: {
|
|
59
|
+
/** Which of the manifest's `panels` this panel is, by id; null for a module without panel types (or an older Tavora). */
|
|
60
|
+
readonly type: string | null;
|
|
61
|
+
getState<T = JsonValue>(): Promise<T | null>;
|
|
62
|
+
setState(value: JsonValue | null): Promise<void>;
|
|
63
|
+
};
|
|
64
|
+
workspace: {
|
|
65
|
+
getViewport(): Promise<TavoraViewport>;
|
|
66
|
+
onResize(fn: (viewport: TavoraViewport) => void): Unsubscribe;
|
|
67
|
+
};
|
|
68
|
+
ui: {
|
|
69
|
+
getTheme(): Promise<TavoraTheme>;
|
|
70
|
+
onThemeChange(fn: (theme: TavoraTheme) => void): Unsubscribe;
|
|
71
|
+
/** The language Tavora shows its own texts in, as a code such as "en" or "de". Show yours in it, or in English when you have no translation. */
|
|
72
|
+
getLanguage(): Promise<string>;
|
|
73
|
+
/** Fires when the player changes the language. */
|
|
74
|
+
onLanguageChange(fn: (language: string) => void): Unsubscribe;
|
|
75
|
+
/**
|
|
76
|
+
* Shows a menu that Tavora draws above every panel, so it is never cut off at the panel's edge. Call it from
|
|
77
|
+
* the click, right-click or key press that asked for it: the host refuses it otherwise (PERMISSION_DENIED).
|
|
78
|
+
* Resolves with the chosen item's id, or null when the player closed the menu without choosing.
|
|
79
|
+
*/
|
|
80
|
+
showMenu(request: MenuRequest): Promise<string | null>;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Files (pictures) this module keeps in the current game, shared by all of its panels. They live
|
|
84
|
+
* in a folder Tavora manages, not in a document, so they can be large: up to 50 MB each and
|
|
85
|
+
* 500 MB together. Only PNG, JPEG, GIF and WebP are accepted, judged by the file itself.
|
|
86
|
+
*/
|
|
87
|
+
blobs: {
|
|
88
|
+
/** Stores a file and returns its id. Rejects with QUOTA_EXCEEDED, or INVALID_PARAMS for another kind of file. */
|
|
89
|
+
put(file: Blob): Promise<BlobInfo>;
|
|
90
|
+
/** The address to show a stored file from, for example as an image source. */
|
|
91
|
+
url(id: string): string;
|
|
92
|
+
list(): Promise<BlobInfo[]>;
|
|
93
|
+
usage(): Promise<BlobUsage>;
|
|
94
|
+
/** Deletes a file; nothing happens when it does not exist. */
|
|
95
|
+
delete(id: string): Promise<void>;
|
|
96
|
+
};
|
|
97
|
+
clipboard: {
|
|
98
|
+
/**
|
|
99
|
+
* Puts a picture on the system clipboard, so the player can paste it anywhere. Call it from the click or key
|
|
100
|
+
* press that asked for it: the host refuses it otherwise (PERMISSION_DENIED), and one press copies one
|
|
101
|
+
* picture. PNG only, a complete one, at most 20 MB and 16384 pixels on a side (INVALID_PARAMS, QUOTA_EXCEEDED).
|
|
102
|
+
*/
|
|
103
|
+
writeImage(png: Blob): Promise<void>;
|
|
104
|
+
};
|
|
105
|
+
links: {
|
|
106
|
+
/**
|
|
107
|
+
* Opens a web address (http or https) in a Browser panel of this game, as a new tab or in a new
|
|
108
|
+
* panel. Call it from the click or key press that asked for it: the host refuses it otherwise
|
|
109
|
+
* (PERMISSION_DENIED). The module learns nothing about the page.
|
|
110
|
+
*/
|
|
111
|
+
open(url: string): Promise<void>;
|
|
112
|
+
};
|
|
113
|
+
network: {
|
|
114
|
+
/**
|
|
115
|
+
* Makes a web request through Tavora, to a host listed in `permissions.network` that the player
|
|
116
|
+
* granted (PERMISSION_DENIED otherwise). HTTPS only, no cookies, at most 5 MB back and 15 seconds.
|
|
117
|
+
* Error statuses such as 404 are answers, not errors; NETWORK_ERROR means no answer came.
|
|
118
|
+
*/
|
|
119
|
+
fetch(url: string, init?: Omit<NetworkRequest, "url">): Promise<NetworkResponse>;
|
|
120
|
+
};
|
|
121
|
+
/** Parts of Tavora that hold a sign-in, used without seeing it. Each needs its connector in `permissions.connectors` and the player's grant (PERMISSION_DENIED otherwise). */
|
|
122
|
+
connectors: {
|
|
123
|
+
/** `ai-usage`: the usage of the Claude and Codex accounts the player set up in Settings > Integrations > AI usage. */
|
|
124
|
+
aiUsage: {
|
|
125
|
+
accounts(): Promise<AiUsageAccountInfo[]>;
|
|
126
|
+
/** Reads one account. Tavora reads each account at most once a minute; asking sooner gives the last reading. */
|
|
127
|
+
read(accountId: string): Promise<AiUsageReading>;
|
|
128
|
+
/** Fires when the player adds, renames or removes an account. */
|
|
129
|
+
onAccountsChange(fn: () => void): Unsubscribe;
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* `discord`: the Discord app on this PC, through Tavora's Discord sign-in. The connection runs while a
|
|
133
|
+
* panel has asked for state(); Tavora keeps the sign-in and sends only the commands in DISCORD_COMMANDS.
|
|
134
|
+
*/
|
|
135
|
+
discord: {
|
|
136
|
+
/** The connection now. The first call starts it for this panel. */
|
|
137
|
+
state(): Promise<DiscordState>;
|
|
138
|
+
onStateChange(fn: (state: DiscordState) => void): Unsubscribe;
|
|
139
|
+
/** Signs in, asking for approval in Discord the first time. Call it from a click or key press handler. */
|
|
140
|
+
connect(): Promise<void>;
|
|
141
|
+
/** Asks Discord to approve muting and deafening, for a sign-in that can't yet (canControl false). From a click or key press. */
|
|
142
|
+
allowControl(): Promise<void>;
|
|
143
|
+
/** Sends one command; resolves with Discord's answer as it came, or why there is none. SELECT_VOICE_CHANNEL and SET_VOICE_SETTINGS only from a click or key press. */
|
|
144
|
+
command<C extends DiscordCommand>(command: C, args: DiscordCommands[C]): Promise<DiscordResult>;
|
|
145
|
+
/** Starts sending an event to this panel (VOICE_CHANNEL_SELECT without a channel, the others for one voice channel). Kept across reconnects. */
|
|
146
|
+
subscribe(event: DiscordEventName, channelId?: string): Promise<void>;
|
|
147
|
+
unsubscribe(event: DiscordEventName, channelId?: string): Promise<void>;
|
|
148
|
+
onEvent(fn: (event: DiscordEvent) => void): Unsubscribe;
|
|
149
|
+
/** A picture from cdn.discordapp.com (an avatar or server icon address) as a small PNG data address, or null. */
|
|
150
|
+
image(url: string): Promise<string | null>;
|
|
151
|
+
};
|
|
152
|
+
};
|
|
153
|
+
lifecycle: {
|
|
154
|
+
/** The panel is hidden; pause timers and animations. Called immediately if already suspended. */
|
|
155
|
+
onSuspend(fn: () => void): Unsubscribe;
|
|
156
|
+
onResume(fn: () => void): Unsubscribe;
|
|
157
|
+
/** Best effort only: autosave must not depend on this firing. */
|
|
158
|
+
onDispose(fn: () => void): Unsubscribe;
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
export interface ConnectOptions {
|
|
162
|
+
/** How long to wait for the host to hand over a port. Default 5000 ms. */
|
|
163
|
+
connectTimeoutMs?: number;
|
|
164
|
+
/** How long to wait for each request. Default 10000 ms. */
|
|
165
|
+
requestTimeoutMs?: number;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Connects to the Tavora host. Safe to call more than once: every call
|
|
169
|
+
* returns the same connection for this window. Rejects with a TavoraError
|
|
170
|
+
* (HOST_UNAVAILABLE, INCOMPATIBLE) if the module cannot run here.
|
|
171
|
+
*/
|
|
172
|
+
export declare function connect(options?: ConnectOptions): Promise<TavoraSDK>;
|
|
173
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAOL,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,KAAK,SAAS,EACd,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,SAAS,EACd,KAAK,YAAY,EAGjB,KAAK,eAAe,EAEpB,KAAK,SAAS,EACd,KAAK,WAAW,EAGhB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,cAAc,EACpB,MAAM,eAAe,CAAC;AAEvB,YAAY,EACV,kBAAkB,EAClB,cAAc,EACd,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,UAAU,EACV,cAAc,EACd,eAAe,EACf,YAAY,EACZ,gBAAgB,EAChB,aAAa,EACb,YAAY,EACZ,cAAc,EACd,eAAe,EACf,SAAS,EACT,eAAe,EACf,SAAS,EACT,SAAS,EACT,WAAW,EACX,cAAc,EACd,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,UAAU,EACV,WAAW,EACX,cAAc,EACd,UAAU,GACX,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAK9H,0EAA0E;AAC1E,eAAO,MAAM,WAAW,UAAU,CAAC;AAEnC;;;;GAIG;AACH,eAAO,MAAM,UAAU,YAAY,CAAC;AAEpC,qBAAa,WAAY,SAAQ,KAAK;IACpC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,iCAAiC;IACjC,QAAQ,CAAC,UAAU,EAAE,UAAU,GAAG,SAAS,CAAC;IAC5C,yDAAyD;IACzD,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;gBAE1B,OAAO,EAAE,YAAY;CAOlC;AAED,KAAK,WAAW,GAAG,MAAM,IAAI,CAAC;AAE9B,6FAA6F;AAC7F,MAAM,WAAW,aAAa;IAC5B,+EAA+E;IAC/E,GAAG,CAAC,CAAC,GAAG,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IACnD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CAC3B;AAED,MAAM,WAAW,SAAS;IACxB,yDAAyD;IACzD,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,IAAI,EAAE;QACJ,UAAU,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;KACnC,CAAC;IACF,4EAA4E;IAC5E,OAAO,EAAE,aAAa,GAAG;QACvB,yFAAyF;QACzF,GAAG,EAAE,aAAa,CAAC;KACpB,CAAC;IACF;;;OAGG;IACH,SAAS,EAAE;QACT,IAAI,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;QACnC,GAAG,CAAC,CAAC,GAAG,SAAS,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,cAAc,GAAG;YAAE,IAAI,EAAE,CAAC,CAAA;SAAE,CAAC,GAAG,IAAI,CAAC,CAAC;QAC/E,mGAAmG;QACnG,GAAG,CAAC,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;QAC3D,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;YAAE,UAAU,CAAC,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QACrE,kEAAkE;QAClE,QAAQ,CAAC,EAAE,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,GAAG,WAAW,CAAC;KAC7D,CAAC;IACF,mGAAmG;IACnG,KAAK,EAAE;QACL,yHAAyH;QACzH,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QAC7B,QAAQ,CAAC,CAAC,GAAG,SAAS,KAAK,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QAC7C,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KAClD,CAAC;IACF,SAAS,EAAE;QACT,WAAW,IAAI,OAAO,CAAC,cAAc,CAAC,CAAC;QACvC,QAAQ,CAAC,EAAE,EAAE,CAAC,QAAQ,EAAE,cAAc,KAAK,IAAI,GAAG,WAAW,CAAC;KAC/D,CAAC;IACF,EAAE,EAAE;QACF,QAAQ,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC;QACjC,aAAa,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,GAAG,WAAW,CAAC;QAC7D,gJAAgJ;QAChJ,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;QAC/B,kDAAkD;QAClD,gBAAgB,CAAC,EAAE,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,GAAG,WAAW,CAAC;QAC9D;;;;WAIG;QACH,QAAQ,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;KACxD,CAAC;IACF;;;;OAIG;IACH,KAAK,EAAE;QACL,iHAAiH;QACjH,GAAG,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;QACnC,8EAA8E;QAC9E,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;QACxB,IAAI,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC5B,KAAK,IAAI,OAAO,CAAC,SAAS,CAAC,CAAC;QAC5B,8DAA8D;QAC9D,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KACnC,CAAC;IACF,SAAS,EAAE;QACT;;;;WAIG;QACH,UAAU,CAAC,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KACtC,CAAC;IACF,KAAK,EAAE;QACL;;;;WAIG;QACH,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KAClC,CAAC;IACF,OAAO,EAAE;QACP;;;;WAIG;QACH,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,cAAc,EAAE,KAAK,CAAC,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;KAClF,CAAC;IACF,8KAA8K;IAC9K,UAAU,EAAE;QACV,sHAAsH;QACtH,OAAO,EAAE;YACP,QAAQ,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAAC;YAC1C,gHAAgH;YAChH,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;YACjD,iEAAiE;YACjE,gBAAgB,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,WAAW,CAAC;SAC/C,CAAC;QACF;;;WAGG;QACH,OAAO,EAAE;YACP,mEAAmE;YACnE,KAAK,IAAI,OAAO,CAAC,YAAY,CAAC,CAAC;YAC/B,aAAa,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GAAG,WAAW,CAAC;YAC9D,0GAA0G;YAC1G,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;YACzB,gIAAgI;YAChI,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;YAC9B,sKAAsK;YACtK,OAAO,CAAC,CAAC,SAAS,cAAc,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;YAChG,gJAAgJ;YAChJ,SAAS,CAAC,KAAK,EAAE,gBAAgB,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;YACtE,WAAW,CAAC,KAAK,EAAE,gBAAgB,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;YACxE,OAAO,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GAAG,WAAW,CAAC;YACxD,iHAAiH;YACjH,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;SAC5C,CAAC;KACH,CAAC;IACF,SAAS,EAAE;QACT,iGAAiG;QACjG,SAAS,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,WAAW,CAAC;QACvC,QAAQ,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,WAAW,CAAC;QACtC,iEAAiE;QACjE,SAAS,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,WAAW,CAAC;KACxC,CAAC;CACH;AAED,MAAM,WAAW,cAAc;IAC7B,0EAA0E;IAC1E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,2DAA2D;IAC3D,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAID;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,SAAS,CAAC,CAQxE"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
// @tavora/sdk: thin typed client for modules running inside Tavora Desktop.
|
|
2
|
+
// The host enforces every check; this package only marshals typed calls.
|
|
3
|
+
// Protocol reference: apps/docs/reference/protocol.md.
|
|
4
|
+
import { BLOB_PATH, CLIPBOARD_PATH, HELLO_MESSAGE, LIMITS, PORT_MESSAGE, PROTOCOL_VERSION, } from "./protocol.js";
|
|
5
|
+
export { BLOB_ID, BLOB_TYPES, DISCORD_COMMANDS, DISCORD_EVENTS, LIMITS, PROTOCOL_VERSION, THEME_TOKENS } from "./protocol.js";
|
|
6
|
+
/** How long a menu may stay open; the host closes it itself when its panel goes. */
|
|
7
|
+
const MENU_TIMEOUT_MS = 60 * 60_000;
|
|
8
|
+
/** Version of this SDK package, sent to the host during the handshake. */
|
|
9
|
+
export const SDK_VERSION = "0.2.2";
|
|
10
|
+
/**
|
|
11
|
+
* The oldest Tavora that has everything this SDK version offers. Put it in your manifest as
|
|
12
|
+
* `minTavora`, so an older Tavora says it needs an update instead of loading a module it cannot run
|
|
13
|
+
* (the starter, `npm create @tavora/module`, does). The changelog names it for every version.
|
|
14
|
+
*/
|
|
15
|
+
export const MIN_TAVORA = "0.17.12";
|
|
16
|
+
export class TavoraError extends Error {
|
|
17
|
+
code;
|
|
18
|
+
/** Set for PERMISSION_DENIED. */
|
|
19
|
+
capability;
|
|
20
|
+
/** Set for CONFLICT: the document's current revision. */
|
|
21
|
+
revision;
|
|
22
|
+
constructor(payload) {
|
|
23
|
+
super(payload.message);
|
|
24
|
+
this.name = "TavoraError";
|
|
25
|
+
this.code = payload.code;
|
|
26
|
+
this.capability = payload.capability;
|
|
27
|
+
this.revision = payload.revision;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
let connection;
|
|
31
|
+
/**
|
|
32
|
+
* Connects to the Tavora host. Safe to call more than once: every call
|
|
33
|
+
* returns the same connection for this window. Rejects with a TavoraError
|
|
34
|
+
* (HOST_UNAVAILABLE, INCOMPATIBLE) if the module cannot run here.
|
|
35
|
+
*/
|
|
36
|
+
export function connect(options = {}) {
|
|
37
|
+
connection ??= openPort(options.connectTimeoutMs ?? 5000)
|
|
38
|
+
.then((port) => createClient(port, options.requestTimeoutMs ?? 10_000))
|
|
39
|
+
.catch((error) => {
|
|
40
|
+
connection = undefined;
|
|
41
|
+
throw error;
|
|
42
|
+
});
|
|
43
|
+
return connection;
|
|
44
|
+
}
|
|
45
|
+
function openPort(timeoutMs) {
|
|
46
|
+
return new Promise((resolve, reject) => {
|
|
47
|
+
const timer = setTimeout(() => {
|
|
48
|
+
window.removeEventListener("message", onMessage);
|
|
49
|
+
reject(new TavoraError({
|
|
50
|
+
code: "HOST_UNAVAILABLE",
|
|
51
|
+
message: "Tavora host did not respond. Is this module running inside Tavora?",
|
|
52
|
+
}));
|
|
53
|
+
}, timeoutMs);
|
|
54
|
+
function onMessage(event) {
|
|
55
|
+
if (event.source !== window || event.data?.type !== PORT_MESSAGE || !event.ports[0])
|
|
56
|
+
return;
|
|
57
|
+
clearTimeout(timer);
|
|
58
|
+
window.removeEventListener("message", onMessage);
|
|
59
|
+
resolve(event.ports[0]);
|
|
60
|
+
}
|
|
61
|
+
window.addEventListener("message", onMessage);
|
|
62
|
+
window.postMessage({ type: HELLO_MESSAGE, v: PROTOCOL_VERSION }, window.location.origin);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
async function createClient(port, requestTimeoutMs) {
|
|
66
|
+
let nextId = 1;
|
|
67
|
+
const pending = new Map();
|
|
68
|
+
const listeners = new Map();
|
|
69
|
+
let suspended = false;
|
|
70
|
+
port.onmessage = (event) => {
|
|
71
|
+
const message = event.data;
|
|
72
|
+
if (typeof message !== "object" || message === null || message.v !== PROTOCOL_VERSION)
|
|
73
|
+
return;
|
|
74
|
+
if ("event" in message) {
|
|
75
|
+
if (message.event === "lifecycle.suspend")
|
|
76
|
+
suspended = true;
|
|
77
|
+
if (message.event === "lifecycle.resume")
|
|
78
|
+
suspended = false;
|
|
79
|
+
for (const fn of listeners.get(message.event) ?? []) {
|
|
80
|
+
try {
|
|
81
|
+
fn(message.data);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
console.error(`[tavora] ${message.event} listener failed`, error);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const entry = pending.get(message.id);
|
|
90
|
+
if (!entry)
|
|
91
|
+
return;
|
|
92
|
+
pending.delete(message.id);
|
|
93
|
+
clearTimeout(entry.timer);
|
|
94
|
+
if (message.ok)
|
|
95
|
+
entry.resolve(message.result);
|
|
96
|
+
else
|
|
97
|
+
entry.reject(new TavoraError(message.error));
|
|
98
|
+
};
|
|
99
|
+
port.start();
|
|
100
|
+
function call(method, params, timeoutMs = requestTimeoutMs) {
|
|
101
|
+
const id = nextId++;
|
|
102
|
+
return new Promise((resolve, reject) => {
|
|
103
|
+
const timer = setTimeout(() => {
|
|
104
|
+
pending.delete(id);
|
|
105
|
+
reject(new TavoraError({ code: "TIMEOUT", message: `${method} timed out` }));
|
|
106
|
+
}, Math.max(timeoutMs, requestTimeoutMs));
|
|
107
|
+
pending.set(id, { resolve: resolve, reject, timer });
|
|
108
|
+
port.postMessage({ v: PROTOCOL_VERSION, id, method, params });
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
function on(event, fn) {
|
|
112
|
+
let set = listeners.get(event);
|
|
113
|
+
if (!set)
|
|
114
|
+
listeners.set(event, (set = new Set()));
|
|
115
|
+
const listener = fn;
|
|
116
|
+
set.add(listener);
|
|
117
|
+
// Initial suspension can arrive before connect() callers register listeners.
|
|
118
|
+
if (event === "lifecycle.suspend" && suspended) {
|
|
119
|
+
try {
|
|
120
|
+
listener(null);
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
console.error(`[tavora] ${event} listener failed`, error);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return () => set.delete(listener);
|
|
127
|
+
}
|
|
128
|
+
const host = await call("bridge.handshake", { protocol: PROTOCOL_VERSION, sdkVersion: SDK_VERSION });
|
|
129
|
+
return {
|
|
130
|
+
host,
|
|
131
|
+
game: {
|
|
132
|
+
getCurrent: () => call("game.getCurrent"),
|
|
133
|
+
},
|
|
134
|
+
storage: {
|
|
135
|
+
get: (key) => call("storage.get", { key }),
|
|
136
|
+
set: async (key, value) => {
|
|
137
|
+
await call("storage.set", { key, value });
|
|
138
|
+
},
|
|
139
|
+
delete: async (key) => {
|
|
140
|
+
await call("storage.delete", { key });
|
|
141
|
+
},
|
|
142
|
+
keys: () => call("storage.keys"),
|
|
143
|
+
app: {
|
|
144
|
+
get: (key) => call("storage.app.get", { key }),
|
|
145
|
+
set: async (key, value) => {
|
|
146
|
+
await call("storage.app.set", { key, value });
|
|
147
|
+
},
|
|
148
|
+
delete: async (key) => {
|
|
149
|
+
await call("storage.app.delete", { key });
|
|
150
|
+
},
|
|
151
|
+
keys: () => call("storage.app.keys"),
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
documents: {
|
|
155
|
+
list: () => call("documents.list"),
|
|
156
|
+
get: (id) => call("documents.get", { id }),
|
|
157
|
+
put: (document) => call("documents.put", document),
|
|
158
|
+
delete: async (id, options = {}) => {
|
|
159
|
+
await call("documents.delete", { id, ...options });
|
|
160
|
+
},
|
|
161
|
+
onChange: (fn) => on("documents.changed", fn),
|
|
162
|
+
},
|
|
163
|
+
panel: {
|
|
164
|
+
type: host.panel?.type ?? null,
|
|
165
|
+
getState: () => call("panel.getState"),
|
|
166
|
+
setState: async (value) => {
|
|
167
|
+
await call("panel.setState", { value });
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
workspace: {
|
|
171
|
+
getViewport: () => call("workspace.getViewport"),
|
|
172
|
+
onResize: (fn) => on("workspace.resize", fn),
|
|
173
|
+
},
|
|
174
|
+
ui: {
|
|
175
|
+
getTheme: () => call("ui.getTheme"),
|
|
176
|
+
onThemeChange: (fn) => on("ui.theme", fn),
|
|
177
|
+
getLanguage: () => call("ui.getLanguage"),
|
|
178
|
+
onLanguageChange: (fn) => on("ui.language", fn),
|
|
179
|
+
// The player may take their time choosing.
|
|
180
|
+
showMenu: (request) => call("ui.showMenu", request, MENU_TIMEOUT_MS),
|
|
181
|
+
},
|
|
182
|
+
blobs: {
|
|
183
|
+
put: async (file) => {
|
|
184
|
+
let response;
|
|
185
|
+
try {
|
|
186
|
+
response = await fetch(new URL(BLOB_PATH, window.location.href), {
|
|
187
|
+
method: "POST",
|
|
188
|
+
body: file,
|
|
189
|
+
headers: { "Content-Type": file.type || "application/octet-stream" },
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
throw new TavoraError({ code: "INTERNAL", message: "could not store the file" });
|
|
194
|
+
}
|
|
195
|
+
const body = (await response.json().catch(() => null));
|
|
196
|
+
if (!response.ok || !body || body.error) {
|
|
197
|
+
throw new TavoraError(body?.error ?? { code: "INTERNAL", message: `could not store the file (${response.status})` });
|
|
198
|
+
}
|
|
199
|
+
return body;
|
|
200
|
+
},
|
|
201
|
+
url: (id) => new URL(`${BLOB_PATH}${encodeURIComponent(id)}`, window.location.href).href,
|
|
202
|
+
list: () => call("blobs.list"),
|
|
203
|
+
usage: () => call("blobs.usage"),
|
|
204
|
+
delete: async (id) => {
|
|
205
|
+
await call("blobs.delete", { id });
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
clipboard: {
|
|
209
|
+
writeImage: async (png) => {
|
|
210
|
+
if (png.type !== "image/png")
|
|
211
|
+
throw new TavoraError({ code: "INVALID_PARAMS", message: "only a PNG picture can be copied" });
|
|
212
|
+
let response;
|
|
213
|
+
try {
|
|
214
|
+
response = await fetch(new URL(CLIPBOARD_PATH, window.location.href), { method: "POST", body: png, headers: { "Content-Type": "image/png" } });
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
throw new TavoraError({ code: "INTERNAL", message: "could not copy the picture" });
|
|
218
|
+
}
|
|
219
|
+
if (response.ok)
|
|
220
|
+
return;
|
|
221
|
+
const body = (await response.json().catch(() => null));
|
|
222
|
+
throw new TavoraError(body?.error ?? { code: "INTERNAL", message: `could not copy the picture (${response.status})` });
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
links: {
|
|
226
|
+
open: async (url) => {
|
|
227
|
+
await call("links.open", { url });
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
network: {
|
|
231
|
+
// The host gives up after its own limit; this waits a little longer so its answer arrives first.
|
|
232
|
+
fetch: (url, init = {}) => call("network.fetch", { ...init, url }, LIMITS.networkTimeoutMs + 5000),
|
|
233
|
+
},
|
|
234
|
+
connectors: {
|
|
235
|
+
aiUsage: {
|
|
236
|
+
accounts: () => call("aiUsage.accounts"),
|
|
237
|
+
read: (accountId) => call("aiUsage.read", { accountId }, LIMITS.aiUsageTimeoutMs + 5000),
|
|
238
|
+
onAccountsChange: (fn) => on("aiUsage.accountsChanged", () => fn()),
|
|
239
|
+
},
|
|
240
|
+
discord: {
|
|
241
|
+
state: () => call("discord.state"),
|
|
242
|
+
onStateChange: (fn) => on("discord.state", fn),
|
|
243
|
+
connect: async () => {
|
|
244
|
+
await call("discord.connect");
|
|
245
|
+
},
|
|
246
|
+
allowControl: async () => {
|
|
247
|
+
await call("discord.allowControl");
|
|
248
|
+
},
|
|
249
|
+
command: (command, args) => call("discord.command", { command, args }, LIMITS.discordTimeoutMs + 5000),
|
|
250
|
+
subscribe: async (event, channelId) => {
|
|
251
|
+
await call("discord.subscribe", channelId === undefined ? { event } : { event, channelId });
|
|
252
|
+
},
|
|
253
|
+
unsubscribe: async (event, channelId) => {
|
|
254
|
+
await call("discord.unsubscribe", channelId === undefined ? { event } : { event, channelId });
|
|
255
|
+
},
|
|
256
|
+
onEvent: (fn) => on("discord.event", fn),
|
|
257
|
+
image: (url) => call("discord.image", { url }),
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
lifecycle: {
|
|
261
|
+
onSuspend: (fn) => on("lifecycle.suspend", fn),
|
|
262
|
+
onResume: (fn) => on("lifecycle.resume", fn),
|
|
263
|
+
onDispose: (fn) => on("lifecycle.dispose", fn),
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|