refactor(tauri.js) rewrite API module in TypeScript, closes #679 #435 (#703)

Co-authored-by: Quentin Goinaud <armaldio@gmail.com>
Co-authored-by: Lucas Fernandes Nogueira <lucasfernandesnog@gmail.com>
Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
This commit is contained in:
nothingismagick 2020-06-27 17:20:00 +02:00 committed by GitHub
parent 14cddd404c
commit 2681ad361b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
73 changed files with 1929 additions and 3651 deletions

5
.changes/ts-api.md Normal file
View File

@ -0,0 +1,5 @@
---
"tauri.js": minor
---
Create UMD, ESM and CJS artifacts for the JavaScript API entry point from TS source using rollup.

6
.changes/window-tauri.md Normal file
View File

@ -0,0 +1,6 @@
---
"tauri.js": minor
---
Renaming `window.tauri` to `window.__TAURI__`, closing #435.
The `__TAURI__` object now follows the TypeScript API structure (e.g. `window.__TAURI__.readTextFile` is now `window.__TAURI__.fs.readTextFile`).

View File

@ -87,7 +87,7 @@ jobs:
- name: install cli deps via yarn
working-directory: ./cli/tauri.js
run: yarn
- name: build cli
- name: build cli & api
working-directory: ./cli/tauri.js
run: yarn build
- name: cache node modules

1
cli/deno Submodule

@ -0,0 +1 @@
Subproject commit 55fd9104fa4da3b7afb96a16bb1ed8378b028a5a

View File

@ -63,6 +63,9 @@ debug.log
package-lock.json
.vscode/settings.json
#api
api/*
# Tauri output
bundle.json
config.json

View File

@ -0,0 +1,23 @@
import 'regenerator-runtime/runtime'
import * as cli from './cli'
import * as dialog from './dialog'
import * as event from './event'
import * as fs from './fs'
import http from './http'
import * as process from './process'
import * as tauri from './tauri'
import * as window from './window'
import * as notification from './notification'
export {
cli,
dialog,
event,
fs,
http,
process,
tauri,
window,
notification
}

View File

@ -0,0 +1,15 @@
import { CliMatches } from './types/cli'
import { promisified } from './tauri'
/**
* gets the CLI matches
*/
async function getMatches(): Promise<CliMatches> {
return await promisified<CliMatches>({
cmd: 'cliMatches'
})
}
export {
getMatches
}

View File

@ -0,0 +1,47 @@
import { OpenDialogOptions, SaveDialogOptions } from './types/dialog'
import { promisified } from './tauri'
/**
* @name openDialog
* @description Open a file/directory selection dialog
* @param [options]
* @param [options.filter]
* @param [options.defaultPath]
* @param [options.multiple=false]
* @param [options.directory=false]
* @returns promise resolving to the select path(s)
*/
async function open(options: OpenDialogOptions = {}): Promise<String | String[]> {
if (typeof options === 'object') {
Object.freeze(options)
}
return await promisified({
cmd: 'openDialog',
options
})
}
/**
* @name save
* @description Open a file/directory save dialog
* @param [options]
* @param [options.filter]
* @param [options.defaultPath]
* @returns promise resolving to the select path
*/
async function save(options: SaveDialogOptions = {}): Promise<String> {
if (typeof options === 'object') {
Object.freeze(options)
}
return await promisified({
cmd: 'saveDialog',
options
})
}
export {
open,
save
}

View File

@ -0,0 +1,36 @@
import { invoke, transformCallback } from './tauri'
import { EventCallback } from './types/event'
/**
* listen to an event from the backend
*
* @param event the event name
* @param handler the event handler callback
*/
function listen(event: string, handler: EventCallback, once = false): void {
invoke({
cmd: 'listen',
event,
handler: transformCallback(handler, once),
once
})
}
/**
* emits an event to the backend
*
* @param event the event name
* @param [payload] the event payload
*/
function emit(event: string, payload?: string): void {
invoke({
cmd: 'emit',
event,
payload
})
}
export {
listen,
emit
}

239
cli/tauri.js/api-src/fs.ts Normal file
View File

@ -0,0 +1,239 @@
import { promisified } from './tauri'
import { BaseDirectory, FsOptions, FsTextFileOption, FsBinaryFileOption, FileEntry } from './types/fs'
/**
* reads a file as text
*
* @param filePath path to the file
* @param [options] configuration object
* @param [options.dir] base directory
* @return
*/
async function readTextFile(filePath: string, options: FsOptions = {}): Promise<string> {
return await promisified({
cmd: 'readTextFile',
path: filePath,
options
})
}
/**
* reads a file as binary
*
* @param filePath path to the file
* @param {Object} [options] configuration object
* @param {BaseDirectory} [options.dir] base directory
* @return {Promise<int[]>}
*/
async function readBinaryFile(filePath: string, options: FsOptions = {}): Promise<string> {
return await promisified({
cmd: 'readBinaryFile',
path: filePath,
options
})
}
/**
* writes a text file
*
* @param file
* @param file.path path of the file
* @param file.contents contents of the file
* @param [options] configuration object
* @param [options.dir] base directory
* @return
*/
async function writeFile(file: FsTextFileOption, options: FsOptions = {}): Promise<void> {
if (typeof options === 'object') {
Object.freeze(options)
}
if (typeof file === 'object') {
Object.freeze(file)
}
return await promisified({
cmd: 'writeFile',
file: file.path,
contents: file.contents,
options
})
}
const CHUNK_SIZE = 65536
/**
* convert an Uint8Array to ascii string
*
* @param arr
* @return ASCII string
*/
function uint8ArrayToString(arr: Uint8Array): string {
if (arr.length < CHUNK_SIZE) {
return String.fromCharCode.apply(null, Array.from(arr))
}
let result = ''
const arrLen = arr.length
for (let i = 0; i < arrLen; i++) {
const chunk = arr.subarray(i * CHUNK_SIZE, (i + 1) * CHUNK_SIZE)
result += String.fromCharCode.apply(null, Array.from(chunk))
}
return result
}
/**
* convert an ArrayBuffer to base64 encoded string
*
* @param buffer
* @return base64 encoded string
*/
function arrayBufferToBase64(buffer: ArrayBuffer): string {
const str = uint8ArrayToString(new Uint8Array(buffer))
return btoa(str)
}
/**
* writes a binary file
*
* @param file
* @param file.path path of the file
* @param file.contents contents of the file
* @param [options] configuration object
* @param [options.dir] base directory
* @return
*/
async function writeBinaryFile(file: FsBinaryFileOption, options: FsOptions = {}): Promise<void> {
if (typeof options === 'object') {
Object.freeze(options)
}
if (typeof file === 'object') {
Object.freeze(file)
}
return await promisified({
cmd: 'writeFile',
file: file.path,
contents: arrayBufferToBase64(file.contents),
options
})
}
/**
* list directory files
*
* @param dir path to the directory to read
* @param [options] configuration object
* @param [options.recursive] whether to list dirs recursively or not
* @param [options.dir] base directory
* @return
*/
async function readDir(dir: string, options: FsOptions = {}): Promise<FileEntry[]> {
return await promisified({
cmd: 'readDir',
path: dir,
options
})
}
/**
* Creates a directory
* If one of the path's parent components doesn't exist
* and the `recursive` option isn't set to true, it will be rejected
*
* @param dir path to the directory to create
* @param [options] configuration object
* @param [options.recursive] whether to create the directory's parent components or not
* @param [options.dir] base directory
* @return
*/
async function createDir(dir: string, options: FsOptions = {}): Promise<void> {
return await promisified({
cmd: 'createDir',
path: dir,
options
})
}
/**
* Removes a directory
* If the directory is not empty and the `recursive` option isn't set to true, it will be rejected
*
* @param dir path to the directory to remove
* @param [options] configuration object
* @param [options.recursive] whether to remove all of the directory's content or not
* @param [options.dir] base directory
* @return
*/
async function removeDir(dir: string, options: FsOptions = {}): Promise<void> {
return await promisified({
cmd: 'removeDir',
path: dir,
options
})
}
/**
* Copy file
*
* @param source
* @param destination
* @param [options] configuration object
* @param [options.dir] base directory
* @return
*/
async function copyFile(source: string, destination: string, options: FsOptions = {}): Promise<void> {
return await promisified({
cmd: 'copyFile',
source,
destination,
options
})
}
/**
* Removes a file
*
* @param file path to the file to remove
* @param [options] configuration object
* @param [options.dir] base directory
* @return
*/
async function removeFile(file: string, options: FsOptions = {}): Promise<void> {
return await promisified({
cmd: 'removeFile',
path: file,
options: options
})
}
/**
* Renames a file
*
* @param oldPath
* @param newPath
* @param [options] configuration object
* @param [options.dir] base directory
* @return
*/
async function renameFile(oldPath: string, newPath: string, options: FsOptions = {}): Promise<void> {
return await promisified({
cmd: 'renameFile',
oldPath,
newPath,
options
})
}
export {
BaseDirectory as Dir,
readTextFile,
readBinaryFile,
writeFile,
writeBinaryFile,
readDir,
createDir,
removeDir,
copyFile,
removeFile,
renameFile
}

View File

@ -0,0 +1,111 @@
import { promisified } from './tauri'
import { HttpOptions, Body, BodyType, ResponseType, PartialOptions } from './types/http'
/**
* makes a HTTP request
*
* @param options request options
*
* @return promise resolving to the response
*/
async function request<T>(options: HttpOptions): Promise<T> {
return await promisified({
cmd: 'httpRequest',
options: options
})
}
/**
* makes a GET request
*
* @param url request URL
* @param options request options
*
* @return promise resolving to the response
*/
async function get<T>(url: string, options: PartialOptions): Promise<T> {
return await request({
method: 'GET',
url,
...options
})
}
/**
* makes a POST request
*
* @param url request URL
* @param body request body
* @param options request options
*
* @return promise resolving to the response
*/
async function post<T>(url: string, body: Body, options: PartialOptions): Promise<T> {
return await request({
method: 'POST',
url,
body,
...options
})
}
/**
* makes a PUT request
*
* @param url request URL
* @param body request body
* @param options request options
*
* @return promise resolving to the response
*/
async function put<T>(url: string, body: Body, options: PartialOptions): Promise<T> {
return await request({
method: 'PUT',
url,
body,
...options
})
}
/**
* makes a PATCH request
*
* @param url request URL
* @param options request options
*
* @return promise resolving to the response
*/
async function patch<T>(url: string, options: PartialOptions): Promise<T> {
return await request({
method: 'PATCH',
url,
...options
})
}
/**
* makes a DELETE request
*
* @param url request URL
* @param options request options
*
* @return promise resolving to the response
*/
async function deleteRequest<T>(url: string, options: PartialOptions): Promise<T> {
return await request({
method: 'DELETE',
url,
...options
})
}
export default {
request,
get,
post,
put,
patch,
delete: deleteRequest,
ResponseType,
BodyType
}

View File

@ -0,0 +1,2 @@
import * as api from './bundle'
export default api

View File

@ -0,0 +1,84 @@
import { Options, PartialOptions, Permission } from './types/notification'
import { promisified } from './tauri'
let permissionSettable = false
let permissionValue = 'default'
function setNotificationPermission(value: Permission): void {
permissionSettable = true
// @ts-expect-error
window.Notification.permission = value
permissionSettable = false
}
// @ts-expect-error
window.Notification = (title: string, options?: PartialOptions) => {
sendNotification({
title,
...options
})
}
window.Notification.requestPermission = requestPermission
Object.defineProperty(window.Notification, 'permission', {
enumerable: true,
get: () => permissionValue,
set(v: Permission) {
if (!permissionSettable) {
throw new Error('Readonly property')
}
permissionValue = v
}
})
isPermissionGranted()
.then(response => {
if (response === null) {
setNotificationPermission('default')
} else {
setNotificationPermission(response ? 'granted' : 'denied')
}
})
.catch(err => { throw err })
async function isPermissionGranted(): Promise<boolean | null> {
if (window.Notification.permission !== 'default') {
return await Promise.resolve(window.Notification.permission === 'granted')
}
return await promisified({
cmd: 'isNotificationPermissionGranted'
})
}
async function requestPermission(): Promise<Permission> {
return await promisified<Permission>({
cmd: 'requestNotificationPermission'
}).then(permission => {
setNotificationPermission(permission)
return permission
})
}
function sendNotification(options: Options | string): void {
if (typeof options === 'object') {
Object.freeze(options)
}
isPermissionGranted()
.then(permission => {
if (permission) {
return promisified({
cmd: 'notification',
options: typeof options === 'string' ? {
body: options
} : options
})
}
})
.catch(err => { throw err })
}
export {
sendNotification,
isPermissionGranted
}

View File

@ -0,0 +1,24 @@
import { promisified } from './tauri'
/**
* spawns a process
*
* @param command the name of the cmd to execute e.g. 'mkdir' or 'node'
* @param [args] command args
* @return promise resolving to the stdout text
*/
async function execute(command: string, args?: string | string[]): Promise<string> {
if (typeof args === 'object') {
Object.freeze(args)
}
return await promisified({
cmd: 'execute',
command,
args: typeof args === 'string' ? [args] : args
})
}
export {
execute
}

View File

@ -0,0 +1,53 @@
declare global {
interface External {
invoke: (command: string) => void
}
}
function s4(): string {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1)
}
function uid(): string {
return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
s4() + '-' + s4() + s4() + s4()
}
function invoke(args: any): void {
window.external.invoke(typeof args === 'object' ? JSON.stringify(args) : args)
}
function transformCallback(callback?: (response: any) => void, once = false): string {
const identifier = uid()
Object.defineProperty(window, identifier, {
value: (result: any) => {
if (once) {
Reflect.deleteProperty(window, identifier)
}
return callback?.(result)
},
writable: false
})
return identifier
}
async function promisified<T>(args: any): Promise<T> {
return await new Promise((resolve, reject) => {
invoke({
callback: transformCallback(resolve),
error: transformCallback(reject),
...args
})
})
}
export {
invoke,
transformCallback,
promisified
}

View File

@ -0,0 +1,17 @@
{
"compilerOptions": {
"outDir": "./dist/",
"strict": true,
"module": "commonjs",
"target": "es5",
"allowJs": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"moduleResolution": "node",
"baseUrl": ".",
"paths": {
"types": ["@types"]
},
},
"include": ["./"]
}

View File

@ -0,0 +1,22 @@
export interface ArgMatch {
/**
* string if takes value
* boolean if flag
* string[] or null if takes multiple values
*/
value: string | boolean | string[] | null
/**
* number of occurrences
*/
occurrences: number
}
export interface SubcommandMatch {
name: string
matches: CliMatches
}
export interface CliMatches {
args: { [name: string]: ArgMatch }
subcommand: SubcommandMatch | null
}

View File

@ -0,0 +1,8 @@
export interface OpenDialogOptions {
filter?: string
defaultPath?: string
multiple?: boolean
directory?: boolean
}
export type SaveDialogOptions = Pick<OpenDialogOptions, 'filter' | 'defaultPath'>

View File

@ -0,0 +1,6 @@
export interface Event {
type: string
payload: unknown
}
export type EventCallback = (event: Event) => void

View File

@ -0,0 +1,41 @@
export enum BaseDirectory {
Audio = 1,
Cache,
Config,
Data,
LocalData,
Desktop,
Document,
Download,
Executable,
Font,
Home,
Picture,
Public,
Runtime,
Template,
Video,
Resource,
App,
}
export interface FsOptions {
dir?: BaseDirectory
}
export interface FsTextFileOption {
path: string
contents: string
}
export interface FsBinaryFileOption {
path: string
contents: ArrayBuffer
}
export interface FileEntry {
path: string
// TODO why not camelCase ?
is_dir: boolean
name: string
}

View File

@ -0,0 +1,33 @@
export enum ResponseType {
JSON = 1,
Text = 2,
Binary = 3
}
export enum BodyType {
Form = 1,
File = 2,
Auto = 3
}
export type Body = object | string | BinaryType
export type HttpVerb = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS' | 'CONNECT' | 'TRACE'
export interface HttpOptions {
method: HttpVerb
url: string
headers?: Record<string, any>
propertys?: Record<string, any>
body?: Body
followRedirects: boolean
maxRedirections: boolean
connectTimeout: number
readTimeout: number
timeout: number
allowCompression: boolean
responseType?: ResponseType
bodyType: BodyType
}
export type PartialOptions = Omit<HttpOptions, 'method' | 'url'>

View File

@ -0,0 +1,8 @@
export interface Options {
title?: string
body?: string
icon?: string
}
export type PartialOptions = Omit<Options, 'title'>
export type Permission = 'granted' | 'denied' | 'default'

View File

@ -0,0 +1,30 @@
import { invoke } from './tauri'
/**
* sets the window title
*
* @param title the new title
*/
function setTitle(title: string): void {
invoke({
cmd: 'setTitle',
title
})
}
/**
* opens an URL on the user default browser
*
* @param url the URL to open
*/
function open(url: string): void {
invoke({
cmd: 'open',
uri: url
})
}
export {
setTitle,
open
}

View File

@ -1,12 +0,0 @@
import tauri from './tauri'
/**
* gets the CLI matches
*/
function getMatches() {
return tauri.cliMatches()
}
export {
getMatches
}

View File

@ -1,32 +0,0 @@
import tauri from './tauri'
/**
* @name openDialog
* @description Open a file/directory selection dialog
* @param {Object} [options]
* @param {String} [options.filter]
* @param {String} [options.defaultPath]
* @param {Boolean} [options.multiple=false]
* @param {Boolean} [options.directory=false]
* @returns {Promise<String|String[]>} promise resolving to the select path(s)
*/
function open (options = {}) {
return tauri.openDialog(options)
}
/**
* @name save
* @description Open a file/directory save dialog
* @param {Object} [options]
* @param {String} [options.filter]
* @param {String} [options.defaultPath]
* @returns {Promise<String>} promise resolving to the select path
*/
function save (options = {}) {
return tauri.saveDialog(options)
}
export {
open,
save
}

View File

@ -1,34 +0,0 @@
import tauri from './tauri'
/**
* The event handler callback
* @callback EventCallback
* @param {Object} event
* @param {String} event.type
* @param {any} [event.payload]
*/
/**
* listen to an event from the backend
*
* @param {String} event the event name
* @param {EventCallback} handler the event handler callback
*/
function listen (event, handler) {
tauri.listen(event, handler)
}
/**
* emits an event to the backend
*
* @param {String} event the event name
* @param {String} [payload] the event payload
*/
function emit (event, payload) {
tauri.emit(event, payload)
}
export {
listen,
emit
}

