Skip to content

Commit

Permalink
Add export auto infer for multi environment
Browse files Browse the repository at this point in the history
  • Loading branch information
HuakunShen committed Jun 22, 2024
1 parent 24ddb63 commit c3d5a77
Show file tree
Hide file tree
Showing 17 changed files with 141 additions and 32 deletions.
2 changes: 1 addition & 1 deletion apps/desktop/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ edition = "2021"
tauri-build = { version = "2.0.0-beta", features = ["isolation"] }

[dependencies]
tauri = { version = "2.0.0-beta", features = ["isolation"] }
tauri = { version = "2.0.0-beta", features = ["isolation", "devtools"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tauri-plugin-clipboard = "2.0.2"
Expand Down
17 changes: 11 additions & 6 deletions apps/desktop/src/lib/sample-worker.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import { getWindowApiClient, getWorkerApiClient, type IFullAPI } from 'tauri-api-adapter'
import {
getWindowApiClient,
getWorkerApiClient,
isInIframe,
isInWorker,
isMain,
type IFullAPI
} from 'tauri-api-adapter'

const client = getWorkerApiClient<IFullAPI>()

client.clipboardReadText().then((text) => {
console.log('Clipboard text from worker:', text)
})

// self.postMessage

// client.clipboardReadText().then((text) => {
// console.log('Clipboard text from worker:', text)
// })
console.log('worker isInIframe:', isInIframe)
console.log('worker isInWorker:', isInWorker)
console.log('worker isMain:', isMain)
26 changes: 17 additions & 9 deletions apps/desktop/src/routes/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,32 +1,40 @@
<script lang="ts">
import { onMount } from 'svelte'
import { defaultServerAPI, exposeApiToWindow, exposeApiToWorker, utils } from 'tauri-api-adapter'
import {
clipboard,
defaultServerAPI,
exposeApiToWindow,
exposeApiToWorker,
isInIframe,
isInWorker,
isMain,
utils
} from 'tauri-api-adapter'
import SampleWorker from '../lib/sample-worker?worker'
// import SampleWorker from '../lib/sample-worker.ts'
// import { createWorker } from '../lib/worker'
let iframe: HTMLIFrameElement
onMount(async () => {
const worker = new SampleWorker()
worker.postMessage('Hello from parent')
window.addEventListener('message', (event) => {
console.log('Parent received message: ', event.data)
clipboard.readText().then((text) => {
console.log('native clipboard text in parent window:', text)
})
const worker = new SampleWorker()
exposeApiToWorker(worker, defaultServerAPI)
if (!(iframe && iframe.contentWindow)) {
return
} else {
// utils.isolateIframeFromTauri(iframe.contentWindow)
// iframe.contentWindow.parent.__TAURI_INTERNALS__ = null
exposeApiToWindow(iframe.contentWindow, defaultServerAPI)
utils.hackIframeToUseParentWindow(iframe.contentWindow)
}
})
</script>

<div class="container p-5">
<h1 class="text-3xl font-bold underline">Parent Page</h1>
<h2>isMain: {isMain}</h2>
<h2>isInIframe: {isInIframe}</h2>
<h2>isInWorker: {isInWorker}</h2>
<iframe
bind:this={iframe}
title="iframe"
Expand Down
28 changes: 27 additions & 1 deletion apps/desktop/src/routes/iframe/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,12 +1,38 @@
<script lang="ts">
import { onMount } from 'svelte'
import * as iframeApi from 'tauri-api-adapter/iframe'
import * as nativeApi from 'tauri-api-adapter/native'
import { defaultServerAPI, exposeApiToWindow, exposeApiToWorker, utils, isMain, isInIframe, isInWorker } from 'tauri-api-adapter'
onMount(async () => {
console.log('iframe Clipboard Text: ', await iframeApi.clipboard.readText())
// window.parent = {
// postMessage: window.parent.postMessage
// }
// window.eval(`
// window.parent = {
// postMessage: window.parent.postMessage
// }
// `)
setTimeout(async () => {
iframeApi.clipboard.readText().then((text) => {
console.log('iframe Clipboard Text: ', text)
})
nativeApi.clipboard.readText().then((text) => {
console.log('native API Clipboard Text: ', text)
})
console.log(
'hack with __TAURI_INTERNALS__',
await window.parent.__TAURI_INTERNALS__.invoke('plugin:clipboard|read_text')
)
}, 1000)
})
</script>

<div class="container p-5">
<h1 class="text-3xl font-bold underline">iframe</h1>
<h2>isMain: {isMain}</h2>
<h2>isInIframe: {isInIframe}</h2>
<h2>isInWorker: {isInWorker}</h2>
</div>
39 changes: 39 additions & 0 deletions packages/tauri-api-adapter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,42 @@ console.log('Clipboard Text: ', await clipboard.readText())
console.log(await sysInfo.cpuInfo())
console.log(await network.getNonEmptyInterfaces())
```

#### Multi Environment

This package supports multiple environments:

1. Regular Webview
2. iframe
3. Web Worker

```ts
// You can import the following functions to check the environment
// This can be used for debug. I wrote the logic to check the environment in the package.
// But it's possible the auto infer logic is wrong.
// In that case you need to import from the correct subpackage
import { isInIframe, isInWorker, isMain } from 'tauri-api-adapter'
```

The imported api objects are auto inferred based on the environment.

If you are in a regular WebView, the returned object will call Tauri APIs the regular way.

If you are in iframe or worker, the returned API object behaves differently.

Just import and use directly, envrioment will be auto inferred.

```ts
import { clipboard, dialog, notification, shell } from 'tauri-api-adapter'
```

If envrioment is not auto inferred correctly, you can import from the correct subpackage.

```ts
import { clipboard } from 'tauri-api-adapter/iframe'
import { clipboard } from 'tauri-api-adapter/worker'
```

## TODO

- [ ] Plan to add permission control system to this package.
4 changes: 3 additions & 1 deletion packages/tauri-api-adapter/src/api/clipboard.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IClipboard } from '@/api/client-types'
import { defaultClientAPI } from '@/client'
import { defaultClientAPI, isMain } from '@/client'
import { Remote } from '@huakunshen/comlink'
import * as _clipboard from 'tauri-plugin-clipboard-api'
import { IClipboardServer } from './server-types'
Expand Down Expand Up @@ -50,3 +50,5 @@ export const nativeClipboard: IClipboard = {
hasFiles: _clipboard.hasFiles,
startMonitor: _clipboard.startMonitor
}

export const clipboard = isMain ? nativeClipboard : comlinkClipboard
4 changes: 3 additions & 1 deletion packages/tauri-api-adapter/src/api/dialog.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IDialog } from '@/api/client-types'
import { defaultClientAPI } from '@/client'
import { defaultClientAPI, isMain } from '@/client'
import { Remote } from '@huakunshen/comlink'
import * as _dialogApi from '@tauri-apps/plugin-dialog'
import { IDialogServer, IFullAPI } from './server-types'
Expand All @@ -21,3 +21,5 @@ export const nativeDialog: IDialog = {
open: _dialogApi.open,
save: _dialogApi.save
}

export const dialog = isMain ? nativeDialog : comlinkDialog
4 changes: 3 additions & 1 deletion packages/tauri-api-adapter/src/api/event.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IEvent, IEventInternal } from '@/api/client-types'
import { defaultClientAPI } from '@/client'
import { defaultClientAPI, isMain } from '@/client'
import { Comlink } from '@/comlink'
import { Remote } from '@huakunshen/comlink'
import * as _eventApi from '@tauri-apps/api/event'
Expand Down Expand Up @@ -61,3 +61,5 @@ export const nativeEvent: IEvent = {
once: _eventApi.once,
listen: _eventApi.listen
}

export const event = isMain ? nativeEvent : comlinkEvent
4 changes: 3 additions & 1 deletion packages/tauri-api-adapter/src/api/fs.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IFs } from '@/api/client-types'
import { defaultClientAPI } from '@/client'
import { defaultClientAPI, isMain } from '@/client'
import { Remote } from '@huakunshen/comlink'
import * as _fs from '@tauri-apps/plugin-fs'
import { IFsServer } from './server-types'
Expand Down Expand Up @@ -40,3 +40,5 @@ export const nativeFs: IFs = {
writeFile: _fs.writeFile,
writeTextFile: _fs.writeTextFile
}

export const fs = isMain ? nativeFs : comlinkFs
4 changes: 3 additions & 1 deletion packages/tauri-api-adapter/src/api/network.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { INetwork } from '@/api/client-types'
import { defaultClientAPI } from '@/client'
import { defaultClientAPI, isMain } from '@/client'
import { Remote } from '@huakunshen/comlink'
import * as _network from 'tauri-plugin-network-api'
import { INetworkServer } from './server-types'
Expand Down Expand Up @@ -32,3 +32,5 @@ export const nativeNetwork: INetwork = {
localServerIsRunning: _network.localServerIsRunning,
scanLocalNetworkOnlineHostsByPort: _network.scanLocalNetworkOnlineHostsByPort
}

export const network = isMain ? nativeNetwork : comlinkNetwork
4 changes: 3 additions & 1 deletion packages/tauri-api-adapter/src/api/notification.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { INotification } from '@/api/client-types'
import { defaultClientAPI } from '@/client'
import { defaultClientAPI, isMain } from '@/client'
import * as Comlink from '@huakunshen/comlink'
import { Remote } from '@huakunshen/comlink'
import { PluginListener } from '@tauri-apps/api/core'
Expand Down Expand Up @@ -54,3 +54,5 @@ export const nativeNotification: INotification = {
onNotificationReceived: notificationApi.onNotificationReceived,
onAction: notificationApi.onAction
}

export const notification = isMain ? nativeNotification : comlinkNotification
4 changes: 3 additions & 1 deletion packages/tauri-api-adapter/src/api/os.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IOs } from '@/api/client-types'
import { defaultClientAPI } from '@/client'
import { defaultClientAPI, isMain } from '@/client'
import { Remote } from '@huakunshen/comlink'
import * as _os from '@tauri-apps/plugin-os'
import { IOsServer } from './server-types'
Expand Down Expand Up @@ -28,3 +28,5 @@ export const nativeOs: IOs = {
version: _os.version,
locale: _os.locale
}

export const os = isMain ? nativeOs : comlinkOs
4 changes: 3 additions & 1 deletion packages/tauri-api-adapter/src/api/shell.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IShell, IShellInternal } from '@/api/client-types'
import { defaultClientAPI } from '@/client'
import { defaultClientAPI, isMain } from '@/client'
import { Comlink } from '@/comlink'
import { Remote } from '@huakunshen/comlink'
import * as shellx from 'tauri-plugin-shellx-api'
Expand Down Expand Up @@ -146,3 +146,5 @@ export const nativeShell: IShell = {
Command: shellx.Command,
Child: shellx.Child
}

export const shell = isMain ? nativeShell : comlinkShell
4 changes: 3 additions & 1 deletion packages/tauri-api-adapter/src/api/system-info.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ISystemInfo } from '@/api/client-types'
import { defaultClientAPI } from '@/client'
import { defaultClientAPI, isMain } from '@/client'
import { Remote } from '@huakunshen/comlink'
import * as _sysinfo from 'tauri-plugin-system-info-api'
import { ISystemInfoServer } from './server-types'
Expand Down Expand Up @@ -60,3 +60,5 @@ export const nativeSysInfo: ISystemInfo = {
debugCommand: _sysinfo.debugCommand,
batteries: _sysinfo.batteries
}

export const sysInfo = isMain ? nativeSysInfo : comlinkSysInfo
3 changes: 2 additions & 1 deletion packages/tauri-api-adapter/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import type { IFullAPI } from './api/server-types'
import { getWindowApiClient, getWorkerApiClient } from './comlink'

export const hasWindow = typeof window !== 'undefined'
export const isInWorker = !hasWindow
export const isInIframe = hasWindow && window !== window.parent
export const isMain = !isInWorker && !isInIframe && window === window.parent
export const workerApi = getWorkerApiClient<IFullAPI>()
// export const iframeSideApi =
/**
* `defaultClientAPI` is auto selected depending on the environment.
*/
Expand Down
14 changes: 10 additions & 4 deletions packages/tauri-api-adapter/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
export * from '@/comlink'
export { defaultClientAPI } from '@/client'
export { defaultClientAPI, hasWindow, isInWorker, isInIframe, isMain } from '@/client'
export { defaultServerAPI } from '@/server'
export * from '@/api/server-types'
export * from '@/api/client-types'
export * as utils from './utils'
export * as nativeApi from '@/native'
export * as workerApi from '@/worker'

export { clipboard } from '@/api/clipboard'
export { dialog } from '@/api/dialog'
export { fs } from '@/api/fs'
export { network } from '@/api/network'
export { notification } from '@/api/notification'
export { os } from '@/api/os'
export { shell } from '@/api/shell'
export { sysInfo } from '@/api/system-info'
export { fetch } from '@/api/fetch'
export { listen, TauriEvent, comlinkEvent as event } from '@/api/event'
export * from '@/worker'
8 changes: 7 additions & 1 deletion packages/tauri-api-adapter/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,17 @@ export function constructAPICallbackExecuter<Payload>(
}
}

/**
* Prevent iframe from accessing Tauri APIs through window.parent.__TAURI_INTERNALS__
* It's possible that this blocks request provided by comlink.
* For example, in the demo sveltekit project, running the hack script in iframe directly works, but running it from the parent window does not.
* In React, running the hack script from parent window works.
* @param iframeWin
*/
export function isolateIframeFromTauri(iframeWin: Window) {
;(iframeWin as any).eval(`window.parent = {
postMessage: window.parent.postMessage
};`)
// ;(iframeWin as any).eval(`window.parent = {};`)
}

export function hackIframeToUseParentWindow(iframeWin: Window) {
Expand Down

0 comments on commit c3d5a77

Please sign in to comment.