import { __classPrivateFieldGet, __classPrivateFieldSet } from './external/tslib/tslib.es6.js'; // Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT var _Channel_onmessage, _Channel_nextMessageIndex, _Channel_pendingMessages, _Channel_messageEndIndex, _Resource_rid; /** * Invoke your custom commands. * * This package is also accessible with `window.__TAURI__.core` when [`app.withGlobalTauri`](https://v2.tauri.app/reference/config/#withglobaltauri) in `tauri.conf.json` is set to `true`. * @module */ /** * A key to be used to implement a special function * on your types that define how your type should be serialized * when passing across the IPC. * @example * Given a type in Rust that looks like this * ```rs * #[derive(serde::Serialize, serde::Deserialize) * enum UserId { * String(String), * Number(u32), * } * ``` * `UserId::String("id")` would be serialized into `{ String: "id" }` * and so we need to pass the same structure back to Rust * ```ts * import { SERIALIZE_TO_IPC_FN } from "@tauri-apps/api/core" * * class UserIdString { * id * constructor(id) { * this.id = id * } * * [SERIALIZE_TO_IPC_FN]() { * return { String: this.id } * } * } * * class UserIdNumber { * id * constructor(id) { * this.id = id * } * * [SERIALIZE_TO_IPC_FN]() { * return { Number: this.id } * } * } * * type UserId = UserIdString | UserIdNumber * ``` * */ // if this value changes, make sure to update it in: // 1. ipc.js // 2. process-ipc-message-fn.js const SERIALIZE_TO_IPC_FN = '__TAURI_TO_IPC_KEY__'; /** * Transforms a callback function to a string identifier that can be passed to the backend. * The backend uses the identifier to `eval()` the callback. * * @return A unique identifier associated with the callback function. * * @since 1.0.0 */ function transformCallback(callback, once = false) { return window.__TAURI_INTERNALS__.transformCallback(callback, once); } class Channel { constructor(onmessage) { _Channel_onmessage.set(this, void 0); // the index is used as a mechanism to preserve message order _Channel_nextMessageIndex.set(this, 0); _Channel_pendingMessages.set(this, []); _Channel_messageEndIndex.set(this, void 0); __classPrivateFieldSet(this, _Channel_onmessage, onmessage || (() => { }), "f"); this.id = transformCallback((rawMessage) => { const index = rawMessage.index; if ('end' in rawMessage) { if (index == __classPrivateFieldGet(this, _Channel_nextMessageIndex, "f")) { this.cleanupCallback(); } else { __classPrivateFieldSet(this, _Channel_messageEndIndex, index, "f"); } return; } const message = rawMessage.message; // Process the message if we're at the right order if (index == __classPrivateFieldGet(this, _Channel_nextMessageIndex, "f")) { __classPrivateFieldGet(this, _Channel_onmessage, "f").call(this, message); __classPrivateFieldSet(this, _Channel_nextMessageIndex, __classPrivateFieldGet(this, _Channel_nextMessageIndex, "f") + 1, "f"); // process pending messages while (__classPrivateFieldGet(this, _Channel_nextMessageIndex, "f") in __classPrivateFieldGet(this, _Channel_pendingMessages, "f")) { const message = __classPrivateFieldGet(this, _Channel_pendingMessages, "f")[__classPrivateFieldGet(this, _Channel_nextMessageIndex, "f")]; __classPrivateFieldGet(this, _Channel_onmessage, "f").call(this, message); // eslint-disable-next-line @typescript-eslint/no-array-delete delete __classPrivateFieldGet(this, _Channel_pendingMessages, "f")[__classPrivateFieldGet(this, _Channel_nextMessageIndex, "f")]; __classPrivateFieldSet(this, _Channel_nextMessageIndex, __classPrivateFieldGet(this, _Channel_nextMessageIndex, "f") + 1, "f"); } if (__classPrivateFieldGet(this, _Channel_nextMessageIndex, "f") === __classPrivateFieldGet(this, _Channel_messageEndIndex, "f")) { this.cleanupCallback(); } } // Queue the message if we're not else { // eslint-disable-next-line security/detect-object-injection __classPrivateFieldGet(this, _Channel_pendingMessages, "f")[index] = message; } }); } cleanupCallback() { Reflect.deleteProperty(window, `_${this.id}`); } set onmessage(handler) { __classPrivateFieldSet(this, _Channel_onmessage, handler, "f"); } get onmessage() { return __classPrivateFieldGet(this, _Channel_onmessage, "f"); } [(_Channel_onmessage = new WeakMap(), _Channel_nextMessageIndex = new WeakMap(), _Channel_pendingMessages = new WeakMap(), _Channel_messageEndIndex = new WeakMap(), SERIALIZE_TO_IPC_FN)]() { return `__CHANNEL__:${this.id}`; } toJSON() { // eslint-disable-next-line security/detect-object-injection return this[SERIALIZE_TO_IPC_FN](); } } class PluginListener { constructor(plugin, event, channelId) { this.plugin = plugin; this.event = event; this.channelId = channelId; } async unregister() { return invoke(`plugin:${this.plugin}|remove_listener`, { event: this.event, channelId: this.channelId }); } } /** * Adds a listener to a plugin event. * * @returns The listener object to stop listening to the events. * * @since 2.0.0 */ async function addPluginListener(plugin, event, cb) { const handler = new Channel(cb); return invoke(`plugin:${plugin}|registerListener`, { event, handler }).then(() => new PluginListener(plugin, event, handler.id)); } /** * Get permission state for a plugin. * * This should be used by plugin authors to wrap their actual implementation. */ async function checkPermissions(plugin) { return invoke(`plugin:${plugin}|check_permissions`); } /** * Request permissions. * * This should be used by plugin authors to wrap their actual implementation. */ async function requestPermissions(plugin) { return invoke(`plugin:${plugin}|request_permissions`); } /** * Sends a message to the backend. * @example * ```typescript * import { invoke } from '@tauri-apps/api/core'; * await invoke('login', { user: 'tauri', password: 'poiwe3h4r5ip3yrhtew9ty' }); * ``` * * @param cmd The command name. * @param args The optional arguments to pass to the command. * @param options The request options. * @return A promise resolving or rejecting to the backend response. * * @since 1.0.0 */ async function invoke(cmd, args = {}, options) { return window.__TAURI_INTERNALS__.invoke(cmd, args, options); } /** * Convert a device file path to an URL that can be loaded by the webview. * Note that `asset:` and `http://asset.localhost` must be added to [`app.security.csp`](https://v2.tauri.app/reference/config/#csp-1) in `tauri.conf.json`. * Example CSP value: `"csp": "default-src 'self' ipc: http://ipc.localhost; img-src 'self' asset: http://asset.localhost"` to use the asset protocol on image sources. * * Additionally, `"enable" : "true"` must be added to [`app.security.assetProtocol`](https://v2.tauri.app/reference/config/#assetprotocolconfig) * in `tauri.conf.json` and its access scope must be defined on the `scope` array on the same `assetProtocol` object. * * @param filePath The file path. * @param protocol The protocol to use. Defaults to `asset`. You only need to set this when using a custom protocol. * @example * ```typescript * import { appDataDir, join } from '@tauri-apps/api/path'; * import { convertFileSrc } from '@tauri-apps/api/core'; * const appDataDirPath = await appDataDir(); * const filePath = await join(appDataDirPath, 'assets/video.mp4'); * const assetUrl = convertFileSrc(filePath); * * const video = document.getElementById('my-video'); * const source = document.createElement('source'); * source.type = 'video/mp4'; * source.src = assetUrl; * video.appendChild(source); * video.load(); * ``` * * @return the URL that can be used as source on the webview. * * @since 1.0.0 */ function convertFileSrc(filePath, protocol = 'asset') { return window.__TAURI_INTERNALS__.convertFileSrc(filePath, protocol); } /** * A rust-backed resource stored through `tauri::Manager::resources_table` API. * * The resource lives in the main process and does not exist * in the Javascript world, and thus will not be cleaned up automatiacally * except on application exit. If you want to clean it up early, call {@linkcode Resource.close} * * @example * ```typescript * import { Resource, invoke } from '@tauri-apps/api/core'; * export class DatabaseHandle extends Resource { * static async open(path: string): Promise { * const rid: number = await invoke('open_db', { path }); * return new DatabaseHandle(rid); * } * * async execute(sql: string): Promise { * await invoke('execute_sql', { rid: this.rid, sql }); * } * } * ``` */ class Resource { get rid() { return __classPrivateFieldGet(this, _Resource_rid, "f"); } constructor(rid) { _Resource_rid.set(this, void 0); __classPrivateFieldSet(this, _Resource_rid, rid, "f"); } /** * Destroys and cleans up this resource from memory. * **You should not call any method on this object anymore and should drop any reference to it.** */ async close() { return invoke('plugin:resources|close', { rid: this.rid }); } } _Resource_rid = new WeakMap(); function isTauri() { // eslint-disable-next-line @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-member-access return !!(globalThis || window).isTauri; } export { Channel, PluginListener, Resource, SERIALIZE_TO_IPC_FN, addPluginListener, checkPermissions, convertFileSrc, invoke, isTauri, requestPermissions, transformCallback };