View File

@ -1,30 +0,0 @@
/**
* @typedef {number} BaseDirectory
*/
/**
* @enum {BaseDirectory}
*/
const Dir = {
Audio: 1,
Cache: 2,
Config: 3,
Data: 4,
LocalData: 5,
Desktop: 6,
Document: 7,
Download: 8,
Executable: 9,
Font: 10,
Home: 11,
Picture: 12,
Public: 13,
Runtime: 14,
Template: 15,
Video: 16,
Resource: 17,
App: 18
}
export {
Dir
}

View File

@ -1,191 +0,0 @@
import tauri from '../tauri'
import { Dir } from './dir'
/**
* reads a file as text
*
* @param {String} filePath path to the file
* @param {Object} [options] configuration object
* @param {BaseDirectory} [options.dir] base directory
* @return {Promise<string>}
*/
function readTextFile (filePath, options = {}) {
return tauri.readTextFile(filePath, options)
}
/**
* reads a file as binary
*
* @param {String} filePath path to the file
* @param {Object} [options] configuration object
* @param {BaseDirectory} [options.dir] base directory
* @return {Promise<int[]>}
*/
function readBinaryFile (filePath, options = {}) {
return tauri.readBinaryFile(filePath, options)
}
/**
* writes a text file
*
* @param {Object} file
* @param {String} file.path path of the file
* @param {String} file.contents contents of the file
* @param {Object} [options] configuration object
* @param {BaseDirectory} [options.dir] base directory
* @return {Promise<void>}
*/
function writeFile (file, options = {}) {
return tauri.writeFile(file, options)
}
const CHUNK_SIZE = 65536;
/**
* convert an Uint8Array to ascii string
*
* @param {Uint8Array} arr
* @return {String}
*/
function uint8ArrayToString(arr) {
if (arr.length < CHUNK_SIZE) {
return String.fromCharCode.apply(null, arr)
}
let result = ''
const arrLen = arr.length
for (let i = 0; i < arrLen; i++) {
const chunk = arr.subarray(i * CHUNK_SIZE, (i + 1) * CHUNK_SIZE)
result += String.fromCharCode.apply(null, chunk)
}
return result
}
/**
* convert an ArrayBuffer to base64 encoded string
*
* @param {ArrayBuffer} buffer
* @return {String}
*/
function arrayBufferToBase64(buffer) {
const str = uint8ArrayToString(new Uint8Array(buffer))
return btoa(str)
}
/**
* writes a binary file
*
* @param {Object} file
* @param {String} file.path path of the file
* @param {ArrayBuffer} file.contents contents of the file
* @param {Object} [options] configuration object
* @param {BaseDirectory} [options.dir] base directory
* @return {Promise<void>}
*/
function writeBinaryFile(file, options = {}) {
return tauri.writeBinaryFile({
...file,
contents: arrayBufferToBase64(file.contents)
}, options)
}
/**
* @typedef {Object} FileEntry
* @property {String} path
* @property {Boolean} is_dir
* @property {String} name
*/
/**
* list directory files
*
* @param {String} dir path to the directory to read
* @param {Object} [options] configuration object
* @param {Boolean} [options.recursive] whether to list dirs recursively or not
* @param {BaseDirectory} [options.dir] base directory
* @return {Promise<FileEntry[]>}
*/
function readDir (dir, options = {}) {
return tauri.readDir(dir, options)
}
/**
* Creates a directory
* If one of the path's parent components doesn't exist
* and the `recursive` option isn't set to true, it will be rejected
*
* @param {String} dir path to the directory to create
* @param {Object} [options] configuration object
* @param {Boolean} [options.recursive] whether to create the directory's parent components or not
* @param {BaseDirectory} [options.dir] base directory
* @return {Promise<void>}
*/
function createDir (dir, options = {}) {
return tauri.createDir(dir, options)
}
/**
* Removes a directory
* If the directory is not empty and the `recursive` option isn't set to true, it will be rejected
*
* @param {String} dir path to the directory to remove
* @param {Object} [options] configuration object
* @param {Boolean} [options.recursive] whether to remove all of the directory's content or not
* @param {BaseDirectory} [options.dir] base directory
* @return {Promise<void>}
*/
function removeDir (dir, options = {}) {
return tauri.removeDir(dir, options)
}
/**
* Copy file
*
* @param {string} source
* @param {string} destination
* @param {object} [options] configuration object
* @param {BaseDirectory} [options.dir] base directory
* @return {Promise<void>}
*/
function copyFile (source, destination, options = {}) {
return tauri.copyFile(source, destination, options)
}
/**
* Removes a file
*
* @param {String} file path to the file to remove
* @param {Object} [options] configuration object
* @param {BaseDirectory} [options.dir] base directory
* @return {Promise<void>}
*/
function removeFile (file, options = {}) {
return tauri.removeFile(file, options)
}
/**
* Renames a file
*
* @param {String} oldPath
* @param {String} newPath
* @param {Object} [options] configuration object
* @param {BaseDirectory} [options.dir] base directory
* @return {Promise<void>}
*/
function renameFile (oldPath, newPath, options = {}) {
return tauri.renameFile(oldPath, newPath, options)
}
export {
Dir,
readTextFile,
readBinaryFile,
writeFile,
writeBinaryFile,
readDir,
createDir,
removeDir,
copyFile,
removeFile,
renameFile
}

View File

@ -1,160 +0,0 @@
import tauri from './tauri'
/**
* @typedef {number} ResponseType
*/
/**
* @enum {ResponseType}
*/
const ResponseType = {
JSON: 1,
Text: 2,
Binary: 3
}
/**
* @typedef {number} BodyType
*/
/**
* @enum {BodyType}
*/
const BodyType = {
Form: 1,
File: 2,
Auto: 3
}
/**
* @typedef {Object} HttpOptions
* @property {String} options.method GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, CONNECT or TRACE
* @property {String} options.url the request URL
* @property {Object} [options.headers] the request headers
* @property {Object} [options.propertys] the request query propertys
* @property {Object|String|Binary} [options.body] the request body
* @property {Boolean} followRedirects whether to follow redirects or not
* @property {Number} maxRedirections max number of redirections
* @property {Number} connectTimeout request connect timeout
* @property {Number} readTimeout request read timeout
* @property {Number} timeout request timeout
* @property {Boolean} allowCompression
* @property {ResponseType} [responseType=1] response type
* @property {BodyType} [bodyType=3] body type
*/
/**
* makes a HTTP request
*
* @param {HttpOptions} options request options
*
* @return {Promise<any>} promise resolving to the response
*/
function request (options) {
return tauri.httpRequest(options)
}
/**
* makes a GET request
*
* @param {String} url request URL
* @param {String|Object|Binary} body request body
* @param {HttpOptions} options request options
*
* @return {Promise<any>} promise resolving to the response
*/
function get (url, options = {}) {
return request({
method: 'GET',
url,
...options
})
}
/**
* makes a POST request
*
* @param {String} url request URL
* @param {String|Object|Binary} body request body
* @param {HttpOptions} options request options
*
* @return {Promise<any>} promise resolving to the response
*/
function post (url, body = void 0, options = {}) {
return request({
method: 'POST',
url,
body,
...options
})
}
/**
* makes a PUT request
*
* @param {String} url request URL
* @param {String|Object|Binary} body request body
* @param {HttpOptions} options request options
*
* @return {Promise<any>} promise resolving to the response
*/
function put (url, body = void 0, options = {}) {
return request({
method: 'PUT',
url,
body,
...options
})
}
/**
* makes a PATCH request
*
* @param {String} url request URL
* @param {HttpOptions} options request options
*
* @return {Promise<any>} promise resolving to the response
*/
function patch (url, options = {}) {
return request({
method: 'PATCH',
url,
...options
})
}
/**
* makes a DELETE request
*
* @param {String} url request URL
* @param {HttpOptions} options request options
*
* @return {Promise<any>} promise resolving to the response
*/
function deleteRequest (url, options = {}) {
return request({
method: 'DELETE',
url,
...options
})
}
export {
request,
get,
post,
put,
patch,
deleteRequest,
ResponseType,
BodyType
}
export default {
request,
get,
post,
put,
patch,
delete: deleteRequest,
ResponseType,
BodyType
}

View File

@ -1,3 +0,0 @@
import tauri from './tauri'
export default tauri

View File

@ -1,16 +0,0 @@
import tauri from './tauri'
/**
* spawns a process
*
* @param {String} command the name of the cmd to execute e.g. 'mkdir' or 'node'
* @param {(String[]|String)} [args] command args
* @return {Promise<String>} promise resolving to the stdout text
*/
function execute (command, args) {
return tauri.execute(command, args)
}
export {
execute
}

View File

@ -1 +0,0 @@
export default window.tauri

View File

@ -1,24 +0,0 @@
import tauri from './tauri'
/**
* sets the window title
*
* @param {String} title the new title
*/
function setTitle (title) {
tauri.setTitle(title)
}
/**
* opens an URL on the user default browser
*
* @param {String} url the URL to open
*/
function open (url) {
tauri.open(url)
}
export {
setTitle,
open
}

View File

