Skip to content
9 changes: 5 additions & 4 deletions e2e/test-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,15 +144,16 @@ export async function getBgColor(selector: string): Promise<string> {

/**
* Update a file in the vrowzer virtual filesystem via the browser API.
* Triggers HMR in serve mode.
* Triggers HMR in serve mode. Resolves when the Workers have applied the change,
* without waiting for HMR.
*
* @param filename - Virtual path (e.g. '/App.vue', '/main.ts')
* @param content - Full new file content
*/
export async function updateFile(filename: string, content: string): Promise<void> {
await page.evaluate(
([path, code]) => {
;(window as any).__vrowzer__.updateFile(path, code)
return (window as any).__vrowzer__.updateFile(path, code)
},
[filename, content] as const
)
Expand All @@ -164,7 +165,7 @@ export async function updateFile(filename: string, content: string): Promise<voi
export async function addFile(filename: string, content: string): Promise<void> {
await page.evaluate(
([path, code]) => {
;(window as any).__vrowzer__.addFile(path, code)
return (window as any).__vrowzer__.addFile(path, code)
},
[filename, content] as const
)
Expand All @@ -175,7 +176,7 @@ export async function addFile(filename: string, content: string): Promise<void>
*/
export async function removeFile(filename: string): Promise<void> {
await page.evaluate(path => {
;(window as any).__vrowzer__.deleteFile(path)
return (window as any).__vrowzer__.deleteFile(path)
}, filename)
}

Expand Down
8 changes: 7 additions & 1 deletion packages/fs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,12 +136,18 @@ const publisher = createFileSystemPublisher([worker])

// node:fs-like API
publisher.writeFile('/main.js', 'export const x = 1') // text
publisher.writeFile('/image.png', arrayBuffer) // binary (zero-copy transfer)
publisher.writeFile('/image.png', arrayBuffer) // binary (each target gets its own copy)
publisher.unlink('/old-file.js')
publisher.mkdir('/new-dir')
publisher.initFiles({ '/main.js': 'code' }) // bulk init
```

`writeFile()` and `unlink()` accept an operation ID, which is set on the message. A Worker that applies such a message can acknowledge it with a `V_FS_ACK` message (`{ type: 'V_FS_ACK', id, error? }`), so that the main thread knows when the change is applied. `@vrowzer/fs` does not send acknowledgements by itself: the Worker that handles the messages decides when a change counts as applied.

```ts
publisher.writeFile('/main.js', 'export const x = 2', { id: crypto.randomUUID() })
```

#### Subscriber (Worker)

```ts
Expand Down
3 changes: 3 additions & 0 deletions packages/fs/docs/watcher/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Virtual filesystem watcher with Pub-Sub sync protocol.

| Variable | Description |
| ------ | ------ |
| [V_FS_ACK](/packages/fs/docs/watcher/variables/V_FS_ACK.md) | |
| [V_FS_INIT](/packages/fs/docs/watcher/variables/V_FS_INIT.md) | |
| [V_FS_MKDIR](/packages/fs/docs/watcher/variables/V_FS_MKDIR.md) | |
| [V_FS_UNLINK](/packages/fs/docs/watcher/variables/V_FS_UNLINK.md) | |
Expand All @@ -27,7 +28,9 @@ Virtual filesystem watcher with Pub-Sub sync protocol.
| [FileSystemInterfaces](/packages/fs/docs/watcher/interfaces/FileSystemInterfaces.md) | Minimal fs interface required by FileSystemSubscriber. |
| [FileSystemPublisher](/packages/fs/docs/watcher/interfaces/FileSystemPublisher.md) | Publisher for broadcasting filesystem operations to Workers. |
| [FileSystemPublisherTarget](/packages/fs/docs/watcher/interfaces/FileSystemPublisherTarget.md) | A postMessage target compatible like Service Worker and Web Worker APIs. |
| [FileSystemPublishOptions](/packages/fs/docs/watcher/interfaces/FileSystemPublishOptions.md) | Options for a single [FileSystemPublisher](/packages/fs/docs/watcher/interfaces/FileSystemPublisher.md) operation. |
| [FileSystemSubscriber](/packages/fs/docs/watcher/interfaces/FileSystemSubscriber.md) | Subscriber for processing filesystem sync messages in Workers. |
| [FSAckMessage](/packages/fs/docs/watcher/interfaces/FSAckMessage.md) | Worker -> Main Thread: Acknowledge a message that has an `id`. |
| [FSInitMessage](/packages/fs/docs/watcher/interfaces/FSInitMessage.md) | Main Thread -> Worker: Initialize files in bulk. Used during setup to populate the virtual filesystem. |
| [FSMkdirMessage](/packages/fs/docs/watcher/interfaces/FSMkdirMessage.md) | Main Thread -> Worker: Create a directory. |
| [FSUnlinkMessage](/packages/fs/docs/watcher/interfaces/FSUnlinkMessage.md) | Main Thread -> Worker: Delete a file. |
Expand Down
20 changes: 20 additions & 0 deletions packages/fs/docs/watcher/interfaces/FSAckMessage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Interface: FSAckMessage

Worker -> Main Thread: Acknowledge a message that has an `id`.

`@vrowzer/fs` does not send it by itself. A Worker sends it after applying the message,
with `error` when applying failed.

## Signature

```ts
export interface FSAckMessage
```

## Properties

| Name | Type | Description |
| --- | --- | --- |
| `error` _(optional)_ | `{ name: string; message: string }` | Why applying the message failed. Absent when it succeeded. |
| `id` | `string` | The `id` of the acknowledged message. |
| `type` | `"V_FS_ACK"` | |
1 change: 1 addition & 0 deletions packages/fs/docs/watcher/interfaces/FSUnlinkMessage.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ export interface FSUnlinkMessage

| Name | Type | Description |
| --- | --- | --- |
| `id` _(optional)_ | `string` | Operation ID. A Worker that applies the message can acknowledge it with [FSAckMessage](/packages/fs/docs/watcher/interfaces/FSAckMessage.md). |
| `path` | `string` | Path of the file to delete. Must not end with '/' (directories use FS_MKDIR with path ending in '/'). |
| `type` | `"V_FS_UNLINK"` | |
5 changes: 3 additions & 2 deletions packages/fs/docs/watcher/interfaces/FSWriteMessage.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ For text files:

For binary files:
{ type: 'V_FS_WRITE', path: '/image.png', encoding: 'binary', content: ArrayBuffer }
-> postMessage(message, [message.content]) // transfer list
-> postMessage(message, [message.content]) // transfer list, a copy for each target

## Signature

Expand All @@ -20,7 +20,8 @@ export interface FSWriteMessage

| Name | Type | Description |
| --- | --- | --- |
| `content` | `string \| ArrayBuffer` | Content of the file. Type depends on encoding: - 'text': UTF-8 string content - 'binary': ArrayBuffer content (transferred via postMessage's transfer list) |
| `content` | `string \| ArrayBuffer` | Content of the file. Type depends on encoding: - 'text': UTF-8 string content - 'binary': ArrayBuffer content (a copy transferred via postMessage's transfer list) |
| `encoding` | [`FSContentEncoding`](/packages/fs/docs/watcher/type-aliases/FSContentEncoding.md) | Encoding of the content. Determines how the Worker should interpret the content. |
| `id` _(optional)_ | `string` | Operation ID. A Worker that applies the message can acknowledge it with [FSAckMessage](/packages/fs/docs/watcher/interfaces/FSAckMessage.md). |
| `path` | `string` | Path of the file to write. Must not end with '/' (directories use FS_MKDIR with path ending in '/'). |
| `type` | `"V_FS_WRITE"` | |
15 changes: 15 additions & 0 deletions packages/fs/docs/watcher/interfaces/FileSystemPublishOptions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Interface: FileSystemPublishOptions

Options for a single [FileSystemPublisher](/packages/fs/docs/watcher/interfaces/FileSystemPublisher.md) operation.

## Signature

```ts
export interface FileSystemPublishOptions
```

## Properties

| Name | Type | Description |
| --- | --- | --- |
| `id` _(optional)_ | `string` | Operation ID set on the message, so that a Worker can acknowledge it with `V_FS_ACK`. |
8 changes: 5 additions & 3 deletions packages/fs/docs/watcher/interfaces/FileSystemPublisher.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ Remove a postMessage target.
### unlink()

```ts
unlink(path: string): void;
unlink(path: string, options?: FileSystemPublishOptions): void;
```

Delete a file.
Expand All @@ -109,6 +109,7 @@ Delete a file.
| Name | Type | Description |
| --- | --- | --- |
| `path` | `string` | Path of the file to delete. Must not end with '/' (directories use mkdir with path ending in '/'). |
| `options` | [`FileSystemPublishOptions`](/packages/fs/docs/watcher/interfaces/FileSystemPublishOptions.md) | Options for this operation, such as its ID _(optional)_ |

#### Returns

Expand All @@ -119,7 +120,7 @@ Delete a file.
### writeFile()

```ts
writeFile(path: string, content: string | ArrayBuffer): void;
writeFile(path: string, content: string | ArrayBuffer, options?: FileSystemPublishOptions): void;
```

Write a file. Encoding is inferred: string → text, ArrayBuffer → binary.
Expand All @@ -129,7 +130,8 @@ Write a file. Encoding is inferred: string → text, ArrayBuffer → binary.
| Name | Type | Description |
| --- | --- | --- |
| `path` | `string` | Path of the file to write. Must not end with '/' (directories use mkdir with path ending in '/'). |
| `content` | `string \| ArrayBuffer` | Content of the file. Type determines encoding: - string: UTF-8 text content - ArrayBuffer: binary content (transferred via postMessage's transfer list for zero-copy performance) |
| `content` | `string \| ArrayBuffer` | Content of the file. Type determines encoding: - string: UTF-8 text content - ArrayBuffer: binary content. Each target receives its own copy via postMessage's transfer list, so the caller's ArrayBuffer stays usable. |
| `options` | [`FileSystemPublishOptions`](/packages/fs/docs/watcher/interfaces/FileSystemPublishOptions.md) | Options for this operation, such as its ID _(optional)_ |

#### Returns

Expand Down
6 changes: 3 additions & 3 deletions packages/fs/docs/watcher/type-aliases/FSContentEncoding.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ File content encoding type.
- 'text': UTF-8 string content (JS, TS, JSON, CSS, HTML, etc.)
- 'binary': ArrayBuffer content (images, WASM, fonts, etc.)

When encoding is 'binary', the content is an ArrayBuffer
and MUST be transferred via postMessage's transfer list
for zero-copy performance.
When encoding is 'binary', the content is an ArrayBuffer.
The publisher transfers a copy of it to each target via postMessage's transfer list,
so the caller's ArrayBuffer stays usable.

## Signature

Expand Down
7 changes: 7 additions & 0 deletions packages/fs/docs/watcher/variables/V_FS_ACK.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Variable: V_FS_ACK

## Signature

```ts
export const V_FS_ACK = 'V_FS_ACK' as const
```
6 changes: 5 additions & 1 deletion packages/fs/src/watcher/protocol.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect, test } from 'vite-plus/test'
import { V_FS_INIT, V_FS_MKDIR, V_FS_UNLINK, V_FS_WRITE } from './protocol.ts'
import { V_FS_ACK, V_FS_INIT, V_FS_MKDIR, V_FS_UNLINK, V_FS_WRITE } from './protocol.ts'

describe('@vrowzer/fs/watcher protocol', () => {
test('V_FS_WRITE constant has correct value', () => {
Expand All @@ -17,4 +17,8 @@ describe('@vrowzer/fs/watcher protocol', () => {
test('V_FS_INIT constant has correct value', () => {
expect(V_FS_INIT).toBe('V_FS_INIT')
})

test('V_FS_ACK constant has correct value', () => {
expect(V_FS_ACK).toBe('V_FS_ACK')
})
})
40 changes: 35 additions & 5 deletions packages/fs/src/watcher/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
* - 'text': UTF-8 string content (JS, TS, JSON, CSS, HTML, etc.)
* - 'binary': ArrayBuffer content (images, WASM, fonts, etc.)
*
* When encoding is 'binary', the content is an ArrayBuffer
* and MUST be transferred via postMessage's transfer list
* for zero-copy performance.
* When encoding is 'binary', the content is an ArrayBuffer.
* The publisher transfers a copy of it to each target via postMessage's transfer list,
* so the caller's ArrayBuffer stays usable.
*/
export type FSContentEncoding = 'text' | 'binary'

Expand All @@ -32,7 +32,7 @@ export type FSContentEncoding = 'text' | 'binary'
*
* For binary files:
* { type: 'V_FS_WRITE', path: '/image.png', encoding: 'binary', content: ArrayBuffer }
* -> postMessage(message, [message.content]) // transfer list
* -> postMessage(message, [message.content]) // transfer list, a copy for each target
*/
export interface FSWriteMessage {
type: 'V_FS_WRITE'
Expand All @@ -47,9 +47,13 @@ export interface FSWriteMessage {
/**
* Content of the file. Type depends on encoding:
* - 'text': UTF-8 string content
* - 'binary': ArrayBuffer content (transferred via postMessage's transfer list)
* - 'binary': ArrayBuffer content (a copy transferred via postMessage's transfer list)
*/
content: string | ArrayBuffer
/**
* Operation ID. A Worker that applies the message can acknowledge it with {@link FSAckMessage}.
*/
id?: string
}

/**
Expand All @@ -61,6 +65,10 @@ export interface FSUnlinkMessage {
* Path of the file to delete. Must not end with '/' (directories use FS_MKDIR with path ending in '/').
*/
path: string
/**
* Operation ID. A Worker that applies the message can acknowledge it with {@link FSAckMessage}.
*/
id?: string
}

/**
Expand Down Expand Up @@ -99,8 +107,30 @@ export type FileSystemSyncMessage =
| FSMkdirMessage
| FSInitMessage

/**
* Worker -> Main Thread: Acknowledge a message that has an `id`.
*
* `@vrowzer/fs` does not send it by itself. A Worker sends it after applying the message,
* with `error` when applying failed.
*/
export interface FSAckMessage {
type: 'V_FS_ACK'
/**
* The `id` of the acknowledged message.
*/
id: string
/**
* Why applying the message failed. Absent when it succeeded.
*/
error?: {
name: string
message: string
}
}

// Constants
export const V_FS_WRITE = 'V_FS_WRITE' as const
export const V_FS_UNLINK = 'V_FS_UNLINK' as const
export const V_FS_MKDIR = 'V_FS_MKDIR' as const
export const V_FS_INIT = 'V_FS_INIT' as const
export const V_FS_ACK = 'V_FS_ACK' as const
Loading
Loading