@ -10,13 +10,15 @@
"url": "https://opencollective.com/tauri"
},
"scripts": {
"build": "webpack --progress",
"build": "rollup -c --silent && webpack --progress",
"build:webpack": "webpack --progress",
"build:api": "rollup -c",
"build-release": "yarn build --display none --progress false",
"test": "jest --runInBand --no-cache --testPathIgnorePatterns=\"(build|dev)\"",
"pretest": "yarn build",
"prepublishOnly": "yarn build-release",
"test:local": "jest --runInBand",
"lint": "eslint --ext ts ./src/**/*.ts",
"lint": "eslint --ext ts ./src/**/*.ts ./api-src/**/*.ts",
"lint-fix": "eslint --fix --ext ts ./src/**/*.ts",
"lint:lockfile": "lockfile-lint --path yarn.lock --type yarn --validate-https --allowed-hosts npm yarn",
"build:tauri[rust]": "cd ../tauri && TAURI_DIST_DIR=../../test/fixture/dist TAURI_DIR=../test/fixture cargo publish --dry-run --allow-dirty"
@ -72,6 +74,12 @@
"@babel/core": "7.10.3",
"@babel/preset-env": "7.10.3",
"@babel/preset-typescript": "7.10.1",
"@rollup/plugin-babel": "^5.0.3",
"@rollup/plugin-commonjs": "13.0.0",
"@rollup/plugin-json": "4.1.0",
"@rollup/plugin-node-resolve": "8.0.1",
"@rollup/plugin-sucrase": "^3.0.2",
"@rollup/plugin-typescript": "4.1.2",
"@types/cross-spawn": "6.0.2",
"@types/fs-extra": "9.0.1",
"@types/http-proxy": "1.17.4",
@ -102,8 +110,12 @@
"lockfile-lint": "4.3.7",
"promise": "8.1.0",
"raw-loader": "4.0.1",
"rollup": "2.17.1",
"rollup-plugin-terser": "6.1.0",
"rollup-plugin-typescript2": "0.27.1",
"toml-loader": "1.0.0",
"ts-loader": "7.0.5",
"tslib": "2.0.0",
"typescript": "3.9.5",
"webpack": "4.43.0",
"webpack-cli": "3.3.12",

View File

@ -0,0 +1,98 @@
// rollup.config.js
import { terser } from 'rollup-plugin-terser'
import resolve from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'
import sucrase from '@rollup/plugin-sucrase'
import { getBabelOutputPlugin } from '@rollup/plugin-babel'
import pkg from './package.json'
export default [{
input: {
'fs': './api-src/fs.ts',
'dialog': './api-src/dialog.ts',
'event': './api-src/event.ts',
'http': './api-src/http.ts',
'index': './api-src/index.ts',
'process': './api-src/process.ts',
'tauri': './api-src/tauri.ts',
'window': './api-src/window.ts',
},
treeshake: true,
perf: true,
output: [
{
dir: 'api/', // if you want to consume in node but want it tiny
entryFileNames: '[name].js',
format: 'cjs',
plugins: [ terser() ],
exports: 'named',
globals: {}
},
{
dir: 'api/esm/', // if you will be transpiling and minifying yourself
entryFileNames: '[name].js',
format: 'esm',
sourcemap: true,
exports: 'named',
globals: {}
}
],
plugins: [
commonjs({}),
sucrase({
exclude: ['node_modules'],
transforms: ['typescript']
}),
resolve({
// pass custom options to the resolve plugin
customResolveOptions: {
moduleDirectory: 'node_modules'
}
})
],
external: [
...Object.keys(pkg.dependencies || {}),
...Object.keys(pkg.peerDependencies || {})
],
watch: {
chokidar: true,
include: 'api-src/**',
exclude: 'node_modules/**'
}
},
{
input: {
'bundle': './api-src/bundle.ts'
},
output: [{
name: '__TAURI__',
dir: 'api/', // if it needs to run in the browser
entryFileNames: 'tauri.bundle.umd.js',
format: 'umd',
plugins: [
getBabelOutputPlugin({
presets: [['@babel/preset-env', { modules: 'umd' }]],
allowAllFormats: true
}),
terser()
],
globals: {
}
}],
plugins: [
sucrase({
exclude: ['node_modules'],
transforms: ['typescript']
}),
resolve({
// pass custom options to the resolve plugin
customResolveOptions: {
moduleDirectory: 'node_modules'
}
})
],
external: [
...Object.keys(pkg.dependencies || {}),
...Object.keys(pkg.peerDependencies || {})
]
}]

View File

@ -1,14 +0,0 @@
import { ensureDirSync, writeFileSync } from 'fs-extra'
import { template } from 'lodash'
import path from 'path'
import { TauriConfig } from './types/config'
export const generate = (outDir: string, cfg: TauriConfig): void => {
// this MUST be from the templates repo
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-var-requires
const apiTemplate = require('../templates/tauri.js').default
const compiledApi = template(apiTemplate)
ensureDirSync(outDir)
writeFileSync(path.join(outDir, 'tauri.js'), compiledApi(cfg), 'utf-8')
}

View File

@ -9,7 +9,6 @@ import http from 'http'
import * as net from 'net'
import os from 'os'
import { findClosestOpenPort } from './helpers/net'
import * as entry from './entry'
import { tauriDir, appDir } from './helpers/app-paths'
import logger from './helpers/logger'
import onShutdown from './helpers/on-shutdown'
@ -75,8 +74,6 @@ class Runner {
this.__whitelistApi(cfg, cargoManifest)
this.__rewriteManifest(cargoManifest as unknown as toml.JsonMap)
entry.generate(tauriDir, cfg)
const runningDevServer = devPath.startsWith('http')
let inlinedAssets: string[] = []
@ -94,13 +91,13 @@ class Runner {
selfHandleResponse: true
})
proxy.on('proxyRes', function(proxyRes: http.IncomingMessage, req: http.IncomingMessage, res: http.ServerResponse) {
proxy.on('proxyRes', function (proxyRes: http.IncomingMessage, req: http.IncomingMessage, res: http.ServerResponse) {
if (req.url === '/') {
const body: Uint8Array[] = []
proxyRes.on('data', function(chunk: Uint8Array) {
proxyRes.on('data', function (chunk: Uint8Array) {
body.push(chunk)
})
proxyRes.on('end', function() {
proxyRes.on('end', function () {
const bodyStr = body.join('')
const indexDir = os.tmpdir()
writeFileSync(path.join(indexDir, 'index.html'), bodyStr)
@ -227,8 +224,6 @@ class Runner {
this.__whitelistApi(cfg, cargoManifest as unknown as CargoManifest)
this.__rewriteManifest(cargoManifest)
entry.generate(tauriDir, cfg)
const inlinedAssets = (await this.__parseHtml(cfg, cfg.build.distDir)).inlinedAssets
process.env.TAURI_INLINED_ASSSTS = inlinedAssets.join('|')
@ -307,9 +302,17 @@ class Runner {
}
const tauriScript = document.createElement('script')
tauriScript.text = readFileSync(path.join(tauriDir, 'tauri.js')).toString()
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-member-access
tauriScript.text = require('../templates/tauri.js').default
document.head.insertBefore(tauriScript, document.head.firstChild)
if (cfg.build.withGlobalTauri) {
const tauriUmdScript = document.createElement('script')
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-member-access
tauriUmdScript.text = require('../api/tauri.bundle.umd').default
document.head.insertBefore(tauriUmdScript, document.head.firstChild)
}
const csp = cfg.tauri.security.csp
if (csp) {
const cspTag = document.createElement('meta')
@ -501,7 +504,7 @@ class Runner {
}
if (cfg.tauri.cli) {
tomlFeatures.push('cli')
tomlFeatures.push('cli')
}
if (typeof manifest.dependencies.tauri === 'string') {

View File

@ -37,6 +37,7 @@ export interface TauriConfig {
devPath: string
beforeDevCommand?: string
beforeBuildCommand?: string
withGlobalTauri?: boolean
}
ctx: {
prod?: boolean

View File

@ -1,7 +1,7 @@
(function () {
function loadAsset(path, type) {
if (path) {
window.tauri.loadAsset(path, type)
window.__TAURI__.loadAsset(path, type)
}
}

View File

@ -6,6 +6,5 @@ WixTools
# These are backup files generated by rustfmt
**/*.rs.bk
tauri.js
config.json
bundle.json

View File

@ -1,385 +0,0 @@
/* eslint-disable */
/**
* * THIS FILE IS GENERATED AUTOMATICALLY.
* DO NOT EDIT.
*
* Please whitelist these API functions in tauri.conf.json
*
**/
// open <a href="..."> links with the Tauri API
/**
* @module tauri
* @description This API interface makes powerful interactions available
* to be run on client side applications. They are opt-in features, and
* must be enabled in tauri.conf.json
*
* Each binding MUST provide these interfaces in order to be compliant,
* and also whitelist them based upon the developer's settings.
*/
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1)
}
const uid = function () {
return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
s4() + '-' + s4() + s4() + s4()
}
<% if (ctx.dev) { %>
/**
* @name __whitelistWarning
* @description Present a stylish warning to the developer that their API
* call has not been whitelisted in tauri.conf.json
* @param {String} func - function name to warn
* @private
*/
const __whitelistWarning = function (func) {
console.warn('%c[Tauri] Danger \ntauri.' + func + ' not whitelisted 💣\n%c\nAdd to tauri.conf.json: \n\ntauri: \n whitelist: { \n ' + func + ': true \n\nReference: https://github.com/tauri-apps/tauri/wiki' + func , 'background: red; color: white; font-weight: 800; padding: 2px; font-size:1.5em', ' ')
}
<% } %>
<% if (ctx.dev) { %>
/**
* @name __reject
* @description is a private promise used to deflect un-whitelisted tauri API calls
* Its only purpose is to maintain thenable structure in client code without
* breaking the application
* * @type {Promise<any>}
* @private
*/
<% } %>
const __reject = new Promise((reject) => { reject })
window.tauri = {
<% if (ctx.dev) { %>
/**
* @name invoke
* @description Calls a Tauri Core feature, such as setTitle
* @param {Object} args
*/
<% } %>
invoke (args) {
Object.freeze(args)
window.external.invoke(JSON.stringify(args))
},
<% if (ctx.dev) { %>
/**
* @name listen
* @description Add an event listener to Tauri backend
* @param {String} event
* @param {Function} handler
* @param {Boolean} once
*/
<% } %>
listen (event, handler, once = false) {
this.invoke({
cmd: 'listen',
event,
handler: this.transformCallback(handler, once),
once
})
},
<% if (ctx.dev) { %>
/**
* @name emit
* @description Emits an evt to the Tauri back end
* @param {String} evt
* @param {Object} payload
*/
<% } %>
emit (evt, payload) {
this.invoke({
cmd: 'emit',
event: evt,
payload
})
},
<% if (ctx.dev) { %>
/**
* @name transformCallback
* @description Registers a callback with a uid
* @param {Function} callback
* @param {Boolean} once
* @returns {*}
*/
<% } %>
transformCallback (callback, once = true) {
const identifier = Object.freeze(uid())
window[identifier] = (result) => {
if (once) {
delete window[identifier]
}
return callback && callback(result)
}
return identifier
},
<% if (ctx.dev) { %>
/**
* @name promisified
* @description Turns a request into a chainable promise
* @param {Object} args
* @returns {Promise<any>}
*/
<% } %>
promisified (args) {
return new Promise((resolve, reject) => {
this.invoke({
callback: this.transformCallback(resolve),
error: this.transformCallback(reject),
...args
})
})
},
<% if (ctx.dev) { %>
/**
* @name readTextFile
* @description Accesses a non-binary file on the user's filesystem
* and returns the content. Permissions based on the app's PID owner
* @param {String} path
* @returns {*|Promise<any>|Promise}
*/
<% } %>
readTextFile (path) {
<% if (tauri.whitelist.readTextFile === true || tauri.whitelist.all === true) { %>
Object.freeze(path)
return this.promisified({ cmd: 'readTextFile', path })
<% } else { %>
<% if (ctx.dev) { %>
__whitelistWarning('readTextFile')
<% } %>
return __reject
<% } %>
},
<% if (ctx.dev) { %>
/**
* @name readBinaryFile
* @description Accesses a binary file on the user's filesystem
* and returns the content. Permissions based on the app's PID owner
* @param {String} path
* @returns {*|Promise<any>|Promise}
*/
<% } %>
readBinaryFile (path) {
<% if (tauri.whitelist.readBinaryFile === true || tauri.whitelist.all === true) { %>
Object.freeze(path)
return this.promisified({ cmd: 'readBinaryFile', path })
<% } else { %>
<% if (ctx.dev) { %>
__whitelistWarning('readBinaryFile')
<% } %>
return __reject
<% } %>
},
<% if (ctx.dev) { %>
/**
* @name writeFile
* @description Write a file to the Local Filesystem.
* Permissions based on the app's PID owner
* @param {Object} cfg
* @param {String} cfg.file
* @param {String|Binary} cfg.contents
*/
<% } %>
writeFile (cfg) {
<% if (tauri.whitelist.writeFile === true || tauri.whitelist.all === true) { %>
Object.freeze(cfg)
this.invoke({ cmd: 'writeFile', file: cfg.file, contents: cfg.contents })
<% } else { %>
<% if (ctx.dev) { %>
__whitelistWarning('writeFile')
<% } %>
return __reject
<% } %>
},
<% if (ctx.dev) { %>
/**
* @name writeBinaryFile
* @description Write a binary file to the Local Filesystem.
* Permissions based on the app's PID owner
* @param {Object} cfg
* @param {String} cfg.file
* @param {String|Binary} cfg.contents
*/
<% } %>
writeBinaryFile (cfg) {
<% if (tauri.whitelist.writeBinaryFile === true || tauri.whitelist.all === true) { %>
Object.freeze(cfg)
this.invoke({ cmd: 'writeBinaryFile', file: cfg.file, contents: cfg.contents })
<% } else { %>
<% if (ctx.dev) { %>
__whitelistWarning('writeBinaryFile')
<% } %>
return __reject
<% } %>
},
<% if (ctx.dev) { %>
/**
* @name listFiles
* @description Get the files in a path.
* Permissions based on the app's PID owner
* @param {String} path
* @returns {*|Promise<any>|Promise}
*/
<% } %>
listFiles (path) {
<% if (tauri.whitelist.listFiles === true || tauri.whitelist.all === true) { %>
Object.freeze(path)
return this.promisified({ cmd: 'listFiles', path })
<% } else { %>
<% if (ctx.dev) { %>
__whitelistWarning('listFiles')
<% } %>
return __reject
<% } %>
},
<% if (ctx.dev) { %>
/**
* @name listDirs
* @description Get the directories in a path.
* Permissions based on the app's PID owner
* @param {String} path
* @returns {*|Promise<any>|Promise}
*/
<% } %>
listDirs (path) {
<% if (tauri.whitelist.listDirs === true || tauri.whitelist.all === true) { %>
Object.freeze(path)
return this.promisified({ cmd: 'listDirs', path })
<% } else { %>
<% if (ctx.dev) { %>
__whitelistWarning('listDirs')
<% } %>
return __reject
<% } %>
},
<% if (ctx.dev) { %>
/**
* @name setTitle
* @description Set the application's title
* @param {String} title
*/
<% } %>
setTitle (title) {
<% if (tauri.whitelist.setTitle === true || tauri.whitelist.all === true) { %>
Object.freeze(title)
this.invoke({ cmd: 'setTitle', title })
<% } else { %>
<% if (ctx.dev) { %>
__whitelistWarning('setTitle')
<% } %>
return __reject
<% } %>
},
<% if (ctx.dev) { %>
/**
* @name open
* @description Open an URI
* @param {String} uri
*/
<% } %>
open (uri) {
<% if (tauri.whitelist.open === true || tauri.whitelist.all === true) { %>
Object.freeze(uri)
this.invoke({ cmd: 'open', uri })
<% } else { %>
<% if (ctx.dev) { %>
__whitelistWarning('open')
<% } %>
return __reject
<% } %>
},
<% if (ctx.dev) { %>
/**
* @name execute
* @description Execute a program with arguments.
* Permissions based on the app's PID owner
* @param {String} command
* @param {String|Array} args
* @returns {*|Promise<any>|Promise}
*/
<% } %>
execute (command, args) {
<% if (tauri.whitelist.execute === true || tauri.whitelist.all === true) { %>
Object.freeze(command)
if (typeof args === 'string' || typeof args === 'object') {
Object.freeze(args)
}
return this.promisified({ cmd: 'execute', command, args: typeof (args) === 'string' ? [args] : args })
<% } else { %>
<% if (ctx.dev) { %>
__whitelistWarning('execute')
<% } %>
return __reject
<% } %>
},
<% if (ctx.dev) { %>
/**
* @name bridge
* @description Securely pass a message to the backend.
* @example
* this.$q.tauri.bridge('QBP/1/ping/client-1', 'pingback')
* @param {String} command - a compressed, slash-delimited and
* versioned API call to the backend.
* @param {String|Object}payload
* @returns {*|Promise<any>|Promise}
*/
<% } %>
bridge (command, payload) {
<% if (tauri.whitelist.bridge === true || tauri.whitelist.all === true) { %>
Object.freeze(command)
if (typeof payload === 'string' || typeof payload === 'object') {
Object.freeze(payload)
}
return this.promisified({ cmd: 'bridge', command, payload: typeof (payload) === 'object' ? [payload] : payload })
<% } else { %>
<% if (ctx.dev) { %>
__whitelistWarning('bridge')
<% } %>
return __reject
<% } %>
},
<% if (ctx.dev) { %>
/**
* @name setup
* @description Inform Rust that the webview has initialized and is
* ready for communication
*/
<% } %>
setup () {
document.querySelector('body').addEventListener('click', function (e) {
let target = e.target
while (target != null) {
if (target.matches ? target.matches('a') : target.msMatchesSelector('a')) {
tauri.open(target.href)
break
}
target = target.parentElement
}
}, true)
tauri.invoke({
cmd: 'init'
})
}
}

View File

@ -1,23 +1,3 @@
/* eslint-disable */
/**
* * THIS FILE IS GENERATED AUTOMATICALLY.
* DO NOT EDIT.
*
* Please whitelist these API functions in tauri.conf.json
*
**/
/**
* @module tauri
* @description This API interface makes powerful interactions available
* to be run on client side applications. They are opt-in features, and
* must be enabled in tauri.conf.json
*
* Each binding MUST provide these interfaces in order to be compliant,
* and also whitelist them based upon the developer's settings.
*/
// polyfills
if (!String.prototype.startsWith) {
String.prototype.startsWith = function (searchString, position) {
@ -27,7 +7,6 @@ if (!String.prototype.startsWith) {
}
// makes the window.external.invoke API available after window.location.href changes
switch (navigator.platform) {
case "Macintosh":
case "MacPPC":
@ -63,765 +42,99 @@ switch (navigator.platform) {
s4() + '-' + s4() + s4() + s4()
}
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
/**
* @typedef {number} BaseDirectory
*/
/**
* @enum {BaseDirectory}
*/
var Dir = {
Audio: 1,
Cache: 2,
Config: 3,
Data: 4,
LocalData: 5,
Desktop: 6,
Document: 7,
Download: 8,
Executable: 9,
Font: 10,
Home: 11,
Picture: 12,
Public: 13,
Runtime: 14,
Template: 15,
Video: 16,
Resource: 17,
App: 18
}
<% if (ctx.dev) { %>
function camelToKebab (string) {
return string.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2').toLowerCase()
}
/**
* @name return __whitelistWarning
* @description Present a stylish warning to the developer that their API
* call has not been whitelisted in tauri.conf.json
* @param {String} func - function name to warn
* @private
*/
var __whitelistWarning = function (func) {
console.warn('%c[Tauri] Danger \ntauri.' + func + ' not whitelisted 💣\n%c\nAdd to tauri.conf.json: \n\ntauri: \n whitelist: { \n ' + camelToKebab(func) + ': true \n\nReference: https://github.com/tauri-apps/tauri/wiki' + func, 'background: red; color: white; font-weight: 800; padding: 2px; font-size:1.5em', ' ')
return __reject()
}
<% } %>
<% if (ctx.dev) { %>
/**
* @name __reject
* @description generates a promise used to deflect un-whitelisted tauri API calls
* Its only purpose is to maintain thenable structure in client code without
* breaking the application
* * @type {Promise<any>}
* @private
*/
<% } %>
var __reject = function () {
return new Promise(function (_, reject) {
reject();
});
}
window.tauri = {
Dir: Dir,
<% if (ctx.dev) { %>
/**
* @name invoke
* @description Calls a Tauri Core feature, such as setTitle
* @param {Object} args
*/
<% } %>
invoke: function invoke(args) {
window.external.invoke(JSON.stringify(args));
},
<% if (ctx.dev) { %>
/**
* @name listen
* @description Add an event listener to Tauri backend
* @param {String} event
* @param {Function} handler
* @param {Boolean} once
*/
<% } %>
listen: function listen(event, handler) {
<% if (tauri.whitelist.event === true || tauri.whitelist.all === true) { %>
var once = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
this.invoke({
cmd: 'listen',
event: event,
handler: window.tauri.transformCallback(handler, once),
once: once
});
<% } else { %>
<% if (ctx.dev) { %>
return __whitelistWarning('event')
<% } %>
return __reject()
<% } %>
},
<% if (ctx.dev) { %>
/**
* @name emit
* @description Emits an evt to the Tauri back end
* @param {String} evt
* @param {Object} payload
*/
<% } %>
emit: function emit(evt, payload) {
<% if (tauri.whitelist.event === true || tauri.whitelist.all === true) { %>
this.invoke({
cmd: 'emit',
event: evt,
payload: payload
});
<% } else { %>
<% if (ctx.dev) { %>
return __whitelistWarning('event')
<% } %>
return __reject()
<% } %>
},
<% if (ctx.dev) { %>
/**
* @name transformCallback
* @description Registers a callback with a uid
* @param {Function} callback
* @param {Boolean} once
* @returns {*}
*/
<% } %>
transformCallback: function transformCallback(callback) {
var once = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var identifier = uid();
window[identifier] = function (result) {
if (once) {
delete window[identifier];
}
return callback && callback(result);
};
return identifier;
},
<% if (ctx.dev) { %>
/**
* @name promisified
* @description Turns a request into a chainable promise
* @param {Object} args
* @returns {Promise<any>}
*/
<% } %>
promisified: function promisified(args) {
var _this = this;
return new Promise(function (resolve, reject) {
_this.invoke(_objectSpread({
callback: _this.transformCallback(resolve),
error: _this.transformCallback(reject)
}, args));
function ownKeys(object, enumerableOnly) {
var keys = Object.keys(object);
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(object);
if (enumerableOnly) symbols = symbols.filter(function (sym) {
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
});
},
<% if (ctx.dev) { %>
/**
* @name readTextFile
* @description Accesses a non-binary file on the user's filesystem
* and returns the content. Permissions based on the app's PID owner
* @param {String} path
* @param {Object} [options]
* @param {BaseDirectory} [options.dir]
* @returns {*|Promise<any>|Promise}
*/
<% } %>
readTextFile: function readTextFile(path, options) {
<% if (tauri.whitelist.readTextFile === true || tauri.whitelist.all === true) { %>
return this.promisified({
cmd: 'readTextFile',
path: path,
options: options
});
<% } else { %>
<% if (ctx.dev) { %>
return __whitelistWarning('readTextFile')
<% } %>
return __reject()
<% } %>
},
<% if (ctx.dev) { %>
/**
* @name readBinaryFile
* @description Accesses a binary file on the user's filesystem
* and returns the content. Permissions based on the app's PID owner
* @param {String} path
* @param {Object} [options]
* @param {BaseDirectory} [options.dir]
* @returns {*|Promise<any>|Promise}
*/
<% } %>
readBinaryFile: function readBinaryFile(path, options) {
<% if (tauri.whitelist.readBinaryFile === true || tauri.whitelist.all === true) { %>
return this.promisified({
cmd: 'readBinaryFile',
path: path,
options: options
});
<% } else { %>
<% if (ctx.dev) { %>
return __whitelistWarning('readBinaryFile')
<% } %>
return __reject()
<% } %>
},
<% if (ctx.dev) { %>
/**
* @name writeFile
* @description Write a file to the Local Filesystem.
* Permissions based on the app's PID owner
* @param {Object} cfg
* @param {String} cfg.file
* @param {String|Binary} cfg.contents
* @param {Object} [options]
* @param {BaseDirectory} [options.dir]
*/
<% } %>
writeFile: function writeFile(cfg, options) {
<% if (tauri.whitelist.writeFile === true || tauri.whitelist.all === true) { %>
if (_typeof(cfg) === 'object') {
Object.freeze(cfg);
}
return this.promisified({
cmd: 'writeFile',
file: cfg.file,
contents: cfg.contents,
options: options
});
<% } else { %>
<% if (ctx.dev) { %>
return __whitelistWarning('writeFile')
<% } %>
return __reject()
<% } %>
},
<% if (ctx.dev) { %>
/**
* @name writeBinaryFile
* @description Write a binary file to the Local Filesystem.
* Permissions based on the app's PID owner
* @param {Object} cfg
* @param {String} cfg.file
* @param {String|Binary} cfg.contents
* @param {Object} [options]
* @param {BaseDirectory} [options.dir]
*/
<% } %>
writeBinaryFile: function writeBinaryFile(cfg, options) {
<% if (tauri.whitelist.writeBinaryFile === true || tauri.whitelist.all === true) { %>
if (_typeof(cfg) === 'object') {
Object.freeze(cfg);
}
return this.promisified({
cmd: 'writeBinaryFile',
file: cfg.file,
contents: cfg.contents,
options: options
});
<% } else { %>
<% if (ctx.dev) { %>
return __whitelistWarning('writeBinaryFile')
<% } %>
return __reject()
<% } %>
},
<% if (ctx.dev) { %>
/**
* @name readDir
* @description Reads a directory
* Permissions based on the app's PID owner
* @param {String} path
* @param {Object} [options]
* @param {Boolean} [options.recursive]
* @param {BaseDirectory} [options.dir]
* @returns {*|Promise<any>|Promise}
*/
<% } %>
readDir: function readDir(path, options) {
<% if (tauri.whitelist.readDir === true || tauri.whitelist.all === true) { %>
return this.promisified({
cmd: 'readDir',
path: path,
options: options
});
<% } else { %>
<% if (ctx.dev) { %>
return __whitelistWarning('readDir')
<% } %>
<% } %>
},
<% if (ctx.dev) { %>
/**
* @name createDir
* @description Creates a directory
* Permissions based on the app's PID owner
* @param {String} path
* @param {Object} [options]
* @param {Boolean} [options.recursive]
* @param {BaseDirectory} [options.dir]
* @returns {*|Promise<any>|Promise}
*/
<% } %>
createDir: function createDir(path, options) {
<% if (tauri.whitelist.createDir === true || tauri.whitelist.all === true) { %>
return this.promisified({
cmd: 'createDir',
path: path,
options: options
});
<% } else { %>
<% if (ctx.dev) { %>
return __whitelistWarning('createDir')
<% } %>
return __reject()
<% } %>
},
<% if (ctx.dev) { %>
/**
* @name removeDir
* @description Removes a directory
* Permissions based on the app's PID owner
* @param {String} path
* @param {Object} [options]
* @param {Boolean} [options.recursive]
* @param {BaseDirectory} [options.dir]
* @returns {*|Promise<any>|Promise}
*/
<% } %>
removeDir: function removeDir(path, options) {
<% if (tauri.whitelist.removeDir === true || tauri.whitelist.all === true) { %>
return this.promisified({
cmd: 'removeDir',
path: path,
options: options
});
<% } else { %>
<% if (ctx.dev) { %>
return __whitelistWarning('removeDir')
<% } %>
return __reject()
<% } %>
},
<% if (ctx.dev) { %>
/**
* @name copyFile
* @description Copy file
* Permissions based on the app's PID owner
* @param {String} source
* @param {String} destination
* @param {Object} [options]
* @param {BaseDirectory} [options.dir]
* @returns {*|Promise<any>|Promise}
*/
<% } %>
copyFile: function copyFile(source, destination, options) {
<% if (tauri.whitelist.copyFile === true || tauri.whitelist.all === true) { %>
return this.promisified({
cmd: 'copyFile',
source: source,
destination: destination,
options: options
});
<% } else { %>
<% if (ctx.dev) { %>
return __whitelistWarning('copyFile')
<% } %>
return __reject()
<% } %>
},
<% if (ctx.dev) { %>
/**
* @name removeFile
* @description Removes a file
* Permissions based on the app's PID owner
* @param {String} path
* @param {Object} [options]
* @param {BaseDirectory} [options.dir]
* @returns {*|Promise<any>|Promise}
*/
<% } %>
removeFile: function removeFile(path, options) {
<% if (tauri.whitelist.removeFile === true || tauri.whitelist.all === true) { %>
return this.promisified({
cmd: 'removeFile',
path: path,
options: options
});
<% } else { %>
<% if (ctx.dev) { %>
return __whitelistWarning('removeFile')
<% } %>
return __reject()
<% } %>
},
<% if (ctx.dev) { %>
/**
* @name renameFile
* @description Renames a file
* Permissions based on the app's PID owner
* @param {String} path
* @param {Object} [options]
* @param {BaseDirectory} [options.dir]
* @returns {*|Promise<any>|Promise}
*/
<% } %>
renameFile: function renameFile(oldPath, newPath, options) {
<% if (tauri.whitelist.renameFile === true || tauri.whitelist.all === true) { %>
return this.promisified({
cmd: 'renameFile',
oldPath: oldPath,
newPath: newPath,
options: options
});
<% } else { %>
<% if (ctx.dev) { %>
return __whitelistWarning('renameFile')
<% } %>
return __reject()
<% } %>
},
<% if (ctx.dev) { %>
/**
* @name setTitle
* @description Set the application's title
* @param {String} title
*/
<% } %>
setTitle: function setTitle(title) {
<% if (tauri.whitelist.setTitle === true || tauri.whitelist.all === true) { %>
this.invoke({
cmd: 'setTitle',
title: title
});
<% } else { %>
<% if (ctx.dev) { %>
return __whitelistWarning('setTitle')
<% } %>
return __reject()
<% } %>
},
<% if (ctx.dev) { %>
/**
* @name open
* @description Open an URI
* @param {String} uri
*/
<% } %>
open: function open(uri) {
<% if (tauri.whitelist.open === true || tauri.whitelist.all === true) { %>
this.invoke({
cmd: 'open',
uri: uri
});
<% } else { %>
<% if (ctx.dev) { %>
return __whitelistWarning('open')
<% } %>
return __reject()
<% } %>
},
<% if (ctx.dev) { %>
/**
* @name execute
* @description Execute a program with arguments.
* Permissions based on the app's PID owner
* @param {String} command
* @param {String|Array} args
* @returns {*|Promise<any>|Promise}
*/
<% } %>
execute: function execute(command, args) {
<% if (tauri.whitelist.execute === true || tauri.whitelist.all === true) { %>
if (_typeof(args) === 'object') {
Object.freeze(args);
}
return this.promisified({
cmd: 'execute',
command: command,
args: typeof args === 'string' ? [args] : args
});
<% } else { %>
<% if (ctx.dev) { %>
return __whitelistWarning('execute')
<% } %>
return __reject()
<% } %>
},
<% if (ctx.dev) { %>
/**
* @name openDialog
* @description Open a file/directory selection dialog
* @param {String} [options]
* @param {String} [options.filter]
* @param {String} [options.defaultPath]
* @param {Boolean} [options.multiple=false]
* @param {Boolean} [options.directory=false]
* @returns {Promise<String|String[]>} promise resolving to the select path(s)
*/
<% } %>
openDialog: function openDialog(options) {
<% if (tauri.whitelist.openDialog === true || tauri.whitelist.all === true) { %>
var opts = options || {}
if (_typeof(options) === 'object') {
opts.default_path = opts.defaultPath
Object.freeze(options);
}
return this.promisified({
cmd: 'openDialog',
options: opts
});
<% } else { %>
<% if (ctx.dev) { %>
return __whitelistWarning('openDialog')
<% } %>
return __reject()
<% } %>
},
<% if (ctx.dev) { %>
/**
* @name saveDialog
* @description Open a file/directory save dialog
* @param {String} [options]
* @param {String} [options.filter]
* @param {String} [options.defaultPath]
* @returns {Promise<String>} promise resolving to the select path
*/
<% } %>
saveDialog: function saveDialog(options) {
<% if (tauri.whitelist.saveDialog === true || tauri.whitelist.all === true) { %>
var opts = options || {}
if (_typeof(options) === 'object') {
opts.default_path = opts.defaultPath
Object.freeze(options);
}
return this.promisified({
cmd: 'saveDialog',
options: opts
});
<% } else { %>
<% if (ctx.dev) { %>
return __whitelistWarning('saveDialog')
<% } %>
return __reject()
<% } %>
},
<% if (ctx.dev) { %>
/**
* @name httpRequest
* @description Makes an HTTP request
* @param {Object} options
* @param {String} options.method GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, CONNECT or TRACE
* @param {String} options.url the request URL
* @param {Object} [options.headers] the request headers
* @param {Object} [options.params] the request query params
* @param {Object|String|Binary} [options.body] the request body
* @param {Boolean} followRedirects whether to follow redirects or not
* @param {Number} maxRedirections max number of redirections
* @param {Number} connectTimeout request connect timeout
* @param {Number} readTimeout request read timeout
* @param {Number} timeout request timeout
* @param {Boolean} allowCompression
* @param {Number} [responseType=1] 1 - JSON, 2 - Text, 3 - Binary
* @param {Number} [bodyType=3] 1 - Form, 2 - File, 3 - Auto
* @returns {Promise<any>}
*/
<% } %>
httpRequest: function httpRequest(options) {
<% if (tauri.whitelist.readBinaryFile === true || tauri.whitelist.all === true) { %>
return this.promisified({
cmd: 'httpRequest',
options: options
});
<% } else { %>
<% if (ctx.dev) { %>
return __whitelistWarning('httpRequest')
<% } %>
return __reject()
<% } %>
},
<% if (ctx.dev) { %>
/**
* @name notification
* @description Display a desktop notification
* @param {Object|String} options the notifications options if an object, otherwise its body
* @param {String} [options.summary] the notification's summary
* @param {String} options.body the notification's body
* @param {String} [options.icon] the notifications's icon
* @returns {*|Promise<any>|Promise}
*/
<% } %>
notification: function notification(options) {
<% if (tauri.whitelist.notification === true || tauri.whitelist.all === true) { %>
if (_typeof(options) === 'object') {
Object.freeze(options);
}
return window.tauri.isNotificationPermissionGranted()
.then(function (permission) {
if (permission) {
return window.tauri.promisified({
cmd: 'notification',
options: typeof options === 'string' ? {
body: options
} : options
});
}
})
<% } else { %>
<% if (ctx.dev) { %>
return __whitelistWarning('notification')
<% } %>
return __reject()
<% } %>
},
isNotificationPermissionGranted: function isNotificationPermissionGranted() {
<% if (tauri.whitelist.notification === true || tauri.whitelist.all === true) { %>
if (window.Notification.permission !== 'default' && window.Notification.permission !== 'loading') {
return Promise.resolve(window.Notification.permission === 'granted')
}
return window.tauri.promisified({
cmd: 'isNotificationPermissionGranted'
})
<% } else { %>
<% if (ctx.dev) { %>
return __whitelistWarning('notification')
<% } %>
return __reject()
<% } %>
},
requestNotificationPermission: function requestNotificationPermission() {
<% if (tauri.whitelist.notification === true || tauri.whitelist.all === true) { %>
return window.tauri.promisified({
cmd: 'requestNotificationPermission'
}).then(function (state) {
setNotificationPermission(state)
return state
})
<% } else { %>
<% if (ctx.dev) { %>
return __whitelistWarning('notification')
<% } %>
return __reject()
<% } %>
},
loadAsset: function loadAsset(assetName, assetType) {
return this.promisified({
cmd: 'loadAsset',
asset: assetName,
assetType: assetType || 'unknown'
})
},
cliMatches: function () {
<% if (tauri.cli) { %>
return this.promisified({
cmd: 'cliMatches'
})
<% } else { %>
<% if (ctx.dev) { %>
console.error('You must add the CLI args configuration under tauri.conf.json > tauri > cli')
return __reject()
<% } %>
return __reject()
<% } %>
keys.push.apply(keys, symbols);
}
};
return keys;
}
<% if (tauri.whitelist.notification === true || tauri.whitelist.all === true) { %>
var notificationPermissionSettable = false
var notificationPermission = 'default'
function setNotificationPermission(value) {
notificationPermissionSettable = true
window.Notification.permission = value
notificationPermissionSettable = false
}
window.Notification = function (title, options) {
if (options === void 0) {
options = {}
function _objectSpread(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i] != null ? arguments[i] : {};
if (i % 2) {
ownKeys(source, true).forEach(function (key) {
_defineProperty(target, key, source[key]);
});
} else if (Object.getOwnPropertyDescriptors) {
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
} else {
ownKeys(source).forEach(function (key) {
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
});
}
options.title = title
window.tauri.notification(options)
}
window.Notification.requestPermission = window.tauri.requestNotificationPermission
return target;
}
Object.defineProperty(window.Notification, 'permission', {
enumerable: true,
get: function () {
return notificationPermission
},
set: function(v) {
if (!notificationPermissionSettable) {
throw new Error("Readonly property")
}
notificationPermission = v
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
if (!window.__TAURI__) {
window.__TAURI__ = {}
}
window.__TAURI__.invoke = function invoke(args) {
window.external.invoke(JSON.stringify(args))
}
window.__TAURI__.transformCallback = function transformCallback(callback) {
var once = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false
var identifier = uid()
window[identifier] = function (result) {
if (once) {
delete window[identifier]
}
});
setNotificationPermission('loading')
window.tauri.isNotificationPermissionGranted()
.then(function (response) {
if (response === null) {
setNotificationPermission('default')
} else {
setNotificationPermission(response ? 'granted' : 'denied')
}
})
<% } %>
return callback && callback(result)
}
return identifier;
}
window.__TAURI__.promisified = function promisified(args) {
var _this = this;
return new Promise(function (resolve, reject) {
_this.invoke(_objectSpread({
callback: _this.transformCallback(resolve),
error: _this.transformCallback(reject)
}, args))
})
}
window.__TAURI__.loadAsset = function loadAsset(assetName, assetType) {
return this.promisified({
cmd: 'loadAsset',
asset: assetName,
assetType: assetType || 'unknown'
})
}
// init tauri API
try {
window.tauri.invoke({
window.__TAURI__.invoke({
cmd: 'init'
})
} catch (e) {
window.addEventListener('DOMContentLoaded', function () {
window.tauri.invoke({
window.__TAURI__.invoke({
cmd: 'init'
})
}, true)
@ -831,7 +144,7 @@ switch (navigator.platform) {
var target = e.target
while (target != null) {
if (target.matches ? target.matches('img') : target.msMatchesSelector('img')) {
window.tauri.loadAsset(target.src, 'image')
window.__TAURI__.loadAsset(target.src, 'image')
.then(function (img) {
target.src = img
})
@ -848,7 +161,10 @@ switch (navigator.platform) {
while (target != null) {
if (target.matches ? target.matches('a') : target.msMatchesSelector('a')) {
if (target.href && target.href.startsWith('http') && target.target === '_blank') {
window.tauri.open(target.href)
window.__TAURI__.invoke({
cmd: 'open',
uri: target.href
})
e.preventDefault()
}
break

View File

@ -1,8 +1,9 @@
<!DOCTYPE html>
<html>
<body>
<script>
function callBackEnd (route, args) {
function callBackEnd(route, args) {
console.log(route)
console.log(args)
var xhr = new XMLHttpRequest()
@ -17,15 +18,15 @@
xhr.send(JSON.stringify(args))
}
function reply (args) {
function reply(args) {
return callBackEnd('reply', args)
}
function sendError (error) {
function sendError(error) {
return callBackEnd('error', error)
}
function testFs (dir) {
function testFs(dir) {
var contents = 'TAURI E2E TEST FILE'
var commandSuffix = (dir ? 'WithDir' : '')
@ -33,37 +34,54 @@
dir: dir || null
}
return window.tauri.writeFile({
return window.__TAURI__.fs.writeFile({
file: 'tauri-test.txt',
contents: contents
}, options).then(function (res) {
reply({ cmd: 'writeFile' + commandSuffix })
return window.tauri.readTextFile('tauri-test.txt', options).then(function (res) {
reply({
cmd: 'writeFile' + commandSuffix
})
return window.__TAURI__.fs.readTextFile('tauri-test.txt', options).then(function (res) {
if (res === contents) {
reply({ cmd: 'readFile' + commandSuffix })
reply({
cmd: 'readFile' + commandSuffix
})
return window.tauri.readDir('.', options).then(res => {
reply({ cmd: 'readDir' + commandSuffix })
return window.tauri.copyFile('tauri-test.txt', 'tauri-test-copy.txt', options)
return window.__TAURI__.fs.readDir('.', options).then(res => {
reply({
cmd: 'readDir' + commandSuffix
})
return window.__TAURI__.fs.copyFile('tauri-test.txt', 'tauri-test-copy.txt', options)
.then(function (res) {
reply({ cmd: 'copyFile' + commandSuffix })
reply({
cmd: 'copyFile' + commandSuffix
})
return window.tauri.createDir('tauri-test-dir', options).then(function (res) {
reply({ cmd: 'createDir' + commandSuffix })
return window.tauri.removeDir('tauri-test-dir', options).then(function (res) {
reply({ cmd: 'removeDir' + commandSuffix })
return window.__TAURI__.fs.createDir('tauri-test-dir', options).then(function (res) {
reply({
cmd: 'createDir' + commandSuffix
})
}).then(function (res) {
return window.tauri.renameFile('tauri-test.txt', 'tauri.testt.txt', options).then(function (res) {
reply({ cmd: 'renameFile' + commandSuffix })
return Promise.all([
window.tauri.removeFile('tauri.testt.txt', options),
window.tauri.removeFile('tauri-test-copy.txt', options)
]).then(function (res) {
reply({ cmd: 'removeFile' + commandSuffix })
return window.__TAURI__.fs.removeDir('tauri-test-dir', options).then(function (res) {
reply({
cmd: 'removeDir' + commandSuffix
})
})
}).then(function (res) {
return window.__TAURI__.fs.renameFile('tauri-test.txt', 'tauri.testt.txt', options)
.then(function (res) {
reply({
cmd: 'renameFile' + commandSuffix
})
return Promise.all([
window.__TAURI__.fs.removeFile('tauri.testt.txt', options),
window.__TAURI__.fs.removeFile('tauri-test-copy.txt', options)
]).then(function (res) {
reply({
cmd: 'removeFile' + commandSuffix
})
})
})
})
})
})
@ -74,21 +92,26 @@
}).catch(sendError)
}
window.tauri.listen('reply', function (res) {
reply({ cmd: 'listen' })
window.__TAURI__.event.listen('reply', function (res) {
reply({
cmd: 'listen'
})
})
window.onTauriInit = function () {
window.tauri.emit('hello')
window.__TAURI__.event.emit('hello')
}
testFs(null).then(function () {
testFs(window.tauri.Dir.Config)
testFs(window.__TAURI__.fs.Dir.Config)
})
setTimeout(function () {
window.tauri.invoke({ cmd: 'exit' })
window.__TAURI__.invoke({
cmd: 'exit'
})
}, 15000)
</script>
</body>
</html>
</html>

View File

@ -12,7 +12,6 @@
"paths": {
"types": ["src/types"]
},
"resolveJsonModule": true
},
"include": ["src"]
"include": ["src", "api-src"]
}

View File

@ -20,7 +20,7 @@ module.exports = {
exclude: /node_modules/
},
{
test: /templates[\\/](tauri|mutation-observer)\.js/,
test: /(templates|api)[\\/].+\.js/,
use: 'raw-loader'
},
{

View File

@ -2,7 +2,7 @@
# yarn lockfile v1
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.1":
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.1", "@babel/code-frame@^7.8.3":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.1.tgz#d5481c5095daa1c57e16e54c6f9198443afb49ff"
integrity sha512-IGhtTmpjGbYzcEDOw7DcQtbQSXcG9ftmAXtWTu9V936vDye4xjjekktFAtgZsWpzTj/X01jocB46mTywm/4SZw==
@ -208,7 +208,7 @@
dependencies:
"@babel/types" "^7.10.1"
"@babel/helper-module-imports@^7.10.1":
"@babel/helper-module-imports@^7.10.1", "@babel/helper-module-imports@^7.7.4":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.1.tgz#dd331bd45bccc566ce77004e9d05fe17add13876"
integrity sha512-SFxgwYmZ3HZPyZwJRiVNLRHWuW2OgE5k2nrVs6D9Iv4PPnXVffuEHy83Sfx/l4SqF+5kyJXjAyUmrG7tNm+qVg==
@ -1189,6 +1189,72 @@
"@nodelib/fs.scandir" "2.1.3"
fastq "^1.6.0"
"@rollup/plugin-babel@^5.0.3":
version "5.0.3"
resolved "https://registry.yarnpkg.com/@rollup/plugin-babel/-/plugin-babel-5.0.3.tgz#8d416865b0da79faf14e07c8d233abe0eac0753d"
integrity sha512-NlaPf4E6YFxeOCbqc+A2PTkB1BSy3rfKu6EJuQ1MGhMHpTVvMqKi6Rf0DlwtnEsTNK9LueUgsGEgp5Occ4KDVA==
dependencies:
"@babel/helper-module-imports" "^7.7.4"
"@rollup/pluginutils" "^3.0.8"
"@rollup/plugin-commonjs@13.0.0":
version "13.0.0"
resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-13.0.0.tgz#8a1d684ba6848afe8b9e3d85649d4b2f6f7217ec"
integrity sha512-Anxc3qgkAi7peAyesTqGYidG5GRim9jtg8xhmykNaZkImtvjA7Wsqep08D2mYsqw1IF7rA3lYfciLgzUSgRoqw==
dependencies:
"@rollup/pluginutils" "^3.0.8"
commondir "^1.0.1"
estree-walker "^1.0.1"
glob "^7.1.2"
is-reference "^1.1.2"
magic-string "^0.25.2"
resolve "^1.11.0"
"@rollup/plugin-json@4.1.0":
version "4.1.0"
resolved "https://registry.yarnpkg.com/@rollup/plugin-json/-/plugin-json-4.1.0.tgz#54e09867ae6963c593844d8bd7a9c718294496f3"
integrity sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw==
dependencies:
"@rollup/pluginutils" "^3.0.8"
"@rollup/plugin-node-resolve@8.0.1":
version "8.0.1"
resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-8.0.1.tgz#364b5938808ee6b5164dea5ef7291be3f7395199"
integrity sha512-KIeAmueDDaYMqMBnUngLVVZhURwxA12nq/YB6nGm5/JpVyOMwI1fCVU3oL/dAnnLBG7oiPXntO5LHOiMrfNXCA==
dependencies:
"@rollup/pluginutils" "^3.0.8"
"@types/resolve" "0.0.8"
builtin-modules "^3.1.0"
deep-freeze "^0.0.1"
deepmerge "^4.2.2"
is-module "^1.0.0"
resolve "^1.14.2"
"@rollup/plugin-sucrase@^3.0.2":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@rollup/plugin-sucrase/-/plugin-sucrase-3.0.2.tgz#681cacef67a0328a903e5f431f461d73899b545b"
integrity sha512-6fglZKerNRlGN7MJLuUX9fFpOT/md4mqje5pC1gltJAdFo++5NqTfPmcJ9sgXSytwqAK6jiwnUQwbBovieaEzw==
dependencies:
"@rollup/pluginutils" "^3.0.1"
sucrase "^3.10.1"
"@rollup/plugin-typescript@4.1.2":
version "4.1.2"
resolved "https://registry.yarnpkg.com/@rollup/plugin-typescript/-/plugin-typescript-4.1.2.tgz#6f910430276ae3e53a47a12ad65820627e7b6ad9"
integrity sha512-+7UlGat/99e2JbmGNnIauxwEhYLwrL7adO/tSJxUN57xrrS3Ps+ZzYpLCDGPZJ57j+ZJTZLLN89KXW9JMEB+jg==
dependencies:
"@rollup/pluginutils" "^3.0.1"
resolve "^1.14.1"
"@rollup/pluginutils@^3.0.1", "@rollup/pluginutils@^3.0.8":
version "3.1.0"
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b"
integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==
dependencies:
"@types/estree" "0.0.39"
estree-walker "^1.0.1"
picomatch "^2.2.2"
"@sindresorhus/is@^0.14.0":
version "0.14.0"
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea"
@ -1314,6 +1380,16 @@
resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d"
integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==
"@types/estree@0.0.39":
version "0.0.39"
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==
"@types/estree@0.0.44":
version "0.0.44"
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.44.tgz#980cc5a29a3ef3bea6ff1f7d021047d7ea575e21"
integrity sha512-iaIVzr+w2ZJ5HkidlZ3EJM8VTZb2MJLCjw3V+505yVts0gRC4UMvjw0d1HPtGqI/HQC/KdsYtayfzl+AXY2R8g==
"@types/events@*":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"
@ -1447,6 +1523,13 @@
resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.4.tgz#15925414e0ad2cd765bfef58842f7e26a7accb24"
integrity sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==
"@types/resolve@0.0.8":
version "0.0.8"
resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194"
integrity sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==
dependencies:
"@types/node" "*"
"@types/sharp@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@types/sharp/-/sharp-0.25.0.tgz#e21501779b110e26f33cec7fb1969e49e0cdc3c0"
@ -1879,6 +1962,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0, ansi-styles@^4.2.1:
"@types/color-name" "^1.1.1"
color-convert "^2.0.1"
any-promise@^1.0.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
integrity sha1-q8av7tzqUugJzcA3au0845Y10X8=
anymatch@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb"
@ -2439,6 +2527,11 @@ buffer@^5.2.1, buffer@^5.5.0:
base64-js "^1.0.2"
ieee754 "^1.1.4"
builtin-modules@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.1.0.tgz#aad97c15131eb76b65b50ef208e7584cd76a7484"
integrity sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==
builtin-status-codes@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
@ -2845,6 +2938,11 @@ commander@^2.20.0, commander@~2.20.3:
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
commander@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==
commander@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae"
@ -3292,6 +3390,11 @@ deep-extend@^0.6.0:
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
deep-freeze@^0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/deep-freeze/-/deep-freeze-0.0.1.tgz#3a0b0005de18672819dfd38cd31f91179c893e84"
integrity sha1-OgsABd4YZygZ39OM0x+RF5yJPoQ=
deep-is@^0.1.3, deep-is@~0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
@ -3897,6 +4000,11 @@ estraverse@^5.1.0:
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.1.0.tgz#374309d39fd935ae500e7b92e8a6b4c720e59642"
integrity sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw==
estree-walker@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700"
integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==
esutils@^2.0.2:
version "2.0.3"
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
@ -4247,6 +4355,15 @@ find-cache-dir@^2.1.0:
make-dir "^2.0.0"
pkg-dir "^3.0.0"
find-cache-dir@^3.3.1:
version "3.3.1"
resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.1.tgz#89b33fad4a4670daa94f855f7fbe31d6d84fe880"
integrity sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==
dependencies:
commondir "^1.0.1"
make-dir "^3.0.2"
pkg-dir "^4.1.0"
find-up@^1.0.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f"
@ -4362,6 +4479,15 @@ fs-constants@^1.0.0:
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
fs-extra@8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
dependencies:
graceful-fs "^4.2.0"
jsonfile "^4.0.0"
universalify "^0.1.0"
fs-extra@9.0.1:
version "9.0.1"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.0.1.tgz#910da0062437ba4c39fedd863f1675ccfefcb9fc"
@ -4522,7 +4648,7 @@ glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0:
dependencies:
is-glob "^4.0.1"
glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
glob@7.1.6, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
version "7.1.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
@ -5237,6 +5363,11 @@ is-installed-globally@^0.3.1:
global-dirs "^2.0.1"
is-path-inside "^3.0.1"
is-module@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591"
integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=
is-natural-number@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8"
@ -5301,6 +5432,13 @@ is-potential-custom-element-name@^1.0.0:
resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz#0c52e54bcca391bb2c494b21e8626d7336c6e397"
integrity sha1-DFLlS8yjkbssSUsh6GJtczbG45c=
is-reference@^1.1.2:
version "1.2.0"
resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.0.tgz#d938b0cf85a0df09849417b274f02fb509293599"
integrity sha512-ZVxq+5TkOx6GQdnoMm2aRdCKADdcrOWXLGzGT+vIA8DMpqEJaRk5AL1bS80zJ2bjHunVmjdzfCt0e4BymIEqKQ==
dependencies:
"@types/estree" "0.0.44"
is-regex@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae"
@ -5932,6 +6070,13 @@ json5@^2.1.2:
dependencies:
minimist "^1.2.5"
jsonfile@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=
optionalDependencies:
graceful-fs "^4.1.6"
jsonfile@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.0.1.tgz#98966cba214378c8c84b82e085907b40bf614179"
@ -6315,6 +6460,13 @@ lru-cache@^5.1.1:
dependencies:
yallist "^3.0.2"
magic-string@^0.25.2:
version "0.25.7"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051"
integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==
dependencies:
sourcemap-codec "^1.4.4"
make-dir@^1.0.0, make-dir@^1.2.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c"
@ -6330,7 +6482,7 @@ make-dir@^2.0.0:
pify "^4.0.1"
semver "^5.6.0"
make-dir@^3.0.0:
make-dir@^3.0.0, make-dir@^3.0.2:
version "3.1.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==
@ -6594,6 +6746,20 @@ ms@2.1.2, ms@^2.1.1:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
mute-stream@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==
mz@^2.7.0:
version "2.7.0"
resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32"
integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==
dependencies:
any-promise "^1.0.0"
object-assign "^4.0.1"
thenify-all "^1.0.0"
nan@^2.12.1:
version "2.14.1"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01"
@ -7237,7 +7403,7 @@ performance-now@^2.1.0:
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1:
picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1, picomatch@^2.2.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad"
integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==
@ -7290,7 +7456,7 @@ pkg-dir@^3.0.0:
dependencies:
find-up "^3.0.0"
pkg-dir@^4.2.0:
pkg-dir@^4.1.0, pkg-dir@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3"
integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==
@ -7537,7 +7703,7 @@ querystring@0.2.0:
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=
randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5:
randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
@ -7884,7 +8050,14 @@ resolve-url@^0.2.1:
resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
resolve@^1.10.0, resolve@^1.10.1, resolve@^1.13.1, resolve@^1.17.0, resolve@^1.3.2:
resolve@1.15.1:
version "1.15.1"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.1.tgz#27bdcdeffeaf2d6244b95bb0f9f4b4653451f3e8"
integrity sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==
dependencies:
path-parse "^1.0.6"
resolve@^1.10.0, resolve@^1.10.1, resolve@^1.11.0, resolve@^1.13.1, resolve@^1.14.1, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.3.2:
version "1.17.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444"
integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==
@ -7945,6 +8118,34 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
hash-base "^3.0.0"
inherits "^2.0.1"
rollup-plugin-terser@6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/rollup-plugin-terser/-/rollup-plugin-terser-6.1.0.tgz#071866585aea104bfbb9dd1019ac523e63c81e45"
integrity sha512-4fB3M9nuoWxrwm39habpd4hvrbrde2W2GG4zEGPQg1YITNkM3Tqur5jSuXlWNzbv/2aMLJ+dZJaySc3GCD8oDw==
dependencies:
"@babel/code-frame" "^7.8.3"
jest-worker "^26.0.0"
serialize-javascript "^3.0.0"
terser "^4.7.0"
rollup-plugin-typescript2@0.27.1:
version "0.27.1"
resolved "https://registry.yarnpkg.com/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.27.1.tgz#4f27193408a8f040139eed3e3db7b0c7f3668200"
integrity sha512-RJl77Bbj1EunAQDC3dK/O2HWuSUX3oJbRGzyLoS5o9W4Hs1Nix3Gavqj1Lzs5Y6Ff4H2xXfmZ1WWUQCYocSbzQ==
dependencies:
"@rollup/pluginutils" "^3.0.8"
find-cache-dir "^3.3.1"
fs-extra "8.1.0"
resolve "1.15.1"
tslib "1.11.2"
rollup@2.17.1:
version "2.17.1"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.17.1.tgz#d01a27c1b76e42efe2cd786609589f6332e81aa6"
integrity sha512-lVrtCXJ+08Eapa0SfApLmRNWNWm2FsYFnLPIJZJvZz2uI2Gv+dfPyu1zgF7KKF/HYFJDvjxbdCbI8lUVMnG7Sg==
optionalDependencies:
fsevents "~2.1.2"
rsvp@^4.8.4:
version "4.8.5"
resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734"
@ -8092,6 +8293,13 @@ serialize-javascript@^2.1.2:
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61"
integrity sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==
serialize-javascript@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-3.1.0.tgz#8bf3a9170712664ef2561b44b691eafe399214ea"
integrity sha512-JIJT1DGiWmIKhzRsG91aS6Ze4sFUrYbltlkg2onR5OrnNM02Kl/hnY/T4FN2omvyeBbQmMJv+K4cPOpGzOTFBg==
dependencies:
randombytes "^2.1.0"
set-blocking@^2.0.0, set-blocking@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
@ -8331,6 +8539,11 @@ source-map@^0.7.3:
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
sourcemap-codec@^1.4.4:
version "1.4.8"
resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
spdx-correct@^3.0.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9"
@ -8653,6 +8866,25 @@ strip-outer@^1.0.0:
dependencies:
escape-string-regexp "^1.0.2"
sucrase@^3.10.1:
version "3.15.0"
resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.15.0.tgz#78596a78be7264a65b52ed8d873883413ef0220c"
integrity sha512-05TJOUfMgckH7wKqfk/1p4G6q16nIeW/GHQwD44vkT0mQMqqzgfHCwkX3whNmwyOo7nVF0jDLwVu/qOBTtsscw==
dependencies:
commander "^4.0.0"
glob "7.1.6"
lines-and-columns "^1.1.6"
mz "^2.7.0"
pirates "^4.0.1"
ts-interface-checker "^0.1.9"
supports-color@6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3"
integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==
dependencies:
has-flag "^3.0.0"
supports-color@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
@ -8822,6 +9054,15 @@ terser@^4.1.2:
source-map "~0.6.1"
source-map-support "~0.5.12"
terser@^4.7.0:
version "4.8.0"
resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17"
integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==
dependencies:
commander "^2.20.0"
source-map "~0.6.1"
source-map-support "~0.5.12"
test-exclude@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e"
@ -8843,6 +9084,20 @@ then-fs@^2.0.0:
dependencies:
promise ">=3.2 <8"
thenify-all@^1.0.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726"
integrity sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=
dependencies:
thenify ">= 3.1.0 < 4"
"thenify@>= 3.1.0 < 4":
version "3.3.1"
resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f"
integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==
dependencies:
any-promise "^1.0.0"
throat@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b"
@ -8978,6 +9233,11 @@ trim-repeated@^1.0.0:
dependencies:
escape-string-regexp "^1.0.2"
ts-interface-checker@^0.1.9:
version "0.1.11"
resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.11.tgz#07e7eddb08212f83fef12c253d0cefa8c70fe1bc"
integrity sha512-Jx6cFBiuCQrRl3CgoIOamIE/toZ8jQJbIlsLGpkBiUpCEUyFcyZ2pvjP8kSXIcz8V5v/murgm/5EfIQapUmh6A==
ts-loader@7.0.5:
version "7.0.5"
resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-7.0.5.tgz#789338fb01cb5dc0a33c54e50558b34a73c9c4c5"
@ -8999,6 +9259,16 @@ tsconfig-paths@^3.9.0:
minimist "^1.2.0"
strip-bom "^3.0.0"
tslib@1.11.2:
version "1.11.2"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.2.tgz#9c79d83272c9a7aaf166f73915c9667ecdde3cc9"
integrity sha512-tTSkux6IGPnUGUd1XAZHcpu85MOkIl5zX49pO+jfsie3eP0B6pyhOlLXm3cAC6T7s+euSDDUUV+Acop5WmtkVg==
tslib@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.0.tgz#18d13fc2dce04051e20f074cc8387fd8089ce4f3"
integrity sha512-lTqkx847PI7xEDYJntxZH89L2/aXInsyF2luSafe/+0fHOMjlBNXdH6th7f70qxLDhul7KZK0zC8V5ZIyHl0/g==
tslib@^1.8.1, tslib@^1.9.0:
version "1.13.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043"
@ -9148,6 +9418,11 @@ unique-string@^2.0.0:
dependencies:
crypto-random-string "^2.0.0"
universalify@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
universalify@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-1.0.0.tgz#b61a1da173e8435b2fe3c67d29b9adf8594bd16d"

View File

@ -58,9 +58,9 @@ if ((Test-Path $dist_path -PathType Any) -Or (Test-Path $src_path -PathType Any)
cargo install --path cli\tauri-bundler --force
cargo install cargo-web
# install the tauri Node CLI.
# install the tauri Node CLI and transpile the TS version of the API.
cd cli\tauri.js
yarn; yarn build
yarn; yarn build;
}
```

View File

@ -37,6 +37,7 @@ tauri-api = { version = "0.6", path = "../tauri-api" }
[build-dependencies]
tauri_includedir_codegen = "0.6.0"
cfg_aliases = "0.1.0"
[dev-dependencies]
proptest = "0.10.0"

View File

@ -1,3 +1,5 @@
use cfg_aliases::cfg_aliases;
#[cfg(any(feature = "embedded-server", feature = "no-server"))]
use std::{
env,
@ -72,4 +74,48 @@ fn shared() {
tauri_path.push("tauri.conf.json");
println!("cargo:rerun-if-changed={:?}", tauri_path);
}
cfg_aliases! {
embedded_server: { feature = "embedded-server" },
no_server: { feature = "no-server" },
assets: { any(feature = "embedded-server", feature = "no-server") },
dev: { not(any(feature = "embedded-server", feature = "no-server")) },
all_api: { feature = "all-api" },
// fs
read_text_file: { any(all_api, feature = "read-text-file") },
read_binary_file: { any(all_api, feature = "read-binary-file") },
write_file: { any(all_api, feature = "write-file") },
write_binary_file: { any(all_api, feature = "write-binary-file") },
read_dir: { any(all_api, feature = "read-dir") },
copy_file: { any(all_api, feature = "copy-file") },
create_dir: { any(all_api, feature = "create_dir") },
remove_dir: { any(all_api, feature = "remove-dir") },
remove_file: { any(all_api, feature = "remove-file") },
rename_file: { any(all_api, feature = "rename-file") },
// window
set_title: { any(all_api, feature = "set-title") },
open: { any(all_api, feature = "open") },
// process
execute: { any(all_api, feature = "execute") },
// event
event: { any(all_api, feature = "event") },
// dialog
open_dialog: { any(all_api, feature = "open-dialog") },
save_dialog: { any(all_api, feature = "save-dialog") },
// http
http_request: { any(all_api, feature = "http-request") },
// cli
cli: { feature = "cli" },
// notification
notification: { any(all_api, feature = "notification") },
}
}

View File

@ -1,5 +1,5 @@
document.getElementById('cli-matches').addEventListener('click', function () {
window.tauri.cliMatches()
window.__TAURI__.cli.getMatches()
.then(registerResponse)
.catch(registerResponse)
})

View File

@ -1,5 +1,5 @@
document.getElementById('log').addEventListener('click', function () {
window.tauri.invoke({
window.__TAURI__.invoke({
cmd: 'logOperation',
event: 'tauri-click',
payload: 'this payload is optional because we used Option in Rust'
@ -7,7 +7,7 @@ document.getElementById('log').addEventListener('click', function () {
})
document.getElementById('request').addEventListener('click', function () {
window.tauri.promisified({
window.__TAURI__.promisified({
cmd: 'performRequest',
endpoint: 'dummy endpoint arg',
body: {
@ -18,6 +18,5 @@ document.getElementById('request').addEventListener('click', function () {
})
document.getElementById('event').addEventListener('click', function () {
window.tauri.emit('js-event', 'this is the payload string')
window.__TAURI__.event.emit('js-event', 'this is the payload string')
})

View File

@ -4,7 +4,7 @@ var multipleInput = document.getElementById('dialog-multiple')
var directoryInput = document.getElementById('dialog-directory')
document.getElementById('open-dialog').addEventListener('click', function () {
window.tauri.openDialog({
window.__TAURI__.dialog.open({
defaultPath: defaultPathInput.value || null,
filter: filterInput.value || null,
multiple: multipleInput.checked,
@ -13,7 +13,7 @@ document.getElementById('open-dialog').addEventListener('click', function () {
console.log(res)
var pathToRead = res
var isFile = pathToRead.match(/\S+\.\S+$/g)
window.tauri.readBinaryFile(pathToRead).then(function (response) {
window.__TAURI__.fs.readBinaryFile(pathToRead).then(function (response) {
if (isFile) {
if (pathToRead.includes('.png') || pathToRead.includes('.jpg')) {
arrayBufferToBase64(new Uint8Array(response), function (base64) {
@ -31,7 +31,7 @@ document.getElementById('open-dialog').addEventListener('click', function () {
})
document.getElementById('save-dialog').addEventListener('click', function () {
window.tauri.saveDialog({
window.__TAURI__.dialog.save({
defaultPath: defaultPathInput.value || null,
filter: filterInput.value || null
}).then(registerResponse).catch(registerResponse)

View File

@ -1,5 +1,6 @@
var dirSelect = document.getElementById('dir')
function getDir () {
function getDir() {
return dirSelect.value ? parseInt(dir.value) : null
}
@ -23,8 +24,10 @@ addClickEnterHandler(
function () {
var pathToRead = pathInput.value
var isFile = pathToRead.match(/\S+\.\S+$/g)
var opts = { dir: getDir() }
var promise = isFile ? window.tauri.readBinaryFile(pathToRead, opts) : window.tauri.readDir(pathToRead, opts)
var opts = {
dir: getDir()
}
var promise = isFile ? window.__TAURI__.fs.readBinaryFile(pathToRead, opts) : window.__TAURI__.fs.readDir(pathToRead, opts)
promise.then(function (response) {
if (isFile) {
if (pathToRead.includes('.png') || pathToRead.includes('.jpg')) {
@ -38,7 +41,7 @@ addClickEnterHandler(
var fileInput = document.getElementById('file-response')
fileInput.value = value
document.getElementById('file-save').addEventListener('click', function () {
window.tauri.writeFile({
window.__TAURI__.fs.writeFile({
file: pathToRead,
contents: fileInput.value
}, {

View File

@ -19,5 +19,5 @@ document.getElementById('make-request').addEventListener('click', function () {
}
options.body = body
window.tauri.httpRequest(options).then(registerResponse).catch(registerResponse)
})
window.__TAURI__.http.request(options).then(registerResponse).catch(registerResponse)
})

View File

@ -284,21 +284,23 @@
})
}
window.tauri.listen('rust-event', function (res) {
window.__TAURI__.event.listen('rust-event', function (res) {
document.getElementById('response').innerHTML = JSON.stringify(res)
})
document.querySelector('.logo').addEventListener('click', function () {
window.tauri.open('https://tauri.studio/')
window.__TAURI__.window.open('https://tauri.studio/')
})
var dirSelect = document.getElementById('dir')
for (var key in window.tauri.Dir) {
var value = window.tauri.Dir[key]
var opt = document.createElement("option")
opt.value = value
opt.innerHTML = key
dirSelect.appendChild(opt)
for (var key in window.__TAURI__.fs.Dir) {
if (isNaN(parseInt(key))) {
var value = window.__TAURI__.fs.Dir[key]
var opt = document.createElement("option")
opt.value = value
opt.innerHTML = key
dirSelect.appendChild(opt)
}
}
</script>

File diff suppressed because one or more lines are too long

View File

@ -4,7 +4,7 @@ addClickEnterHandler(
document.getElementById('open-url'),
urlInput,
function () {
window.tauri.open(urlInput.value)
window.__TAURI__.window.open(urlInput.value)
}
)
@ -14,6 +14,6 @@ addClickEnterHandler(
document.getElementById('set-title'),
titleInput,
function () {
window.tauri.setTitle(titleInput.value)
window.__TAURI__.window.setTitle(titleInput.value)
}
)

View File

@ -1,7 +1,8 @@
{
"build": {
"distDir": "../dist",
"devPath": "../dist"
"devPath": "../dist",
"withGlobalTauri": true
},
"ctx": {},
"tauri": {

View File

@ -1,732 +0,0 @@
/* eslint-disable */
/**
* * THIS FILE IS GENERATED AUTOMATICALLY.
* DO NOT EDIT.
*
* Please whitelist these API functions in tauri.conf.json
*
**/
/**
* @module tauri
* @description This API interface makes powerful interactions available
* to be run on client side applications. They are opt-in features, and
* must be enabled in tauri.conf.json
*
* Each binding MUST provide these interfaces in order to be compliant,
* and also whitelist them based upon the developer's settings.
*/
// polyfills
if (!String.prototype.startsWith) {
String.prototype.startsWith = function (searchString, position) {
position = position || 0
return this.substr(position, searchString.length) === searchString
}
}
// makes the window.external.invoke API available after window.location.href changes
switch (navigator.platform) {
case "Macintosh":
case "MacPPC":
case "MacIntel":
case "Mac68K":
window.external = this
invoke = function (x) {
webkit.messageHandlers.invoke.postMessage(x);
}
break;
case "Windows":
case "WinCE":
case "Win32":
case "Win64":
break;
default:
window.external = this
invoke = function (x) {
window.webkit.messageHandlers.external.postMessage(x);
}
break;
}
(function () {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1)
}
var uid = function () {
return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
s4() + '-' + s4() + s4() + s4()
}
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
/**
* @typedef {number} BaseDirectory
*/
/**
* @enum {BaseDirectory}
*/
var Dir = {
Audio: 1,
Cache: 2,
Config: 3,
Data: 4,
LocalData: 5,
Desktop: 6,
Document: 7,
Download: 8,
Executable: 9,
Font: 10,
Home: 11,
Picture: 12,
Public: 13,
Runtime: 14,
Template: 15,
Video: 16,
Resource: 17,
App: 18
}
function camelToKebab (string) {
return string.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2').toLowerCase()
}
/**
* @name return __whitelistWarning
* @description Present a stylish warning to the developer that their API
* call has not been whitelisted in tauri.conf.json
* @param {String} func - function name to warn
* @private
*/
var __whitelistWarning = function (func) {
console.warn('%c[Tauri] Danger \ntauri.' + func + ' not whitelisted 💣\n%c\nAdd to tauri.conf.json: \n\ntauri: \n whitelist: { \n ' + camelToKebab(func) + ': true \n\nReference: https://github.com/tauri-apps/tauri/wiki' + func, 'background: red; color: white; font-weight: 800; padding: 2px; font-size:1.5em', ' ')
return __reject()
}
/**
* @name __reject
* @description generates a promise used to deflect un-whitelisted tauri API calls
* Its only purpose is to maintain thenable structure in client code without
* breaking the application
* * @type {Promise<any>}
* @private
*/
var __reject = function () {
return new Promise(function (_, reject) {
reject();
});
}
window.tauri = {
Dir: Dir,
/**
* @name invoke
* @description Calls a Tauri Core feature, such as setTitle
* @param {Object} args
*/
invoke: function invoke(args) {
window.external.invoke(JSON.stringify(args));
},
/**
* @name listen
* @description Add an event listener to Tauri backend
* @param {String} event
* @param {Function} handler
* @param {Boolean} once
*/
listen: function listen(event, handler) {
var once = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
this.invoke({
cmd: 'listen',
event: event,
handler: window.tauri.transformCallback(handler, once),
once: once
});
},
/**
* @name emit
* @description Emits an evt to the Tauri back end
* @param {String} evt
* @param {Object} payload
*/
emit: function emit(evt, payload) {
this.invoke({
cmd: 'emit',
event: evt,
payload: payload
});
},
/**
* @name transformCallback
* @description Registers a callback with a uid
* @param {Function} callback
* @param {Boolean} once
* @returns {*}
*/
transformCallback: function transformCallback(callback) {
var once = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var identifier = uid();
window[identifier] = function (result) {
if (once) {
delete window[identifier];
}
return callback && callback(result);
};
return identifier;
},
/**
* @name promisified
* @description Turns a request into a chainable promise
* @param {Object} args
* @returns {Promise<any>}
*/
promisified: function promisified(args) {
var _this = this;
return new Promise(function (resolve, reject) {
_this.invoke(_objectSpread({
callback: _this.transformCallback(resolve),
error: _this.transformCallback(reject)
}, args));
});
},
/**
* @name readTextFile
* @description Accesses a non-binary file on the user's filesystem
* and returns the content. Permissions based on the app's PID owner
* @param {String} path
* @param {Object} [options]
* @param {BaseDirectory} [options.dir]
* @returns {*|Promise<any>|Promise}
*/
readTextFile: function readTextFile(path, options) {
return this.promisified({
cmd: 'readTextFile',
path: path,
options: options
});
},
/**
* @name readBinaryFile
* @description Accesses a binary file on the user's filesystem
* and returns the content. Permissions based on the app's PID owner
* @param {String} path
* @param {Object} [options]
* @param {BaseDirectory} [options.dir]
* @returns {*|Promise<any>|Promise}
*/
readBinaryFile: function readBinaryFile(path, options) {
return this.promisified({
cmd: 'readBinaryFile',
path: path,
options: options
});
},
/**
* @name writeFile
* @description Write a file to the Local Filesystem.
* Permissions based on the app's PID owner
* @param {Object} cfg
* @param {String} cfg.file
* @param {String|Binary} cfg.contents
* @param {Object} [options]
* @param {BaseDirectory} [options.dir]
*/
writeFile: function writeFile(cfg, options) {
if (_typeof(cfg) === 'object') {
Object.freeze(cfg);
}
return this.promisified({
cmd: 'writeFile',
file: cfg.file,
contents: cfg.contents,
options: options
});
},
/**
* @name readDir
* @description Reads a directory
* Permissions based on the app's PID owner
* @param {String} path
* @param {Object} [options]
* @param {Boolean} [options.recursive]
* @param {BaseDirectory} [options.dir]
* @returns {*|Promise<any>|Promise}
*/
readDir: function readDir(path, options) {
return this.promisified({
cmd: 'readDir',
path: path,
options: options
});
},
/**
* @name createDir
* @description Creates a directory
* Permissions based on the app's PID owner
* @param {String} path
* @param {Object} [options]
* @param {Boolean} [options.recursive]
* @param {BaseDirectory} [options.dir]
* @returns {*|Promise<any>|Promise}
*/
createDir: function createDir(path, options) {
return this.promisified({
cmd: 'createDir',
path: path,
options: options
});
},
/**
* @name removeDir
* @description Removes a directory
* Permissions based on the app's PID owner
* @param {String} path
* @param {Object} [options]
* @param {Boolean} [options.recursive]
* @param {BaseDirectory} [options.dir]
* @returns {*|Promise<any>|Promise}
*/
removeDir: function removeDir(path, options) {
return this.promisified({
cmd: 'removeDir',
path: path,
options: options
});
},
/**
* @name copyFile
* @description Copy file
* Permissions based on the app's PID owner
* @param {String} source
* @param {String} destination
* @param {Object} [options]
* @param {BaseDirectory} [options.dir]
* @returns {*|Promise<any>|Promise}
*/
copyFile: function copyFile(source, destination, options) {
return this.promisified({
cmd: 'copyFile',
source: source,
destination: destination,
options: options
});
},
/**
* @name removeFile
* @description Removes a file
* Permissions based on the app's PID owner
* @param {String} path
* @param {Object} [options]
* @param {BaseDirectory} [options.dir]
* @returns {*|Promise<any>|Promise}
*/
removeFile: function removeFile(path, options) {
return this.promisified({
cmd: 'removeFile',
path: path,
options: options
});
},
/**
* @name renameFile
* @description Renames a file
* Permissions based on the app's PID owner
* @param {String} path
* @param {Object} [options]
* @param {BaseDirectory} [options.dir]
* @returns {*|Promise<any>|Promise}
*/
renameFile: function renameFile(oldPath, newPath, options) {
return this.promisified({
cmd: 'renameFile',
oldPath: oldPath,
newPath: newPath,
options: options
});
},
/**
* @name setTitle
* @description Set the application's title
* @param {String} title
*/
setTitle: function setTitle(title) {
this.invoke({
cmd: 'setTitle',
title: title
});
},
/**
* @name open
* @description Open an URI
* @param {String} uri
*/
open: function open(uri) {
this.invoke({
cmd: 'open',
uri: uri
});
},
/**
* @name execute
* @description Execute a program with arguments.
* Permissions based on the app's PID owner
* @param {String} command
* @param {String|Array} args
* @returns {*|Promise<any>|Promise}
*/
execute: function execute(command, args) {
if (_typeof(args) === 'object') {
Object.freeze(args);
}
return this.promisified({
cmd: 'execute',
command: command,
args: typeof args === 'string' ? [args] : args
});
},
/**
* @name openDialog
* @description Open a file/directory selection dialog
* @param {String} [options]
* @param {String} [options.filter]
* @param {String} [options.defaultPath]
* @param {Boolean} [options.multiple=false]
* @param {Boolean} [options.directory=false]
* @returns {Promise<String|String[]>} promise resolving to the select path(s)
*/
openDialog: function openDialog(options) {
var opts = options || {}
if (_typeof(options) === 'object') {
opts.default_path = opts.defaultPath
Object.freeze(options);
}
return this.promisified({
cmd: 'openDialog',
options: opts
});
},
/**
* @name saveDialog
* @description Open a file/directory save dialog
* @param {String} [options]
* @param {String} [options.filter]
* @param {String} [options.defaultPath]
* @returns {Promise<String>} promise resolving to the select path
*/
saveDialog: function saveDialog(options) {
var opts = options || {}
if (_typeof(options) === 'object') {
opts.default_path = opts.defaultPath
Object.freeze(options);
}
return this.promisified({
cmd: 'saveDialog',
options: opts
});
},
/**
* @name httpRequest
* @description Makes an HTTP request
* @param {Object} options
* @param {String} options.method GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, CONNECT or TRACE
* @param {String} options.url the request URL
* @param {Object} [options.headers] the request headers
* @param {Object} [options.params] the request query params
* @param {Object|String|Binary} [options.body] the request body
* @param {Boolean} followRedirects whether to follow redirects or not
* @param {Number} maxRedirections max number of redirections
* @param {Number} connectTimeout request connect timeout
* @param {Number} readTimeout request read timeout
* @param {Number} timeout request timeout
* @param {Boolean} allowCompression
* @param {Number} [responseType=1] 1 - JSON, 2 - Text, 3 - Binary
* @param {Number} [bodyType=3] 1 - Form, 2 - File, 3 - Auto
* @returns {Promise<any>}
*/
httpRequest: function httpRequest(options) {
return this.promisified({
cmd: 'httpRequest',
options: options
});
},
/**
* @name notification
* @description Display a desktop notification
* @param {Object|String} options the notifications options if an object, otherwise its body
* @param {String} [options.summary] the notification's summary
* @param {String} options.body the notification's body
* @param {String} [options.icon] the notifications's icon
* @returns {*|Promise<any>|Promise}
*/
notification: function notification(options) {
if (_typeof(options) === 'object') {
Object.freeze(options);
}
return window.tauri.isNotificationPermissionGranted()
.then(function (permission) {
if (permission) {
return window.tauri.promisified({
cmd: 'notification',
options: typeof options === 'string' ? {
body: options
} : options
});
}
})
},
isNotificationPermissionGranted: function isNotificationPermissionGranted() {
if (window.Notification.permission !== 'default' && window.Notification.permission !== 'loading') {
return Promise.resolve(window.Notification.permission === 'granted')
}
return window.tauri.promisified({
cmd: 'isNotificationPermissionGranted'
})
},
requestNotificationPermission: function requestNotificationPermission() {
return window.tauri.promisified({
cmd: 'requestNotificationPermission'
}).then(function (state) {
setNotificationPermission(state)
return state
})
},
loadAsset: function loadAsset(assetName, assetType) {
return this.promisified({
cmd: 'loadAsset',
asset: assetName,
assetType: assetType || 'unknown'
})
},
cliMatches: function () {
return this.promisified({
cmd: 'cliMatches'
})
}
};
var notificationPermissionSettable = false
var notificationPermission = 'default'
function setNotificationPermission(value) {
notificationPermissionSettable = true
window.Notification.permission = value
notificationPermissionSettable = false
}
window.Notification = function (title, options) {
if (options === void 0) {
options = {}
}
options.title = title
window.tauri.notification(options)
}
window.Notification.requestPermission = window.tauri.requestNotificationPermission
Object.defineProperty(window.Notification, 'permission', {
enumerable: true,
get: function () {
return notificationPermission
},
set: function(v) {
if (!notificationPermissionSettable) {
throw new Error("Readonly property")
}
notificationPermission = v
}
});
setNotificationPermission('loading')
window.tauri.isNotificationPermissionGranted()
.then(function (response) {
if (response === null) {
setNotificationPermission('default')
} else {
setNotificationPermission(response ? 'granted' : 'denied')
}
})
// init tauri API
try {
window.tauri.invoke({
cmd: 'init'
})
} catch (e) {
window.addEventListener('DOMContentLoaded', function () {
window.tauri.invoke({
cmd: 'init'
})
}, true)
}
document.addEventListener('error', function (e) {
var target = e.target
while (target != null) {
if (target.matches ? target.matches('img') : target.msMatchesSelector('img')) {
window.tauri.loadAsset(target.src, 'image')
.then(function (img) {
target.src = img
})
break
}
target = target.parentElement
}
}, true)
// open <a href="..."> links with the Tauri API
function __openLinks() {
document.querySelector('body').addEventListener('click', function (e) {
var target = e.target
while (target != null) {
if (target.matches ? target.matches('a') : target.msMatchesSelector('a')) {
if (target.href && target.href.startsWith('http') && target.target === '_blank') {
window.tauri.open(target.href)
e.preventDefault()
}
break
}
target = target.parentElement
}
}, true)
}
if (document.readyState === 'complete' || document.readyState === 'interactive') {
__openLinks()
} else {
window.addEventListener('DOMContentLoaded', function () {
__openLinks()
}, true)
}
})()

View File

@ -10,7 +10,7 @@ use std::{
use web_view::{builder, Content, WebView};
use super::App;
#[cfg(feature = "embedded-server")]
#[cfg(embedded_server)]
use crate::api::tcp::{get_available_port, port_is_available};
use tauri_api::config::get;
@ -20,7 +20,7 @@ pub(crate) fn run(application: &mut App) -> crate::Result<()> {
let main_content = setup_content()?;
// setup the server url for the embedded-server
#[cfg(feature = "embedded-server")]
#[cfg(embedded_server)]
let server_url = {
if let Content::Url(ref url) = &main_content {
String::from(url)
@ -46,7 +46,7 @@ pub(crate) fn run(application: &mut App) -> crate::Result<()> {
)?;
// spawn the embedded server on our server url
#[cfg(feature = "embedded-server")]
#[cfg(embedded_server)]
spawn_server(server_url.to_string())?;
// spin up the updater process
@ -60,7 +60,7 @@ pub(crate) fn run(application: &mut App) -> crate::Result<()> {
}
// setup content for dev-server
#[cfg(not(any(feature = "embedded-server", feature = "no-server")))]
#[cfg(dev)]
fn setup_content() -> crate::Result<Content<String>> {
let config = get()?;
if config.build.dev_path.starts_with("http") {
@ -79,7 +79,7 @@ fn setup_content() -> crate::Result<Content<String>> {
}
// setup content for embedded server
#[cfg(feature = "embedded-server")]
#[cfg(embedded_server)]
fn setup_content() -> crate::Result<Content<String>> {
let (port, valid) = setup_port()?;
let url = (if valid {
@ -93,14 +93,14 @@ fn setup_content() -> crate::Result<Content<String>> {
}
// setup content for no-server
#[cfg(feature = "no-server")]
#[cfg(no_server)]
fn setup_content() -> crate::Result<Content<String>> {
let html = include_str!(concat!(env!("OUT_DIR"), "/index.tauri.html"));
Ok(Content::Html(html.to_string()))
}
// get the port for the embedded server
#[cfg(feature = "embedded-server")]
#[cfg(embedded_server)]
fn setup_port() -> crate::Result<(String, bool)> {
let config = get()?;
if config.tauri.embedded_server.port == "random" {
@ -120,7 +120,7 @@ fn setup_port() -> crate::Result<(String, bool)> {
}
// setup the server url for embedded server
#[cfg(feature = "embedded-server")]
#[cfg(embedded_server)]
fn setup_server_url(port: String) -> crate::Result<String> {
let config = get()?;
let mut url = format!("{}:{}", config.tauri.embedded_server.host, port);
@ -131,7 +131,7 @@ fn setup_server_url(port: String) -> crate::Result<String> {
}
// spawn the embedded server
#[cfg(feature = "embedded-server")]
#[cfg(embedded_server)]
fn spawn_server(server_url: String) -> crate::Result<()> {
spawn(move || {
let server = tiny_http::Server::http(
@ -294,13 +294,13 @@ mod test {
env::set_current_dir(tauri_dir).expect("failed to change cwd");
let res = super::setup_content();
#[cfg(feature = "embedded-server")]
#[cfg(embedded_server)]
match res {
Ok(Content::Url(u)) => assert!(u.contains("http://")),
_ => assert!(false),
}
#[cfg(feature = "no-server")]
#[cfg(no_server)]
match res {
Ok(Content::Html(s)) => {
let dist_dir = match option_env!("TAURI_DIST_DIR") {
@ -319,7 +319,7 @@ mod test {
_ => assert!(false),
}
#[cfg(not(any(feature = "embedded-server", feature = "no-server")))]
#[cfg(dev)]
match res {
Ok(Content::Url(dp)) => assert_eq!(dp, config.build.dev_path),
Ok(Content::Html(s)) => {
@ -334,7 +334,7 @@ mod test {
}
}
#[cfg(feature = "embedded-server")]
#[cfg(embedded_server)]
#[test]
fn check_setup_port() {
let res = super::setup_port();
@ -346,7 +346,7 @@ mod test {
proptest! {
#![proptest_config(ProptestConfig::with_cases(10000))]
#[cfg(feature = "embedded-server")]
#[cfg(embedded_server)]
#[test]
fn check_server_url(port in (any::<u32>().prop_map(|v| v.to_string()))) {
let p = port.clone();

View File

@ -1,11 +1,24 @@
mod cmd;
mod dialog;
#[allow(unused_imports)]
mod file_system;
mod http;
mod init;
mod salt;
#[cfg(any(feature = "embedded-server", feature = "no-server"))]
use std::path::PathBuf;
use init::init;
#[cfg(assets)]
mod asset;
#[cfg(open)]
mod browser;
#[cfg(any(open_dialog, save_dialog))]
mod dialog;
#[cfg(event)]
mod event;
#[cfg(http_request)]
mod http;
#[cfg(notification)]
mod notification;
use web_view::WebView;
#[allow(unused_variables)]
@ -24,25 +37,28 @@ pub(crate) fn handle<T: 'static>(webview: &mut WebView<'_, T>, arg: &str) -> cra
event_init = event_init
))?;
}
#[cfg(any(feature = "all-api", feature = "read-text-file"))]
ReadTextFile {
path,
options,
callback,
error,
} => {
#[cfg(read_text_file)]
file_system::read_text_file(webview, path, options, callback, error);
#[cfg(not(read_text_file))]
whitelist_error(webview, error, "readTextFile");
}
#[cfg(any(feature = "all-api", feature = "read-binary-file"))]
ReadBinaryFile {
path,
options,
callback,
error,
} => {
#[cfg(read_binary_file)]
file_system::read_binary_file(webview, path, options, callback, error);
#[cfg(not(read_binary_file))]
whitelist_error(webview, error, "readBinaryFile");
}
#[cfg(any(feature = "all-api", feature = "write-file"))]
WriteFile {
file,
contents,
@ -50,9 +66,11 @@ pub(crate) fn handle<T: 'static>(webview: &mut WebView<'_, T>, arg: &str) -> cra
callback,
error,
} => {
#[cfg(write_file)]
file_system::write_file(webview, file, contents, options, callback, error);
#[cfg(not(write_file))]
whitelist_error(webview, error, "writeFile");
}
#[cfg(any(feature = "all-api", feature = "write-binary-file"))]
WriteBinaryFile {
file,
contents,
@ -62,16 +80,17 @@ pub(crate) fn handle<T: 'static>(webview: &mut WebView<'_, T>, arg: &str) -> cra
} => {
file_system::write_binary_file(webview, file, contents, options, callback, error);
}
#[cfg(any(feature = "all-api", feature = "read-dir"))]
ReadDir {
path,
options,
callback,
error,
} => {
#[cfg(read_dir)]
file_system::read_dir(webview, path, options, callback, error);
#[cfg(not(read_dir))]
whitelist_error(webview, error, "readDir");
}
#[cfg(any(feature = "all-api", feature = "copy-file"))]
CopyFile {
source,
destination,
@ -79,36 +98,44 @@ pub(crate) fn handle<T: 'static>(webview: &mut WebView<'_, T>, arg: &str) -> cra
callback,
error,
} => {
#[cfg(copy_file)]
file_system::copy_file(webview, source, destination, options, callback, error);
#[cfg(not(copy_file))]
whitelist_error(webview, error, "copyFile");
}
#[cfg(any(feature = "all-api", feature = "create-dir"))]
CreateDir {
path,
options,
callback,
error,
} => {
#[cfg(create_dir)]
file_system::create_dir(webview, path, options, callback, error);
#[cfg(not(create_dir))]
whitelist_error(webview, error, "createDir");
}
#[cfg(any(feature = "all-api", feature = "remove-dir"))]
RemoveDir {
path,
options,
callback,
error,
} => {
#[cfg(remove_dir)]
file_system::remove_dir(webview, path, options, callback, error);
#[cfg(not(remove_dir))]
whitelist_error(webview, error, "removeDir");
}
#[cfg(any(feature = "all-api", feature = "remove-file"))]
RemoveFile {
path,
options,
callback,
error,
} => {
#[cfg(remove_file)]
file_system::remove_file(webview, path, options, callback, error);
#[cfg(not(remove_file))]
whitelist_error(webview, error, "removeFile");
}
#[cfg(any(feature = "all-api", feature = "rename-file"))]
RenameFile {
old_path,
new_path,
@ -116,24 +143,33 @@ pub(crate) fn handle<T: 'static>(webview: &mut WebView<'_, T>, arg: &str) -> cra
callback,
error,
} => {
#[cfg(rename_file)]
file_system::rename_file(webview, old_path, new_path, options, callback, error);
#[cfg(not(rename_file))]
whitelist_error(webview, error, "renameFile");
}
#[cfg(any(feature = "all-api", feature = "set-title"))]
SetTitle { title } => {
#[cfg(set_title)]
webview.set_title(&title)?;
#[cfg(not(set_title))]
throw_whitelist_error(webview, "title");
}
#[cfg(any(feature = "all-api", feature = "execute"))]
Execute {
command,
args,
callback,
error,
} => {
#[cfg(execute)]
crate::call(webview, command, args, callback, error);
#[cfg(not(execute))]
throw_whitelist_error(webview, "execute");
}
#[cfg(any(feature = "all-api", feature = "open"))]
Open { uri } => {
open_fn(uri)?;
#[cfg(open)]
browser::open(uri);
#[cfg(not(open))]
throw_whitelist_error(webview, "open");
}
ValidateSalt {
salt,
@ -142,304 +178,127 @@ pub(crate) fn handle<T: 'static>(webview: &mut WebView<'_, T>, arg: &str) -> cra
} => {
salt::validate(webview, salt, callback, error);
}
#[cfg(any(feature = "all-api", feature = "event"))]
Listen {
event,
handler,
once,
} => {
let js_string = listen_fn(event, handler, once)?;
webview.eval(&js_string)?;
#[cfg(event)]
{
let js_string = event::listen_fn(event, handler, once)?;
webview.eval(&js_string)?;
}
#[cfg(not(event))]
throw_whitelist_error(webview, "event");
}
#[cfg(any(feature = "all-api", feature = "event"))]
Emit { event, payload } => {
#[cfg(event)]
crate::event::on_event(event, payload);
#[cfg(not(event))]
throw_whitelist_error(webview, "event");
}
#[cfg(any(feature = "all-api", feature = "open-dialog"))]
OpenDialog {
options,
callback,
error,
} => {
#[cfg(open_dialog)]
dialog::open(webview, options, callback, error);
#[cfg(not(open_dialog))]
whitelist_error(webview, error, "title");
}
#[cfg(any(feature = "all-api", feature = "save-dialog"))]
SaveDialog {
options,
callback,
error,
} => {
#[cfg(save_dialog)]
dialog::save(webview, options, callback, error);
#[cfg(not(save_dialog))]
throw_whitelist_error(webview, "saveDialog");
}
#[cfg(any(feature = "all-api", feature = "http-request"))]
HttpRequest {
options,
callback,
error,
} => {
#[cfg(http_request)]
http::make_request(webview, *options, callback, error);
#[cfg(not(http_request))]
whitelist_error(webview, error, "httpRequest");
}
#[cfg(any(feature = "embedded-server", feature = "no-server"))]
#[cfg(assets)]
LoadAsset {
asset,
asset_type,
callback,
error,
} => {
load_asset(webview, asset, asset_type, callback, error)?;
asset::load(webview, asset, asset_type, callback, error);
}
CliMatches { callback, error } => {
#[cfg(cli)]
crate::execute_promise(
webview,
move || match crate::cli::get_matches() {
Some(matches) => Ok(serde_json::to_string(matches)?),
None => Err(anyhow::anyhow!(r#""failed to get matches""#)),
},
callback,
error,
);
#[cfg(not(cli))]
whitelist_error(webview, error, "cli");
}
#[cfg(feature = "cli")]
CliMatches { callback, error } => crate::execute_promise(
webview,
move || match crate::cli::get_matches() {
Some(matches) => Ok(serde_json::to_string(matches)?),
None => Err(anyhow::anyhow!(r#""failed to get matches""#)),
},
callback,
error,
),
#[cfg(any(feature = "all-api", feature = "notification"))]
Notification {
options,
callback,
error,
} => {
notification(webview, options, callback, error)?;
#[cfg(notification)]
notification::send(webview, options, callback, error);
#[cfg(not(notification))]
whitelist_error(webview, error, "notification");
}
#[cfg(any(feature = "all-api", feature = "notification"))]
IsNotificationPermissionGranted { callback, error } => {
crate::execute_promise(
webview,
move || {
let settings = crate::settings::read_settings()?;
if let Some(allow_notification) = settings.allow_notification {
Ok(allow_notification.to_string())
} else {
Ok("null".to_string())
}
},
callback,
error,
);
#[cfg(notification)]
notification::is_permission_granted(webview, callback, error);
#[cfg(not(notification))]
whitelist_error(webview, error, "notification");
}
RequestNotificationPermission { callback, error } => {
#[cfg(notification)]
notification::request_permission(webview, callback, error);
#[cfg(not(notification))]
whitelist_error(webview, error, "notification");
}
#[cfg(any(feature = "all-api", feature = "notification"))]
RequestNotificationPermission { callback, error } => crate::execute_promise_sync(
webview,
move || {
let mut settings = crate::settings::read_settings()?;
let granted = r#""granted""#.to_string();
let denied = r#""denied""#.to_string();
if let Some(allow_notification) = settings.allow_notification {
return Ok(if allow_notification { granted } else { denied });
}
let answer = tauri_api::dialog::ask(
"This app wants to show notifications. Do you allow?",
"Permissions",
);
match answer {
tauri_api::dialog::DialogSelection::Yes => {
settings.allow_notification = Some(true);
crate::settings::write_settings(settings)?;
Ok(granted)
}
tauri_api::dialog::DialogSelection::No => Ok(denied),
_ => Ok(r#""default""#.to_string()),
}
},
callback,
error,
),
}
Ok(())
}
}
}
fn init() -> crate::Result<String> {
#[cfg(not(any(feature = "all-api", feature = "event")))]
return Ok(String::from(""));
#[cfg(any(feature = "all-api", feature = "event"))]
return Ok(format!(
"
window['{queue}'] = [];
window['{fn}'] = function (payload, salt, ignoreQueue) {{
const listeners = (window['{listeners}'] && window['{listeners}'][payload.type]) || []
if (!ignoreQueue && listeners.length === 0) {{
window['{queue}'].push({{
payload: payload,
salt: salt
}})
}}
if (listeners.length > 0) {{
window.tauri.promisified({{
cmd: 'validateSalt',
salt: salt
}}).then(function () {{
for (let i = listeners.length - 1; i >= 0; i--) {{
const listener = listeners[i]
if (listener.once)
listeners.splice(i, 1)
listener.handler(payload)
}}
}})
}}
}}
",
fn = crate::event::emit_function_name(),
queue = crate::event::event_queue_object_name(),
listeners = crate::event::event_listeners_object_name()
));
}
#[cfg(any(feature = "all-api", feature = "open"))]
fn open_fn(uri: String) -> crate::Result<()> {
crate::spawn(move || {
#[cfg(test)]
assert!(uri.contains("http://"));
#[cfg(not(test))]
webbrowser::open(&uri).expect("Failed to open webbrowser with uri");
});
Ok(())
}
#[cfg(any(feature = "all-api", feature = "event"))]
fn listen_fn(event: String, handler: String, once: bool) -> crate::Result<String> {
Ok(format!(
"if (window['{listeners}'] === void 0) {{
window['{listeners}'] = {{}}
}}
if (window['{listeners}']['{evt}'] === void 0) {{
window['{listeners}']['{evt}'] = []
}}
window['{listeners}']['{evt}'].push({{
handler: window['{handler}'],
once: {once_flag}
}});
for (let i = 0; i < (window['{queue}'] || []).length; i++) {{
const e = window['{queue}'][i];
window['{emit}'](e.payload, e.salt, true)
}}
",
listeners = crate::event::event_listeners_object_name(),
queue = crate::event::event_queue_object_name(),
emit = crate::event::emit_function_name(),
evt = event,
handler = handler,
once_flag = if once { "true" } else { "false" }
))
}
#[cfg(any(feature = "embedded-server", feature = "no-server"))]
fn load_asset<T: 'static>(
#[allow(dead_code)]
fn whitelist_error<T: 'static>(
webview: &mut WebView<'_, T>,
asset: String,
asset_type: String,
callback: String,
error: String,
) -> crate::Result<()> {
let handle = webview.handle();
crate::execute_promise(
webview,
move || {
let mut path = PathBuf::from(if asset.starts_with('/') {
asset.replacen("/", "", 1)
} else {
asset.clone()
});
let mut read_asset;
loop {
read_asset = crate::assets::ASSETS.get(&format!(
"{}/{}",
option_env!("TAURI_DIST_DIR")
.expect("tauri apps should be built with the TAURI_DIST_DIR environment variable"),
path.to_string_lossy()
));
if read_asset.is_err() {
match path.iter().next() {
Some(component) => {
let first_component = component.to_str().expect("failed to read path component");
path = PathBuf::from(path.to_string_lossy().replacen(
format!("{}/", first_component).as_str(),
"",
1,
));
}
None => {
return Err(anyhow::anyhow!("Asset '{}' not found", asset));
}
}
} else {
break;
}
}
if asset_type == "image" {
let ext = if asset.ends_with("gif") {
"gif"
} else if asset.ends_with("png") {
"png"
} else {
"jpeg"
};
Ok(format!(
r#""data:image/{};base64,{}""#,
ext,
base64::encode(&read_asset.expect("Failed to read asset type").into_owned())
))
} else {
handle
.dispatch(move |_webview| {
let asset_bytes = &read_asset.expect("Failed to read asset type").into_owned();
let asset_str =
&std::str::from_utf8(asset_bytes).expect("failed to convert asset bytes to u8 slice");
if asset_type == "stylesheet" {
_webview.inject_css(asset_str)
} else {
_webview.eval(asset_str)
}
})
.map_err(|err| err.into())
.map(|_| r#""Asset loaded successfully""#.to_string())
}
},
callback,
error,
error_fn: String,
whitelist_key: &str,
) {
let reject_code = tauri_api::rpc::format_callback(
error_fn,
format!(r#""'{}' not whitelisted""#, whitelist_key),
);
Ok(())
webview
.eval(&reject_code)
.expect("failed to eval whitelist error")
}
#[cfg(any(feature = "all-api", feature = "notification"))]
fn notification<T: 'static>(
webview: &mut WebView<'_, T>,
options: cmd::NotificationOptions,
callback: String,
error: String,
) -> crate::Result<()> {
crate::execute_promise(
webview,
move || {
let mut notification = tauri_api::notification::Notification::new();
notification.body(options.body);
if let Some(title) = options.title {
notification.title(title);
}
if let Some(icon) = options.icon {
notification.icon(icon);
}
notification
.show()
.map_err(|e| anyhow::anyhow!(r#""{}""#, e.to_string()))?;
Ok("".to_string())
},
callback,
error,
);
Ok(())
#[allow(dead_code)]
fn throw_whitelist_error<T: 'static>(webview: &mut WebView<'_, T>, whitelist_key: &str) {
let reject_code = format!(r#"throw new Error("'{}' not whitelisted")"#, whitelist_key);
webview
.eval(&reject_code)
.expect("failed to eval whitelist error")
}
#[cfg(test)]
@ -449,16 +308,16 @@ mod test {
#[test]
// test to see if check init produces a string or not.
fn check_init() {
if cfg!(not(any(feature = "all-api", feature = "event"))) {
if cfg!(not(event)) {
let res = super::init();
match res {
Ok(s) => assert_eq!(s, ""),
Err(_) => assert!(false),
}
} else if cfg!(any(feature = "all-api", feature = "event")) {
} else if cfg!(event) {
let res = super::init();
match res {
Ok(s) => assert!(s.contains("window.tauri.promisified")),
Ok(s) => assert!(s.contains("window.__TAURI__.promisified")),
Err(_) => assert!(false),
}
}
@ -466,10 +325,10 @@ mod test {
// check the listen_fn for various usecases.
proptest! {
#[cfg(any(feature = "all-api", feature = "event"))]
#[cfg(event)]
#[test]
fn check_listen_fn(event in "", handler in "", once in proptest::bool::ANY) {
let res = super::listen_fn(event, handler, once);
let res = super::event::listen_fn(event, handler, once);
match res {
Ok(_) => assert!(true),
Err(_) => assert!(false)
@ -479,14 +338,10 @@ mod test {
// Test the open func to see if proper uris can be opened by the browser.
proptest! {
#[cfg(any(feature = "all-api", feature = "open"))]
#[cfg(open)]
#[test]
fn check_open(uri in r"(http://)([\\w\\d\\.]+([\\w]{2,6})?)") {
let res = super::open_fn(uri);
match res {
Ok(_) => assert!(true),
Err(_) => assert!(false),
}
super::browser::open(uri);
}
}
}

View File

@ -0,0 +1,79 @@
use std::path::PathBuf;
use web_view::WebView;
pub fn load<T: 'static>(
webview: &mut WebView<'_, T>,
asset: String,
asset_type: String,
callback: String,
error: String,
) {
let handle = webview.handle();
crate::execute_promise(
webview,
move || {
let mut path = PathBuf::from(if asset.starts_with('/') {
asset.replacen("/", "", 1)
} else {
asset.clone()
});
let mut read_asset;
loop {
read_asset = crate::assets::ASSETS.get(&format!(
"{}/{}",
option_env!("TAURI_DIST_DIR")
.expect("tauri apps should be built with the TAURI_DIST_DIR environment variable"),
path.to_string_lossy()
));
if read_asset.is_err() {
match path.iter().next() {
Some(component) => {
let first_component = component.to_str().expect("failed to read path component");
path = PathBuf::from(path.to_string_lossy().replacen(
format!("{}/", first_component).as_str(),
"",
1,
));
}
None => {
return Err(anyhow::anyhow!("Asset '{}' not found", asset));
}
}
} else {
break;
}
}
if asset_type == "image" {
let ext = if asset.ends_with("gif") {
"gif"
} else if asset.ends_with("png") {
"png"
} else {
"jpeg"
};
Ok(format!(
r#""data:image/{};base64,{}""#,
ext,
base64::encode(&read_asset.expect("Failed to read asset type").into_owned())
))
} else {
handle
.dispatch(move |_webview| {
let asset_bytes = &read_asset.expect("Failed to read asset type").into_owned();
let asset_str =
&std::str::from_utf8(asset_bytes).expect("failed to convert asset bytes to u8 slice");
if asset_type == "stylesheet" {
_webview.inject_css(asset_str)
} else {
_webview.eval(asset_str)
}
})
.map_err(|err| err.into())
.map(|_| r#""Asset loaded successfully""#.to_string())
}
},
callback,
error,
);
}

View File

@ -0,0 +1,10 @@
#[cfg(open)]
pub fn open(uri: String) {
crate::spawn(move || {
#[cfg(test)]
assert!(uri.contains("http://"));
#[cfg(not(test))]
webbrowser::open(&uri).expect("Failed to open webbrowser with uri");
});
}

View File

@ -43,21 +43,18 @@ pub struct NotificationOptions {
#[serde(tag = "cmd", rename_all = "camelCase")]
pub enum Cmd {
Init {},
#[cfg(any(feature = "all-api", feature = "read-text-file"))]
ReadTextFile {
path: String,
options: Option<FileOperationOptions>,
callback: String,
error: String,
},
#[cfg(any(feature = "all-api", feature = "read-binary-file"))]
ReadBinaryFile {
path: String,
options: Option<FileOperationOptions>,
callback: String,
error: String,
},
#[cfg(any(feature = "all-api", feature = "write-file"))]
WriteFile {
file: String,
contents: String,
@ -65,7 +62,6 @@ pub enum Cmd {
callback: String,
error: String,
},
#[cfg(any(feature = "all-api", feature = "write-binary-file"))]
WriteBinaryFile {
file: String,
contents: String,
@ -73,14 +69,12 @@ pub enum Cmd {
callback: String,
error: String,
},
#[cfg(any(feature = "all-api", feature = "read-dir"))]
ReadDir {
path: String,
options: Option<DirOperationOptions>,
callback: String,
error: String,
},
#[cfg(any(feature = "all-api", feature = "copy-file"))]
CopyFile {
source: String,
destination: String,
@ -88,21 +82,18 @@ pub enum Cmd {
callback: String,
error: String,
},
#[cfg(any(feature = "all-api", feature = "create-dir"))]
CreateDir {
path: String,
options: Option<DirOperationOptions>,
callback: String,
error: String,
},
#[cfg(any(feature = "all-api", feature = "remove-dir"))]
RemoveDir {
path: String,
options: Option<DirOperationOptions>,
callback: String,
error: String,
},
#[cfg(any(feature = "all-api", feature = "remove-file"))]
RemoveFile {
path: String,
options: Option<FileOperationOptions>,
@ -110,7 +101,6 @@ pub enum Cmd {
error: String,
},
#[serde(rename_all = "camelCase")]
#[cfg(any(feature = "all-api", feature = "rename-file"))]
RenameFile {
old_path: String,
new_path: String,
@ -118,18 +108,15 @@ pub enum Cmd {
callback: String,
error: String,
},
#[cfg(any(feature = "all-api", feature = "set-title"))]
SetTitle {
title: String,
},
#[cfg(any(feature = "all-api", feature = "execute"))]
Execute {
command: String,
args: Vec<String>,
callback: String,
error: String,
},
#[cfg(any(feature = "all-api", feature = "open"))]
Open {
uri: String,
},
@ -138,30 +125,25 @@ pub enum Cmd {
callback: String,
error: String,
},
#[cfg(any(feature = "all-api", feature = "event"))]
Listen {
event: String,
handler: String,
once: bool,
},
#[cfg(any(feature = "all-api", feature = "event"))]
Emit {
event: String,
payload: Option<String>,
},
#[cfg(any(feature = "all-api", feature = "open-dialog"))]
OpenDialog {
options: OpenDialogOptions,
callback: String,
error: String,
},
#[cfg(any(feature = "all-api", feature = "save-dialog"))]
SaveDialog {
options: SaveDialogOptions,
callback: String,
error: String,
},
#[cfg(any(feature = "all-api", feature = "http-request"))]
HttpRequest {
options: Box<HttpRequestOptions>,
callback: String,
@ -175,23 +157,19 @@ pub enum Cmd {
callback: String,
error: String,
},
#[cfg(feature = "cli")]
CliMatches {
callback: String,
error: String,
},
#[cfg(any(feature = "all-api", feature = "notification"))]
Notification {
options: NotificationOptions,
callback: String,
error: String,
},
#[cfg(any(feature = "all-api", feature = "notification"))]
RequestNotificationPermission {
callback: String,
error: String,
},
#[cfg(any(feature = "all-api", feature = "notification"))]
IsNotificationPermissionGranted {
callback: String,
error: String,

View File

@ -10,6 +10,7 @@ fn map_response(response: Response) -> String {
}
}
#[cfg(open_dialog)]
pub fn open<T: 'static>(
webview: &mut WebView<'_, T>,
options: OpenDialogOptions,
@ -33,6 +34,7 @@ pub fn open<T: 'static>(
);
}
#[cfg(save_dialog)]
pub fn save<T: 'static>(
webview: &mut WebView<'_, T>,
options: SaveDialogOptions,

View File

@ -0,0 +1,27 @@
#[cfg(event)]
pub fn listen_fn(event: String, handler: String, once: bool) -> crate::Result<String> {
Ok(format!(
"if (window['{listeners}'] === void 0) {{
window['{listeners}'] = {{}}
}}
if (window['{listeners}']['{evt}'] === void 0) {{
window['{listeners}']['{evt}'] = []
}}
window['{listeners}']['{evt}'].push({{
handler: window['{handler}'],
once: {once_flag}
}});
for (let i = 0; i < (window['{queue}'] || []).length; i++) {{
const e = window['{queue}'][i];
window['{emit}'](e.payload, e.salt, true)
}}
",
listeners = crate::event::event_listeners_object_name(),
queue = crate::event::event_queue_object_name(),
emit = crate::event::emit_function_name(),
evt = event,
handler = handler,
once_flag = if once { "true" } else { "false" }
))
}

View File

@ -10,6 +10,7 @@ use std::io::Write;
use super::cmd::{DirOperationOptions, FileOperationOptions};
#[cfg(read_dir)]
pub fn read_dir<T: 'static>(
webview: &mut WebView<'_, T>,
path: String,
@ -38,6 +39,7 @@ pub fn read_dir<T: 'static>(
);
}
#[cfg(copy_file)]
pub fn copy_file<T: 'static>(
webview: &mut WebView<'_, T>,
source: String,
@ -65,6 +67,7 @@ pub fn copy_file<T: 'static>(
);
}
#[cfg(create_dir)]
pub fn create_dir<T: 'static>(
webview: &mut WebView<'_, T>,
path: String,
@ -94,6 +97,7 @@ pub fn create_dir<T: 'static>(
);
}
#[cfg(remove_dir)]
pub fn remove_dir<T: 'static>(
webview: &mut WebView<'_, T>,
path: String,
@ -123,6 +127,7 @@ pub fn remove_dir<T: 'static>(
);
}
#[cfg(remove_file)]
pub fn remove_file<T: 'static>(
webview: &mut WebView<'_, T>,
path: String,
@ -143,6 +148,7 @@ pub fn remove_file<T: 'static>(
);
}
#[cfg(rename_file)]
pub fn rename_file<T: 'static>(
webview: &mut WebView<'_, T>,
old_path: String,
@ -170,6 +176,7 @@ pub fn rename_file<T: 'static>(
);
}
#[cfg(write_file)]
pub fn write_file<T: 'static>(
webview: &mut WebView<'_, T>,
file: String,
@ -194,6 +201,7 @@ pub fn write_file<T: 'static>(
);
}
#[cfg(write_binary_file)]
pub fn write_binary_file<T: 'static>(
webview: &mut WebView<'_, T>,
file: String,
@ -222,6 +230,7 @@ pub fn write_binary_file<T: 'static>(
);
}
#[cfg(read_text_file)]
pub fn read_text_file<T: 'static>(
webview: &mut WebView<'_, T>,
path: String,
@ -240,6 +249,7 @@ pub fn read_text_file<T: 'static>(
);
}
#[cfg(read_binary_file)]
pub fn read_binary_file<T: 'static>(
webview: &mut WebView<'_, T>,
path: String,

View File

@ -0,0 +1,36 @@
pub fn init() -> crate::Result<String> {
#[cfg(not(event))]
return Ok(String::from(""));
#[cfg(event)]
return Ok(format!(
"
window['{queue}'] = [];
window['{fn}'] = function (payload, salt, ignoreQueue) {{
const listeners = (window['{listeners}'] && window['{listeners}'][payload.type]) || []
if (!ignoreQueue && listeners.length === 0) {{
window['{queue}'].push({{
payload: payload,
salt: salt
}})
}}
if (listeners.length > 0) {{
window.__TAURI__.promisified({{
cmd: 'validateSalt',
salt: salt
}}).then(function () {{
for (let i = listeners.length - 1; i >= 0; i--) {{
const listener = listeners[i]
if (listener.once)
listeners.splice(i, 1)
listener.handler(payload)
}}
}})
}}
}}
",
fn = crate::event::emit_function_name(),
queue = crate::event::event_queue_object_name(),
listeners = crate::event::event_listeners_object_name()
));
}

View File

@ -0,0 +1,86 @@
use super::cmd::NotificationOptions;
use web_view::WebView;
pub fn send<T: 'static>(
webview: &mut WebView<'_, T>,
options: NotificationOptions,
callback: String,
error: String,
) {
crate::execute_promise(
webview,
move || {
let mut notification = tauri_api::notification::Notification::new();
notification.body(options.body);
if let Some(title) = options.title {
notification.title(title);
}
if let Some(icon) = options.icon {
notification.icon(icon);
}
notification
.show()
.map_err(|e| anyhow::anyhow!(r#""{}""#, e.to_string()))?;
Ok("".to_string())
},
callback,
error,
);
}
pub fn is_permission_granted<T: 'static>(
webview: &mut WebView<'_, T>,
callback: String,
error: String,
) {
crate::execute_promise(
webview,
move || {
let settings = crate::settings::read_settings()?;
if let Some(allow_notification) = settings.allow_notification {
Ok(allow_notification.to_string())
} else {
Ok("null".to_string())
}
},
callback,
error,
);
}
pub fn request_permission<T: 'static>(
webview: &mut WebView<'_, T>,
callback: String,
error: String,
) {
crate::execute_promise_sync(
webview,
move || {
let mut settings = crate::settings::read_settings()?;
let granted = r#""granted""#.to_string();
let denied = r#""denied""#.to_string();
if let Some(allow_notification) = settings.allow_notification {
return Ok(if allow_notification { granted } else { denied });
}
let answer = tauri_api::dialog::ask(
"This app wants to show notifications. Do you allow?",
"Permissions",
);
match answer {
tauri_api::dialog::DialogSelection::Yes => {
settings.allow_notification = Some(true);
crate::settings::write_settings(settings)?;
Ok(granted)
}
tauri_api::dialog::DialogSelection::No => {
settings.allow_notification = Some(false);
crate::settings::write_settings(settings)?;
Ok(denied)
}
_ => Ok(r#""default""#.to_string()),
}
},
callback,
error,
);
}

View File

@ -3,14 +3,14 @@
windows_subsystem = "windows"
)]
#[cfg(any(feature = "embedded-server", feature = "no-server"))]
#[cfg(assets)]
pub mod assets;
pub mod event;
#[cfg(feature = "embedded-server")]
#[cfg(embedded_server)]
pub mod server;
pub mod settings;
#[cfg(feature = "cli")]
#[cfg(cli)]
pub mod cli;
mod app;

View File

@ -8,7 +8,7 @@ use tauri_api::path::{resolve_path, BaseDirectory};
#[derive(Default, Deserialize, Serialize)]
pub struct Settings {
#[cfg(any(feature = "all-api", feature = "notification"))]
#[cfg(notification)]
pub allow_notification: Option<bool>,
}
@ -18,7 +18,10 @@ fn get_settings_path() -> tauri_api::Result<String> {
pub(crate) fn write_settings(settings: Settings) -> crate::Result<()> {
let settings_path = get_settings_path()?;
std::fs::create_dir(Path::new(&settings_path).parent().unwrap())?;
let settings_folder = Path::new(&settings_path).parent().unwrap();
if !settings_folder.exists() {
std::fs::create_dir(settings_folder)?;
}
File::create(settings_path)
.map_err(|e| anyhow!(e))
.and_then(|mut f| {

View File

@ -1,219 +0,0 @@
/* eslint-disable */
/**
* * THIS FILE IS GENERATED AUTOMATICALLY.
* DO NOT EDIT.
*
* Please whitelist these API functions in tauri.conf.json
*
**/
/**
* @module tauri
* @description This API interface makes powerful interactions available
* to be run on client side applications. They are opt-in features, and
* must be enabled in tauri.conf.json
*
* Each binding MUST provide these interfaces in order to be compliant,
* and also whitelist them based upon the developer's settings.
*/
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1)
}
var uid = function () {
return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
s4() + '-' + s4() + s4() + s4()
}
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
var __reject = function () {
return new Promise(function (_, reject) {
reject();
});
}
window.tauri = {
invoke: function invoke(args) {
window.external.invoke(JSON.stringify(args));
},
listen: function listen(event, handler) {
return __reject()
},
emit: function emit(evt, payload) {
return __reject()
},
transformCallback: function transformCallback(callback) {
var once = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var identifier = Object.freeze(uid());
window[identifier] = function (result) {
if (once) {
delete window[identifier];
}
return callback && callback(result);
};
return identifier;
},
promisified: function promisified(args) {
var _this = this;
return new Promise(function (resolve, reject) {
_this.invoke(_objectSpread({
callback: _this.transformCallback(resolve),
error: _this.transformCallback(reject)
}, args));
});
},
readTextFile: function readTextFile(path) {
return __reject()
},
readBinaryFile: function readBinaryFile(path) {
return __reject()
},
writeFile: function writeFile(cfg) {
return __reject()
},
listFiles: function listFiles(path) {
return __reject()
},
listDirs: function listDirs(path) {
return __reject()
},
setTitle: function setTitle(title) {
return __reject()
},
open: function open(uri) {
return __reject()
},
execute: function execute(command, args) {
return __reject()
},
bridge: function bridge(command, payload) {
return __reject()
},
loadAsset: function loadAsset(assetName, assetType) {
return this.promisified({
cmd: 'loadAsset',
asset: assetName,
asset_type: assetType || 'unknown'
})
}
};
// init tauri API
try {
window.tauri.invoke({
cmd: 'init'
})
} catch (e) {
window.addEventListener('DOMContentLoaded', function () {
window.tauri.invoke({
cmd: 'init'
})
}, true)
}
document.addEventListener('error', function (e) {
var target = e.target
while (target != null) {
if (target.matches ? target.matches('img') : target.msMatchesSelector('img')) {
window.tauri.loadAsset(target.src, 'image')
.then(img => {
target.src = img
})
break
}
target = target.parentElement
}
}, true)
window.addEventListener('DOMContentLoaded', function () {
// open <a href="..."> links with the Tauri API
document.querySelector('body').addEventListener('click', function (e) {
var target = e.target
while (target != null) {
if (target.matches ? target.matches('a') : target.msMatchesSelector('a')) {
window.tauri.open(target.href)
break
}
target = target.parentElement
}
}, true)
}, true)